📄 smplfile.c
字号:
/* TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp> Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA smplfile.c core and WAVE,AIFF/AIFF-C importer by Kentaro Sato <kentaro@ranvis.com>*/#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#include <stdlib.h>#include <math.h>#ifndef NO_STRING_H#include <string.h>#else#include <strings.h>#endif#include "timidity.h"#include "common.h"#include "controls.h"#include "filter.h"#include "instrum.h"#include "output.h"#include "playmidi.h"#include "resample.h"#include "tables.h"typedef int (*SampleImporterDiscriminateProc)(char *sample_file); /* returns 0 if file may be loadable */typedef int (*SampleImporterSampleLoaderProc)(char *sample_file, Instrument *inst); /* sets inst->samples, inst->sample and returns 0 if loaded */ /* inst is pre-allocated, and is freed by caller if loading failed */ /* -1 to let caller give up testing other importers */typedef struct { char *extension; /* file extension excluding '.' */ SampleImporterDiscriminateProc discriminant; SampleImporterSampleLoaderProc load; /* either extension or discriminant may be NULL */ int added; /* for get_importers()'s internal use */} SampleImporter, *SampleImporterRef;Instrument *extract_sample_file(char *sample_file);static int get_importers(const char *sample_file, int limit, SampleImporter **importers);static int get_next_importer(char *sample_file, int start, int count, SampleImporter **importers);static double ConvertFromIeeeExtended(const char *);static int import_wave_discriminant(char *sample_file);static int import_wave_load(char *sample_file, Instrument *inst);static int import_aiff_discriminant(char *sample_file);static int import_aiff_load(char *sample_file, Instrument *inst);static SampleImporter sample_importers[] = { {"wav", import_wave_discriminant, import_wave_load}, {"aiff", import_aiff_discriminant, import_aiff_load}, {NULL, NULL, NULL},};Instrument *extract_sample_file(char *sample_file){ Instrument *inst; SampleImporter *importers[10], *importer; int i, j, count, result; Sample *sample; if ((count = get_importers(sample_file, sizeof importers / sizeof importers[0], importers)) == 0) return NULL; inst = (Instrument *)safe_malloc(sizeof(Instrument)); inst->type = INST_PCM; inst->instname = NULL; inst->samples = 0; inst->sample = NULL; i = 0; importer = NULL; while ((i = get_next_importer(sample_file, i, count, importers)) < count) { if ((result = importers[i]->load(sample_file, inst)) == 0) { importer = importers[i]; break; } if (result == -1) /* importer told to give up test */ break; j = inst->samples; while(j > 0) { if (inst->sample[--j].data_alloced) free(inst->sample[j].data); } inst->samples = 0; free(inst->sample); inst->sample = NULL; i++; /* try next */ } if (importer == NULL) { free_instrument(inst); return NULL; } /* post-process */ if (inst->instname == NULL) { const char *name; name = pathsep_strrchr(sample_file); if (name == NULL) name = sample_file - 1; inst->instname = strdup(name + 1); } for(i = 0; i < inst->samples; i++) { sample = &inst->sample[i]; /* If necessary do some anti-aliasing filtering */ if (antialiasing_allowed) antialiasing((int16 *)sample->data, sample->data_length >> FRACTION_BITS, sample->sample_rate, play_mode->rate); /* resample it if possible */ if (sample->note_to_use && !(sample->modes & MODES_LOOPING)) pre_resample(sample);#ifdef LOOKUP_HACK squash_sample_16to8(sample);#endif } return inst;}#define ADD_IMPORTER importer->added = 1; \ importers[count++] = importer;/* returns number of importers which may be suitable for the file */static int get_importers(const char *sample_file, int limit, SampleImporter **importers){ SampleImporter *importer; int count; const char *extension; count = 0; importer = sample_importers; while(importer->load != NULL && count < limit) { importer->added = 0; importer++; } /* first, extension matched importers */ extension = pathsep_strrchr(sample_file); if (extension != NULL && (extension = strrchr(extension, '.')) != NULL) { extension++; /* ones which have discriminant first */ importer = sample_importers; while(importer->load != NULL && count < limit) { if (!importer->added && importer->extension != NULL && importer->discriminant != NULL && strcasecmp(extension, importer->extension) == 0) {ADD_IMPORTER} importer++; } /* then ones which don't have discriminant */ importer = sample_importers; while(importer->load != NULL && count < limit) { if (!importer->added && importer->extension != NULL && importer->discriminant == NULL && strcasecmp(extension, importer->extension) == 0) {ADD_IMPORTER} importer++; } } /* lastly, ones which has discriminant */ importer = sample_importers; while(importer->load != NULL && count < limit) { if (!importer->added && importer->discriminant != NULL) {ADD_IMPORTER} importer++; } return count;}/* returns importer index for the file *//* returns count if no importer available */static int get_next_importer(char *sample_file, int start, int count, SampleImporter **importers){ int i; for(i = start; i < count; i++) { if (importers[i]->discriminant != NULL) { if (importers[i]->discriminant(sample_file) != 0) continue; } return i; } return i;}/*************** Sample Importers ***************/#define MAX_SAMPLE_CHANNELS 16/* from instrum.c */#define READ_CHAR(thing) \ if (1 != tf_read(&tmpchar, 1, 1, tf)) goto fail; \ thing = tmpchar;#define READ_SHORT_LE(thing) \ if (1 != tf_read(&tmpshort, 2, 1, tf)) goto fail; \ thing = LE_SHORT(tmpshort);#define READ_LONG_LE(thing) \ if (1 != tf_read(&tmplong, 4, 1, tf)) goto fail; \ thing = LE_LONG(tmplong);#define READ_SHORT_BE(thing) \ if (1 != tf_read(&tmpshort, 2, 1, tf)) goto fail; \ thing = BE_SHORT(tmpshort);#define READ_LONG_BE(thing) \ if (1 != tf_read(&tmplong, 4, 1, tf)) goto fail; \ thing = BE_LONG(tmplong);const uint8 pan_mono[] = {64}; /* center */const uint8 pan_stereo[] = {1,127}; /* left,right */const uint8 pan_3ch[] = {1,127,64}; /* left,right,center*//* pannings below are set by guess *//*const uint8 pan_quad[] = {1,127,16,112};*/ /* front-left?,front-right?,rear-left?,rear-right? */const uint8 pan_4ch[] = {1,64,127,64}; /* left,center,right,surround?*/const uint8 pan_6ch[] = {1,32,64,127,95,64}; /* left,left-center?,center,right,right-center?,surround? */const uint8 *const gen_pan_list[6] = { pan_mono, pan_stereo, pan_3ch, pan_4ch, NULL, pan_6ch,};typedef struct { uint8 baseNote; int8 detune; uint8 lowNote; uint8 highNote; uint8 lowVelocity; uint8 highVelocity; int16 gain;} GeneralInstrumentInfo;static void initialize_sample(Instrument *inst, int frames, int sample_bits, int sample_rate);static void apply_GeneralInstrumentInfo(int samples, Sample *sample, const GeneralInstrumentInfo *info);/* read_sample_data() flags */#define SAMPLE_BIG_ENDIAN (1 << 0)#define SAMPLE_8BIT_UNSIGNED (1 << 1)static int read_sample_data(int32 flags, struct timidity_file *tf, int bits, int samples, int frames, sample_t **sdata);/*************** WAV Importer ***************/typedef struct { int16 wFormatTag; uint16 wChannels; uint32 dwSamplesPerSec; uint32 dwAvgBytesPerSec; uint16 wBlockAlign; uint16 wBitsPerSample;} WAVFormatChunk;typedef struct { int32 dwSamplePeriod; int32 dwMIDIUnityNote; uint32 dwMIDIPitchFraction; int hasLoop, loopType; int32 loop_dwStart, loop_dwEnd, loop_dwFraction;} WAVSamplerChunk;static int read_WAVFormatChunk(struct timidity_file *tf, WAVFormatChunk *fmt, int psize);static int read_WAVSamplerChunk(struct timidity_file *tf, WAVSamplerChunk *smpl, int psize);static int read_WAVInstrumentChunk(struct timidity_file *tf, GeneralInstrumentInfo *inst, int psize);static int import_wave_discriminant(char *sample_file){ struct timidity_file *tf; char buf[12]; if ((tf = open_file(sample_file, 1, OF_NORMAL)) == NULL) return 1; if (tf_read(buf, 12, 1, tf) != 1 || memcmp(&buf[0], "RIFF", 4) != 0 || memcmp(&buf[8], "WAVE", 4) != 0) { close_file(tf); return 1; } close_file(tf); return 0;}#define WAVE_CHUNKFLAG_SAMPLER (1 << 0)#define WAVE_CHUNKFLAG_INSTRUMENT (1 << 1)static int import_wave_load(char *sample_file, Instrument *inst){ struct timidity_file *tf; char buf[12]; int state; /* initial > fmt_read > data_read */ int i, chunk_size, type_index, type_size, samples; int32 chunk_flags; Sample *sample; WAVFormatChunk format; WAVSamplerChunk samplerc; GeneralInstrumentInfo instc; if ((tf = open_file(sample_file, 1, OF_NORMAL)) == NULL) return 1; if (tf_read(buf, 12, 1, tf) != 1 || memcmp(&buf[0], "RIFF", 4) != 0 || memcmp(&buf[8], "WAVE", 4) != 0) { close_file(tf); return 1; } ctl->cmsg(CMSG_INFO, VERB_NOISY, "Loading WAV: %s", sample_file); state = chunk_flags = 0; type_index = 4, type_size = 8; for(;;) { if (tf_read(&buf[type_index], type_size, 1, tf) != 1) break; chunk_size = LE_LONG(*(int32 *)&buf[4 + 4]); if (memcmp(&buf[4 + 0], "fmt ", 4) == 0) { if (state != 0 /* only one format chunk is required */ || chunk_size < 0x10) /* too small */ break; if (!read_WAVFormatChunk(tf, &format, chunk_size)) break; if (format.wChannels < 1 /* invalid range */ || format.wChannels > MAX_SAMPLE_CHANNELS || format.wFormatTag != 1 /* compressed */ || format.wBitsPerSample & 0x7 /* padding not supported */ || format.wBitsPerSample > 16) /* more than 16-bit is not supported */ break; state++; } else if (memcmp(&buf[4 + 0], "data", 4) == 0) { int frames; sample_t *sdata[MAX_SAMPLE_CHANNELS]; if (state != 1) break; frames = chunk_size / format.wBlockAlign; inst->samples = samples = format.wChannels; inst->sample = (Sample *)safe_malloc(sizeof(Sample) * samples); ctl->cmsg(CMSG_INFO, VERB_NOISY, "Format: %d-bits %dHz %dch, %d frames", format.wBitsPerSample, format.dwSamplesPerSec, samples, frames); initialize_sample(inst, frames, format.wBitsPerSample, format.dwSamplesPerSec); /* load waveform data */ for(i = 0; i < samples; i++) { inst->sample[i].data = sdata[i] = (sample_t *)safe_malloc(sizeof(sample_t) * frames); inst->sample[i].data_alloced = 1; } if (!read_sample_data(SAMPLE_8BIT_UNSIGNED, tf, format.wBitsPerSample, samples, frames, sdata)) break; state++; } else if (!(chunk_flags & WAVE_CHUNKFLAG_SAMPLER) && memcmp(&buf[4 + 0], "smpl", 4) == 0) { if (!read_WAVSamplerChunk(tf, &samplerc, chunk_size)) break; chunk_flags |= WAVE_CHUNKFLAG_SAMPLER; } else if (!(chunk_flags & WAVE_CHUNKFLAG_INSTRUMENT) && memcmp(&buf[4 + 0], "inst", 4) == 0) { if (!read_WAVInstrumentChunk(tf, &instc, chunk_size)) break; chunk_flags |= WAVE_CHUNKFLAG_INSTRUMENT; } else if (tf_seek(tf, chunk_size, SEEK_CUR) == -1) break; type_index = 4 - (chunk_size & 1); type_size = 8 + (chunk_size & 1); } close_file(tf); if (chunk_flags & WAVE_CHUNKFLAG_SAMPLER) { uint8 modes; int32 sample_rate, root_freq; uint32 loopStart, loopEnd; sample_rate = 1000000000 / samplerc.dwSamplePeriod; root_freq = freq_table[samplerc.dwMIDIUnityNote]; if (samplerc.dwMIDIPitchFraction != 0 && samplerc.dwMIDIUnityNote != 127) /* no table data */ { int32 diff; diff = freq_table[samplerc.dwMIDIUnityNote + 1] - root_freq; root_freq += (float)samplerc.dwMIDIPitchFraction * diff / 0xFFFFFFFF; } if (samplerc.hasLoop) { const uint8 loopModes[] = {MODES_LOOPING, MODES_LOOPING | MODES_PINGPONG, MODES_LOOPING | MODES_REVERSE}; modes = loopModes[samplerc.loopType]; loopStart = samplerc.loop_dwStart << FRACTION_BITS; loopEnd = samplerc.loop_dwEnd << FRACTION_BITS; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -