# /* Copyright (C) 2001 Stefan Westerfeld stefan@space.twc.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "artsmodulessynth.h" #include #include #include #include #include #include #include #include #include using namespace std; namespace Arts { namespace PatchLoader { typedef unsigned char byte; typedef unsigned short int word; typedef unsigned int dword; typedef char sbyte; typedef short int sword; typedef int sdword; static int pos = 0; static int apos = 0; inline void xRead(FILE *file, int len, void *data) { // printf("(0x%2x) - 0x%02x ... reading %d bytes\n",apos,pos,len); pos += len; apos += len; if(fread(data, len, 1, file) != 1) fprintf(stdout, "short read\n"); } inline void skip(FILE *file, int len) { // printf("(0x%2x) - 0x%02x ... skipping %d bytes\n",apos,pos,len); pos += len; apos += len; while(len > 0) { char junk; if(fread(&junk, 1, 1, file) != 1) fprintf(stdout, "short read\n"); len--; } } inline void readBytes(FILE *file, unsigned char *bytes, int len) { xRead(file, len, bytes); } inline void readString(FILE *file, char *str, int len) { xRead(file, len, str); } /* readXXX with sizeof(xxx) == 1 */ inline void readByte(FILE *file, byte& b) { xRead(file, 1, &b); } /* readXXX with sizeof(xxx) == 2 */ inline void readWord(FILE *file, word& w) { byte h, l; xRead(file, 1, &l); xRead(file, 1, &h); w = (h << 8) + l; } inline void readSWord(FILE *file, sword& sw) { word w; readWord(file, w); sw = (sword)w; } /* readXXX with sizeof(xxx) == 4 */ inline void readDWord(FILE *file, dword& dw) { byte h, l, hh, hl; xRead(file, 1, &l); xRead(file, 1, &h); xRead(file, 1, &hl); xRead(file, 1, &hh); dw = (hh << 24) + (hl << 16) + (h << 8) + l; } struct PatHeader { char id[12]; /* ID='GF1PATCH110' */ char manufacturer_id[10]; /* Manufacturer ID */ char description[60]; /* Description of the contained Instruments or copyright of manufacturer. */ byte instruments; /* Number of instruments in this patch */ byte voices; /* Number of voices for sample */ byte channels; /* Number of output channels (1=mono,2=stereo) */ word waveforms; /* Number of waveforms */ word mastervolume; /* Master volume for all samples */ dword size; /* Size of the following data */ char reserved[36]; /* reserved */ PatHeader(FILE *file) { readString(file, id, 12); readString(file, manufacturer_id, 10); readString(file, description, 60); /* skip(file, 2);*/ readByte(file, instruments); readByte(file, voices); readByte(file, channels); readWord(file, waveforms); readWord(file, mastervolume); readDWord(file, size); readString(file, reserved, 36); } }; struct PatInstrument { word number; char name[16]; dword size; /* Size of the whole instrument in bytes. */ byte layers; char reserved[40]; /* layer? */ word layerUnknown; dword layerSize; byte sampleCount; /* number of samples in this layer (?) */ char layerReserved[40]; PatInstrument(FILE *file) { readWord(file, number); readString(file, name, 16); readDWord(file, size); readByte(file, layers); readString(file, reserved, 40); /* layer: (?) */ readWord(file, layerUnknown); readDWord(file, layerSize); readByte(file, sampleCount); readString(file, reserved, 40); } }; struct PatPatch { char filename[7]; /* Wave file name */ byte fractions; /* Fractions */ dword wavesize; /* Wave size. Size of the wave digital data */ dword loopStart; dword loopEnd; word sampleRate; dword minFreq; dword maxFreq; dword origFreq; sword fineTune; byte balance; byte filterRate[6]; byte filterOffset[6]; byte tremoloSweep; byte tremoloRate; byte tremoloDepth; byte vibratoSweep; byte vibratoRate; byte vibratoDepth; byte waveFormat; sword freqScale; word freqScaleFactor; char reserved[36]; PatPatch(FILE *file) { readString(file, filename, 7); readByte(file, fractions); readDWord(file, wavesize); readDWord(file, loopStart); readDWord(file, loopEnd); readWord(file, sampleRate); readDWord(file, minFreq); readDWord(file, maxFreq); readDWord(file, origFreq); readSWord(file, fineTune); readByte(file, balance); readBytes(file, filterRate, 6); readBytes(file, filterOffset, 6); readByte(file, tremoloSweep); readByte(file, tremoloRate); readByte(file, tremoloDepth); readByte(file, vibratoSweep); readByte(file, vibratoRate); readByte(file, vibratoDepth); readByte(file, waveFormat); readSWord(file, freqScale); readWord(file, freqScaleFactor); readString(file, reserved, 36); } }; } class CachedPat : public CachedObject { protected: struct stat oldstat; string filename; bool initOk; long dataSize; CachedPat(Cache *cache, const string& filename); ~CachedPat(); public: struct Data { PatchLoader::PatPatch patch; mcopbyte *rawdata; Data(FILE *file) : patch(file) { rawdata = new mcopbyte[patch.wavesize]; fread(rawdata, 1, patch.wavesize, file); // sign conversion only for 16bit! if(patch.waveFormat & (1 << 1)) { for(unsigned int i = 1; i < patch.wavesize; i+=2) rawdata[i] ^= 0x80; } // unfold ping-pong loops if(patch.waveFormat & (1 << 3)) { int looplen = patch.loopEnd - patch.loopStart; arts_assert(looplen > 0); mcopbyte *newdata = new mcopbyte[patch.wavesize + looplen]; // copy head memcpy(&newdata[0], &rawdata[0], patch.loopStart + looplen); // 16 bit unfolding only: for(int i=0; i dList; static CachedPat *load(Cache *cache, const string& filename); /** * validity test for the cache - returns false if the object is having * reflecting the correct contents anymore (e.g. if the file on the * disk has changed), and there is no point in keeping it in the cache any * longer */ bool isValid(); /** * memory usage for the cache */ int memoryUsage(); }; CachedPat *CachedPat::load(Cache *cache, const string& filename) { CachedPat *pat; pat = (CachedPat *)cache->get(string("CachedPat:")+filename); if(!pat) { pat = new CachedPat(cache, filename); if(!pat->initOk) // loading failed { pat->decRef(); return 0; } } return pat; } bool CachedPat::isValid() { if(!initOk) return false; struct stat newstat; lstat(filename.c_str(),&newstat); return(newstat.st_mtime == oldstat.st_mtime); } int CachedPat::memoryUsage() { return dataSize; } CachedPat::CachedPat(Cache *cache, const string& filename) : CachedObject(cache), filename(filename), initOk(false), dataSize(0) { setKey(string("CachedPat:")+filename); if(lstat(filename.c_str(),&oldstat) == -1) { arts_info("CachedPat: Can't stat file '%s'", filename.c_str()); return; } FILE *patfile = fopen(filename.c_str(), "r"); if(patfile) { //PatchLoader::PatHeader header(patfile); PatchLoader::PatInstrument ins(patfile); for(int i=0;ipatch.wavesize; } fclose(patfile); arts_debug("loaded pat %s",filename.c_str()); arts_debug(" %d patches, datasize total is %d bytes", ins.sampleCount, dataSize); initOk = true; } } CachedPat::~CachedPat() { while(!dList.empty()) { delete dList.front(); dList.pop_front(); } } class Synth_PLAY_PAT_impl : virtual public Synth_PLAY_PAT_skel, virtual public StdSynthModule { protected: string _filename; CachedPat *pat; CachedPat::Data *selected; float fpos; public: Synth_PLAY_PAT_impl() { pat = 0; selected = 0; } void unload() { if(pat) { pat->decRef(); pat = 0; } } ~Synth_PLAY_PAT_impl() { unload(); } void streamStart() { fpos = 0.0; selected = 0; } void calculateBlock(unsigned long samples) { /* the normal offset is 60 60 = 12*5 so we scale with 2^5 = 32 */ float freq = frequency[0]; /* * 32.0 / 2.0; * why /2.0? */ int ifreq = (int)(freq * 1024.0); if(!selected && pat) { int bestdiff = 20000 * 1024; list::iterator i; for(i = pat->dList.begin(); i != pat->dList.end(); i++) { int diff = ::abs(double(ifreq - (*i)->patch.origFreq)); if(diff < bestdiff) { selected = *i; bestdiff = diff; } } /* drums */ if(selected && selected->patch.freqScaleFactor == 0) ifreq = selected->patch.origFreq; } if(selected) { const PatchLoader::PatPatch& patch = selected->patch; /* * thats just a *guess*, I have no idea how tuning actually * should work */ #if 0 float tonetune = float(pat.fineTune)/float(pat.freqScaleFactor); float tuning = pow(2.0, tonetune/12.0); freq *= tuning; #endif //printf("tonetune: %f\n",tonetune); //printf("tuning: %f\n",tuning); float step = double(patch.sampleRate)/samplingRateFloat * float(ifreq) / float(patch.origFreq); for(unsigned int i = 0; i < samples; i++) { // ASSUME 16bit signed, native byte order int ifpos = int(fpos)*2; // looped? bidi? backw? if((patch.waveFormat & ((1 << 2) + (1 << 3) + (1 << 4))) == (1 << 2)) { while(ifpos >= signed(patch.loopEnd)) { ifpos -= (patch.loopEnd - patch.loopStart); fpos -= (patch.loopEnd - patch.loopStart)/2; } } short int *x = (short int *)&selected->rawdata[ifpos]; float sample = (ifpos >= 0 && ifpos < signed(patch.wavesize))? x[0]/32768.0:0.0; float sample1 = ((ifpos+2) >= 0 && (ifpos+2) < signed(patch.wavesize))? x[1]/32768.0:0.0; float error = fpos - (int)fpos; outvalue[i] = sample * (1.0-error) + sample1 * error; fpos += step; } } else { for(unsigned int i = 0; i < samples; i++) outvalue[i] = 0.0; } } void clearDList() { selected = 0; } string filename() { return _filename; } void filename(const string& newFile) { if(newFile == _filename) return; unload(); pat = CachedPat::load(Cache::the(), newFile); _filename = newFile; filename_changed(newFile); } }; REGISTER_IMPLEMENTATION(Synth_PLAY_PAT_impl); }