我在我的QB应用程序中播放命令,如下所示:
PLAY "MSe8f#4f#8f#8g8a8b4.a4.g4.f#4.o0b8o1e8e8e4d8e2."
我想将这些转换成现代应用程序可以使用的东西.有什么想法吗?我正在搞乱FreeBasic中的应用程序.
您可以使用这样的工具(C代码)将Play字符串转换为WAV文件:
// file: play2wav.c #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <math.h> #ifndef M_PI #define M_PI 3.14159265358 #endif double Note2Freq(int Note) // Note=1 = C1 (32.7032 Hz),Note=84 = B7 (3951.07 Hz) { double f = 0; if (Note > 0) f = 440 * exp(log(2) * (Note - 46) / 12); return f; } int Name2SemitonesFromC(char c) { static const int semitonesFromC[7] = { 9,11,2,4,5,7 }; // A,B,C,D,E,F,G if (c < 'A' && c > 'G') return -1; return semitonesFromC[c - 'A']; } typedef struct tPlayer { enum { StateParsing,StateGenerating,} State; int Tempo; int Duration; int Octave; enum { ModeNormal,ModeLegato,ModeStaccato,} Mode; int Note; double NoteDuration; double NoteTime; unsigned SampleRate; } tPlayer; void PlayerInit(tPlayer* pPlayer,unsigned SampleRate) { pPlayer->State = StateParsing; pPlayer->Tempo = 120; // [32,255] quarter notes per minute pPlayer->Duration = 4; // [1,64] pPlayer->Octave = 4; // [0,6] pPlayer->Mode = ModeNormal; pPlayer->Note = 0; pPlayer->SampleRate = SampleRate; } int PlayerGetSample(tPlayer* pPlayer,const char** ppMusicString,short* pSample) { int number; int note = 0; int duration = 0; int dotCnt = 0; double sample; double freq; *pSample = 0; while (pPlayer->State == StateParsing) { char c = **ppMusicString; if (c == '\0') return 0; ++*ppMusicString; if (isspace(c)) continue; c = toupper(c); switch (c) { case 'O': c = **ppMusicString; if (c < '0' || c > '6') return 0; pPlayer->Octave = c - '0'; ++*ppMusicString; break; case '<': if (pPlayer->Octave > 0) pPlayer->Octave--; break; case '>': if (pPlayer->Octave < 6) pPlayer->Octave++; break; case 'M': c = toupper(**ppMusicString); switch (c) { case 'L': pPlayer->Mode = ModeLegato; break; case 'N': pPlayer->Mode = ModeNormal; break; case 'S': pPlayer->Mode = ModeStaccato; break; case 'B': case 'F': // skip MB and MF break; default: return 0; } ++*ppMusicString; break; // ML/MN/MS,MB/MF case 'L': case 'T': number = 0; for (;;) { char c2 = **ppMusicString; if (isdigit(c2)) { number = number * 10 + c2 - '0'; ++*ppMusicString; } else break; } switch (c) { case 'L': if (number < 1 || number > 64) return 0; pPlayer->Duration = number; break; case 'T': if (number < 32 || number > 255) return 0; pPlayer->Tempo = number; break; } break; // Ln/Tn case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'N': case 'P': switch (c) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': note = 1 + pPlayer->Octave * 12 + Name2SemitonesFromC(c); break; // A...G case 'P': note = 0; break; // P case 'N': number = 0; for (;;) { char c2 = **ppMusicString; if (isdigit(c2)) { number = number * 10 + c2 - '0'; ++*ppMusicString; } else break; } if (number < 0 || number > 84) return 0; note = number; break; // N } // got note # if (c >= 'A' && c <= 'G') { char c2 = **ppMusicString; if (c2 == '+' || c2 == '#') { if (note < 84) note++; ++*ppMusicString; } else if (c2 == '-') { if (note > 1) note--; ++*ppMusicString; } } // applied sharps and flats duration = pPlayer->Duration; if (c != 'N') { number = 0; for (;;) { char c2 = **ppMusicString; if (isdigit(c2)) { number = number * 10 + c2 - '0'; ++*ppMusicString; } else break; } if (number < 0 || number > 64) return 0; if (number > 0) duration = number; } // got note duration while (**ppMusicString == '.') { dotCnt++; ++*ppMusicString; } // got dots pPlayer->Note = note; pPlayer->NoteDuration = 1.0 / duration; while (dotCnt--) { duration *= 2; pPlayer->NoteDuration += 1.0 / duration; } pPlayer->NoteDuration *= 60 * 4. / pPlayer->Tempo; // in seconds now pPlayer->NoteTime = 0; pPlayer->State = StateGenerating; break; // A...G/N/P default: return 0; } // switch (c) } // pPlayer->State == StateGenerating // Calculate the next sample for the current note sample = 0; // QuickBasic Play() frequencies appear to be 1 octave higher than // on the piano. freq = Note2Freq(pPlayer->Note) * 2; if (freq > 0) { double f = freq; while (f < pPlayer->SampleRate / 2 && f < 8000) // Cap max frequency at 8 KHz { sample += exp(-0.125 * f / freq) * sin(2 * M_PI * f * pPlayer->NoteTime); f += 2 * freq; // Use only odd harmonics } sample *= 15000; sample *= exp(-pPlayer->NoteTime / 0.5); // Slow decay } if ((pPlayer->Mode == ModeNormal && pPlayer->NoteTime >= pPlayer->NoteDuration * 7 / 8) || (pPlayer->Mode == ModeStaccato && pPlayer->NoteTime >= pPlayer->NoteDuration * 3 / 4)) sample = 0; if (sample > 32767) sample = 32767; if (sample < -32767) sample = -32767; *pSample = (short)sample; pPlayer->NoteTime += 1.0 / pPlayer->SampleRate; if (pPlayer->NoteTime >= pPlayer->NoteDuration) pPlayer->State = StateParsing; return 1; } int PlayToFile(const char* pFileInName,const char* pFileOutName,unsigned SampleRate) { int err = EXIT_FAILURE; FILE *fileIn = NULL,*fileOut = NULL; tPlayer player; short sample; char* pMusicString = NULL; const char* p; size_t sz = 1,len = 0; char c; unsigned char uc; unsigned long sampleCnt = 0,us; if ((fileIn = fopen(pFileInName,"rb")) == NULL) { fprintf(stderr,"can't open file \"%s\"\n",pFileInName); goto End; } if ((fileOut = fopen(pFileOutName,"wb")) == NULL) { fprintf(stderr,"can't create file \"%s\"\n",pFileOutName); goto End; } if ((pMusicString = malloc(sz)) == NULL) { NoMemory: fprintf(stderr,"can't allocate memory\n"); goto End; } // Load the input file into pMusicString[] while (fread(&c,1,fileIn)) { pMusicString[len++] = c; if (len == sz) { char* p; sz *= 2; if (sz < len) goto NoMemory; p = realloc(pMusicString,sz); if (p == NULL) goto NoMemory; pMusicString = p; } } pMusicString[len] = '\0'; // Make pMusicString[] an ASCIIZ string // First,a dry run to simply count samples (needed for the WAV header) PlayerInit(&player,SampleRate); p = pMusicString; while (PlayerGetSample(&player,&p,&sample)) sampleCnt++; if (p != pMusicString + len) { fprintf(stderr,"Parsing error near byte %u: \"%c%c%c\"\n",(unsigned)(p - pMusicString),(p > pMusicString) ? p[-1] : ' ',p[0],(p - pMusicString + 1 < len) ? p[1] : ' '); goto End; } // Write the output file // ChunkID fwrite("RIFF",fileOut); // ChunkSize us = 36 + 2 * sampleCnt; uc = us % 256; fwrite(&uc,fileOut); uc = us / 256 % 256; fwrite(&uc,fileOut); uc = us / 256 / 256 % 256; fwrite(&uc,fileOut); uc = us / 256 / 256 / 256 % 256; fwrite(&uc,fileOut); // Format + Subchunk1ID fwrite("WAVEfmt ",8,fileOut); // Subchunk1Size uc = 16; fwrite(&uc,fileOut); uc = 0; fwrite(&uc,fileOut); fwrite(&uc,fileOut); // AudioFormat uc = 1; fwrite(&uc,fileOut); // NumChannels uc = 1; fwrite(&uc,fileOut); // SampleRate uc = SampleRate % 256; fwrite(&uc,fileOut); uc = SampleRate / 256 % 256; fwrite(&uc,fileOut); // ByteRate us = (unsigned long)SampleRate * 2; uc = us % 256; fwrite(&uc,fileOut); // BlockAlign uc = 2; fwrite(&uc,fileOut); // BitsPerSample uc = 16; fwrite(&uc,fileOut); // Subchunk2ID fwrite("data",fileOut); // Subchunk2Size us = sampleCnt * 2; uc = us % 256; fwrite(&uc,fileOut); // Data PlayerInit(&player,&sample)) { uc = (unsigned)sample % 256; fwrite(&uc,fileOut); uc = (unsigned)sample / 256 % 256; fwrite(&uc,fileOut); } err = EXIT_SUCCESS; End: if (pMusicString != NULL) free(pMusicString); if (fileOut != NULL) fclose(fileOut); if (fileIn != NULL) fclose(fileIn); return err; } int main(int argc,char** argv) { if (argc == 3) // return PlayToFile(argv[1],argv[2],44100); // Use this for 44100 sample rate return PlayToFile(argv[1],16000); printf("Usage:\n play2wav <Input-QBASIC-Play-String-file> <Output-Wav-file>\n"); return EXIT_FAILURE; }
用gcc编译:
gcc play2wav.c -o play2wav.exe
测试文件,JingleBells.txt:
t200l4o2mneel2el4eel2el4egl3cl8dl1el4ffl3fl8fl4fel2el8eel4edde l2dgl4eel2el4eel2el4egl3cl8dl1el4ffl3fl8fl4fel2el8efl4ggfdl2c
跑:
play2wav.exe JingleBells.txt JingleBells.wav
喜欢听JingleBells.wav!