ref: 1d506023c7b62d91572df862f7b116dc15661842
dir: /sfotool/main.c/
//--------------------------------------------// // SFOTool // // License: Public Domain (www.unlicense.org) // //--------------------------------------------// #include <stdio.h> #include <string.h> typedef char sfo_fourcc[4]; #define SFO_FourCCEquals(a, b) (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3]) struct sfo_riffchunk { sfo_fourcc id; unsigned int size; }; struct sfo_wavheader { char RIFF[4]; unsigned int ChunkSize; char WAVE[4], fmt[4]; unsigned int Subchunk1Size; unsigned short AudioFormat,NumOfChan; unsigned int SamplesPerSec, bytesPerSec; unsigned short blockAlign, bitsPerSample; char Subchunk2ID[4]; unsigned int Subchunk2Size; }; static void sfo_copy(FILE* src, FILE* trg, unsigned int size) { unsigned int block; unsigned char buf[512]; for (; size; size -= block) { block = (size > sizeof(buf) ? sizeof(buf) : size); fread(buf, 1, block, src); fwrite(buf, 1, block, trg); } } static int sfo_riffchunk_read(struct sfo_riffchunk* parent, struct sfo_riffchunk* chunk, FILE* f) { int is_riff, is_list; if (parent && sizeof(sfo_fourcc) + sizeof(unsigned int) > parent->size) return 0; if (!fread(&chunk->id, sizeof(sfo_fourcc), 1, f) || *chunk->id <= ' ' || *chunk->id >= 'z') return 0; if (!fread(&chunk->size, sizeof(unsigned int), 1, f)) return 0; if (parent && sizeof(sfo_fourcc) + sizeof(unsigned int) + chunk->size > parent->size) return 0; if (parent) parent->size -= sizeof(sfo_fourcc) + sizeof(unsigned int) + chunk->size; is_riff = SFO_FourCCEquals(chunk->id, "RIFF"), is_list = SFO_FourCCEquals(chunk->id, "LIST"); if (is_riff && parent) return 0; /* not allowed */ if (!is_riff && !is_list) return 1; /* custom type without sub type */ if (!fread(&chunk->id, sizeof(sfo_fourcc), 1, f) || *chunk->id <= ' ' || *chunk->id >= 'z') return 0; chunk->size -= sizeof(sfo_fourcc); return 1; } int main(int argc, const char** argv) { const char* arg_sf_in = (argc > 1 ? argv[1] : NULL); const char* arg_smpl = (argc > 2 ? argv[2] : NULL); const char* arg_sf_out = (argc > 3 ? argv[3] : NULL); char ext_sf_in = (arg_sf_in ? arg_sf_in [strlen(arg_sf_in)-1] | 0x20 : '\0'); char ext_smpl = (arg_smpl ? arg_smpl [strlen(arg_smpl)-1] | 0x20 : '\0'); char ext_sf_out = (arg_sf_out ? arg_sf_out[strlen(arg_sf_out)-1] | 0x20 : '\0'); struct sfo_riffchunk chunkHead, chunkList, chunk; FILE* f_sf_in = NULL, *f_smpl = NULL, *f_sf_out = NULL; if (argc < 2 || argc > 4) { print_usage: fprintf(stderr, "Usage Help:\n"); fprintf(stderr, "%s <SF2/SFO>: Show type of sample stream contained (PCM or OGG)\n", argv[0]); fprintf(stderr, "%s <SF2> <WAV>: Dump PCM sample stream to .WAV file\n", argv[0]); fprintf(stderr, "%s <SFO> <OGG>: Dump OGG sample stream to .OGG file\n", argv[0]); fprintf(stderr, "%s <SF2/SFO> <WAV> <SF2>: Write new .SF2 soundfont file using PCM sample stream from .WAV file\n", argv[0]); fprintf(stderr, "%s <SF2/SFO> <OGG> <SFO>: Write new .SFO soundfont file using OGG sample stream from .OGG file\n", argv[0]); if (f_sf_in) fclose(f_sf_in); if (f_smpl) fclose(f_smpl); if (f_sf_out) fclose(f_sf_out); return 1; } f_sf_in = fopen(arg_sf_in, "rb"); if (!f_sf_in) { fprintf(stderr, "Error: Passed input file '%s' does not exist\n\n", arg_sf_in); goto print_usage; } if (!sfo_riffchunk_read(NULL, &chunkHead, f_sf_in) || !SFO_FourCCEquals(chunkHead.id, "sfbk")) { fprintf(stderr, "Error: Passed input file '%s' is not a valid soundfont file\n\n", arg_sf_in); goto print_usage; } while (sfo_riffchunk_read(&chunkHead, &chunkList, f_sf_in)) { unsigned int pos_listsize = (unsigned int)ftell(f_sf_in) - 8; if (!SFO_FourCCEquals(chunkList.id, "sdta")) { fseek(f_sf_in, chunkList.size, SEEK_CUR); continue; } for (; sfo_riffchunk_read(&chunkList, &chunk, f_sf_in); fseek(f_sf_in, chunkList.size, SEEK_CUR)) { int is_pcm = SFO_FourCCEquals(chunk.id, "smpl"); if (!is_pcm && !SFO_FourCCEquals(chunk.id, "smpo")) continue; printf("Soundfont file '%s' contains a %s sample stream\n", arg_sf_in, (is_pcm ? "PCM" : "OGG")); if (ext_sf_in != '2' && ext_sf_in != 'o') printf(" Warning: Soundfont file has unknown file extension (should be .SF2 or .SFO)\n"); if (ext_sf_in == '2' && !is_pcm) printf(" Warning: Soundfont file has .SF%c extension but sample stream is %s\n", '2', "OGG (should be .SFO)"); if (ext_sf_in == 'o' && is_pcm) printf(" Warning: Soundfont file has .SF%c extension but sample stream is %s\n", 'O', "PCM (should be .SF2)"); if (arg_sf_out) { unsigned int pos_smpchunk, end_smpchunk, len_smpl, end_sf, len_list_in, len_list_out; printf("Writing file '%s' with samples from '%s'\n", arg_sf_out, arg_smpl); if (ext_sf_out != '2' && ext_sf_out != 'o') printf(" Warning: Soundfont file has unknown file extension (should be .SF2 or .SFO)\n"); if (ext_smpl != 'v' && ext_smpl != 'g') printf(" Warning: Sample file has unknown file extension (should be .WAV or .OGG)\n"); if (ext_sf_out == '2' && ext_smpl != 'v') printf(" Warning: Soundfont file has .SF%c extension but sample file is .%s\n", '2', "OGG"); if (ext_sf_out == 'o' && ext_smpl == 'v') printf(" Warning: Soundfont file has .SF%c extension but sample file is .%s\n", 'O', "WAV"); f_smpl = fopen(arg_smpl, "rb"); if (!f_smpl) { fprintf(stderr, "Error: Unable to open input file '%s'\n\n", arg_smpl); goto print_usage; } if (ext_smpl == 'v') { struct sfo_wavheader wav_hdr; fread(&wav_hdr, sizeof(wav_hdr), 1, f_smpl); if (!SFO_FourCCEquals(wav_hdr.Subchunk2ID, "data") || !SFO_FourCCEquals(wav_hdr.RIFF, "RIFF") || !SFO_FourCCEquals(wav_hdr.WAVE, "WAVE") || !SFO_FourCCEquals(wav_hdr.fmt, "fmt ") || wav_hdr.Subchunk1Size != 16 || wav_hdr.AudioFormat != 1 || wav_hdr.NumOfChan != 1 || wav_hdr.bytesPerSec != wav_hdr.SamplesPerSec * sizeof(short) || wav_hdr.bitsPerSample != sizeof(short) * 8) { fprintf(stderr, "Input .WAV file is not a valid raw PCM encoded wave file\n\n"); goto print_usage; } len_smpl = wav_hdr.Subchunk2Size; } else { fseek(f_smpl, 0, SEEK_END); len_smpl = (unsigned int)ftell(f_smpl); fseek(f_smpl, 0, SEEK_SET); } f_sf_out = fopen(arg_sf_out, "wb"); if (!f_sf_out) { fprintf(stderr, "Error: Unable to open output file '%s'\n\n", arg_sf_out); goto print_usage; } pos_smpchunk = (unsigned int)(ftell(f_sf_in) - sizeof(struct sfo_riffchunk)); end_smpchunk = pos_smpchunk + (unsigned int)sizeof(struct sfo_riffchunk) + chunk.size; fseek(f_sf_in, 0, SEEK_END); end_sf = (unsigned int)ftell(f_sf_in); /* Write data before list chunk size */ fseek(f_sf_in, 0, SEEK_SET); sfo_copy(f_sf_in, f_sf_out, pos_listsize); /* Write new list chunk size */ fread(&len_list_in, 4, 1, f_sf_in); len_list_out = len_list_in - chunk.size + len_smpl; fwrite(&len_list_out, 4, 1, f_sf_out); /* Write data until sample chunk */ sfo_copy(f_sf_in, f_sf_out, pos_smpchunk - pos_listsize - 4); /* Write sample chunk */ fwrite((ext_smpl == 'v' ? "smpl" : "smpo"), 4, 1, f_sf_out); fwrite(&len_smpl, 4, 1, f_sf_out); sfo_copy(f_smpl, f_sf_out, len_smpl); fclose(f_smpl); /* Write data after sample chunk */ fseek(f_sf_in, end_smpchunk, SEEK_SET); sfo_copy(f_sf_in, f_sf_out, end_sf - end_smpchunk); fclose(f_sf_out); } else if (arg_smpl) { f_smpl = fopen(arg_smpl, "wb"); printf("Writing file '%s' with %s sample stream\n", arg_smpl, (is_pcm ? "PCM" : "OGG")); if (ext_smpl != 'v' && ext_smpl != 'g') printf(" Warning: Sample file has unknown file extension (should be .WAV or .OGG)\n"); if (ext_smpl == 'v' && !is_pcm) printf(" Warning: Sample file has .%s extension but sample stream is %s\n", "WAV", "OGG (should be .OGG)"); if (ext_smpl == 'g' && is_pcm) printf(" Warning: Sample file has .%s extension but sample stream is %s\n", "OGG", "PCM (should be .WAV)"); if (!f_smpl) { fprintf(stderr, "Unable to open output file '%s'\n\n", arg_smpl); goto print_usage; } if (is_pcm) { struct sfo_wavheader wav_hdr; memcpy(wav_hdr.Subchunk2ID, "data", 4); memcpy(wav_hdr.RIFF, "RIFF", 4); memcpy(wav_hdr.WAVE, "WAVE", 4); memcpy(wav_hdr.fmt, "fmt ", 4); wav_hdr.Subchunk1Size = 16; wav_hdr.AudioFormat = 1; wav_hdr.NumOfChan = 1; wav_hdr.Subchunk2Size = (unsigned int)chunk.size; wav_hdr.ChunkSize = sizeof(wav_hdr) - 4 - 4 + wav_hdr.Subchunk2Size; wav_hdr.SamplesPerSec = 22050; wav_hdr.bytesPerSec = 22050 * sizeof(short); wav_hdr.blockAlign = 1 * sizeof(short); wav_hdr.bitsPerSample = sizeof(short) * 8; fwrite(&wav_hdr, sizeof(wav_hdr), 1, f_smpl); } sfo_copy(f_sf_in, f_smpl, chunk.size); fclose(f_smpl); printf("DONE\n"); } fclose(f_sf_in); return 0; } } fprintf(stderr, "Passed input file is not a valid soundfont file\n\n"); goto print_usage; }