prosperon/source/engine/thirdparty/TinySoundFont/sfotool/main.c

201 lines
9 KiB
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;
}