diff options
author | Eugene Sandulenko | 2004-10-21 22:37:37 +0000 |
---|---|---|
committer | Eugene Sandulenko | 2004-10-21 22:37:37 +0000 |
commit | 9d0b746aaaaf78cfcc18e866e4375ad1de533bc4 (patch) | |
tree | f6570ed9ff19e0111704e638c6eec19e0f454f15 /backends/midi/mt32 | |
parent | 9cb88f1d46388561457f44fb19dbe8d508ba14fc (diff) | |
download | scummvm-rg350-9d0b746aaaaf78cfcc18e866e4375ad1de533bc4.tar.gz scummvm-rg350-9d0b746aaaaf78cfcc18e866e4375ad1de533bc4.tar.bz2 scummvm-rg350-9d0b746aaaaf78cfcc18e866e4375ad1de533bc4.zip |
Patch #1048326 Better MT-32 support
svn-id: r15635
Diffstat (limited to 'backends/midi/mt32')
-rw-r--r-- | backends/midi/mt32/.cvsignore | 1 | ||||
-rw-r--r-- | backends/midi/mt32/freeverb.cpp | 356 | ||||
-rw-r--r-- | backends/midi/mt32/freeverb.h | 246 | ||||
-rw-r--r-- | backends/midi/mt32/mt32.cpp | 135 | ||||
-rw-r--r-- | backends/midi/mt32/partial.cpp | 591 | ||||
-rw-r--r-- | backends/midi/mt32/partial.h | 115 | ||||
-rw-r--r-- | backends/midi/mt32/structures.h | 780 | ||||
-rw-r--r-- | backends/midi/mt32/synth.cpp | 4564 | ||||
-rw-r--r-- | backends/midi/mt32/synth.h | 170 |
9 files changed, 6958 insertions, 0 deletions
diff --git a/backends/midi/mt32/.cvsignore b/backends/midi/mt32/.cvsignore new file mode 100644 index 0000000000..39a06683b7 --- /dev/null +++ b/backends/midi/mt32/.cvsignore @@ -0,0 +1 @@ +.deps diff --git a/backends/midi/mt32/freeverb.cpp b/backends/midi/mt32/freeverb.cpp new file mode 100644 index 0000000000..56fd5e4001 --- /dev/null +++ b/backends/midi/mt32/freeverb.cpp @@ -0,0 +1,356 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * Copyright (C) 2000 Jezar at Dreampoint + * + * This code is public domain + * + * Parts of this code are: + * + * 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. + * + * $Header$ + * + */ + +// Comb filter implementation +// +// Written by +// http://www.dreampoint.co.uk +// This code is public domain + +#include "stdafx.h" +#include "backends/midi/mt32/freeverb.h" + +comb::comb() +{ + filterstore = 0; + bufidx = 0; +} + +void comb::setbuffer(float *buf, int size) +{ + buffer = buf; + bufsize = size; +} + +void comb::mute() +{ + for (int i=0; i<bufsize; i++) + buffer[i]=0; +} + +void comb::setdamp(float val) +{ + damp1 = val; + damp2 = 1-val; +} + +float comb::getdamp() +{ + return damp1; +} + +void comb::setfeedback(float val) +{ + feedback = val; +} + +float comb::getfeedback() +{ + return feedback; +} + +// Allpass filter implementation + +allpass::allpass() +{ + bufidx = 0; +} + +void allpass::setbuffer(float *buf, int size) +{ + buffer = buf; + bufsize = size; +} + +void allpass::mute() +{ + for (int i=0; i<bufsize; i++) + buffer[i]=0; +} + +void allpass::setfeedback(float val) +{ + feedback = val; +} + +float allpass::getfeedback() +{ + return feedback; +} + +// Reverb model implementation + +revmodel::revmodel() +{ + // Tie the components to their buffers + combL[0].setbuffer(bufcombL1,combtuningL1); + combR[0].setbuffer(bufcombR1,combtuningR1); + combL[1].setbuffer(bufcombL2,combtuningL2); + combR[1].setbuffer(bufcombR2,combtuningR2); + combL[2].setbuffer(bufcombL3,combtuningL3); + combR[2].setbuffer(bufcombR3,combtuningR3); + combL[3].setbuffer(bufcombL4,combtuningL4); + combR[3].setbuffer(bufcombR4,combtuningR4); + combL[4].setbuffer(bufcombL5,combtuningL5); + combR[4].setbuffer(bufcombR5,combtuningR5); + combL[5].setbuffer(bufcombL6,combtuningL6); + combR[5].setbuffer(bufcombR6,combtuningR6); + combL[6].setbuffer(bufcombL7,combtuningL7); + combR[6].setbuffer(bufcombR7,combtuningR7); + combL[7].setbuffer(bufcombL8,combtuningL8); + combR[7].setbuffer(bufcombR8,combtuningR8); + allpassL[0].setbuffer(bufallpassL1,allpasstuningL1); + allpassR[0].setbuffer(bufallpassR1,allpasstuningR1); + allpassL[1].setbuffer(bufallpassL2,allpasstuningL2); + allpassR[1].setbuffer(bufallpassR2,allpasstuningR2); + allpassL[2].setbuffer(bufallpassL3,allpasstuningL3); + allpassR[2].setbuffer(bufallpassR3,allpasstuningR3); + allpassL[3].setbuffer(bufallpassL4,allpasstuningL4); + allpassR[3].setbuffer(bufallpassR4,allpasstuningR4); + + // Set default values + allpassL[0].setfeedback(0.5f); + allpassR[0].setfeedback(0.5f); + allpassL[1].setfeedback(0.5f); + allpassR[1].setfeedback(0.5f); + allpassL[2].setfeedback(0.5f); + allpassR[2].setfeedback(0.5f); + allpassL[3].setfeedback(0.5f); + allpassR[3].setfeedback(0.5f); + setwet(initialwet); + setroomsize(initialroom); + setdry(initialdry); + setdamp(initialdamp); + setwidth(initialwidth); + setmode(initialmode); + + // Buffer will be full of rubbish - so we MUST mute them + mute(); +} + +void revmodel::mute() +{ + int i; + + if (getmode() >= freezemode) + return; + + for (i=0;i<numcombs;i++) + { + combL[i].mute(); + combR[i].mute(); + } + for (i=0;i<numallpasses;i++) + { + allpassL[i].mute(); + allpassR[i].mute(); + } +} + +void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) +{ + float outL,outR,input; + + while(numsamples-- > 0) + { + int i; + + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(i=0; i<numcombs; i++) + { + outL += combL[i].process(input); + outR += combR[i].process(input); + } + + // Feed through allpasses in series + for(i=0; i<numallpasses; i++) + { + outL = allpassL[i].process(outL); + outR = allpassR[i].process(outR); + } + + // Calculate output REPLACING anything already there + *outputL = outL*wet1 + outR*wet2 + *inputL*dry; + *outputR = outR*wet1 + outL*wet2 + *inputR*dry; + + // Increment sample pointers, allowing for interleave (if any) + inputL += skip; + inputR += skip; + outputL += skip; + outputR += skip; + } +} + +void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) +{ + float outL,outR,input; + + while(numsamples-- > 0) + { + int i; + + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(i=0; i<numcombs; i++) + { + outL += combL[i].process(input); + outR += combR[i].process(input); + } + + // Feed through allpasses in series + for(i=0; i<numallpasses; i++) + { + outL = allpassL[i].process(outL); + outR = allpassR[i].process(outR); + } + + // Calculate output MIXING with anything already there + *outputL += outL*wet1 + outR*wet2 + *inputL*dry; + *outputR += outR*wet1 + outL*wet2 + *inputR*dry; + + // Increment sample pointers, allowing for interleave (if any) + inputL += skip; + inputR += skip; + outputL += skip; + outputR += skip; + } +} + +void revmodel::update() +{ +// Recalculate internal values after parameter change + + int i; + + wet1 = wet*(width/2 + 0.5f); + wet2 = wet*((1-width)/2); + + if (mode >= freezemode) + { + roomsize1 = 1; + damp1 = 0; + gain = muted; + } + else + { + roomsize1 = roomsize; + damp1 = damp; + gain = fixedgain; + } + + for(i=0; i<numcombs; i++) + { + combL[i].setfeedback(roomsize1); + combR[i].setfeedback(roomsize1); + } + + for(i=0; i<numcombs; i++) + { + combL[i].setdamp(damp1); + combR[i].setdamp(damp1); + } +} + +// The following get/set functions are not inlined, because +// speed is never an issue when calling them, and also +// because as you develop the reverb model, you may +// wish to take dynamic action when they are called. + +void revmodel::setroomsize(float value) +{ + roomsize = (value*scaleroom) + offsetroom; + update(); +} + +float revmodel::getroomsize() +{ + return (roomsize-offsetroom)/scaleroom; +} + +void revmodel::setdamp(float value) +{ + damp = value*scaledamp; + update(); +} + +float revmodel::getdamp() +{ + return damp/scaledamp; +} + +void revmodel::setwet(float value) +{ + wet = value*scalewet; + update(); +} + +float revmodel::getwet() +{ + return wet/scalewet; +} + +void revmodel::setdry(float value) +{ + dry = value*scaledry; +} + +float revmodel::getdry() +{ + return dry/scaledry; +} + +void revmodel::setwidth(float value) +{ + width = value; + update(); +} + +float revmodel::getwidth() +{ + return width; +} + +void revmodel::setmode(float value) +{ + mode = value; + update(); +} + +float revmodel::getmode() +{ + if (mode >= freezemode) + return 1; + else + return 0; +} + +//ends + diff --git a/backends/midi/mt32/freeverb.h b/backends/midi/mt32/freeverb.h new file mode 100644 index 0000000000..b0d32c8f3f --- /dev/null +++ b/backends/midi/mt32/freeverb.h @@ -0,0 +1,246 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * Copyright (C) 2000 Jezar at Dreampoint + * + * This code is public domain + * + * Parts of this code are: + * + * 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. + * + * $Header$ + * + */ + +// Macro for killing denormalled numbers +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// Based on IS_DENORMAL macro by Jon Watte +// This code is public domain + +#ifndef _freeverb_h_ +#define _freeverb_h_ + +#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f + +// Comb filter class declaration + +class comb +{ +public: + comb(); + void setbuffer(float *buf, int size); + inline float process(float inp); + void mute(); + void setdamp(float val); + float getdamp(); + void setfeedback(float val); + float getfeedback(); +private: + float feedback; + float filterstore; + float damp1; + float damp2; + float *buffer; + int bufsize; + int bufidx; +}; + + +// Big to inline - but crucial for speed + +inline float comb::process(float input) +{ + float output; + + output = buffer[bufidx]; + undenormalise(output); + + filterstore = (output*damp2) + (filterstore*damp1); + undenormalise(filterstore); + + buffer[bufidx] = input + (filterstore*feedback); + + if(++bufidx>=bufsize) bufidx = 0; + + return output; +} + +// Allpass filter declaration + +class allpass +{ +public: + allpass(); + void setbuffer(float *buf, int size); + inline float process(float inp); + void mute(); + void setfeedback(float val); + float getfeedback(); +// private: + float feedback; + float *buffer; + int bufsize; + int bufidx; +}; + + +// Big to inline - but crucial for speed + +inline float allpass::process(float input) +{ + float output; + float bufout; + + bufout = buffer[bufidx]; + undenormalise(bufout); + + output = -input + bufout; + buffer[bufidx] = input + (bufout*feedback); + + if(++bufidx>=bufsize) bufidx = 0; + + return output; +} + + +// Reverb model tuning values + +const int numcombs = 8; +const int numallpasses = 4; +const float muted = 0; +const float fixedgain = 0.015f; +const float scalewet = 3; +const float scaledry = 2; +const float scaledamp = 0.4f; +const float scaleroom = 0.28f; +const float offsetroom = 0.7f; +const float initialroom = 0.5f; +const float initialdamp = 0.5f; +const float initialwet = 1/scalewet; +const float initialdry = 0; +const float initialwidth = 1; +const float initialmode = 0; +const float freezemode = 0.5f; +const int stereospread = 23; + +// These values assume 44.1KHz sample rate +// they will probably be OK for 48KHz sample rate +// but would need scaling for 96KHz (or other) sample rates. +// The values were obtained by listening tests. +const int combtuningL1 = 1116; +const int combtuningR1 = 1116+stereospread; +const int combtuningL2 = 1188; +const int combtuningR2 = 1188+stereospread; +const int combtuningL3 = 1277; +const int combtuningR3 = 1277+stereospread; +const int combtuningL4 = 1356; +const int combtuningR4 = 1356+stereospread; +const int combtuningL5 = 1422; +const int combtuningR5 = 1422+stereospread; +const int combtuningL6 = 1491; +const int combtuningR6 = 1491+stereospread; +const int combtuningL7 = 1557; +const int combtuningR7 = 1557+stereospread; +const int combtuningL8 = 1617; +const int combtuningR8 = 1617+stereospread; +const int allpasstuningL1 = 556; +const int allpasstuningR1 = 556+stereospread; +const int allpasstuningL2 = 441; +const int allpasstuningR2 = 441+stereospread; +const int allpasstuningL3 = 341; +const int allpasstuningR3 = 341+stereospread; +const int allpasstuningL4 = 225; +const int allpasstuningR4 = 225+stereospread; + + +// Reverb model declaration + +class revmodel +{ +public: + revmodel(); + void mute(); + void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void setroomsize(float value); + float getroomsize(); + void setdamp(float value); + float getdamp(); + void setwet(float value); + float getwet(); + void setdry(float value); + float getdry(); + void setwidth(float value); + float getwidth(); + void setmode(float value); + float getmode(); +private: + void update(); +private: + float gain; + float roomsize,roomsize1; + float damp,damp1; + float wet,wet1,wet2; + float dry; + float width; + float mode; + + // The following are all declared inline + // to remove the need for dynamic allocation + // with its subsequent error-checking messiness + + // Comb filters + comb combL[numcombs]; + comb combR[numcombs]; + + // Allpass filters + allpass allpassL[numallpasses]; + allpass allpassR[numallpasses]; + + // Buffers for the combs + float bufcombL1[combtuningL1]; + float bufcombR1[combtuningR1]; + float bufcombL2[combtuningL2]; + float bufcombR2[combtuningR2]; + float bufcombL3[combtuningL3]; + float bufcombR3[combtuningR3]; + float bufcombL4[combtuningL4]; + float bufcombR4[combtuningR4]; + float bufcombL5[combtuningL5]; + float bufcombR5[combtuningR5]; + float bufcombL6[combtuningL6]; + float bufcombR6[combtuningR6]; + float bufcombL7[combtuningL7]; + float bufcombR7[combtuningR7]; + float bufcombL8[combtuningL8]; + float bufcombR8[combtuningR8]; + + // Buffers for the allpasses + float bufallpassL1[allpasstuningL1]; + float bufallpassR1[allpasstuningR1]; + float bufallpassL2[allpasstuningL2]; + float bufallpassR2[allpasstuningR2]; + float bufallpassL3[allpasstuningL3]; + float bufallpassR3[allpasstuningR3]; + float bufallpassL4[allpasstuningL4]; + float bufallpassR4[allpasstuningR4]; +}; + +#endif//_freeverb_h_ + +//ends + diff --git a/backends/midi/mt32/mt32.cpp b/backends/midi/mt32/mt32.cpp new file mode 100644 index 0000000000..cb32b6e5e9 --- /dev/null +++ b/backends/midi/mt32/mt32.cpp @@ -0,0 +1,135 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2004 The ScummVM project + * + * YM2612 tone generation code written by Tomoaki Hayasaka. + * Used under the terms of the GNU General Public License. + * Adpated to ScummVM by Jamieson Christian. + * + * 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. + * + * $Header$ + */ + +#include "backends/midi/emumidi.h" + +#include "common/util.h" +#include "common/file.h" + +#include "backends/midi/mt32/synth.h" + +class MidiDriver_MT32 : public MidiDriver_Emulated { +private: + CSynthMT32 *_synth; + + const char *rom_path; + +protected: + void generate_samples(int16 *buf, int len); + +public: + MidiDriver_MT32(SoundMixer *mixer, const char *path); + virtual ~MidiDriver_MT32(); + + int open(); + void close(); + void send(uint32 b); + uint32 property(int prop, uint32 param) { return 0; } + + void setPitchBendRange(byte channel, uint range) { } + void sysEx(byte *msg, uint16 length); + + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + + + // AudioStream API + bool isStereo() const { return true; } + int getRate() const { return 32000; } +}; + + +//////////////////////////////////////// +// +// MidiDriver_MT32 +// +//////////////////////////////////////// + + +MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer, const char *path) + : MidiDriver_Emulated(mixer) { + _synth = new CSynthMT32(); + rom_path = path; + File::addDefaultDirectory(path); +} + +MidiDriver_MT32::~MidiDriver_MT32() { + delete _synth; +} + +int MidiDriver_MT32::open() { + SynthProperties prop; + + if (_isOpen) + return MERR_ALREADY_OPEN; + + MidiDriver_Emulated::open(); + + prop.SampleRate = getRate(); // 32000; + prop.UseReverb = true; + prop.UseDefault = true; + //prop.RevType = 0; + //prop.RevTime = 5; + //prop.RevLevel = 3; + + _synth->ClassicOpen(rom_path, prop); + + _mixer->setupPremix(this); + + return 0; +} + +void MidiDriver_MT32::send(uint32 b) { + _synth->PlayMsg(b); +} + +void MidiDriver_MT32::sysEx(byte *msg, uint16 length) { + _synth->PlaySysex(msg, length); +} + +void MidiDriver_MT32::close() { + if (!_isOpen) + return; + _isOpen = false; + + // Detach the premix callback handler + _mixer->setupPremix(0); + + _synth->Close(); +} + +void MidiDriver_MT32::generate_samples(int16 *data, int len) { + _synth->MT32_CallBack((Bit8u *)data, len, _mixer->getMusicVolume()); +} + + +//////////////////////////////////////// +// +// MidiDriver_MT32 factory +// +//////////////////////////////////////// + +MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer, const char *path) { + return new MidiDriver_MT32(mixer, path); +} diff --git a/backends/midi/mt32/partial.cpp b/backends/midi/mt32/partial.cpp new file mode 100644 index 0000000000..363752ea7e --- /dev/null +++ b/backends/midi/mt32/partial.cpp @@ -0,0 +1,591 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * Based on Tristan's conversion of Canadacow's code + * + * 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. + * + * $Header$ + * + */ + +// Implementation of the MT-32 partial class + +#include "backends/midi/mt32/synth.h" +#include "backends/midi/mt32/partial.h" + +INLINE void CPartialMT32::generateSamples(Bit16s * partialBuf, long length) { + if (!isActive) return; + if (alreadyOutputed) return; + + + alreadyOutputed = true; + + // Generate samples + + int r; + int i; + Bit32s envval, ampval, filtval; + soundaddr *pOff = &partCache->partialOff; + int noteval = partCache->keyedval; + for(i=0;i<length;i++) { + Bit32s ptemp = 0; + + if(partCache->envs[AMPENV].sustaining) { + ampval = partCache->ampEnvCache; + } else { + if(partCache->envs[AMPENV].count<=0) { + + ampval = getAmpEnvelope(partCache,tmppoly); + isActive = partCache->playPartial; +//TODO: check what is going on here +// if (ampval < 0) ampval = 0; + //printf("%d %d\n", (int)ampval, (int)isActive); + if(!isActive) { + tmppoly->partActive[timbreNum] = false; + tmppoly->isActive = tmppoly->partActive[0] || tmppoly->partActive[1] || tmppoly->partActive[2] || tmppoly->partActive[3]; + } + if(ampval>=128) ampval = 127; + + ampval = amptable[ampval]; + int tmpvel = tmppoly->vel; + if(tcache->ampenvdir==1) tmpvel = 127-tmpvel; + ampval = (ampval * ampveltable[tmpvel][(int)tcache->ampEnv.velosens]) >> 8; + //if(partCache->envs[AMPENV].sustaining) + partCache->ampEnvCache = ampval; + } else { + ampval = partCache->ampEnvCache; + } + --partCache->envs[AMPENV].count; + } + + int delta = 0x10707; + + // Calculate Pitch envelope + int lfoat = 0x1000; + int pdep; + if(partCache->pitchsustain) { + // Calculate LFO position + // LFO does not kick in completely until pitch envelope sustains + + if(tcache->lfodepth>0) { + partCache->lfopos++; + + if(partCache->lfopos>=tcache->lfoperiod) partCache->lfopos = 0; + int lfoatm = (partCache->lfopos << 16) / tcache->lfoperiod; + + int lfoatr = sintable[lfoatm]; + + lfoat = lfoptable[tcache->lfodepth][lfoatr]; + } + pdep = partCache->pitchEnvCache; + + + } else { + envval = getPitchEnvelope(partCache,tmppoly); + int pd=tcache->pitchEnv.depth; + pdep = penvtable[pd][envval]; + if(partCache->pitchsustain) partCache->pitchEnvCache = pdep; + + } + + + // Get waveform - either PCM or synthesized sawtooth or square + + + if (tcache->PCMPartial) { + // PCM partial + + if(!partCache->PCMDone) { + + int addr,len,tmppcm; + partialTable *tPCM = &tcache->convPCM; + + if(tPCM->aggSound==-1) { + delta = wavtabler[tPCM->pcmnum][noteval]; + addr = tPCM->addr; + len = tPCM->len; + + } else { + tmppcm = LoopPatterns[tPCM->aggSound][partCache->looppos]; + addr = PCM[tmppcm].addr; + len = PCM[tmppcm].len; + delta = looptabler[tPCM->aggSound][partCache->looppos][noteval]; + } + + if(ampval>0) { + int ra,rb,dist; + int taddr; + if(delta<0x10000) { + // Linear sound interpolation + + taddr = addr + pOff->pcmoffs.pcmplace; + ra = romfile[taddr]; + rb = romfile[taddr+1]; + + dist = rb-ra; + r = (ra + ((dist * (Bit32s)(pOff->pcmoffs.pcmoffset>>8)) >>8)); + + } else { + + //r = romfile[addr + pOff->pcmoffs.pcmplace]; + // Sound decimation + // The right way to do it is to use a lowpass filter on the waveform before selecting + // a point. This is too slow. The following appoximates this as fast as possible + int idelta = delta >> 16; + int ix; + taddr = addr + pOff->pcmoffs.pcmplace; + ra = 0; + for(ix=0;ix<idelta;ix++) { + ra += romfile[taddr++]; + } + r = ra / idelta; + + + + } + } else r = 0; + + ptemp = r; + + if ((pOff->pcmoffs.pcmplace) >= len) { + if(tPCM->aggSound==-1) { + if(tPCM->loop) { + pOff->pcmabs = 0; + } else { + partCache->PCMDone = true; + partCache->playPartial = false; + } + + } else { + partCache->looppos++; + if(LoopPatterns[tPCM->aggSound][partCache->looppos]==-1) partCache->looppos=0; + pOff->pcmabs = 0; + } + //LOG_MSG("tPCM %d loops %d done %d playPart %d", tPCM->pcmnum, tPCM->loop, partCache->PCMDone, partCache->playPartial); + + } + + + } + + } else { + // Synthesis partial + int divis, hdivis, ofs, ofs3, toff; + int minorplace; + + int wf = tcache->waveform ; + + divis = divtable[noteval]>>15; + + if(pOff->pcmoffs.pcmplace>=divis) pOff->pcmoffs.pcmplace = (Bit16u)(pOff->pcmoffs.pcmplace-divis); + + toff = pOff->pcmoffs.pcmplace; + minorplace = pOff->pcmoffs.pcmoffset >> 14; + + int pa, pb; + + if(ampval>0) { + + filtval = getFiltEnvelope((Bit16s)ptemp,partCache,tmppoly); + + //LOG_MSG("Filtval: %d", filtval); + + if(wf==0) { + // Square waveform. Made by combining two pregenerated bandlimited + // sawtooth waveforms + // Pulse width is not yet correct + + hdivis = divis >> 1; + int divmark = smalldivtable[noteval]; + //int pw = (tcache->pulsewidth * pulsemod[filtval]) >> 8; + + + ofs = toff % (hdivis); + + ofs3 = toff + ((divmark*pulsetable[partCache->pulsewidth])>>16); + ofs3 = ofs3 % (hdivis); + + pa = waveforms[1][noteval][(ofs<<2)+minorplace]; + pb = waveforms[0][noteval][(ofs3<<2)+minorplace]; + //ptemp = pa+pb+pulseoffset[tcache->pulsewidth]; + ptemp = (pa+pb)*4; + + // Non-bandlimited squarewave + /* + ofs = (divis*pulsetable[tcache->pulsewidth])>>8; + if(toff < ofs) { + ptemp = 1 * WGAMP; + } else { + ptemp = -1 * WGAMP; + }*/ + + + } else { + // Sawtooth. Made by combining the full cosine and half cosine according + // to how it looks on the MT-32. What it really does it takes the + // square wave and multiplies it by a full cosine + // TODO: This area here crashes DosBox due to read overflow + int offsetpos = (toff<<2)+minorplace; + //int a = 0; + if(toff < sawtable[noteval][partCache->pulsewidth]) { + while(offsetpos>waveformsize[2][noteval]) { + offsetpos-=waveformsize[2][noteval]; + } + ptemp = waveforms[2][noteval][offsetpos]; + } else { + while(offsetpos>waveformsize[3][noteval]) { + offsetpos-=waveformsize[3][noteval]; + } + ptemp = waveforms[3][noteval][offsetpos]; + } + ptemp = ptemp *4; + + // ptemp = (int)(sin((double)toff / 100.0) * 100.0); + //ptemp = pa; + + // This is the correct way + // Seems slow to me (though bandlimited) -- doesn't seem to + // sound any better though + /* + hdivis = divis >> 1; + int divmark = smalldivtable[noteval]; + //int pw = (tcache->pulsewidth * pulsemod[filtval]) >> 8; + + + ofs = toff % (hdivis); + + ofs3 = toff + ((divmark*pulsetable[tcache->pulsewidth])>>16); + ofs3 = ofs3 % (hdivis); + + pa = waveforms[0][noteval][ofs]; + pb = waveforms[1][noteval][ofs3]; + ptemp = ((pa+pb) * waveforms[3][noteval][toff]) / WGAMP; + ptemp = ptemp *4; + */ + + + + } + + //Very exact filter + //ptemp[t] = (int)iir_filter((float)ptemp[t],&partCache->history[t],filtcoeff[filtval][tcache->filtEnv.resonance]); + if(filtval>((FILTERGRAN*15)/16)) filtval = ((FILTERGRAN*15)/16); + ptemp = (Bit32s)(usefilter)((float)ptemp,&partCache->history[0],filtcoeff[filtval][(int)tcache->filtEnv.resonance], tcache->filtEnv.resonance); + } else ptemp = 0; + + //ptemp[t] = Moog1(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance); + //ptemp[t] = Moog2(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance); + //ptemp[t] = simpleLowpass(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance); + + // Use this to mute analogue synthesis + // ptemp = 0; + + + } + + // Build delta for position of next sample + /* + delta = (delta * tcache->fineshift)>>12; + delta = (delta * pdep)>>12; + delta = (delta * lfoat)>>12; + if(tcache->useBender) delta = (delta * *tmppoly->bendptr)>>12; + */ + + // Fix delta code + __int64 tdelta = (__int64)delta; + tdelta = (tdelta * tcache->fineshift)>>12; + tdelta = (tdelta * pdep)>>12; + tdelta = (tdelta * lfoat)>>12; + if(tcache->useBender) tdelta = (tdelta * *tmppoly->bendptr)>>12; + + // Add calculated delta to our waveform offset + pOff->pcmabs+=(int)tdelta; + + // Put volume envelope over generated sample + ptemp = (ptemp * ampval) >> 9; + ptemp = (ptemp * *tmppoly->volumeptr) >> 7; + + partCache->envs[AMPENV].envpos++; + partCache->envs[PITCHENV].envpos++; + partCache->envs[FILTENV].envpos++; + + *partialBuf++ = (Bit16s)ptemp; + } + + +} + +INLINE void CPartialMT32::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { + // Early exit if no need to mix + if(tibrePair==NULL) return; + +#if USE_MMX == 0 + int i; + for(i=0;i<len;i++) { + Bit32s tmp1 = buf1[i]; + Bit32s tmp2 = buf2[i]; + tmp1 += tmp2; + buf1[i] = (Bit16s)tmp1; + } +#else + len = (len>>2)+4; +#ifdef I_ASM + __asm { + mov ecx, len + mov esi, buf1 + mov edi, buf2 + +mixloop1: + movq mm1, [edi] + movq mm2, [esi] + paddw mm1,mm2 + movq [esi],mm1 + add edi,8 + add esi,8 + + dec ecx + cmp ecx,0 + jg mixloop1 + emms + } +#else + atti386_mixBuffers(buf1, buf2, len); +#endif +#endif +} + +INLINE void CPartialMT32::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) { +#if USE_MMX != 2 + int i; + for(i=0;i<len;i++) { + float a, b; + a = ((float)buf1[i]) / 8192.0; + b = ((float)buf2[i]) / 8192.0; + a = (a * b) + a; + if(a>1.0) a = 1.0; + if(a<-1.0) a = -1.0; + buf1[i] = (Bit16s)(a * 8192.0); + + //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i]; + } +#else + len = (len>>2)+4; +#ifdef I_ASM + __asm { + mov ecx, len + mov esi, buf1 + mov edi, buf2 + +mixloop2: + movq mm1, [esi] + movq mm2, [edi] + movq mm3, mm1 + pmulhw mm1, mm2 + paddw mm1,mm3 + movq [esi],mm1 + add edi,8 + add esi,8 + + dec ecx + cmp ecx,0 + jg mixloop2 + emms + } +#else + atti386_mixBuffersRingMix(buf1, buf2, len); +#endif +#endif +} + +INLINE void CPartialMT32::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) { +#if USE_MMX != 2 + int i; + for(i=0;i<len;i++) { + float a, b; + a = ((float)buf1[i]) / 8192.0; + b = ((float)buf2[i]) / 8192.0; + a *= b; + if(a>1.0) a = 1.0; + if(a<-1.0) a = -1.0; + buf1[i] = (Bit16s)(a * 8192.0); + //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10); + } +#else + len = (len>>2)+4; +#ifdef I_ASM + __asm { + mov ecx, len + mov esi, buf1 + mov edi, buf2 + +mixloop3: + movq mm1, [esi] + movq mm2, [edi] + pmulhw mm1, mm2 + movq [esi],mm1 + add edi,8 + add esi,8 + + dec ecx + cmp ecx,0 + jg mixloop3 + emms + } +#else + atti386_mixBuffersRing(buf1, buf2, len); +#endif +#endif +} + +INLINE void CPartialMT32::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) { + int i,m; + m=0; + for(i=0;i<len;i++) { + *outBuf++ = (*buf1); + buf1++; + *outBuf++ = (*buf2); + buf2++; + } + +} + +bool CPartialMT32::produceOutput(Bit16s * partialBuf, long length) { + if (!isActive) return false; + if (alreadyOutputed) return false; + int i; + + //alreadyOutputed = true; + + memset(&pairBuffer[0],0,length*4); + memset(&myBuffer[0],0,length*4); + // Check for dependant partial + if(tibrePair != NULL) { + if ((tibrePair->ownerChan == this->ownerChan) && (!tibrePair->alreadyOutputed)) { + tibrePair->generateSamples(pairBuffer,length); + } + } else { + if((useMix!=0) && (useMix != 3)){ + // Generate noise for parialless ring mix + for(i=0;i<length;i++) pairBuffer[i] = smallnoise[i] << 2; + } + } + + generateSamples(myBuffer, length); +/* FILE *fo = fopen("/tmp/samp.raw", "a"); + for(i = 0; i < length; i++) + fwrite(myBuffer + i, 1, 2, fo); + fclose(fo); +*/ + Bit16s * p1buf, * p2buf; + + if((partNum==0) || ((partNum==1) && (tibrePair==NULL))) { + p1buf = &myBuffer[0]; + p2buf = &pairBuffer[0]; + } else { + p2buf = &myBuffer[0]; + p1buf = &pairBuffer[0]; + } + +// LOG_MSG("useMix: %d", useMix); + + switch(useMix) { + case 0: + // Standard sound mix + mixBuffers(p1buf, p2buf, length); + break; + case 1: + // Ring modulation with sound mix + mixBuffersRingMix(p1buf, p2buf, length); + break; + case 2: + // Ring modulation alone + mixBuffersRing(p1buf, p2buf, length); + break; + case 3: + // Stereo mixing. One partial to one channel, one to another. + mixBuffersStereo(p1buf, p2buf, partialBuf, length); + return true; + break; + default: + mixBuffers(p1buf, p2buf, length); + break; + } + + + int m; + m = 0; + Bit16s leftvol, rightvol; + if (!tmppoly->isRy) { + leftvol = tmppoly->pansetptr->leftvol; + rightvol = tmppoly->pansetptr->rightvol; + } else { + leftvol = (Bit16s)drumPan[tmppoly->pcmnum][0]; + rightvol = (Bit16s)drumPan[tmppoly->pcmnum][1]; + } + +#if USE_MMX == 0 + for(i=0;i<length;i++) { + partialBuf[m] = (Bit16s)(((Bit32s)p1buf[i] * (Bit32s)leftvol) >> 16); + m++; + partialBuf[m] = (Bit16s)(((Bit32s)p1buf[i] * (Bit32s)rightvol) >> 16); + m++; + } +#else + long quadlen = (length >> 1)+2; +#ifdef I_ASM + __asm { + mov ecx,quadlen + mov ax, leftvol + shl eax,16 + mov ax, rightvol + movd mm1, eax + movd mm2, eax + psllq mm1, 32 + por mm1, mm2 + mov edi, partialBuf + mov esi, p1buf +mmxloop1: + mov bx, [esi] + add esi,2 + mov dx, [esi] + add esi,2 + + mov ax, dx + shl eax, 16 + mov ax, dx + movd mm2,eax + psllq mm2, 32 + mov ax, bx + shl eax, 16 + mov ax, bx + movd mm3,eax + por mm2,mm3 + + pmulhw mm2, mm1 + movq [edi], mm2 + add edi, 8 + + dec ecx + cmp ecx,0 + jg mmxloop1 + emms + } +#else + atti386_PartProductOutput(quadlen, leftvol, rightvol, partialBuf, p1buf); +#endif +#endif + + return true; +} diff --git a/backends/midi/mt32/partial.h b/backends/midi/mt32/partial.h new file mode 100644 index 0000000000..769284abf0 --- /dev/null +++ b/backends/midi/mt32/partial.h @@ -0,0 +1,115 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * Based on Tristan's conversion of Canadacow's code + * + * 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. + * + * $Header$ + * + */ + +#ifndef __CPARTIALMT32_H__ +#define __CPARTIALMT32_H__ + +#include "backends/midi/mt32/structures.h" + +// Class definition of MT-32 partials. 32 in all. +class CPartialMT32 { +private: + int useMix; + int partNum; + + int pN; + + + + + Bit16s myBuffer[2048]; + // For temporary output of paired buffer + Bit16s pairBuffer[2048]; + + void mixBuffers(Bit16s * buf1, Bit16s * buf2, int len); + void mixBuffersRingMix(Bit16s * buf1, Bit16s * buf2, int len); + void mixBuffersRing(Bit16s * buf1, Bit16s * buf2, int len); + void mixBuffersStereo(Bit16s * buf1, Bit16s * buf2, Bit16s * outBuf, int len); + + +public: + patchCache *tcache; + patchCache cachebackup[4]; + + //FILE *fp; + //FILE *fp2; + + dpoly::partialStatus *partCache; + + CPartialMT32 *tibrePair; + bool isActive; + bool alreadyOutputed; + int ownerChan; + Bit64s age; + int timbreNum; + dpoly *tmppoly; + + CPartialMT32(int partialNum) { + + isActive = false; + pN = partialNum; + + /* + sprintf(buffer, "partial%d.raw",pN); + fp = fopen(buffer,"wb"); + + sprintf(buffer, "partial%dx.raw",pN); + fp2 = fopen(buffer,"wb"); + */ + + + }; + + void startPartial(dpoly *usePoly, patchCache *useCache, dpoly::partialStatus * usePart, CPartialMT32 * pairPart, int mixType, int num, int ownChan, int timNum) { + + //LOG_MSG("Starting partial %d for %d", num, ownChan); + tmppoly = usePoly; + tcache = useCache; + partCache = usePart; + tibrePair = pairPart; + isActive = true; + useMix = mixType; + partNum = num; + age = 0; + ownerChan = ownChan; + alreadyOutputed = false; + timbreNum = timNum; + memset(usePart->history,0,sizeof(usePart->history)); + + } + + void stopPartial(void) { isActive = false; } + + + // Returns true only if data written to buffer + // This function (unline the one below it) returns processed stereo samples + // made from combining this single partial with its pair, if it has one. + bool produceOutput(Bit16s * partialBuf, long length); + + // This function produces mono sample output of the specific partial + void generateSamples(Bit16s * partialBuf, long length); + +}; + + +#endif + diff --git a/backends/midi/mt32/structures.h b/backends/midi/mt32/structures.h new file mode 100644 index 0000000000..7caf10cec0 --- /dev/null +++ b/backends/midi/mt32/structures.h @@ -0,0 +1,780 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * Based on Tristan's conversion of Canadacow's code + * + * 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. + * + * $Header$ + * + */ + +#if !defined __MT32STRUCTURES_H__ +#define __MT32STRUCTURES_H__ + +#include "stdafx.h" +#include "common/scummsys.h" + +#if defined(_MSC_VER) +typedef unsigned __int64 Bit64u; +typedef signed __int64 Bit64s; +#else +typedef unsigned long long Bit64u; +typedef signed long long Bit64s; +#endif +typedef unsigned int Bit32u; +typedef signed int Bit32s; +typedef unsigned short int Bit16u; +typedef signed short int Bit16s; +typedef unsigned char Bit8u; +typedef signed char Bit8s; + +// The occurences of __int64 should be changed to Bit64s +#define __int64 Bit64u + +#define INLINE + + +static inline void LOG_MSG(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fprintf(stdout, "\n"); + fflush(stdout); +} + +#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW__)) + +#define ALIGN_PACKED + +#else + +//#define ALIGN_PACKED __attribute__ ((__packed__)) +#define ALIGN_PACKED __attribute__ ((aligned (1))) + +#ifdef HAVE_X86 +#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value)) +#define cpuid_flag (1 << 21) + +static inline bool atti386_DetectCPUID() +{ + unsigned int result; + + /* is there a cpuid */ + result = cpuid_flag; /* set test */ + eflag(result); + if (!(result & cpuid_flag)) + return false; + + result = 0; /* clear test */ + eflag(result); + if (result & cpuid_flag) + return false; + + return true; +} + +static inline bool atti386_DetectSIMD() +{ + unsigned int result; + + if (atti386_DetectCPUID() == false) + return false; + + /* check cpuid */ + __asm__ __volatile__( + "movl $1, %%eax \n" \ + "cpuid \n" \ + "movl %%edx, %0 \n" \ + : "=r"(result) : : "eax", "ebx", "ecx", "edx"); + + if (result & (1 << 25)) + return true; + + return false; +} + +static inline bool atti386_Detect3DNow() +{ + unsigned int result; + + if (atti386_DetectCPUID() == false) + return false; + + /* get cpuid */ + __asm__ __volatile__( + "movl $0x80000001, %%eax \n" \ + "cpuid \n" \ + "movl %%edx, %0 \n" \ + : "=r"(result) : : "eax", "ebx", "ecx", "edx"); + + if (result & 0x80000000) + return true; + + return false; +} + + +static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr) +{ + + __asm__ __volatile__ ( + "pushl %1 \n" \ + "pushl %2 \n" \ + "movss 0(%0), %%xmm1 \n" \ + "movups 0(%1), %%xmm2 \n" \ + "movlps 0(%2), %%xmm3 \n" \ + " \n" \ + "shufps $0x44, %%xmm3, %%xmm3 \n" \ + " \n" \ + "mulps %%xmm3, %%xmm2 \n" \ + " \n" \ + "subss %%xmm2, %%xmm1 \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "subss %%xmm2, %%xmm1 \n" \ + " \n" \ + "movss %%xmm1, 0(%2) \n" \ + " \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "addss %%xmm2, %%xmm1 \n" \ + " \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "addss %%xmm2, %%xmm1 \n" \ + " \n" \ + "movss %%xmm3, 4(%2) \n" \ + " \n" \ + "addl $16, %1 \n" \ + "addl $8, %2 \n" \ + " \n" \ + "movups 0(%1), %%xmm2 \n" \ + " \n" \ + "movlps 0(%2), %%xmm3 \n" \ + "shufps $0x44, %%xmm3, %%xmm3 \n" \ + " \n" \ + "mulps %%xmm3, %%xmm2 \n" \ + " \n" \ + "subss %%xmm2, %%xmm1 \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "subss %%xmm2, %%xmm1 \n" \ + " \n" \ + "movss %%xmm1, 0(%2) \n" \ + " \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "addss %%xmm2, %%xmm1 \n" \ + " \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "addss %%xmm2, %%xmm1 \n" \ + " \n" \ + "movss %%xmm3, 4(%2) \n" \ + "movss %%xmm1, 0(%0) \n" \ + "popl %2 \n" \ + "popl %1 \n" \ + : : "r"(output), "r"(coef_ptr), "r"(hist1_ptr) + : "xmm1", "xmm2", "xmm3", "memory"); + + return(*output); +} + +static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr) +{ + float tmp; + + __asm__ __volatile__ ( + "movq %0, %%mm1 \n" \ + " \n" \ + "movl %1, %%ebx \n" \ + "movq 0(%%ebx), %%mm2 \n" \ + " \n" \ + "movl %2, %%eax; \n" \ + "movq 0(%%eax), %%mm3 \n" \ + " \n" \ + "pfmul %%mm3, %%mm2 \n" \ + "pfsub %%mm2, %%mm1 \n" \ + " \n" \ + "psrlq $32, %%mm2 \n" \ + "pfsub %%mm2, %%mm1 \n" \ + " \n" \ + "movd %%mm1, %3 \n" \ + " \n" \ + "addl $8, %%ebx \n" \ + "movq 0(%%ebx), %%mm2 \n" \ + "movq 0(%%eax), %%mm3 \n" \ + " \n" \ + "pfmul %%mm3, %%mm2 \n" \ + "pfadd %%mm2, %%mm1 \n" \ + " \n" \ + "psrlq $32, %%mm2 \n" \ + "pfadd %%mm2, %%mm1 \n" \ + " \n" \ + "pushl %3 \n" \ + "popl 0(%%eax) \n" \ + " \n" \ + "movd %%mm3, 4(%%eax) \n" \ + " \n" \ + "addl $8, %%ebx \n" \ + "addl $8, %%eax \n" \ + " \n" \ + "movq 0(%%ebx), %%mm2 \n" \ + "movq 0(%%eax), %%mm3 \n" \ + " \n" \ + "pfmul %%mm3, %%mm2 \n" \ + "pfsub %%mm2, %%mm1 \n" \ + " \n" \ + "psrlq $32, %%mm2 \n" \ + "pfsub %%mm2, %%mm1 \n" \ + " \n" \ + "movd %%mm1, %3 \n" \ + " \n" \ + "addl $8, %%ebx \n" \ + "movq 0(%%ebx), %%mm2 \n" \ + "movq 0(%%eax), %%mm3 \n" \ + " \n" \ + "pfmul %%mm3, %%mm2 \n" \ + "pfadd %%mm2, %%mm1 \n" \ + " \n" \ + "psrlq $32, %%mm2 \n" \ + "pfadd %%mm2, %%mm1 \n" \ + " \n" \ + "pushl %3 \n" \ + "popl 0(%%eax) \n" \ + "movd %%mm3, 4(%%eax) \n" \ + " \n" \ + "movd %%mm1, %0 \n" \ + "femms \n" \ + : "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp) + : "eax", "ebx", "mm1", "mm2", "mm3", "memory"); + + return(output); +} + +static inline float atti386_iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) +{ + return 0; +} + +static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd) +{ + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movw %1, %%ax \n" \ + "shll $16, %%eax \n" \ + "movw %1, %%ax \n" \ + "movd %%eax, %%mm3 \n" \ + "movd %%eax, %%mm2 \n" \ + "psllq $32, %%mm3 \n" \ + "por %%mm2, %%mm3 \n" \ + "movl %2, %%esi \n" \ + "movl %3, %%edi \n" \ + "1: \n" \ + "movq 0(%%esi), %%mm1 \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "pmulhw %%mm3, %%mm1 \n" \ + "paddw %%mm2, %%mm1 \n" \ + "movq %%mm1, 0(%%edi) \n" \ + " \n" \ + "addl $8, %%esi \n" \ + "addl $8, %%edi \n" \ + " \n" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd) + : "eax", "ecx", "edi", "esi", "mm1", "mm2", "mm3", "memory"); +} + +// FIXME: This is buggy +static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor) +{ + __asm__ __volatile__( + "movl %4, %%ecx \n" \ + "shrl $1, %%ecx \n" \ + "addl $4, %%ecx \n" \ + "pushl %%ecx \n" \ + " \n" \ + "movl %0, %%esi \n" \ + "movups 0(%%esi), %%xmm1 \n" \ + " \n" \ + "movl %1, %%esi \n" \ + "movl %2, %%edi \n" \ + "1: \n" \ + "xorl %%eax, %%eax \n" \ + "movw 0(%1), %%ax \n" \ + "cwde \n" \ + "incl %1 \n" \ + "incl %1 \n" \ + "movd %%eax, %%mm1 \n" \ + "psrlq $32, %%mm1 \n" \ + "movw 0(%1), %%ax \n" \ + "incl %1 \n" \ + "incl %1 \n" \ + "movd %%eax, %%mm2 \n" \ + "por %%mm2, %%mm1 \n" \ + " \n" \ + "decl %%ecx \n" \ + "jnz 1b \n" \ + " \n" \ + "popl %%ecx \n" \ + "movl %1, %%esi \n" \ + "movl %3, %%edi \n" \ + "incl %%esi \n" \ + "2: \n" \ + "decl %%ecx \n" \ + "jnz 2b \n" \ + : : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len) + : "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory"); +} + +static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) +{ + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movl %1, %%esi \n" \ + "movl %2, %%edi \n" \ + "1: \n" \ + "movq 0(%%edi), %%mm1 \n" \ + "movq 0(%%esi), %%mm2 \n" \ + "paddw %%mm2, %%mm1 \n" \ + "movq %%mm1, 0(%%esi) \n" \ + "addl $8, %%edi \n" \ + "addl $8, %%esi \n" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : : "g"(len), "g"(buf1), "g"(buf2) + : "ecx", "edi", "esi", "mm1", "mm2", "memory"); +} + +static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) +{ + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movl %1, %%esi \n" \ + "movl %2, %%edi \n" \ + "1: \n" \ + "movq 0(%%esi), %%mm1 \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "movq %%mm1, %%mm3 \n" \ + "pmulhw %%mm2, %%mm1 \n" \ + "paddw %%mm3, %%mm1 \n" \ + "movq %%mm1, 0(%%esi) \n" \ + "addl $8, %%edi \n" \ + "addl $8, %%esi \n" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : : "g"(len), "g"(buf1), "g"(buf2) + : "ecx", "edi", "esi", "mm1", "mm2", "mm3", "memory"); +} + +static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) +{ + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movl %1, %%esi \n" \ + "movl %2, %%edi \n" \ + "1: \n" \ + "movq 0(%%esi), %%mm1 \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "pmulhw %%mm2, %%mm1 \n" \ + "movq %%mm1, 0(%%esi) \n" \ + "addl $8, %%edi \n" \ + "addl $8, %%esi \n" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : : "g"(len), "g"(buf1), "g"(buf2) + : "ecx", "edi", "esi", "mm1", "mm2", "memory"); +} + +static inline void atti386_PartProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol, + Bit16s *partialBuf, Bit16s *p1buf) +{ + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movw %1, %%ax \n" \ + "shll $16, %%eax \n" \ + "movw %2, %%ax \n" \ + "movd %%eax, %%mm1 \n" \ + "movd %%eax, %%mm2 \n" \ + "psllq $32, %%mm1 \n" \ + "por %%mm2, %%mm1 \n" \ + "movl %3, %%edi \n" \ + "movl %4, %%esi \n" \ + "1: \n" \ + "movw 0(%%esi), %%bx \n" \ + "addl $2, %%esi \n" \ + "movw 0(%%esi), %%dx \n" \ + "addl $2, %%esi \n" \ + "" \ + "movw %%dx, %%ax \n" \ + "shll $16, %%eax \n" \ + "movw %%dx, %%ax \n" \ + "movd %%eax, %%mm2 \n" \ + "psllq $32, %%mm2 \n" \ + "movw %%bx, %%ax \n" \ + "shll $16, %%eax \n" \ + "movw %%bx, %%ax \n" \ + "movd %%eax, %%mm3 \n" \ + "por %%mm3, %%mm2 \n" \ + "" \ + "pmulhw %%mm1, %%mm2 \n" \ + "movq %%mm2, 0(%%edi) \n" \ + "addl $8, %%edi \n" \ + "" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : :"g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf) + : "eax", "ebx", "ecx", "edx", "edi", "esi", "mm1", "mm2", "mm3", "memory"); +} +#endif + +#endif + +extern bool enabled3DNow; +extern bool enabledSSE; + +#pragma pack(1) +struct timbreParam { + struct commonParam { + char name[10]; + char pstruct12; // 1&2 0-12 (1-13) + char pstruct34; // #3&4 0-12 (1-13) + char pmute; // 0-15 (0000-1111) + char nosustain; // 0-1(Normal, No sustain) + } ALIGN_PACKED common; + + struct partialParam { + struct wgParam { + char coarse; // 0-96 (C1,C#1-C9) + char fine; // 0-100 (-50 - +50) + char keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2) + char bender; // 0,1 (ON/OFF) + char waveform; // 0-1 (SQU/SAW) + char pcmwave; // 0-127 (1-128) + char pulsewid; // 0-100 + char pwvelo; // 0-14 (-7 - +7) + } ALIGN_PACKED wg; + + struct envParam { + char depth; // 0-10 + char sensitivity; // 1-100 + char timekeyfollow; // 0-4 + char time[4]; // 1-100 + char level[5]; // 1-100 (-50 - +50) + } ALIGN_PACKED env; + + struct lfoParam { + char rate; // 0-100 + char depth; // 0-100 + char modsense; // 0-100 + } ALIGN_PACKED lfo; + + struct tvfParam { + char cutoff; // 0-100 + char resonance; // 0-30 + char keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2) + char biaspoint; // 0-127 (<1A-<7C >1A-7C) + char biaslevel; // 0-14 (-7 - +7) + char envdepth; // 0-100 + char envsense; // 0-100 + char envdkf; // DEPTH KEY FOLL0W 0-4 + char envtkf; // TIME KEY FOLLOW 0-4 + char envtime[5]; // 1-100 + char envlevel[4]; // 1-100 + } ALIGN_PACKED tvf; + + struct tvaParam { + char level; // 0-100 + char velosens; // 0-100 + char biaspoint1; // 0-127 (<1A-<7C >1A-7C) + char biaslevel1; // 0-12 (-12 - 0) + char biaspoint2; // 0-127 (<1A-<7C >1A-7C) + char biaslevel2; // 0-12 (-12 - 0) + char envtkf; // TIME KEY FOLLOW 0-4 + char envvkf; // VELOS KEY FOLL0W 0-4 + char envtime[5]; // 1-100 + char envlevel[4]; // 1-100 + } ALIGN_PACKED tva; + + } ALIGN_PACKED partial[4]; + //char dummy[20]; +} ALIGN_PACKED; + +struct memParams { + struct patchTemp { + char timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm) + char timbreNum; // TIMBRE NUMBER 0-63 + char keyShift; // KEY SHIFT 0-48 (-24 - +24) + char fineTune; // FINE TUNE 0-100 (-50 - +50) + char benderRange; // BENDER RANGE 0-24 + char assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4) + char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON) + char dummy; // (DUMMY) + char outlevel; // OUTPUT LEVEL 0-100 + char panpot; // PANPOT 0-14 (R-L) + char dummyv[6]; + } ALIGN_PACKED tmpSettings[8]; + struct ryhTemp { + char timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF) + char outlevel; // OUTPUT LEVEL 0-100 + char panpot; // PANPOT 0-14 (R-L) + char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON) + } ALIGN_PACKED rhySettings[64]; + + timbreParam timTemp[8]; + + struct patchArea { + char timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm) + char timbreNum; // TIMBRE NUMBER 0-63 + char keyShift; // KEY SHIFT 0-48 (-24 - +24) + char fineTune; // FINE TUNE 0-100 (-50 - +50) + char benderRange; // BENDER RANGE 0-24 + char assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4) + char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON) + char dummy; // (DUMMY) + } ALIGN_PACKED pSettings[128]; + timbreParam patch[192]; + struct systemArea { + char masterTune; // MASTER TUNE 0-127 432.1-457.6Hz + char reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay) + char reverbTime; // REVERB TIME 0-7 (1-8) + char reverbLevel; // REVERB LEVEL 0-7 (1-8) + char reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32 + char chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF) + char masterVol; // MASTER VOLUME 0-100 + } ALIGN_PACKED system; + +} ALIGN_PACKED; + +struct memBanks { + char pTemp[8][sizeof(memParams::patchTemp)]; + char rTemp[64][sizeof(memParams::ryhTemp)]; + char tTemp[8][sizeof(timbreParam)]; + char patchmemory[128][sizeof(memParams::patchArea)]; + char patchbanks[128][sizeof(timbreParam)]; + char timbrebanks[64][sizeof(timbreParam)]; + char systemBank[sizeof(memParams::systemArea)]; +} ALIGN_PACKED; + +struct memAbsolute { + char mt32memory[sizeof(memBanks)]; +} ALIGN_PACKED; + +#pragma pack() + +struct partialFormat { + Bit32u addr; + Bit16u len; + bool loop; + float tune; + Bit32s ampval; +}; + +struct partialTable { + Bit32u addr; + Bit32u len; + Bit32u pcmnum; + Bit32s ampval; + bool loop; + Bit32s aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations +}; + + + +union soundaddr { + Bit32u pcmabs; + struct offsets { +#if defined(SCUMM_LITTLE_ENDIAN) + Bit16u pcmoffset; + Bit16u pcmplace; +#else + Bit16u pcmplace; + Bit16u pcmoffset; +#endif + } pcmoffs; +}; + + +struct volset { + Bit16s leftvol; + Bit16s rightvol; + Bit16s leftvol2; + Bit16s rightvol2; +}; + +struct patchCache { + int rawPCM; + partialTable convPCM; + + bool playPartial; + bool usePartial; + bool PCMPartial; + char waveform; + int pulsewidth; + int pwsens; + int pitchshift; + int fineshift; + bool sustain; + + int lfodepth; + int lforate; + Bit32u lfoperiod; + int modsense; + + int keydir; + int pitchkeyfollow; + int pitchkeydir; + + int filtkeyfollow; + + int tvfbias; + int tvfblevel; + int tvfdir; + + int ampbias[2]; + int ampblevel[2]; + int ampdir[2]; + + int ampdepth; + int ampenvdir; + int amplevel; + int tvfdepth; + + int prevsample; + + bool useBender; + + timbreParam::partialParam::envParam pitchEnv; + timbreParam::partialParam::tvaParam ampEnv; + timbreParam::partialParam::tvfParam filtEnv; + + Bit32s ampsustain; + Bit32s pitchsustain; + Bit32s filtsustain; + + Bit32u partCount; + + Bit8u padding[64]; //Used to pad the patch cache to 4096 bytes. This replaces an imul with a shl 12 + +}; + +struct dpoly { + bool isPlaying; + bool isDecay; + bool isActive; + + bool partActive[4]; + + bool isRy; + Bit32u *bendptr; + Bit32u drumbend; + Bit32s *volumeptr; + volset *pansetptr; + + int pcmnum; + int freq; + int freqnum; + int vel; + + Bit32u partCount; + + soundaddr pcmoff; + Bit32u pcmdelta; + + + struct partialStatus { + // Note played on keyboard + int noteval; + // Keyfollowed note values + int keyedval; + + // Keyfollowed filter values + int realval; + int filtval; + // Keyfollowed filter w/o table + int filtnoval; + int pulsewidth; + + struct envstatus { + Bit32s envpos; + Bit32s envstat; + Bit32s envbase; + Bit32s envdist; + Bit32s envsize; + + bool sustaining; + bool decaying; + bool notdecayed; + Bit32u decay; + Bit32s prevlevel; + + Bit32s counter; + Bit32s count; + + } envs[4]; + + Bit32u lfopos; + soundaddr partialOff; + soundaddr wgOff; + + Bit32u ampEnvCache; + Bit32u pitchEnvCache; + + bool isDecayed; + bool PCMDone; + + float history[32]; + + float pastfilt; + bool pitchsustain; + bool playPartial; + bool usePartial; + + int looppos; + int partNum; + + patchCache *tcache; + + void * myPart; + + + + } pStatus[4]; + + + int chan; + + int origpat; + int drumnum; + + int age; + + bool pedalhold; + bool firstsamp; + + Bit32u P1Mix; + Bit32u P2Mix; + bool sustain; +}; + +#endif diff --git a/backends/midi/mt32/synth.cpp b/backends/midi/mt32/synth.cpp new file mode 100644 index 0000000000..9a35c7e5cb --- /dev/null +++ b/backends/midi/mt32/synth.cpp @@ -0,0 +1,4564 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * Based on Tristan's conversion of Canadacow's code + * + * 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. + * + * $Header$ + * + */ + +#include "stdafx.h" + +#include "backends/midi/mt32/synth.h" +#include "backends/midi/mt32/partial.h" +#include "backends/midi/mt32/freeverb.h" +#include "common/system.h" +#include "common/file.h" + +#define NOMANSLAND + +/* ************************** */ +/* CSynth reporting codes */ +/* ************************** */ + +/* files missing */ +#define ERR_PRESET1 1 +#define ERR_PRESET2 2 +#define ERR_DRUMPAT 3 +#define ERR_PATCHLOG 4 +#define ERR_MT32ROM 5 + +/* HW spec */ +#define PRESENT_SSE 6 +#define PRESENT_3DNOW 7 +#define USING_SSE 8 +#define USING_3DNOW 9 + + +/* General info */ +#define LCD_MESSAGE 10 +#define DEV_RESET 11 +#define DEV_RECONFIG 12 +#define NEW_REVERB_MODE 13 +#define NEW_REVERB_TIME 14 +#define NEW_REVERB_LEVEL 15 + +#pragma pack(1) +static union mt32ramFormat { + memParams params; + memBanks patchabs; + memAbsolute memabs; + + + // System memory 10 + + // Display 20 + + // Reset 7F + + +} ALIGN_PACKED mt32ram, mt32default; +#pragma pack() +// Borrowed from Borland's site +int axtoi(char *hexStg) { + unsigned int n = 0; // position in string + unsigned int m = 0; // position in digit[] to shift + unsigned int count; // loop index + int intValue = 0; // integer value of hex string + int digit[512]; // hold values to convert + while (n < strlen(hexStg)) { + if (hexStg[n]=='\0') + break; + if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9 + digit[n] = hexStg[n] & 0x0f; //convert to int + else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f + digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int + else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F + digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int + else break; + n++; + } + count = n; + m = n - 1; + n = 0; + while(n < count) { + // digit[n] is value of hex digit at position n + // (m << 2) is the number of positions to shift + // OR the bits into return value + intValue = intValue | (digit[n] << (m << 2)); + m--; // adjust the position to set + n++; // next digit to process + } + return (intValue); +} + + +typedef struct { + unsigned int length; /* size of filter */ + float *history; /* pointer to history in filter */ + float *coef; /* pointer to coefficients of filter */ +} FILTER; + + +#define FILTER_SECTIONS 2 /* 2 filter sections for 24 db/oct filter */ + +typedef struct { + double a0, a1, a2; /* numerator coefficients */ + double b0, b1, b2; /* denominator coefficients */ +} BIQUAD; + +BIQUAD ProtoCoef[FILTER_SECTIONS]; /* Filter prototype coefficients, + 1 for each filter section */ + +void prewarp(double *a0, double *a1, double *a2, double fc, double fs); +void bilinear( + double a0, double a1, double a2, /* numerator coefficients */ + double b0, double b1, double b2, /* denominator coefficients */ + double *k, /* overall gain factor */ + double fs, /* sampling rate */ + float *coef); /* pointer to 4 iir coefficients */ + + +/* + * ---------------------------------------------------------- + * Pre-warp the coefficients of a numerator or denominator. + * Note that a0 is assumed to be 1, so there is no wrapping + * of it. + * ---------------------------------------------------------- + */ +void prewarp( + double *a0, double *a1, double *a2, + double fc, double fs) +{ + double wp, pi; + + pi = 4.0 * atan(1.0); + wp = 2.0 * fs * tan(pi * fc / fs); + + *a2 = (*a2) / (wp * wp); + *a1 = (*a1) / wp; +} + + +/* + * ---------------------------------------------------------- + * bilinear() + * + * Transform the numerator and denominator coefficients + * of s-domain biquad section into corresponding + * z-domain coefficients. + * + * Store the 4 IIR coefficients in array pointed by coef + * in following order: + * beta1, beta2 (denominator) + * alpha1, alpha2 (numerator) + * + * Arguments: + * a0-a2 - s-domain numerator coefficients + * b0-b2 - s-domain denominator coefficients + * k - filter gain factor. initially set to 1 + * and modified by each biquad section in such + * a way, as to make it the coefficient by + * which to multiply the overall filter gain + * in order to achieve a desired overall filter gain, + * specified in initial value of k. + * fs - sampling rate (Hz) + * coef - array of z-domain coefficients to be filled in. + * + * Return: + * On return, set coef z-domain coefficients + * ---------------------------------------------------------- + */ +void bilinear( + double a0, double a1, double a2, /* numerator coefficients */ + double b0, double b1, double b2, /* denominator coefficients */ + double *k, /* overall gain factor */ + double fs, /* sampling rate */ + float *coef /* pointer to 4 iir coefficients */ +) +{ + double ad, bd; + + /* alpha (Numerator in s-domain) */ + ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0; + /* beta (Denominator in s-domain) */ + bd = 4. * b2 * fs * fs + 2. * b1* fs + b0; + + /* update gain constant for this section */ + *k *= ad/bd; + + /* Denominator */ + *coef++ = (2. * b0 - 8. * b2 * fs * fs) + / bd; /* beta1 */ + *coef++ = (4. * b2 * fs * fs - 2. * b1 * fs + b0) + / bd; /* beta2 */ + + /* Nominator */ + *coef++ = (2. * a0 - 8. * a2 * fs * fs) + / ad; /* alpha1 */ + *coef = (4. * a2 * fs * fs - 2. * a1 * fs + a0) + / ad; /* alpha2 */ +} + +void szxform( + double *a0, double *a1, double *a2, /* numerator coefficients */ + double *b0, double *b1, double *b2, /* denominator coefficients */ + double fc, /* Filter cutoff frequency */ + double fs, /* sampling rate */ + double *k, /* overall gain factor */ + float *coef) /* pointer to 4 iir coefficients */ +{ + /* Calculate a1 and a2 and overwrite the original values */ + prewarp(a0, a1, a2, fc, fs); + prewarp(b0, b1, b2, fc, fs); + bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef); +} + + + +#ifdef HAVE_X86 +#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW__)) +bool DetectSIMD() +{ + + bool found_simd; + _asm + + { + + pushfd + pop eax // get EFLAGS into eax + mov ebx,eax // keep a copy + xor eax,0x200000 + // toggle CPUID bit + + push eax + popfd // set new EFLAGS + pushfd + pop eax // EFLAGS back into eax + + xor eax,ebx + // have we changed the ID bit? + + je NO_SIMD + // No, no CPUID instruction + + // we could toggle the + // ID bit so CPUID is present + mov eax,1 + + cpuid // get processor features + test edx,1<<25 // check the SIMD bit + jz NO_SIMD + mov found_simd,1 + jmp DONE + NO_SIMD: + mov found_simd,0 + DONE: + } + + return found_simd; + +} + +bool Detect3DNow() { + bool found3D = false; + __asm { + pushfd + pop eax + mov edx, eax + xor eax, 00200000h + push eax + popfd + pushfd + pop eax + xor eax, edx + jz NO_3DNOW + + mov eax, 80000000h + cpuid + + cmp eax, 80000000h + jbe NO_3DNOW + + mov eax, 80000001h + cpuid + test edx, 80000000h + jz NO_3DNOW + mov found3D, 1 +NO_3DNOW: + + } + return found3D; +} +#else +bool DetectSIMD() +{ + return atti386_DetectSIMD(); +} +bool Detect3DNow() +{ + return atti386_Detect3DNow(); +} +#endif +#endif + +#ifdef NOMANSLAND + +//#define SETRATE 32000 +#define SETRATE myProp.SampleRate +//#define SETRATE 44100 + +// Used to regenerate waveform file after sampling rate change +#define MAKEWAVES 0 + +// Used to dump drum patches to syx file for viewing +#define DUMPDRUMS 0 + +#define USEREVERB 1 + +// Debuging stuff +// Shows the instruments played +#define DISPLAYINSTR 0 +// Shows number of partials MT-32 is playing +#define MONITORPARTIALS 1 +// Dump syx file of temp tibres right before reset +#define SAVECUSTOM 0 + + + +// Constant tuning for now +#define TUNING 440.0 +#define SAMPLETUNING 207.64 +#define MIDDLEC 60 + +#define ROMSIZE 512*1024 +#define PCMSIZE ROMSIZE/2 +#define GRAN 512 +#define LN 2.30258509 + +#define MAXPOLY 64 +#define MAXPARTIALS 32 + +// Reverb room sizes (in metres) + +#define REV_ROOMSIZE 2.50f +#define REV_HALLSIZE 3.60f +#define REV_PLATESIZE 1.50f +#define REV_TAPSIZE 1.0f + +// Reverb t60 coeff + +#define REV_ROOMT60 ( REV_ROOMSIZE * REV_ROOMSIZE * REV_ROOMSIZE ) / 5 +#define REV_HALLT60 ( REV_HALLSIZE * REV_HALLSIZE * REV_HALLSIZE ) / 5 +#define REV_PLATET60 ( REV_PLATESIZE * REV_PLATESIZE * REV_PLATESIZE ) / 7 +#define REV_TAPT60 ( REV_TAPSIZE * REV_TAPSIZE * REV_TAPSIZE ) / 1 + +//#define HLRATIO 2.0f + +#define SYSEX_SIZE 512 + +// These are all the filters I tried without much success +Bit16s Moog1(Bit16s wg, float *hist, float usefilt, float res) { + float f, p, q; //filter coefficients + float t1, t2; //temporary buffers + + // Set coefficients given frequency & resonance [0.0...1.0] + + float frequency = usefilt; + float in = (float)wg/32767.0; + float resonance = res / 31.0; + resonance = resonance * resonance; + + q = 1.0f - frequency; + //p = frequency + 0.8f * frequency * q; + p = frequency + 0.8f * frequency * q; + + f = p + p - 1.0f; + q = resonance * (1.0f + 0.5f * q * (1.0f - q + 5.6f * q * q)); + + // Filter (in [-1.0...+1.0]) + + in -= q * hist[4]; //feedback + t1 = hist[1]; + hist[1] = (in + hist[0]) * p - hist[1] * f; + t2 = hist[2]; + hist[2] = (hist[1] + t1) * p - hist[2] * f; + t1 = hist[3]; + hist[3] = (hist[2] + t2) * p - hist[3] * f; + hist[4] = (hist[3] + t1) * p - hist[4] * f; + + hist[4] = hist[4] - hist[4] * hist[4] * hist[4] * 0.166667f; //clipping + hist[0] = in; + //LOG_MSG("In %d Hist: %f", wg, hist[4]*32767); + return (Bit16s)(hist[4]*32767.0); +} + +Bit16s Moog2(Bit16s wg, float *hist, float usefilt, float resonance) { + + float res = resonance / 30.0; + double f = usefilt; + double invf = 1.0 - f; + double fb = res * (1.0 - 0.15 * f * f); + float input = (float)wg/32767.0; + input -= hist[4] * fb; + input *= 0.35013 * (f*f)*(f*f); + hist[1] = input + 0.3 * hist[5] + (invf) * hist[1]; // Pole 1 + hist[5] = input; + hist[2] = hist[1] + 0.3 * hist[6] + (invf) * hist[2]; // Pole 2 + hist[6] = hist[1]; + hist[3] = hist[2] + 0.3 * hist[7] + (invf) * hist[3]; // Pole 3 + hist[7] = hist[2]; + hist[4] = hist[3] + 0.3 * hist[0] + (invf) * hist[4]; // Pole 4 + hist[0] = hist[3]; + return (Bit16s)(hist[4]*32767.0); +} + +Bit16s simpleLowpass(Bit16s wg, float *hist, float usefilt, float resonance) { + + float in = (float)wg/32767.0; + float res_lp = resonance / 31.0; + res_lp = res_lp * res_lp; + float cut_lp = usefilt; + float n1, n2, fb_lp; + + n1 = hist[0]; + n2 = hist[1]; + + fb_lp = res_lp+res_lp/(1-cut_lp); + n1=n1+cut_lp*(in-n1+fb_lp*(n1-n2)); + n2=n2+cut_lp*(n1-n2); + + hist[0] = n1; + hist[1] = n2; + + return (int)(n2*32767.0); + +} + +/* Reverb stuff */ + +#define NUM_COMBS 6 + +typedef struct { + float coef; + float lastval; +} LOWPASS_STATE; + +typedef struct { + float tau; + float g; + float gsqu; + float *delbuf; + int bufsiz; + int bufpos; +} COMB_STATE; + +typedef struct { + int lastsamp; + int cursamp; + int done; + LOWPASS_STATE lowpass[NUM_COMBS]; + COMB_STATE comb[NUM_COMBS]; + COMB_STATE allpass[2]; +} ST_REVERB; + +class Reverb { +private: + + ST_REVERB *revstate; + + int SR; + +public: + + Reverb(float t60, float hlratio, float dur, float hall_f, int smpr); + + ~Reverb(); + + void run(float *lchan, float *rchan, float revfrac); + + float lowpass(float input, LOWPASS_STATE *state); + + float lpcomb(float input, LOWPASS_STATE* lpstate, COMB_STATE* cstate); + float allpassfilt(float input, COMB_STATE* state); +}; + +/* +t60 = reverb time +hlratio = ratio of low freq t60 to high freq t60 +dur = duration of event/dealloc. on last samp +hall_fact= mult. factor for delay times +revstate = running values for event reverb +*/ +Reverb::Reverb(float t60, float hlratio, float dur, float hall_fact, int sampling_rate) +{ + revstate = new ST_REVERB; + SR = sampling_rate; + int i; + float glow[NUM_COMBS], ghi[NUM_COMBS]; + /* initialize sample counter and compute last sample */ + revstate->cursamp=0; + revstate->lastsamp = (int)(dur*(float)SR); + revstate->done=0; + + /* ALLPASS INITIALIZATIONS */ + revstate->allpass[0].tau = .006 * hall_fact; + revstate->allpass[1].tau = .0065 * hall_fact; + + /* allocate allpass delay buffers and head/tail ptr. */ + for(i=0; i<2; i++){ + revstate->allpass[i].bufsiz = (int) (revstate->allpass[i].tau*SR + .5); + revstate->allpass[i].delbuf = + new float[revstate->allpass[i].bufsiz]; + memset(revstate->allpass[i].delbuf, 0, + revstate->allpass[i].bufsiz*sizeof(float)); + revstate->allpass[i].bufpos = -1; + } + + revstate->allpass[0].g = .71f; + revstate->allpass[1].g = .7f; + + revstate->allpass[0].gsqu = + revstate->allpass[0].g * revstate->allpass[0].g; + revstate->allpass[1].gsqu = + revstate->allpass[1].g * revstate->allpass[1].g; + + /* COMB AND IIR LOWPASS FILTER INITIALIZATIONS */ + + revstate->comb[0].tau = .0050 * hall_fact; + revstate->comb[1].tau = .0068 * hall_fact; + revstate->comb[2].tau = .0056 * hall_fact; + revstate->comb[3].tau = .0072 * hall_fact; + revstate->comb[4].tau = .0061 * hall_fact; + revstate->comb[5].tau = .0078 * hall_fact; + + /* allocate comb delay buffers and head/tail ptr. */ + for(i=0; i<NUM_COMBS; i++) { + revstate->comb[i].bufsiz = (int)(revstate->comb[i].tau * SR + .5); + + revstate->comb[i].delbuf = + new float[revstate->comb[i].bufsiz]; + memset(revstate->comb[i].delbuf, 0, + revstate->comb[i].bufsiz*sizeof(float)); + + revstate->comb[i].bufpos = -1; + + revstate->lowpass[i].lastval = 0.; + } + + /* if hlratio set by user, set various values */ + if (hlratio != 0.) { + for(i=0; i<NUM_COMBS; i++) { + + /* compute reverb attenuation factor for hi and low */ + /* frequency reverberation times */ + glow[i] = + pow(10.,(-3. * revstate->comb[i].tau) / t60); + ghi[i] = + pow(10.,(-3. * revstate->comb[i].tau)/( t60 * hlratio)); + + /* compute recursive lowpass factor and comb attenuation */ + /* factor to produce the correct reverberation time for */ + /* both hi and low frequencies */ + revstate->lowpass[i].coef = (glow[i] - ghi[i])/(glow[i] + ghi[i]); + revstate->comb[i].g = glow[i] * (1. - revstate->lowpass[i].coef); + } + } + /* else, use default g's and coef's */ + else { + revstate->lowpass[0].coef = .24f; revstate->lowpass[1].coef = .26f; + revstate->lowpass[2].coef = .28f; revstate->lowpass[3].coef = .29f; + revstate->lowpass[4].coef = .30f; revstate->lowpass[5].coef = .32f; + + for(i=0; i<6; i++) { + + /* compute reverb attenuation factor and comb */ + /* attenuation factor based on default coef */ + glow[i] = + pow(10., (-3. * revstate->comb[i].tau) / t60); + revstate->comb[i].g = glow[i] * + (1. - revstate->lowpass[i].coef); + } + } +} + +Reverb:: ~Reverb() +{ + int i; + + for(i=0; i<NUM_COMBS; i++) delete[] revstate->comb[i].delbuf; + for(i=0; i<2; i++) delete[] revstate->allpass[i].delbuf; + delete revstate; +} + + +INLINE void Reverb::run(float *lchan, float *rchan, float revfrac) + /* lchan,rchan non-reverberated input samples */ + /* revfrac percent of output to be reverberated */ + { + int i; + float lchanrev, rchanrev, tot=0; + + //cout << " in run \n"; + + if (revstate->done) { + *lchan = 0.0; + *rchan = 0.0; + return; + } + + for (i=0; i<NUM_COMBS; i++) + tot = tot + lpcomb( (*lchan) + (*rchan), + &(revstate->lowpass[i]), + &(revstate->comb[i]) + ); + + tot = tot/(float)NUM_COMBS; + + + lchanrev = allpassfilt(tot * .7, &(revstate->allpass[0])); + // rchanrev = lchanrev ; + rchanrev = allpassfilt(tot * .7, &(revstate->allpass[1])); + + if (revstate->cursamp == revstate->lastsamp) { + for(i=0; i<NUM_COMBS; i++) delete[] revstate->comb[i].delbuf; + for(i=0; i<2; i++) delete[] revstate->allpass[i].delbuf; + revstate->done = 1; + } + + (revstate->cursamp)++; + + *lchan = lchanrev*revfrac + (*lchan)*(1. - revfrac) ; + *rchan = rchanrev*revfrac + (*rchan)*(1. - revfrac) ; + + //*lchan = lchanrev*revfrac + (*lchan) ; + //*rchan = rchanrev*revfrac + (*rchan) ; + + // cout << "lchan = \t" << *lchan <<endl; + //cout << "rchan = \t" << *rchan <<endl; + + +} + +INLINE float Reverb::lowpass(float input, LOWPASS_STATE *state) +{ + /* simple IIR lowpass filter algorithm */ + /* y(n) = x(n) + coef * y(n-1) */ + state->lastval = (input + state->coef * state->lastval); + return(state->lastval); +} + +INLINE float Reverb::lpcomb(float input, LOWPASS_STATE *lpstate, COMB_STATE *cstate) +{ + + float temp; + + /* move head-tail pointer in circular queue */ + cstate->bufpos = (cstate->bufpos + 1) % cstate->bufsiz; + + /* pop circular queue */ + temp = cstate->delbuf[cstate->bufpos]; + + /* add new value to end of queue */ + lpstate->lastval = (cstate->delbuf[cstate->bufpos] + lpstate->coef * lpstate->lastval); + + cstate->delbuf[cstate->bufpos] = + input + cstate->g * + //lowpass(cstate->delbuf[cstate->bufpos], lpstate); + lpstate->lastval; + + /* return popped value */ + return(temp); + +} + +INLINE float Reverb::allpassfilt(float input, COMB_STATE* state) +{ + float temp; + + /* move head-tail pointer in circular queue */ + state->bufpos = (state->bufpos + 1) % state->bufsiz; + + /* pop circular queue */ + temp = state->delbuf[state->bufpos]; + + /* add new value to end of queue */ + state->delbuf[state->bufpos] = input + + state->g * state->delbuf[state->bufpos]; + + /* return a sum of the current in with the delay out */ + return(-1.* state->g * input + (1. - state->gsqu) * temp); + +} + +/* End reverb stuff */ + + + +/* Begin filter stuff */ + +void InitFilter(float fs, float fc, float *icoeff, float Q, float resfac) { + + float *coef; + unsigned nInd; + double a0, a1, a2, b0, b1, b2; + double k; /* overall gain factor */ + + /* Section 1 */ + ProtoCoef[0].a0 = 1.0; + ProtoCoef[0].a1 = 0; + ProtoCoef[0].a2 = 0; + ProtoCoef[0].b0 = 1.0; + ProtoCoef[0].b1 = 0.765367; + ProtoCoef[0].b2 = 1.0; + + + /* Section 2 */ + ProtoCoef[1].a0 = 1.0; + ProtoCoef[1].a1 = 0; + ProtoCoef[1].a2 = 0; + ProtoCoef[1].b0 = 1.0; + ProtoCoef[1].b1 = 1.847759; + ProtoCoef[1].b2 = 1.0; + + k = 1.5; /* Set overall filter gain */ + coef = icoeff+1; /* Skip k, or gain */ + + for (nInd = 0; nInd < 2; nInd++) + { + a0 = ProtoCoef[nInd].a0; + a1 = ProtoCoef[nInd].a1; + a2 = ProtoCoef[nInd].a2; + + b0 = ProtoCoef[nInd].b0; + b1 = ProtoCoef[nInd].b1 / Q; /* Divide by resonance or Q +*/ + b2 = ProtoCoef[nInd].b2; + szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef); + coef += 4; /* Point to next filter +section */ + } + icoeff[0] = k; +} + + +#if FILTER_FLOAT == 1 + +iir_filter_type usefilter; + +#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW__)) +float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel) +{ + float *hist2_ptr; + float output; + + hist2_ptr = hist1_ptr + 1; /* next history */ + + /* 1st number of coefficients array is overall input scale factor, + * or filter gain */ + output = (input) * (*coef_ptr++); + + __asm { + + movss xmm1, output + + mov eax, coef_ptr + movups xmm2, [eax] + + mov eax, hist1_ptr + movlps xmm3, [eax] + shufps xmm3, xmm3, 44h + // hist2_ptr, hist1_ptr, hist2_ptr, hist1_ptr + + mulps xmm2, xmm3 + + subss xmm1, xmm2 + // Rotate elements right + shufps xmm2, xmm2, 39h + subss xmm1, xmm2 + + // Store new_hist + movss DWORD PTR [eax], xmm1 + + // Rotate elements right + shufps xmm2, xmm2, 39h + addss xmm1, xmm2 + + // Rotate elements right + shufps xmm2, xmm2, 39h + addss xmm1, xmm2 + + // Store previous hist + movss DWORD PTR [eax+4], xmm3 + + add coef_ptr, 16 + add hist1_ptr, 8 + + mov eax, coef_ptr + movups xmm2, [eax] + + mov eax, hist1_ptr + movlps xmm3, [eax] + shufps xmm3, xmm3, 44h + // hist2_ptr, hist1_ptr, hist2_ptr, hist1_ptr + + mulps xmm2, xmm3 + + subss xmm1, xmm2 + // Rotate elements right + shufps xmm2, xmm2, 39h + subss xmm1, xmm2 + + // Store new_hist + movss DWORD PTR [eax], xmm1 + + // Rotate elements right + shufps xmm2, xmm2, 39h + addss xmm1, xmm2 + + // Rotate elements right + shufps xmm2, xmm2, 39h + addss xmm1, xmm2 + + // Store previous hist + movss DWORD PTR [eax+4], xmm3 + + movss output, xmm1 + } + + output *= ResonInv[revLevel]; + + return(output); + +} + +float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) +{ + float *hist2_ptr; + float output; + float tmp; + + hist2_ptr = hist1_ptr + 1; /* next history */ + + /* 1st number of coefficients array is overall input scale factor, + * or filter gain */ + output = (input) * (*coef_ptr++); + + // I find it very sad that 3DNow requires twice as many instructions as Intel's SSE + // Intel does have the upper hand here. + __asm { + movq mm1, output + mov ebx, coef_ptr + movq mm2, [ebx] + + mov eax, hist1_ptr; + movq mm3, [eax] + + pfmul mm2, mm3 + pfsub mm1, mm2 + + psrlq mm2, 32 + pfsub mm1, mm2 + + // Store new hist + movd tmp, mm1 + + add ebx, 8 + movq mm2, [ebx] + movq mm3, [eax] + + pfmul mm2, mm3 + pfadd mm1, mm2 + + psrlq mm2, 32 + pfadd mm1, mm2 + + push tmp + pop DWORD PTR [eax] + + movd DWORD PTR [eax+4], mm3 + + add ebx, 8 + add eax, 8 + + movq mm2, [ebx] + movq mm3, [eax] + + pfmul mm2, mm3 + pfsub mm1, mm2 + + psrlq mm2, 32 + pfsub mm1, mm2 + + // Store new hist + movd tmp, mm1 + + add ebx, 8 + movq mm2, [ebx] + movq mm3, [eax] + + pfmul mm2, mm3 + pfadd mm1, mm2 + + psrlq mm2, 32 + pfadd mm1, mm2 + + push tmp + pop DWORD PTR [eax] + movd DWORD PTR [eax+4], mm3 + + movd output, mm1 + + femms + } + + output *= ResonInv[revLevel]; + + return(output); +} +#else + +#ifdef HAVE_X86 +float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel) +{ + float *hist2_ptr; + float output; + + hist2_ptr = hist1_ptr + 1; /* next history */ + + /* 1st number of coefficients array is overall input scale factor, + * or filter gain */ + output = (input) * (*coef_ptr++); + + output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr); + output *= ResonInv[revLevel]; + + return(output); +} + +float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) +{ + float *hist2_ptr; + float output; + + hist2_ptr = hist1_ptr + 1; /* next history */ + + /* 1st number of coefficients array is overall input scale factor, + * or filter gain */ + output = (input) * (*coef_ptr++); + + output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr); + + output *= ResonInv[revLevel]; + + return(output); +} +#endif + +#endif + +float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel) +{ + float *hist2_ptr; + float output,new_hist; + + hist2_ptr = hist1_ptr + 1; /* next history */ + + /* 1st number of coefficients array is overall input scale factor, + * or filter gain */ + output = (input) * (*coef_ptr++); + + output = output - *hist1_ptr * (*coef_ptr++); + new_hist = output - *hist2_ptr * (*coef_ptr++); /* poles */ + + output = new_hist + *hist1_ptr * (*coef_ptr++); + output = output + *hist2_ptr * (*coef_ptr++); /* zeros */ + + *hist2_ptr++ = *hist1_ptr; + *hist1_ptr++ = new_hist; + hist1_ptr++; + hist2_ptr++; + + // i = 1 + output = output - *hist1_ptr * (*coef_ptr++); + new_hist = output - *hist2_ptr * (*coef_ptr++); /* poles */ + + output = new_hist + *hist1_ptr * (*coef_ptr++); + output = output + *hist2_ptr * (*coef_ptr++); /* zeros */ + + *hist2_ptr++ = *hist1_ptr; + *hist1_ptr++ = new_hist; + + output *= ResonInv[revLevel]; + + return(output); +} + +#endif + +#if FILTER_64BIT == 1 +// 64-bit version +long iir_filter(long input, __int64 *hist1_ptr, __int64 *coef_ptr) +{ + unsigned int i; + __int64 *hist2_ptr; + __int64 output,new_hist,history1,history2; + + hist2_ptr = hist1_ptr + 1; // next history + + // 1st number of coefficients array is overall input scale factor, + // or filter gain + output = (input * (*coef_ptr++)); + + for (i = 0 ; i < 2; i++) + { + history1 = *hist1_ptr; // history values + history2 = *hist2_ptr; + + output = output - ((history1 * (*coef_ptr++))>>20); + new_hist = output - ((history2 * (*coef_ptr++))>>20); // poles + + output = new_hist + ((history1 * (*coef_ptr++))>>20); + output = output + ((history2 * (*coef_ptr++))>>20); // zeros + + *hist2_ptr++ = *hist1_ptr; + *hist1_ptr++ = new_hist; + hist1_ptr++; + hist2_ptr++; + } + + return(output>>20); +} + +#endif + +#if FILTER_INT == 1 +long iir_filter(long input, signed long *hist1_ptr, signed long *coef_ptr) +{ + unsigned int i; + signed long *hist2_ptr; + signed long output,new_hist,history1,history2; + + hist2_ptr = hist1_ptr + 1; // next history + + // 1st number of coefficients array is overall input scale factor, + // or filter gain + output = (input * (*coef_ptr++)); + + for (i = 0 ; i < 2; i++) + { + history1 = *hist1_ptr; // history values + history2 = *hist2_ptr; + + output = output - ((history1 * (*coef_ptr++))>>10); + new_hist = output - ((history2 * (*coef_ptr++))>>10); // poles + + output = new_hist + ((history1 * (*coef_ptr++))>>10); + output = output + ((history2 * (*coef_ptr++))>>10); // zeros + + *hist2_ptr++ = *hist1_ptr; + *hist1_ptr++ = new_hist; + hist1_ptr++; + hist2_ptr++; + } + + return(output>>10); +} +#endif + +/* end filter stuff */ + +partialFormat PCM[54]; +partialTable PCMList[128]; +Bit32u PCMReassign[55]; +Bit32s PCMLoopTable[55]; + +timbreParam drums[30]; + +Bit16s romfile[PCMSIZE+GRAN]; // 256K +static Bit16s chantable[32]; // 64 bytes +static Bit16s miditable[9]; // 18 bytes + +static CPartialMT32 *partTable[MAXPARTIALS]; +static Bit32s PartialReserveTable[32]; + +// For debuging partial allocation +//static FILE *pInfo; +struct partUsage { + int active[32]; + int assign[32]; + int owner[32]; + int status[32]; +}; + +static Bit32s activeChannels; + +// Some optimization stuff +Bit32s divtable[256]; // 1K +Bit32s smalldivtable[256]; // 1K +static Bit16s freqtable[256]; // 512 bytes +static Bit32u sqrtable[101]; // 404 bytes +static Bit32s keytable[256]; // 1K +static Bit32u wavtable[256]; // 1K +Bit32u wavtabler[64][256]; // 64K +Bit32u looptabler[16][16][256]; // 256K +static Bit32u drumdelta[256]; // 1K +Bit16s sintable[65536]; // 128K +static Bit32s ptable[101]; // 404 bytes +static Bit32s lfotable[101]; // 404 bytes +Bit32s penvtable[16][128]; // 8K +static Bit32s fildeptable[5][128]; // 3K +static Bit32s timekeytable[5][128]; // 3K +static Bit32s filveltable[128][128]; // 64K +static Bit32s veltkeytable[5][128]; // 3K +Bit32s pulsetable[101]; // 400 bytes +Bit32s pulseoffset[101]; // 400 bytes +Bit32s sawtable[128][128]; // 64K +static Bit32s restable[201]; // 804 bytes +//static Bit32s biastable[13]; // 56 bytes +static Bit32s ampbiastable[16][128]; // 8K +static Bit32s fbiastable[16][128]; // 8K +static int filttable[2][128][256]; // 256K +static int nfilttable[128][128][128]; // 64K +float filtcoeff[FILTERGRAN][32][16]; // 512K - hmmm +#if FILTER_64BIT == 1 +static __int64 filtcoefffix[FILTERGRAN][32][16]; +#endif +#if FILTER_INT == 1 +static Bit32s filtcoefffix[FILTERGRAN][32][16]; +#endif +static float revtable[8]; // 16 bytes +static Bit32s finetable[201]; // 804 bytes +Bit32u lfoptable[101][128]; // 32K +Bit32s ampveltable[128][64]; // 32K +Bit32s pwveltable[15][128]; +static Bit32s envtimetable[101]; // 404 bytes +static Bit32s decaytimetable[101]; // 404 bytes +static Bit32s lasttimetable[101]; // 404 bytes +Bit32s amptable[129]; // 516 bytes +static Bit32s voltable[129]; // 516 bytes +static float padjtable[51]; // 204 bytes +static Bit32s bendtable[49]; // 195 bytes +float ResonFactor[32]; +float ResonInv[32]; + +Bit16s smallnoise[441]; // 4410 bytes at 44Khz +Bit32s samplepos = 0; + +Bit16s* waveforms[4][256]; // 2K +Bit32u waveformsize[4][256]; +Bit16s tmpforms[4][65536]; // 128K +Bit16s finalforms[4][8192]; // 64K + +// Corresponding drum patches as matched to keyboard +Bit8s DrumTable[42] = { + 0, 0, 10, 1, 11, 5, 4, 6, 4, 29, 3, 7, 3, 2, 8, 2, 9, -1, -1, 22, + -1, 12, -1, -1, -1, 18, 19, 13, 14, 15, 16, 17, 20, 21, 27, 24, + 26, 25, 28, -1, 23, -1 }; + +// Pan-pot position of drums +Bit16s drmPanTable[42] = { + 64, 64, 72, 64, 48, 72, 24, 72, 24, 72, 48, 72, 48, 96, 72, 96, 48, 1, 1, 40, + 1, 64, 1, 1, 1, 104, 88, 48, 40, 32, 64, 80, 104 , 104, 40, 88, + 40, 40, 32, 1, 16, 1 }; + +Bit8u PartialStruct[13] = { + 0, 0, 2, 2, 1, 3, + 3, 0, 3, 0, 2, 1, 3 }; + +Bit8u PartMixStruct[13] = { + 0, 1, 0, 1, 1, 0, + 1, 3, 3, 2, 2, 2, 2 }; + +Bit8u InitInstr[8] = { + 68, 48, 95, 78, 41, 3, 110, 122}; + +Bit8s LoopPatterns[16][16] = { + 2,3,4,5,6,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 8,9,10,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1, + 17,18,19,20,21,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 22,23,24,25,26,27,28,29,-1,-1,-1,-1,-1,-1,-1,-1, + 30,31,32,33,34,35,36,37,-1,-1,-1,-1,-1,-1,-1,-1, + 45,46,47,48,49,50,51,52,53,-1,-1,-1,-1,-1,-1,-1, + 15,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 30,35,32,33,34,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +}; + + +Bit32s LoopPatternTuning[16][16] = { + 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1, + 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1, + 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1, + 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1, + 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1, + 0x2590B,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0x1294A,0x1294A,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + +}; + + +// These are division constants for the TVF depth key follow +Bit32u depexp[5] = {3000,950,485,255,138}; + +//Amplitude time velocity follow exponential coefficients +double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637}; +double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539}; + +//Envelope time keyfollow exponential coefficients +double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215}; +double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067}; + +// This caches the drum information +patchCache drumCache[30][4]; +int drumPan[30][2]; + +bool isEnabled; + + +Reverb *myReverb; +revmodel *newReverb; +bool usingSIMD; + +Bit16s mastervolume; + +Bit32u curRevMode; +Bit32u curRevTime; +Bit32u curRevLevel; + +Bit32u partialsPlayed; // Variable containing the whole count of partials played +Bit32u avgPartials; // Tally of average number of partials a second +Bit32s partialChan[9]; // The count of partials played per channel + +#if SAVECUSTOM == 1 +Bit32u filenum = 0; +#endif + +/* +t60 = reverb time +hlratio = ratio of low freq t60 to high freq t60 +dur = duration of event/dealloc. on last samp +hall_fact= mult. factor for delay times +revstate = running values for event reverb +*/ + +// t60 follows standard sabine equation +// t60 = .049 * (V / A) +// Where V = is the volume of the enclosure in cubic feet +// And A is the absorbtion of the room in square feet +// Using metric measurements (metres) the .049 is replaced with 0.161 + + + +void InitReverb(Bit32u newRevMode, Bit32u newRevTime, Bit32u sampRate) { + if(newReverb != NULL) delete newReverb; + newReverb = new revmodel(); + + /* + if(myReverb != NULL) delete myReverb; + + float t60; + switch(newRevMode) { + case 0: + t60 = .161f * REV_ROOMT60; + break; + case 1: + t60 = .161f * REV_HALLT60; + break; + case 2: + t60 = .161f * REV_PLATET60; + break; + case 3: + t60 = .161f * REV_TAPT60; + break; + default: + t60 = .161f * REV_ROOMT60; + break; + } + + t60 = t60 * 0.625; + myReverb = new Reverb(t60,HLRATIO,sampRate/(8/(newRevTime+1)),(newRevTime+1),sampRate); + */ + curRevTime = newRevTime; + curRevMode = newRevMode; + + switch(newRevMode) { + case 0: + newReverb->setroomsize((float).1); + newReverb->setdamp((float).75); + break; + case 1: + newReverb->setroomsize((float).5); + newReverb->setdamp((float).5); + break; + case 2: + newReverb->setroomsize((float).5); + newReverb->setdamp((float).1); + break; + case 3: + newReverb->setroomsize((float)1); + newReverb->setdamp((float).75); + break; + default: + newReverb->setroomsize((float).1); + newReverb->setdamp((float).5); + break; + } + newReverb->setdry(1); + newReverb->setwet((float)mt32ram.params.system.reverbLevel / 8.0); + newReverb->setwidth((float)curRevTime / 8.0); + + +} + +class MidiChannel { +public: + bool isPlaying; + + volset volumesetting; + + int reverbat, reverbleft, reverbright; + int panpot; + + patchCache pcache[4]; + + Bit32u bend; + Bit32s volume; + + dpoly polyTable[MAXPOLY]; + + +private: + bool isRy; + int sampRate; + int freq; + int channum; + int partialCount; + int velocity; + long sndoff; + int octave; + int note; + + int patch; + char currentInstr[20]; + + int keyshift; + int assignmode; + int storedpatch; + bool sustain; + bool init; + + Bit32u P1Mix; + Bit32u P2Mix; + + bool holdpedal; + + +public: + MidiChannel(int samp, int cnum); + //short getSample(short *lspecial, short *rspecial); + void PlayPoly(int m, int f,int vel); + void PlayDrum(int m, int f,int vel); + void InitKeyFollow(dpoly *tpoly); + void PlayNote(int f, int vel); + void StopNote(int f, int vel); + void AllStop(); + void SetVolume(int vol); + void SetPan(int vol); + void SetBend(int vol); + void SetModulation(int vol); + void SetPatch(int patchnum,int drumNum); + void SetHoldPedal(bool pedalval); + void StopPedalHold(); + void RefreshPatch(); + void CheckNoteList(); + + int FixKeyfollow(int srckey, int *dir); + int FixBiaslevel(int srcpnt, int *dir); + + //Bit32s getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly, bool inDecay); + //Bit32s getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly, bool inDecay); + //Bit32s getFiltEnvelope(Bit16s wg, dpoly::partialStatus *pStat, dpoly *poly, bool inDecay); + + //void StartDecay(int envnum, Bit32s startval, dpoly::partialStatus *pStat, dpoly *poly); + +}; + +void MidiChannel::SetHoldPedal(bool pedalval) { + holdpedal = pedalval; +} + +void MidiChannel::SetBend(int vol) { + + //int tmpbend = ((vol - 0x2000) * (int)mt32ram.params.pSettings[storedpatch].benderRange) >> 13; + //bend = bendtable[tmpbend+24]; + + float bend_range = (float)mt32ram.params.pSettings[storedpatch].benderRange / 24; + bend = 4096 + (int)((float)(vol - 8192) * bend_range); +} + +void MidiChannel::SetModulation(int vol) { + + // Just a bloody guess, as always, before I get things figured out + int t; + for(t=0;t<4;t++) { + if(pcache[t].playPartial) { + int newrate = (pcache[t].modsense * vol) >> 7; + //pcache[t].lfoperiod = lfotable[newrate]; + pcache[t].lfodepth = newrate; + } + } + +} + +INLINE void StartDecay(int envnum, Bit32s startval, dpoly::partialStatus *pStat, dpoly *poly) { + + patchCache *tcache = pStat->tcache; + dpoly::partialStatus::envstatus *tStat = &pStat->envs[envnum]; + + tStat->sustaining = false; + tStat->decaying = true; + tStat->envpos = 0; + tStat->envbase = startval; + + switch(envnum) { + case AMPENV: + tStat->envsize = (decaytimetable[(int)tcache->ampEnv.envtime[4]] * timekeytable[(int)tcache->ampEnv.envtkf][poly->freqnum]) >> 8; + tStat->envdist = -startval; + break; + case FILTENV: + tStat->envsize = (decaytimetable[(int)tcache->filtEnv.envtime[4]] * timekeytable[(int)tcache->filtEnv.envtkf][poly->freqnum]) >> 8; + tStat->envdist = -startval; + break; + case PITCHENV: + tStat->envsize = (decaytimetable[(int)tcache->pitchEnv.time[3]] * timekeytable[(int)tcache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8 ; + tStat->envdist = tcache->pitchEnv.level[4] - startval; + break; + default: + break; + } + tStat->envsize++; + + +} + + + + + +INLINE Bit32s getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly) { + Bit32s tc; + + patchCache *tcache = pStat->tcache; + dpoly::partialStatus::envstatus *tStat = &pStat->envs[AMPENV]; + + if(!pStat->playPartial) return 0; + + if(tStat->decaying) { + + if(pStat->isDecayed) { + pStat->playPartial = false; + + tc = 0; + } else { + tc = tStat->envbase; + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + if (tc < 0) + tc = 0; + if((tStat->envpos >= tStat->envsize) || (tc == 0)){ + pStat->PCMDone = true; + pStat->isDecayed = true; + pStat->playPartial = false; + } + } + + } else { + + if((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { + if(tStat->envstat==-1) { + tStat->envbase = 0; + } else { + tStat->envbase = tcache->ampEnv.envlevel[tStat->envstat]; + } + tStat->envstat++; + tStat->envpos = 0; + + switch(tStat->envstat) { + case 0: + //Spot for velocity time follow + //Only used for first attack + tStat->envsize = (envtimetable[(int)tcache->ampEnv.envtime[tStat->envstat]] * veltkeytable[(int)tcache->ampEnv.envvkf][poly->vel]) >> 8; + //LOG_MSG("Envstat %d, size %d, %d %d", tStat->envstat, tStat->envsize, tcache->ampEnv.envtime[tStat->envstat], veltkeytable[tcache->ampEnv.envvkf][poly->vel]); + break; + case 3: + // Final attack envelope uses same time table as the decay + //tStat->envsize = decaytimetable[tcache->ampEnv.envtime[tStat->envstat]]; + tStat->envsize = lasttimetable[(int)tcache->ampEnv.envtime[(int)tStat->envstat]]; + //LOG_MSG("Envstat %d, size %d", tStat->envstat, tStat->envsize); + break; + case 4: + //LOG_MSG("Envstat %d, size %d", tStat->envstat, tStat->envsize); + tc =tcache->ampsustain; + if(!poly->sustain) + StartDecay(AMPENV, tc, pStat, poly); + else + tStat->sustaining = true; + + goto PastCalc; + default: + //Spot for timekey follow + //Only used in subsquent envelope parameters, including the decay + tStat->envsize = (envtimetable[(int)tcache->ampEnv.envtime[tStat->envstat]] * timekeytable[(int)tcache->ampEnv.envtkf][poly->freqnum]) >> 8; + + //LOG_MSG("Envstat %d, size %d", tStat->envstat, tStat->envsize); + break; + } + + tStat->envsize++; + tStat->envdist = tcache->ampEnv.envlevel[tStat->envstat] - tStat->envbase; + + if(tStat->envdist != 0) { + tStat->counter = abs(tStat->envsize / tStat->envdist); + //LOG_MSG("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist); + } else { + tStat->counter = 0; + //LOG_MSG("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist); + } + } + + tc = tStat->envbase; + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + tStat->count = tStat->counter; + + +PastCalc: + + tc = (tc * (Bit32s)tcache->amplevel) >> 7; + + } + + // Prevlevel storage is bottle neck + tStat->prevlevel = tc; + + //Bias level crap stuff now + //I unrolled the loop + + int dist,bias; + + // Bias 1 + if(tcache->ampblevel[0]!=0) { + bias = tcache->ampbias[0]; + if(tcache->ampdir[0]==0) { + // < Bias + if(poly->freqnum < bias) { + dist = bias-poly->freqnum; + tc = (tc * ampbiastable[tcache->ampblevel[0]][dist]) >> 8; + + } + } else { + // > Bias + if(poly->freqnum > bias) { + dist = poly->freqnum-bias; + tc = (tc * ampbiastable[tcache->ampblevel[0]][dist]) >> 8; + } + } + } + + //Bias 2 + if(tcache->ampblevel[1]!=0) { + bias = tcache->ampbias[1]; + if(tcache->ampdir[1]==0) { + // < Bias + if(poly->freqnum < bias) { + dist = bias-poly->freqnum; + tc = (tc * ampbiastable[tcache->ampblevel[1]][dist]) >> 8; + + } + } else { + // > Bias + if(poly->freqnum > bias) { + dist = poly->freqnum-bias; + tc = (tc * ampbiastable[tcache->ampblevel[1]][dist]) >> 8; + } + } + } + + + + return tc; +} + + +INLINE Bit32s getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly) { + patchCache *tcache = pStat->tcache; + dpoly::partialStatus::envstatus *tStat = &pStat->envs[PITCHENV]; + + Bit32s tc; + pStat->pitchsustain = false; + if(tStat->decaying) { + + if((pStat->isDecayed) || (tStat->envpos >= tStat->envsize)) { + tc = tcache->pitchEnv.level[4]; + } else { + tc = tStat->envbase; + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + } + } else { + + if(tStat->envstat==3) { + tc =tcache->pitchsustain; + if(poly->sustain) { + pStat->pitchsustain = true; + } else { + StartDecay(PITCHENV, tc, pStat, poly); + } + + } else { + + if((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { + tStat->envstat++; + + tStat->envbase = tcache->pitchEnv.level[tStat->envstat]; + tStat->envsize = (envtimetable[(int)tcache->pitchEnv.time[tStat->envstat]] * timekeytable[(int)tcache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8; + + + tStat->envpos = 0; + tStat->envsize++; + tStat->envdist = tcache->pitchEnv.level[tStat->envstat+1] - tStat->envbase; + } + + tc = tStat->envbase; + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + + } + tStat->prevlevel = tc; + + + } + + return tc; + +} + + +INLINE Bit32s getFiltEnvelope(Bit16s wg, dpoly::partialStatus *pStat, dpoly *poly) { + + int reshigh; + + //float *hist = pStat->history; + //__int64 *hist = pStat->history; + //long *hist = pStat->history; + int filt,cutoff,depth,keyfollow, realfollow; + + patchCache *tcache = pStat->tcache; + dpoly::partialStatus::envstatus *tStat = &pStat->envs[FILTENV]; + + keyfollow = pStat->filtval; + realfollow = pStat->realval; + + int fr = poly->freqnum; + + if(tStat->decaying) { + if(pStat->isDecayed) { + reshigh = 0; + } else { + reshigh = tStat->envbase; + reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + if(tStat->envpos >= tStat->envsize) reshigh = 0; + } + + } else { + if(tStat->envstat==4) { + reshigh =tcache->filtsustain; + if(!poly->sustain) StartDecay(FILTENV, reshigh, pStat, poly); + } else { + + if((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { + if(tStat->envstat==-1) { + tStat->envbase = 0; + } else { + tStat->envbase = tcache->filtEnv.envlevel[tStat->envstat]; + } + tStat->envstat++; + tStat->envpos = 0; + if(tStat->envstat==3) { + tStat->envsize = lasttimetable[(int)tcache->filtEnv.envtime[tStat->envstat]]; + } else { + tStat->envsize = (envtimetable[(int)tcache->filtEnv.envtime[tStat->envstat]] * timekeytable[(int)tcache->filtEnv.envtkf][poly->freqnum]) >> 8; + } + + tStat->envsize++; + tStat->envdist = tcache->filtEnv.envlevel[tStat->envstat] - tStat->envbase; + } + + reshigh = tStat->envbase; + reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + + } + tStat->prevlevel = reshigh; + } + + cutoff = (tcache->filtEnv.cutoff); + + //if(tcache->waveform==1) reshigh = (reshigh * 3) >> 2; + + depth = (tcache->filtEnv.envdepth); + + //int sensedep = (depth * 127-tcache->filtEnv.envsense) >> 7; + depth = (depth * filveltable[poly->vel][(int)tcache->filtEnv.envsense]) >> 8; + + int bias = tcache->tvfbias; + int dist; + + + if(bias!=0) { + //LOG_MSG("Cutoff before %d", cutoff); + if(tcache->tvfdir == 0) { + if(fr < bias) { + dist = bias - fr; + cutoff = (cutoff * fbiastable[tcache->tvfblevel][dist]) >> 8; + + } + } else { + // > Bias + if(fr > bias) { + dist = fr - bias; + cutoff = (cutoff * fbiastable[tcache->tvfblevel][dist]) >> 8; + } + + } + //LOG_MSG("Cutoff after %d", cutoff); + + } + + + + depth = (depth * fildeptable[tcache->tvfdepth][fr]) >> 8; + reshigh = (reshigh * depth) >> 7; + + + Bit32s tmp; + + cutoff *= keyfollow; + cutoff /= realfollow; + + reshigh *= keyfollow; + reshigh /= realfollow; + + if(cutoff>100) cutoff = 100; + if(reshigh>100) reshigh = 100; + if(cutoff<0) cutoff = 0; + if(reshigh<0) reshigh = 0; + tmp = nfilttable[fr][cutoff][reshigh]; + //tmp *= keyfollow; + //tmp /= realfollow; + + //LOG_MSG("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256); + + return tmp; + //reshigh = (reshigh * depth * 6)>>10; + + filt = (cutoff + reshigh) * keyfollow; + filt = filt / realfollow; + + + + + if(filt<0) filt = 0; + if(filt>=200) filt = 199; + tmp = filttable[(int)tcache->waveform][fr][filt]; + return tmp; + +} + +MidiChannel::MidiChannel(int samp, int cnum) { + isRy = holdpedal = isPlaying = false; + volumesetting.rightvol = volumesetting.leftvol = volumesetting.rightvol2 = volumesetting.leftvol2 = 32767; + patch = storedpatch = 0; + sampRate = samp; + channum = cnum; + volume = 102; + panpot = 64; + init = true; + bend = 0x1000; + memset(polyTable,0,sizeof(polyTable)); + memset(pcache, 0, sizeof(pcache)); + + if(cnum==8) { + isRy = true; + int pan; + volume = 102; + // Cache drum patches + int q,m; + for(q=0;q<30;q++) { + SetPatch(0,q); + for(m=0;m<42;m++) { + if(DrumTable[m]==q) { + pan = drmPanTable[m]; + if(pan<64) { + drumPan[q][0] = 32767; // lv + drumPan[q][1] = pan << 9; // rv + } else { + drumPan[q][0] = (63-(pan-63)) << 9; // lv + drumPan[q][1] = 32767; // rv + } + break; + } + } + } + } + init = false; +} + +INLINE int MidiChannel::FixBiaslevel(int srcpnt, int *dir) { + int noteat = srcpnt & 63; + int outnote; + *dir = 1; + if(srcpnt < 64) *dir = 0; + outnote = 33 + noteat; + //LOG_MSG("Bias note %d, dir %d", outnote, *dir); + + return outnote; + +} + +INLINE int MidiChannel::FixKeyfollow(int srckey, int *dir) { + if (srckey>=0 && srckey<=16) { + //int keyfix[17] = { 256, 128, 64, 0, 32, 64, 96, 128, 128+32, 192, 192+32, 256, 256+64, 256+128, 512, 259, 269 }; + int keyfix[17] = { 256*16, 128*16, 64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116}; + + if (srckey<3) + *dir = -1; + else if (srckey==3) + *dir = 0; + else + *dir = 1; + + return keyfix[srckey]; + } else { + //LOG_MSG("Missed key: %d", srckey); + return 256; + } +} + + +void MidiChannel::RefreshPatch() { + SetPatch(storedpatch,-1); +} + +void MidiChannel::SetPatch(int patchnum,int drumNum) { + int i, j, k, pcm,t; + + //int chanoff = channum; + /* TRISTAN: flush all partials on this channel. This is a hack. */ +// for(i=0; i < MAXPARTIALS; i++) +// if(partTable[i]->ownerChan == channum) +// partTable[i]->isActive = false; + + /* TRISTAN: check if any partials are still playing on this channel, if * + * so then duplicate the cached data from the channel to the partial so that * + * we can change the channels cache without affecting the partial. Hopefully * + * this is fairly rare. */ + if (storedpatch != patchnum) + for (i = 0; i < MAXPARTIALS; i++) + if(partTable[i]->ownerChan == channum) + if (partTable[i]->isActive) + { + /* copy cache data */ + for (j = 0; j < 4; j++) + partTable[i]->cachebackup[j] = pcache[j]; + + /* update pointers */ + for (j = 0; j < partTable[i]->timbreNum; j++) + for (k = 0; k < 4; k++) + partTable[i]->tmppoly[j].pStatus[k].tcache = partTable[i]->cachebackup + k; + + partTable[i]->tcache = partTable[i]->cachebackup; + } + + storedpatch = patchnum; + + patch = (mt32ram.params.pSettings[patchnum].timbreGroup * 64) + mt32ram.params.pSettings[patchnum].timbreNum; + + timbreParam timSrc; + //timSrc = mt32ram.params.patch[patch]; + if (drumNum==-1) { + timSrc = mt32ram.params.timTemp[channum]; + memset(¤tInstr,0,16); + memcpy(¤tInstr,timSrc.common.name,10); + + + } else { + // This is to cache all the drum tibres ahead of time + timSrc = drums[drumNum]; + } + //LOG_MSG("Reloading patch %d", channum); + sustain = (timSrc.common.nosustain == 0); + P1Mix = PartMixStruct[(int)timSrc.common.pstruct12]; + P2Mix = PartMixStruct[(int)timSrc.common.pstruct34]; + + //sustain = true; + partialCount = 0; + + for(t=0;t<4;t++) { + + // Calculate and cache common parameters + pcm = timSrc.partial[t].wg.pcmwave; + pcache[t].rawPCM = pcm; + + pcache[t].convPCM = PCMList[pcm]; + pcache[t].useBender = (timSrc.partial[t].wg.bender == 1); + + // pcm > -1 + switch (t) { + case 0: + pcache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct12] >> 1) & 0x1; + break; + case 1: + pcache[t].PCMPartial = PartialStruct[(int)timSrc.common.pstruct12] & 0x1; + break; + case 2: + pcache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct34] >> 1) & 0x1; + break; + case 3: + pcache[t].PCMPartial = PartialStruct[(int)timSrc.common.pstruct34] & 0x1; + break; + default: + break; + } + + + if( ((timSrc.common.pmute >> (t)) & 0x1) == 1 ) { + pcache[t].playPartial = true; + pcache[t].usePartial = true; + + // Hack and a half... needed so drum partial numbers come through + pcache[0].partCount = t+1; + + partialCount++; + } else { + pcache[t].playPartial = false; + pcache[t].usePartial = false; + continue; + } + + pcache[t].sustain = (timSrc.common.nosustain == 0); + pcache[t].waveform = timSrc.partial[t].wg.waveform; + pcache[t].pulsewidth = timSrc.partial[t].wg.pulsewid; + pcache[t].pwsens = timSrc.partial[t].wg.pwvelo; + pcache[t].pitchkeyfollow = FixKeyfollow(timSrc.partial[t].wg.keyfollow, &pcache[t].pitchkeydir); + + // Calculate and cache pitch stuff + pcache[t].pitchshift = (timSrc.partial[t].wg.coarse+mt32ram.params.pSettings[patchnum].keyShift); + Bit32s pFine, tFine, fShift; + pFine = (Bit32s)timSrc.partial[t].wg.fine; + tFine = (Bit32s)mt32ram.params.pSettings[patchnum].fineTune; + fShift = ((pFine - 50) + (tFine - 50)) + 100; + pcache[t].fineshift = finetable[fShift]; + + keyshift = mt32ram.params.pSettings[patchnum].keyShift-24; + assignmode = mt32ram.params.pSettings[patchnum].assignMode; + + pcache[t].pitchEnv = timSrc.partial[t].env; + pcache[t].pitchEnv.sensitivity = (int)((float)pcache[t].pitchEnv.sensitivity*1.27); + pcache[t].pitchsustain = pcache[t].pitchEnv.level[3]; + + // Calculate and cache TVA envelope stuff + pcache[t].ampEnv = timSrc.partial[t].tva; + int l; + for(l=0;l<4;l++) { + pcache[t].ampEnv.envlevel[l] = (int)((float)pcache[t].ampEnv.envlevel[l]*1.27); + } + pcache[t].ampEnv.level = (int)((float)pcache[t].ampEnv.level*1.27); + float tvelo = ((float)pcache[t].ampEnv.velosens/100.0); + float velo = (fabs(tvelo-.5)/.5); + pcache[t].ampenvdir = 0; + if(tvelo<.5) pcache[t].ampenvdir = 1; + velo *= 63.0; + pcache[t].ampEnv.velosens = (int)(velo); + + pcache[t].ampbias[0] = FixBiaslevel(pcache[t].ampEnv.biaspoint1, &pcache[t].ampdir[0]); + pcache[t].ampblevel[0] = 12-pcache[t].ampEnv.biaslevel1; + pcache[t].ampbias[1] = FixBiaslevel(pcache[t].ampEnv.biaspoint2, &pcache[t].ampdir[1]); + pcache[t].ampblevel[1] = 12-pcache[t].ampEnv.biaslevel2; + pcache[t].ampdepth = pcache[t].ampEnv.envvkf * pcache[t].ampEnv.envvkf; + pcache[t].ampsustain = pcache[t].ampEnv.envlevel[3]; + pcache[t].amplevel = pcache[t].ampEnv.level; + + + // Calculate and cache filter stuff + pcache[t].filtEnv = timSrc.partial[t].tvf; + pcache[t].tvfdepth = pcache[t].filtEnv.envdkf; + pcache[t].filtkeyfollow = FixKeyfollow(pcache[t].filtEnv.keyfollow, &pcache[t].keydir); + pcache[t].filtEnv.envdepth = (int)((float)pcache[t].filtEnv.envdepth * 1.27); + pcache[t].tvfbias = FixBiaslevel(pcache[t].filtEnv.biaspoint, &pcache[t].tvfdir); + pcache[t].tvfblevel = pcache[t].filtEnv.biaslevel; + pcache[t].filtsustain = pcache[t].filtEnv.envlevel[3]; + + // Calculate and cache LFO stuff + //pcache[t].lfodepth = (int)((float)timSrc.partial[t].lfo.depth * 1.27); + pcache[t].lfodepth = timSrc.partial[t].lfo.depth; + pcache[t].lfoperiod = lfotable[(int)timSrc.partial[t].lfo.rate]; + pcache[t].lforate = timSrc.partial[t].lfo.rate; + pcache[t].modsense = timSrc.partial[t].lfo.modsense; + + } + //LOG_MSG("Res 1: %d 2: %d 3: %d 4: %d", pcache[0].waveform, pcache[1].waveform, pcache[2].waveform, pcache[3].waveform); + + if(drumNum!=-1) memcpy(drumCache[drumNum],pcache,sizeof(pcache)); + if(!init) AllStop(); + + //LOG_MSG("Channel #%d set instrument: %s - %d - %d - %d - %d - pc %d", chanoff, currentInstr, timSrc.partial[0].wg.pcmwave, timSrc.partial[1].wg.pcmwave, timSrc.partial[2].wg.pcmwave, timSrc.partial[3].wg.pcmwave, pcache[0].partCount); + +} + +void MidiChannel::SetVolume(int vol) { + + volume = voltable[vol]; + +} + +void MidiChannel::SetPan(int pan) { + panpot = pan; + + if(pan<64) { + volumesetting.leftvol = 32767; + volumesetting.rightvol = pan << 9; + + } + if(pan>=64) { + volumesetting.rightvol = 32767; + volumesetting.leftvol = (63-(pan-63)) << 9; + + } + + + + //LOG(LOG_ERROR|LOG_MISC,"Pan %d",panpot); +} + +INLINE Bit16s RingMod(Bit16s p1, Bit16s p2, bool useFirst) { + if(useFirst) { + //return (Bit16s)( ( ((float)p1/32767.0) * ((float)p2/32767.0) ) * 32767); + return (Bit16s)( ((Bit32s)p1 * (Bit32s)p2) >> 15); + + } else { + // An interesting undocumented feature of the MT-32 + // Putting ring mod on a muted partial introduces noise to the ring modulator + // Dune 2 makes use of this + return (Bit16s)( ((Bit32s)smallnoise[samplepos/100] * (Bit32s)p2) >> 15); + } +} + + +INLINE void MidiChannel::InitKeyFollow(dpoly *tpoly) { + // Setup partial keyfollow + int keyfollow = 0; + int tmpval = tpoly->freqnum,t; + int keyedval; + + // Note follow relative to middle C + + for(t=0;t<4;t++) { + + // Calculate keyfollow for pitch + switch(pcache[t].pitchkeydir) { + case -1: + keyfollow = ((int)((MIDDLEC*2-tmpval*2)/2) * pcache[t].pitchkeyfollow) >> 12; + break; + case 0: + keyfollow = 0; + break; + case 1: + keyfollow = ((int)((tmpval*2-MIDDLEC*2)/2)*pcache[t].pitchkeyfollow)>>12; + break; + } + if((pcache[t].pitchkeyfollow>4096) && (pcache[t].pitchkeyfollow<4200)) { + // Be sure to round up on keys below MIDDLEC + if(((tmpval*2-MIDDLEC*2)/2) < 0) keyfollow++; + } + keyedval = (keyfollow + pcache[t].pitchshift); + if(keyedval>108) keyedval = 108; + if(keyedval<12) keyedval = 12; + + tpoly->pStatus[t].keyedval = keyedval; + tpoly->pStatus[t].noteval = tmpval; + + // Calculate keyfollow for filter + + int realfol = ((tmpval*2)-(MIDDLEC*2))/2; + switch(pcache[t].keydir) { + case -1: + keyfollow = ((int)((MIDDLEC*2-tmpval*2)/2) * pcache[t].filtkeyfollow )>>12; + break; + case 0: + keyfollow = tmpval; + break; + case 1: + keyfollow = ((int)((tmpval*2-(MIDDLEC*2))/2) * pcache[t].filtkeyfollow )>>12; + break; + } + + if (keyfollow>108) keyfollow=108; + if (keyfollow<-108) keyfollow =-108; + tpoly->pStatus[t].filtnoval = keyfollow+108; + tpoly->pStatus[t].filtval = keytable[keyfollow+108]; + tpoly->pStatus[t].realval = keytable[realfol+108]; + + } + + +} + +CPartialMT32 * AllocPartial(int chanNum) { + int i; + + CPartialMT32 *outPart = NULL; + +#if MAXPARTIALS == 32 + for(i=0;i<MAXPARTIALS;i++) { + if(PartialReserveTable[i] == chanNum) { + if(outPart == NULL) { + if(!partTable[i]->isActive) { + outPart = partTable[i]; + } + } + } + } + if(outPart != NULL) { + outPart->isActive = true; + outPart->age = 0; + } else { + for(i=0;i<MAXPARTIALS;i++) { + if(!partTable[i]->isActive) { + outPart = partTable[i]; + outPart->isActive = true; + outPart->age = 0; + break; + } + } + } +#else + for(i=0;i<MAXPARTIALS;i++) { + if(outPart == NULL) { + if(!partTable[i]->isActive) { + outPart = partTable[i]; + } + } + } + if(outPart != NULL) { + outPart->isActive = true; + outPart->age = 0; + } + +#endif + return outPart; +} + +int GetFreePartialCount(void) { + int i; + int count = 0; + memset(partialChan,0,sizeof(partialChan)); + for(i=0;i<MAXPARTIALS;i++) { + if(!partTable[i]->isActive) { + count++; + } else { + partialChan[partTable[i]->ownerChan]++; + } + } + return count; +} + + +INLINE void MidiChannel::PlayPoly(int m, int f,int vel) { + + CPartialMT32 *tmpParts[4]; + f += keyshift; + if((f<0) || (f >255)) return; + freq = freqtable[f]; + dpoly *tpoly = &polyTable[m]; + + tpoly->isPlaying = true; + tpoly->isDecay = false; + tpoly->isActive = true; + tpoly->pcmoff.pcmabs = 0; + tpoly->freq = freq; + tpoly->freqnum = f; + tpoly->pcmdelta = 0x100; + tpoly->age = 0; + tpoly->vel = vel; + tpoly->chan = this->channum; + tpoly->pedalhold = false; + tpoly->firstsamp = true; + memset(tpoly->pStatus,0,sizeof(tpoly->pStatus)); + int x,e; + + for(x=0;x<4;x++) { + + tpoly->pStatus[x].partNum = x; + tpoly->pStatus[x].lfopos = 0; + tpoly->pStatus[x].playPartial = pcache[x].playPartial; + tpoly->pStatus[x].usePartial = pcache[x].usePartial; + tpoly->pStatus[x].tcache = &pcache[x]; + tpoly->pStatus[x].pulsewidth = pcache[x].pulsewidth + pwveltable[pcache[x].pwsens][vel]; + + if(tpoly->pStatus[x].pulsewidth > 100) tpoly->pStatus[x].pulsewidth = 100; + if(tpoly->pStatus[x].pulsewidth < 0) tpoly->pStatus[x].pulsewidth = 0; + + if(pcache[x].playPartial) { + tmpParts[x] = AllocPartial(channum); + } else { + tmpParts[x] = NULL; + } + tpoly->pStatus[x].myPart = (void *)tmpParts[x]; + + for(e=0;e<4;e++) { + tpoly->pStatus[x].envs[e].envstat = -1; + tpoly->pStatus[x].envs[e].sustaining = false; + tpoly->pStatus[x].envs[e].decaying = false; + tpoly->pStatus[x].envs[e].envpos = 0; + tpoly->pStatus[x].envs[e].count = 0; + tpoly->pStatus[x].envs[e].counter = 0; + + + } + } + + bool allnull = true; + for(x=0;x<4;x++) { + //if(tmpParts[x] != NULL) allnull = false; + if(pcache[x].playPartial) allnull = false; + } + //if(allnull) LOG_MSG("No paritals to play for %s", this->currentInstr); + + tpoly->partCount = pcache[0].partCount; + tpoly->P1Mix = P1Mix; + tpoly->P2Mix = P2Mix; + tpoly->sustain = sustain; + tpoly->isRy = false; + tpoly->bendptr = &bend; + tpoly->volumeptr = &volume; + tpoly->pansetptr = &volumesetting; + + InitKeyFollow(tpoly); + + for(x=0;x<4;x++) { + if(tmpParts[x] != NULL) { + int pairPart, useMix, partNum; + switch(x) { + case 0: + useMix = P1Mix; + partNum = 0; + pairPart = 1; + break; + case 1: + useMix = P1Mix; + partNum = 1; + pairPart = 0; + break; + case 2: + useMix = P2Mix; + partNum = 0; + pairPart = 3; + break; + case 3: + useMix = P2Mix; + partNum = 1; + pairPart = 2; + break; + default: + useMix = P1Mix; + partNum = 0; + pairPart = 0; + break; + } + tmpParts[x]->startPartial(tpoly,tpoly->pStatus[x].tcache,&tpoly->pStatus[x],tmpParts[pairPart],useMix,partNum,channum,x); + tpoly->partActive[x] = true; + } else { + tpoly->partActive[x] = false; + } + } + +#if DISPLAYINSTR == 1 + memset(¤tInstr,0,16); + memcpy(¤tInstr,mt32ram.params.patch[patch].common.name,10); + //LOG_MSG("MT32 chan %d (\"%s\") s note poly %d - Vel %d Freq %d Vol %d", channum, currentInstr, m, vel, f, volume); +#endif +} + +INLINE void MidiChannel::PlayDrum(int m, int f,int vel) { + if(!((f>=35) && (f<= 76))) return; + CPartialMT32 *tmpParts[4]; + freq = freqtable[60]; + dpoly *tpoly = &polyTable[m]; + + tpoly->drumnum = f; + tpoly->isPlaying = true; + tpoly->isDecay = false; + tpoly->isActive = true; + tpoly->pcmnum = DrumTable[f-35]; + tpoly->pcmoff.pcmabs = 0; + tpoly->freq = freq; + tpoly->freqnum = 60; + tpoly->pcmdelta = 0x100; + tpoly->age = 0; + tpoly->vel = vel; + tpoly->chan = this->channum; + tpoly->pedalhold = false; + tpoly->firstsamp = true; + memset(tpoly->pStatus,0,sizeof(tpoly->pStatus)); + memcpy(pcache,drumCache[tpoly->pcmnum],sizeof(pcache)); + int x,e; + for(x=0;x<4;x++) { + tpoly->pStatus[x].partNum = x; + tpoly->pStatus[x].playPartial = pcache[x].playPartial; + tpoly->pStatus[x].usePartial = pcache[x].usePartial; + tpoly->pStatus[x].tcache = &drumCache[tpoly->pcmnum][x]; + + if(pcache[x].playPartial) { + tmpParts[x] = AllocPartial(channum); + } else { + tmpParts[x] = NULL; + } + tpoly->pStatus[x].myPart = (void *)tmpParts[x]; + + for(e=0;e<4;e++) { + tpoly->pStatus[x].envs[e].envstat = -1; + tpoly->pStatus[x].envs[e].count = 0; + tpoly->pStatus[x].envs[e].counter = 0; + } + } + + tpoly->P1Mix = PartMixStruct[(int)drums[tpoly->pcmnum].common.pstruct12]; + tpoly->P2Mix = PartMixStruct[(int)drums[tpoly->pcmnum].common.pstruct34]; + tpoly->sustain = (drums[tpoly->pcmnum].common.nosustain == 0); + tpoly->isRy = true; + tpoly->bendptr = &tpoly->drumbend; + tpoly->drumbend = 0x1000; + + tpoly->partCount = pcache[0].partCount; + tpoly->volumeptr = &volume; + tpoly->pansetptr = &volumesetting; + + InitKeyFollow(tpoly); + + for(x=0;x<4;x++) { + if(tmpParts[x] != NULL) { + int pairPart, useMix, partNum; + switch(x) { + case 0: + useMix = P1Mix; + partNum = 0; + pairPart = 1; + break; + case 1: + useMix = P1Mix; + partNum = 1; + pairPart = 0; + break; + case 2: + useMix = P2Mix; + partNum = 0; + pairPart = 3; + break; + case 3: + useMix = P2Mix; + partNum = 1; + pairPart = 2; + break; + default: + useMix = P1Mix; + partNum = 0; + pairPart = 0; + break; + } + tmpParts[x]->startPartial(tpoly,tpoly->pStatus[x].tcache,&tpoly->pStatus[x],tmpParts[pairPart],useMix,partNum,channum,x); + } + } + +#if DISPLAYINSTR == 1 + memset(¤tInstr,0,16); + memcpy(¤tInstr,drums[tpoly->pcmnum].common.name,10); + //LOG_MSG("MT32 drum chan (f %d = %d) (\"%s\") starting note poly %d - Velocity %d", f, tpoly->pcmnum, currentInstr, m, vel); +#endif +} + +/* +bool FreePartials(int needed, int chanNum) { + int i; + int myChanPrior = (int)mt32ram.params.system.reserveSettings[chanNum]; + if(myChanPrior<partialChan[chanNum]) { + //This can have more channels, must kill off those with less priority + int most, mostchan; + while(needed > 0) { + int selectChan = -1; + //Find the worst offender with more partials than allocated and kill them + most = -1; + mostchan = -1; + int diff; + + for(i=0;i<9;i++) { + diff = partialChan[i] - (int)mt32ram.params.system.reserveSettings[i]; + + if(diff>0) { + if(diff>most) { + most = diff; + mostchan = i; + } + } + } + selectChan = mostchan; + if(selectChan==-1) { + // All channels are within the allocated limits, you suck + // Look for first partial not of this channel that's decaying perhaps? + return false; + } + bool found; + int oldest; + int oldnum; + while(partialChan[selectChan] > (int)mt32ram.params.system.reserveSettings[selectChan]) { + oldest = -1; + oldnum = -1; + found = false; + for(i=0;i<32;i++) { + if(partTable[i]->isActive) { + if(partTable[i]->ownerChan == selectChan) { + found = true; + if(partTable[i]->age > oldest) { + oldest = partTable[i]->age; + oldnum = i; + } + } + } + } + if(!found) break; + partTable[oldnum]->stopPartial(); + --partialChan[selectChan]; + --needed; + } + + } + return true; + + } else { + //This channel has reached its max, must kill off its own + bool found; + int oldest; + int oldnum; + while(needed > 0) { + oldest = -1; + oldnum = -1; + found = false; + for(i=0;i<32;i++) { + if(partTable[i]->isActive) { + if(partTable[i]->ownerChan == chanNum) { + found = true; + if(partTable[i]->age > oldest) { + oldest = partTable[i]->age; + oldnum = i; + } + } + } + } + if(!found) break; + partTable[oldnum]->stopPartial(); + --needed; + } + // Couldn't free enough partials, sorry + if(needed>0) return false; + return true; + } + +} +*/ +bool FreePartials(int needed, int chanNum) { + + int i; +#if MAXPARTIALS == 32 + // Reclaim partials reserved for this channel + // Kill those that are already decaying first + /* + for(i=0;i<32;i++) { + if(PartialReserveTable[i] == chanNum) { + if(partTable[i]->ownerChan != chanNum) { + if(partTable[i]->partCache->envs[AMPENV].decaying) { + partTable[i]->isActive = false; + --needed; + if(needed<=0) return true; + } + } + } + + }*/ + // Then kill those with the lowest channel priority --- oldest at the moment + bool found; + Bit64s prior, priornum; + dpoly *killPoly; + found = true; + while(found) { + found = false; + prior = -1; + priornum = -1; + + for(i=0;i<32;i++) { + if(PartialReserveTable[i] == chanNum) { + if(partTable[i]->isActive) { + if(partTable[i]->ownerChan != chanNum) { + /* + if(mt32ram.params.system.reserveSettings[partTable[i]->ownerChan] < prior) { + prior = mt32ram.params.system.reserveSettings[partTable[i]->ownerChan]; + priornum = i; + }*/ + if(partTable[i]->age > prior) { + prior = partTable[i]->age; + priornum = i; + } + + found = true; + } + } + } + } + if(priornum != -1) { + partTable[priornum]->isActive = false; + + killPoly = partTable[priornum]->tmppoly; + killPoly->partActive[partTable[priornum]->timbreNum] = false; + killPoly->isActive = killPoly->partActive[0] || killPoly->partActive[1] || killPoly->partActive[2] || killPoly->partActive[3]; + --needed; + if(needed<=0) return true; + } + } + + + // Kill off the oldest partials within this channel + Bit64s oldest, oldlist; + + while(needed>0) { + oldest = -1; + oldlist = -1; + for(i=0;i<32;i++) { + if(partTable[i]->isActive) { + if(partTable[i]->ownerChan == chanNum) { + if(partTable[i]->age > oldest) { + oldest = partTable[i]->age; + oldlist = i; + } + } + } + } + if(oldlist != -1) { + partTable[oldlist]->isActive = false; + killPoly = partTable[oldlist]->tmppoly; + killPoly->partActive[partTable[oldlist]->timbreNum] = false; + killPoly->isActive = killPoly->partActive[0] || killPoly->partActive[1] || killPoly->partActive[2] || killPoly->partActive[3]; + --needed; + } else { + break; + } + } + if(needed<=0) return true; + + + return false; +#else + //No priority table when not using standard MT-32 configuration + // Kill off the oldest partials within this channel + int oldest, oldlist; + dpoly *killPoly; + oldest = -1; + oldlist = -1; + while(needed>0) { + for(i=0;i<MAXPARTIALS;i++) { + if(partTable[i]->ownerChan == chanNum) { + if(partTable[i]->age > oldest) { + oldest = partTable[i]->age; + oldlist = i; + } + } + } + if(oldlist != -1) { + partTable[oldlist]->isActive = false; + killPoly = partTable[oldlist]->tmppoly; + killPoly->partActive[partTable[oldlist]->timbreNum] = false; + killPoly->isActive = killPoly->partActive[0] || killPoly->partActive[1] || killPoly->partActive[2] || killPoly->partActive[3]; + --needed; + } else { + break; + } + } + if(needed<=0) return true; + //LOG_MSG("Out of paritals!"); + return false; +#endif + + +} + + +void MidiChannel::CheckNoteList() { + int q,t; + for(q=0;q<MAXPOLY;q++) { + if(polyTable[q].isActive) { + bool isActive = false; + CPartialMT32 * tmpPart; + for(t=0;t<4;t++) { + tmpPart = (CPartialMT32 *)polyTable[q].pStatus[t].myPart; + if(tmpPart != NULL) { + if(tmpPart->ownerChan == channum) { + isActive = isActive || tmpPart->isActive; + } + } + } + polyTable[q].isActive = isActive; + + } + } +} + +INLINE void MidiChannel::PlayNote(int f,int vel) { + int m; + sndoff=0; + velocity = vel; + + isPlaying = false; + + //if(channum!=0) return; + + if(isRy) memcpy(pcache,drumCache[DrumTable[f-35]],sizeof(pcache)); + + // POLY1 mode, Single Assign + // Haven't found any software that uses any of the other poly modes + if(!isRy) { + for(m=0;m<MAXPOLY;m++) { + if((polyTable[m].isActive) && (polyTable[m].freqnum == f)) { + StopNote(f,vel); + break; + } + } + } + + int needPartials = pcache[0].partCount; + + if(needPartials > GetFreePartialCount()) { + if(!FreePartials(needPartials, channum)) { + // Before we quit, see if there are other channels willing to donate + if(needPartials > GetFreePartialCount()) { + // Unable to get needed partials to play this note + return; + } + } + } + + // Find free note allocator + for(m=0;m<MAXPOLY;m++) { + if(!polyTable[m].isActive){ + isPlaying=true; + if (!isRy) { + PlayPoly(m,f,vel); + } else { + if(DrumTable[f-35]>-1) PlayDrum(m,f,vel); + } + break; + } + } + + +} +void MidiChannel::AllStop() { + int q,t; + for(q=0;q<MAXPOLY;q++) { + dpoly *tpoly = &polyTable[q]; + if(tpoly->isPlaying) { + tpoly->isDecay = true; + for(t=0;t<4;t++) { + + //memset(tpoly->pStatus[t].decay,0,sizeof(tpoly->pStatus[t].decay)); + //tpoly->pStatus[t].isDecayed = 0; + //memset(tpoly->pStatus[t].decaying,true,sizeof(tpoly->pStatus[t].decaying)); + + StartDecay(AMPENV,tpoly->pStatus[t].envs[AMPENV].prevlevel, &tpoly->pStatus[t], tpoly); + StartDecay(FILTENV,tpoly->pStatus[t].envs[FILTENV].prevlevel, &tpoly->pStatus[t], tpoly); + StartDecay(PITCHENV,tpoly->pStatus[t].envs[PITCHENV].prevlevel, &tpoly->pStatus[t], tpoly); + + tpoly->pStatus[t].pitchsustain = false; + } + tpoly->isPlaying = false; + } + + } + +} + +void MidiChannel::StopPedalHold() { + int q; + for(q=0;q<MAXPOLY;q++) { + dpoly *tpoly; + tpoly = &polyTable[q]; + if (tpoly->pedalhold) StopNote(tpoly->freqnum,0); + } + +} + +void MidiChannel::StopNote(int f,int vel) { + // Find oldest note... yes, the MT-32 can be reconfigured to kill different note first + // This is simplest + int oldest = -1; + int oldage = 0; + int count = 0; + int q,t; + bool found = false; + dpoly *tpoly; + + // Non-sustaining instruments ignore stop note commands. + // They die away eventually anyway + //if(!tpoly->sustain) return; + + //LOG_MSG("MT32 chan %d (\"%s\") stopping note %d", this->channum, currentInstr, f); + + for(q=0;q<MAXPOLY;q++) { + tpoly = &polyTable[q]; + + if(tpoly->isPlaying) { + if(tpoly->freqnum == f) { + if (holdpedal) { + tpoly->pedalhold = true; + + } else { + if(tpoly->sustain) { + tpoly->isDecay = true; + + + for(t=0;t<4;t++) { + //memset(tpoly->pStatus[t].decay,0,sizeof(tpoly->pStatus[t].decay)); + //tpoly->pStatus[t].isDecayed = 0; + //memset(tpoly->pStatus[t].decaying,true,sizeof(tpoly->pStatus[t].decaying)); + StartDecay(AMPENV,tpoly->pStatus[t].envs[AMPENV].prevlevel, &tpoly->pStatus[t], tpoly); + StartDecay(FILTENV,tpoly->pStatus[t].envs[FILTENV].prevlevel, &tpoly->pStatus[t], tpoly); + StartDecay(PITCHENV,tpoly->pStatus[t].envs[PITCHENV].prevlevel, &tpoly->pStatus[t], tpoly); + + tpoly->pStatus[t].pitchsustain = false; + } + tpoly->isPlaying = false; + } + //return; + } + found = true; + + + } + } + + } + + if(f!=-1) return; + oldest = -1; + for(q=0;q<MAXPOLY;q++) { + tpoly = &polyTable[q]; + + if((tpoly->isPlaying) && (!tpoly->isDecay) && (tpoly->chan==channum)) { + if(tpoly->age>=oldage) { + oldage = tpoly->age; + oldest = q; + } + count++; + } + } + + if(oldest!=-1) { + tpoly = &polyTable[oldest]; + tpoly->isDecay = true; + for(t=0;t<4;t++) { + //memset(tpoly->pStatus[t].decay,0,sizeof(tpoly->pStatus[t].decay)); + //tpoly->pStatus[t].isDecayed = 0; + //memset(tpoly->pStatus[t].decaying,true,sizeof(tpoly->pStatus[t].decaying)); + StartDecay(AMPENV,tpoly->pStatus[t].envs[AMPENV].prevlevel, &tpoly->pStatus[t], tpoly); + StartDecay(FILTENV,tpoly->pStatus[t].envs[FILTENV].prevlevel, &tpoly->pStatus[t], tpoly); + StartDecay(PITCHENV,tpoly->pStatus[t].envs[PITCHENV].prevlevel, &tpoly->pStatus[t], tpoly); + + tpoly->pStatus[t].pitchsustain = false; + } + tpoly->isPlaying = false; + //LOG(LOG_MISC|LOG_ERROR,"MT32 chan %d stopping note %d, %d remaining", this->channum, oldest, count-1); + } + +} + +MidiChannel *mchan[16]; + +#endif + + +bool CSynthMT32::InitTables(const char *baseDir ) { + +#ifdef NOMANSLAND + + int noteat,f; + + //LOG_MSG("MT-32 Initializing Pitch Tables"); + for(f=-108;f<109;f++) { + keytable[f+108] = (int)(256 * pow((float)2,(float)f/24.0)); + //LOG_MSG("KT %d = %d", f, keytable[f+108]); + + } + float ff; + for(f=0;f<=101;f++) { + ff = (float)f/100.00; + sqrtable[f] = (int)(100*sqrt(ff)); + float crapff = ff * (22000.0/32000.0); + if (crapff>1.0) crapff = 1.0; + //filttable[f] = (ff) * (22000.0/16000.0); + + } + + for(f=0;f<8;f++) { + ff = (float)f/14.00; + revtable[f] = (ff); + } + File fp; +#if MAKEWAVES == 1 + fp.open("waveforms.raw", File::kFileWriteMode); +#else + fp.open("waveforms.raw"); +#endif + if(!fp.isOpen()) { + // TODO : Fail driver init + return false; + + error("Unable to open waveforms.raw"); + exit(0); + } + + for(f=12;f<109;f++) { + + //int octave = (f / 12) - 6; + //int note = f % 12; + + //int cents = (octave * 100) + (note * 100); + //int freq = (int)((double)262 * pow((double)2,(double)((double)cents/1200))); + int freq = (int)(TUNING * pow( 2.0, ((double)f - 69.0) / 12.0 )); + freqtable[f] = freq; + divtable[f] = (int)( ((float)SETRATE / (float)(freq))); + smalldivtable[f] = divtable[f] << 8; + divtable[f] = divtable[f] << 16; + int rsaw,dep; + + for(rsaw=0;rsaw<=100;rsaw++) { + //(66-(((A8-50)/50)^.63)*50)/132 + float fsaw = (float)rsaw; + if(rsaw<50) fsaw = 50.0; + int tmpdiv = divtable[f] << 1; + + float sawfact = (66.0-(pow((fsaw-50.0)/50,.63)*50.0))/132.0; + sawtable[f][rsaw] = (int)(sawfact * (float)tmpdiv) >> 16; + //LOG_MSG("F %d divtable %d saw %d sawtable %d", f, divtable[f]>>16, rsaw, sawtable[f][rsaw]); + + } + + + for(dep=0;dep<5;dep++) { + if(dep>0) { + float depfac = 3000; + float ff1, tempdep; + depfac = (float)depexp[dep]; + + ff1 = ((float)f - (float)MIDDLEC) / depfac; + tempdep = pow((float)2,(float)ff) * 256; + fildeptable[dep][f] = (int)tempdep; + + ff1 = exp(tkcatconst[dep] * ((float)MIDDLEC-(float)f)) * tkcatmult[dep]; + timekeytable[dep][f] = (int)(ff1 * 256); + + } else { + fildeptable[dep][f] = 256; + timekeytable[dep][f] = 256; + } + } + //LOG_MSG("F %d d1 %x d2 %x d3 %x d4 %x d5 %x", f, fildeptable[0][f],fildeptable[1][f],fildeptable[2][f],fildeptable[3][f],fildeptable[4][f]); + + + + + noteat = 69-12; + +#if MAKEWAVES ==1 + double ampsize = WGAMP; + int halfdiv = divtable[f] >> 1; + int fa=0; + + float period = ((float)SETRATE / ((float)freq)); + float m=2*(int)(period/2)+1.0f; + float k=(int)(((float)50.0/100.0)*period); + double sd = (2.0*PI)/((((float)divtable[f]/65536.0)) * 4.0); + double sa = 0.0; + + //LOG_MSG("F %d sd %f div %d", f, sd, divtable[f]); + + int j; + float dumbfire; + double square=0.0f; + double saw = 0.0f; + + memset(waveformsize, 0,sizeof(tmpforms)); + + while(sa<=(2.0*PI)) { + float sqp; + + if(sa<PI) { + sqp = -1; + sqp = sqp + (.25 * (sa/PI)); + } else { + sqp=1; + sqp = sqp - (.25 * ((sa-PI)/PI)); + } + + square=0; + saw = 0; + bool odd = true; + for(Bit32s sinus=1;(sinus*freq)<(SETRATE);sinus++) { + float sinusval = (((1.0/((float)sinus))*(sin(((float)sinus)*sa)))); + saw=saw + sinusval; + } + + + dumbfire = sa/2; + + //This works pretty good + tmpforms[2][fa] += cos(dumbfire) * -ampsize; + tmpforms[3][(fa*2)] += cos(sa-PI) * -ampsize; + tmpforms[3][(fa*2)+1] += cos((sa+(sd/2))-PI) * -ampsize; + + tmpforms[0][fa] += (saw * -ampsize)/2; + tmpforms[1][fa] += (saw * ampsize)/2; + + //tmpforms[1][fa>>4] += saw * ampsize; + + + //waveforms[1][f][fa] = ((prevv2 - prevv)) * ampsize; + + + fa++; + sa+=sd; + } + //LOG_MSG("f num %d freq %d and fa %d", f, freq, fa); + + + waveformsize[0][f] = waveformsize[1][f] = waveformsize[2][f] = fa*2; + waveformsize[3][f] = fa*4; + + for (int i = 0; i < 4; ++i) { + waveforms[i][f] = (Bit16s *)malloc(waveformsize[i][f]); + memcpy(waveforms[i][f], &tmpforms[i][0],waveformsize[i][f]); + // TODO / FIXME: The following code is not endian safe! + out = fp.write(waveforms[i][f],waveformsize[i][f]); + } +#else + waveformsize[0][f] = waveformsize[1][f] = waveformsize[2][f] = divtable[f]>>13; + waveformsize[3][f] = divtable[f]>>12; + + for (int i = 0; i < 4; ++i) { + waveforms[i][f] = (Bit16s *)malloc(waveformsize[i][f]); + for (int j = 0; j < waveformsize[i][f]/2; ++j) + waveforms[i][f][j] = fp.readSint16LE(); + } +#endif + + + // Create the pitch tables + + float tuner = (32000.0 / (float)SETRATE) * 65536.0; + + wavtable[f] = (int)(tuner * ((float)freq/(float)SAMPLETUNING)); // C4 Tuning?; + drumdelta[f] = (int)(tuner * ((float)freq/(float)SAMPLETUNING)); // C4 Tuning?; + int pc,lp,tr,ln; + for(pc=0;pc<54;pc++) { + wavtabler[pc][f] = (int)(tuner * ((float)freq/PCM[pc].tune)); + } + for(lp=0;lp<16;lp++) { + for(ln=0;ln<16;ln++) { + looptabler[lp][ln][f] = (int)((float)LoopPatternTuning[lp][ln] * ((float)freq/(float)220.0)); + } + } + + for(tr=0;tr<=200;tr++) { + float brsq, brsa; + float ftr = (float)tr; + + // Verified exact on MT-32 + if(tr>100) ftr=100+(pow(((ftr-100)/100),3)*100); + brsq = exp(0.0464 * ftr) / 5; + //EXP((90*0.0175)^2) + brsq = exp(pow(((float)tr * 0.0153),2)); + + // I think this is the one + brsq = pow((float)10,((float)(tr/50.0)-1)); + + //brsa = exp(0.04 * ftr) / 5; + + //Last good one that worked + //brsa = exp(pow(((float)tr * 0.0133),2)); + brsa = exp(pow(((float)tr * 0.005),3)) * 125; + + brsa = pow((float)10,((float)(tr/46.5)-1))/2; + + //brsa = pow(exp((ftr-40)/40),1.15); + //brsq = exp((ftr-35)/35); + //brsq = exp((ftr-35)/23); + //brsa = exp((ftr-35)/23); + //brsq = pow((ftr / 200), LN) * 48.0; + //brsa = pow((ftr / 200), LN) * 32.0; + //brsq = brsa = + + filttable[0][f][tr] = (int)(((float)freq * brsq)/(float)(SETRATE/2)*FILTERGRAN); + if(filttable[0][f][tr]>=((FILTERGRAN*15)/16)) filttable[0][f][tr] = ((FILTERGRAN*15)/16); + filttable[1][f][tr] = (int)(((float)freq * brsa)/(float)(SETRATE/2)*FILTERGRAN); + if(filttable[1][f][tr]>=((FILTERGRAN*15)/16)) filttable[1][f][tr] = ((FILTERGRAN*15)/16); + + } + + int cf, tf; + for(cf=0;cf<=100;cf++) { + float cfmult = (float)cf; + + for(tf=0;tf<=100;tf++) { + + //float tfadd = exp((((float)tf / 100.0) - 1.03) * 3.0) * 100; + //float tfadd = (pow(((float)tf /100.0),4) * 100.0) - 10; + float tfadd = tf - 0; + + if (tfadd < 0) tfadd = 0; + float freqsum = exp((cfmult + tfadd) / 30.0) / 4.0; + //float freqsum = exp((cfmult + tfadd) / 33.7) / 3.0; + + + nfilttable[f][cf][tf] = (int)(((float)freq * freqsum)/(float)(SETRATE/2)*FILTERGRAN); + if(nfilttable[f][cf][tf]>=((FILTERGRAN*15)/16)) nfilttable[f][cf][tf] = ((FILTERGRAN*15)/16); + } + } + } + + fp.close(); + + int j,res; + float fres, tres; + for(res=0;res<31;res++) { + fres = (float)res/30.0; + ResonFactor[res] = (pow((float)2,log(pow((float)fres,(float)16))) * 2.5)+1.0; + ResonInv[res] = 1 / ResonFactor[res]; + + } + + for(j=0;j<FILTERGRAN;j++) { + for(res=0;res<31;res++) { + tres = ResonFactor[res]; + InitFilter((float)SETRATE, (((float)(j+1.0)/FILTERGRAN)) * ((float)SETRATE/2), filtcoeff[j][res],tres, fres); + + // 64-bit variant +#if FILTER_64BIT == 1 + for(int co=0;co<9;co++) { + filtcoefffix[j][res][co] = (__int64)(filtcoeff[j][res][co] * pow(2,20)); + + } +#endif + +#if FILTER_INT == 1 + for(int co=0;co<9;co++) { + filtcoefffix[j][res][co] = (long)(filtcoeff[j][res][co] * pow(2,10)); + + } +#endif + } + + } + + int period = 65536; + + int ang; + for(ang=0;ang<period;ang++) { + int halfang = (period / 2); + int angval = ang % halfang; + float tval = (((float)angval / (float)halfang) - 0.5) * 2; + if(ang>=halfang) tval = -tval; + sintable[ang] = (int)(tval * 50.0)+50; + + } + + + //for(ang=0;ang<period;ang++) sintable[period] *= 50; + int velt, dep; + float tempdep; + for(velt=0;velt<128;velt++) { + for(dep=0;dep<5;dep++) { + if(dep>0) { + float ff1 = exp(3.5*tvcatconst[dep] * (59.0-(float)velt)) * tvcatmult[dep]; + tempdep = 256.0 * ff1; + veltkeytable[dep][velt] = (int)tempdep; + if((velt % 16) == 0) { + //LOG_MSG("Key %d, depth %d, factor %d", velt, dep, (int)tempdep); + } + } else { + veltkeytable[dep][velt] = 256; + } + } +#define divpart 14.285714285714285714285714285714 + + for(dep=-7;dep<8;dep++) { + float fldep = fabs((float)dep) / 7.0; + fldep = pow((float)fldep,(float)2.5); + if(dep<0) fldep = fldep * -1.0; + pwveltable[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0); + + } + } + + for(dep=0;dep<=100;dep++) { + for(velt=0;velt<128;velt++) { + float fdep = (float)dep * 0.000347013; // Another MT-32 constant + float fv = ((float)velt - 64.0)/7.26; + float flogdep = pow((float)10, (float)(fdep * fv)); + float fbase; + + if(velt>64) { + filveltable[velt][dep] = (int)(flogdep * 256.0); + } else { + //lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96)); + fbase = 1 - (pow(((float)dep / 100.0),.25) * ((float)(64-velt) / 96.0)); + filveltable[velt][dep] = (int)(fbase * 256.0); + + } + + //LOG_MSG("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]); + } + } + + int lf; + for(lf=0;lf<=100;lf++) { + float elf = (float)lf; + + // General envelope + float logtime = elf * 0.088362939; + envtimetable[lf] = (int)((exp(logtime)/312.12) * (float)SETRATE); + + // Decay envelope -- shorter for some reason + // This is also the timing for the envelope right before the + // amp and filter envelope sustains + + lasttimetable[lf] = decaytimetable[lf] = (int)((exp(logtime)/(312.12*2)) * (float)SETRATE); + + //lasttimetable[lf] = (int)((exp(logtime)/(312.12*6)) * (float)SETRATE); + + // Finetuning table + //finetable[lf] = (int) ((pow(2, (((float)lf/100.0)-0.5)/6.0))*4096.0); + + } + for(lf=0;lf<=200;lf++) { + finetable[lf] = (int) ((pow((float)2, (float)((((float)lf/200.0)-1.0)/12.0))*4096.0)); + + } + for(lf=0;lf<=48;lf++) { + bendtable[lf] = (int) ((pow((float)2, (float)((((float)lf/12.0)-2.0)))*4096.0)); + + } + + float lff; + for(lf=0;lf<128;lf++) { + for(velt = 0;velt<64;velt++) { + lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96)); + //lff = ((128.0 - (float)lf) / 128.0) * ((float)velt / 64); + //lff = 1.0 / pow(10, lff/2); + + + + ampveltable[lf][velt] =(int)(lff * 256.0); + //LOG_MSG("Ampveltable: %d, %d = %d", lf, velt, ampveltable[lf][velt]); + } + } + for(lf=0;lf<=127;lf++) { + restable[lf] = (int)( (pow((float)2,pow((float)(lf/127.0),(float)2.5))-1)*100 ); + //LOG_MSG("lf %d = amp %d", lf, restable[lf]); + } + + for(lf=0;lf<=127;lf++) { + //amptable[lf] = (int)( (pow(2,pow((float)lf/127.0,1))-1)*127 ); + //amptable[lf] = lf; + //float pubical = (float)lf/127.0; // Yes, as in boobical pubical (because its cubical!) + + // Converting MIDI to volume. + // Thanks Microsoft DDK + // value = 2^(log10((index/127)^4))*127 where index = 0..127 + amptable[lf] = voltable[lf] = (int)(127.0 * pow((float)2,log(pow((float)(lf/127.0),(float)4)))); + + // Nope, lets try this again + //amptable[lf] = voltable[lf] = (int)(127.0 * log10((float)lf/12.70)); + + //amptable[lf] = (int)(pubical * pubical * pubical * 127.0); + + // Once more... + + //float indec = 128.0 - (float)lf; + //indec = -(indec / 1.33333); + voltable[lf] = amptable[lf] = (int)(127.0 * pow((float)lf/127.0,LN)); + + //indec = 40 * log((float)lf / 127.0); + //voltable[lf] = (int)(127.0 * exp(indec/40.0)); + + //LOG_MSG("lf %d = vol %d", lf, voltable[lf]); + } + for(lf=0;lf<441;lf++) { + int myRand; + myRand = rand(); + myRand = ((myRand - 16383) * WGAMP) >> 18; + smallnoise[lf] = (Bit16s)myRand; + } + + for(lf=0;lf<=100;lf++) { + ptable[lf] = (int)(pow((float)2,(float)((float)(lf-50)/25.0)) * 256); + + } + float tdist; + for(lf=0;lf<=50;lf++) { + if(lf==0) + padjtable[lf] = 7; + else if (lf==1) + padjtable[lf] = 6; + else if (lf==2) + padjtable[lf] = 5; + else if (lf==3) + padjtable[lf] = 4; + else if (lf==4) + padjtable[lf] = 4-(.333333f); + else if (lf==5) + padjtable[lf] = 4-(.333333f*2); + else if (lf==6) + padjtable[lf] = 3; + else if ((lf>6) && (lf<=12)) { + tdist = (lf-6.0) / 6.0; + padjtable[lf] = 3.0 - tdist; + } else if ((lf>12) && (lf<=25)) { + tdist = (lf-12.0) / 13.0; + padjtable[lf] = 2.0 - tdist; + + } else { + tdist = (lf-25.0) / 25.0; + padjtable[lf] = 1.0 - tdist; + } + //LOG_MSG("lf %d = padj %f", lf, padjtable[lf]); + } + for(lf=0;lf<=100;lf++) { + float mv = (float)lf / 100.0; + float pt = mv-0.5; + if(pt<0) pt = 0; + + pulsetable[lf] = (int)((pt) * 215.04) + 128; + pulseoffset[lf] = (int)(pt * WGAMP); + + /* + // I am certain of this: Verified by hand LFO log */ + lfotable[lf] = (Bit32s)(((float)SETRATE) / (pow((float)1.088883372,(float)lf) * 0.021236044)); + + //LOG_MSG("lf %d = lfo %d pulsetable %d", lf, lfotable[lf], pulsetable[lf]); + } + + float lfp, depf, finalval, tlf; + int depat, pval, depti; + for(lf=0;lf<=10;lf++) { + // I believe the depth is cubed or something + + for(depat=0;depat<=100;depat++) { + if(lf>0) { + depti = abs(depat-50); + tlf = (float)lf - padjtable[depti]; + if(tlf<0) tlf = 0; + lfp = exp(0.713619942 * tlf) / 407.4945111; + + if(depat<50) + finalval = 4096.0 * pow((float)2,(float)-lfp); + else + finalval = 4096.0 * pow((float)2, (float)lfp); + pval = (int)(finalval); + + penvtable[lf][depat] = pval; + } else { + penvtable[lf][depat] = 4096; + + } + + //LOG_MSG("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp); + + } + } + for(lf=0;lf<=100;lf++) { + // Maybe its linear + // It is - verified on MT-32 - one of the few things linear + lfp = ((float)lf * .1904) / 310.55; + + for(depat=0;depat<=100;depat++) { + depf = ((float)depat - 50.0) / 50.0; + //finalval = pow(2, lfp * depf * .5); + finalval = 4096.0 + (4096.0 * lfp * depf); + + pval = (int)(finalval); + + lfoptable[lf][depat] = pval; + + //LOG_MSG("lf %d depat %d pval %x", lf,depat,pval); + + } + } + + + int distval; + float amplog, dval; + + for(lf=0;lf<=12;lf++) { + for(distval=0;distval<128;distval++) { + if(lf==0) { + amplog = 0; + dval = 1; + ampbiastable[lf][distval] = 256; + } else { + amplog = pow((float)1.431817011,(float)lf) / PI; + dval = ((128.0-(float)distval)/128.0); + amplog = exp(amplog); + dval = pow(amplog,dval)/amplog; + ampbiastable[lf][distval] = (int)(dval * 256.0); + } + //LOG_MSG("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, ampbiastable[lf][distval],amplog); + } + + + + } + + for(lf=0;lf<=14;lf++) { + for(distval=0;distval<128;distval++) { + float filval = fabs((((float)lf - 7.0) * 12.0) / 7.0); + + if(lf==7) { + amplog = 0; + dval = 1; + fbiastable[lf][distval] = 256; + } else { + //amplog = pow(1.431817011,filval) / PI; + amplog = pow((float)1.531817011, (float)filval) / PI; + dval = ((128.0-(float)distval)/128.0); + amplog = exp(amplog); + dval = pow(amplog,dval)/amplog; + if(lf<8) { + fbiastable[lf][distval] = (int)(dval * 256.0); + } else { + dval = pow((float)dval, (float).3333333); + if(dval<.01) dval = .01f; + dval = 1 / dval; + fbiastable[lf][distval] = (int)(dval * 256.0); + } + } + //LOG_MSG("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, fbiastable[lf][distval],amplog); + } + + + + } + + // Benchmark 3DNow, Floating point, and SSE filters +/* + Bit32u bench; + __time64_t start, end; + float histval[50]; + + _time64(&start); + for(bench=0;bench<20000000;bench++) { + iir_filter_sse(0,&histval[0],filtcoeff[0][0],0); + } + _time64(&end); + //LOG_MSG("Bench completed in %ld seconds", end - start); +*/ + + +#endif + + return true; +} + +BOOL RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack) { + +#ifdef NOMANSLAND + + File fp; + fp.open("waveforms.raw", File::kFileWriteMode); + + if(!fp.isOpen()) return FALSE; + + double ampsize = WGAMP; + int f; + for(f=12;f<109;f++) { + + if(callBack != NULL) { + int perc = ((f - 12) * 100) / 96; + (callBack)(perc); + } + + //int octave = (f / 12) - 6; + //int note = f % 12; + + //int cents = (octave * 100) + (note * 100); + int freq = (int)(TUNING * pow( 2.0, ((double)f - 69.0) / 12.0 )); + freqtable[f] = freq; + divtable[f] = (int)( ((float)sampRate / (float)freq) ); + smalldivtable[f] = divtable[f] << 8; + divtable[f] = divtable[f] << 16; + + //int halfdiv = divtable[f] >> 1; + + //float period = ((float)sampRate / ((float)freq)); + //float m=2*(int)(period/2)+1.0f; + //float k=(int)(((float)50.0/100.0)*period); + + + double sd = (2.0*PI)/((float)divtable[f]/4096.0); + double sa = 0.0; + + + int fa=0; + + + int j; + float dumbfire; + double square=0.0f; + double saw = 0.0f; + + memset(tmpforms, 0,sizeof(tmpforms)); + + while(sa<=(2.0*PI)) { + float sqp; + + if(sa<PI) { + sqp = -1; + sqp = sqp + (.25 * (sa/PI)); + } else { + sqp=1; + sqp = sqp - (.25 * ((sa-PI)/PI)); + } + + square=0; + saw = 0; + //bool odd = true; + for(double sinus=1.0;sinus<256.0;sinus++) { + float sinusval = (((1/(sinus))*(sin(sinus*sa)))); + if((sinus*freq)<(sampRate*2)) saw=saw + sinusval; + } + + dumbfire = sa /2 ; + + //This works pretty good + tmpforms[2][fa>>4] += (Bit16s)(cos(dumbfire) * -ampsize); + tmpforms[3][fa>>3] += (Bit16s)(cos(sa-PI) * -ampsize); + + tmpforms[0][fa>>4] += (Bit16s)(saw * -ampsize); + tmpforms[1][fa>>4] += (Bit16s)(saw * ampsize); + + fa++; + sa+=sd; + } + + for(j=0;j<=(divtable[f]>>16);j++) { + finalforms[0][j] = tmpforms[0][j] >> 5; + finalforms[1][j] = tmpforms[1][j] >> 5; + finalforms[2][j] = tmpforms[2][j] >> 4; + } + for(j=0;j<=(divtable[f]>>15);j++) { + finalforms[3][j] = tmpforms[3][j] >> 3; + } + + int out; + + out = fp.write(finalforms[0],divtable[f]>>15); + out = fp.write(finalforms[1],divtable[f]>>15); + out = fp.write(finalforms[2],divtable[f]>>15); + out = fp.write(finalforms[3],divtable[f]>>14); + + + } + fp.close(); +#endif + + return TRUE; + + + + +} + +bool CSynthMT32::ClassicOpen(const char *baseDir, SynthProperties useProp) { + +#ifdef NOMANSLAND + + if (isOpen) return false; + int i; + // Initalize patch information + Bit8u sysexBuf[SYSEX_SIZE]; + Bit16u syslen = 0; + + bool inSys = false; + + File fp; + Bit8u c; + + myProp = useProp; + + usefilter = &iir_filter_normal; + + + for(i=0;i<MAXPARTIALS;i++) { + partTable[i] = new CPartialMT32(i); + } + + //pInfo = fopen("partial.nfo","wb"); + + + //LOG_MSG("MT-32 Initializing patch banks"); + + for(initmode=0;initmode<2;initmode++) { + + switch(initmode) { + case 0: + fp.open("Preset1.syx"); + if(!fp.isOpen()) { + // TODO : Fail driver init + error("Unable to open Preset1.syx"); + return false; + + //exit(0); + } + break; + case 1: + fp.open("Preset2.syx"); + if(!fp.isOpen()) { + // TODO : Fail driver init + error("Unable to open Preset2.syx"); + return false; + + //exit(0); + } + break; + default: + + // TODO : Fail driver init + return false; + + break; + } + + while(!fp.eof()) { + c = fp.readByte(); + sysexBuf[syslen] = c; + syslen++; + if(c==0xf0) { + inSys = true; + } + if ((c==0xf7) && (inSys)) { + PlaySysex(&sysexBuf[0],syslen); + inSys = false; + syslen = 0; + } + } + fp.close(); + + } + + //LOG_MSG("MT-32 Initializing Drums"); + + File fDrums; + fDrums.open("drumpat.rom"); + if(!fDrums.isOpen()) { + // TODO : Fail driver init + // printf("MT-32 Init Error - Missing drumpat.rom\n"); + error("Unable to open drumpat.rom"); + return false; + + //exit(0); + } + int drumnum=0; + while(!fDrums.eof()) { + //Read common area + fDrums.read(&drums[drumnum].common,14); + int t; + for(t=0;t<4;t++) { + if (((drums[drumnum].common.pmute >> t) & 0x1) == 0x1) { + fDrums.read(&drums[drumnum].partial[t],58); + //LOG_MSG("Loaded drum #%d - t %d", drumnum,t); + } + } + //LOG_MSG("Loaded drum #%d - %s", drumnum,drums[drumnum].common.name); + drumnum++; + } + fDrums.close(); + +#if DUMPDRUMS == 1 + fp.open("drumsys.syx", File::kFileWriteMode); + char dumbtext[10], tmpb; + memset(dumbtext,0,10); + for(drumnum=0;drumnum<30;drumnum++) { + // Sysex header + tmpb = 0xf0; fp.write(&tmpb,1); + tmpb = 0x41; fp.write(&tmpb,1); + tmpb = 0x10; fp.write(&tmpb,1); + tmpb = 0x16; fp.write(&tmpb,1); + tmpb = 0x12; fp.write(&tmpb,1); + + int useaddr = drumnum * 256; + char lsb = useaddr & 0x7f; + char isb = (useaddr >> 7) & 0x7f; + char msb = ((useaddr >> 14) & 0x7f) | 0x08; + //Address + fp.write(&msb, 1); + fp.write(&isb, 1); + fp.write(&lsb, 1); + unsigned int checksum = msb + isb + lsb; + + //Data + fp.write(&drums[drumnum].common,0xe); + fp.write(&drums[drumnum].partial[0],0x3a); + fp.write(&drums[drumnum].partial[1],0x3a); + fp.write(&drums[drumnum].partial[2],0x3a); + fp.write(&drums[drumnum].partial[3],0x3a); + //Checksum + char *dat = (char *)&drums[drumnum]; + int ch; + for(ch=0;ch<246;ch++) checksum += *dat++; + checksum = (checksum & 0x7f); + if(checksum) checksum = 0x80 - checksum; + + fp.write(&checksum,1); + + //End of sysex + tmpb = 0xf7; fp.write(&tmpb,1); + } + fp.close(); +#endif + + //LOG_MSG("MT-32 Initializing Partials"); + + File fPatch; + fPatch.open("patchlog.cfg"); + + if(!fPatch.isOpen()) { + // TODO : Fail driver init + // printf("MT-32 Init Error - Missing patchlog.cfg\n"); + error("Unable to open patchlog.cfg"); + return false; + + //exit(0); + } + + for(i=0;i<54;i++) { + PCMReassign[i] = i; + PCM[i].tune = 220.0; + PCM[i].ampval = 256; + + } + //PCM[53].ampval = 128; + if (!fPatch.eof()) { + char tbuf[512]; + char *cp; + fPatch.gets(tbuf,sizeof(tbuf)); + Bit32u patchstart = 0; //axtoi(tbuf); + Bit32u patchend = 0; + Bit32u patchcount = 0; + //Bit16s *romaddr = &romfile[0]; + while (!fPatch.eof()) { + fPatch.gets(tbuf,sizeof(tbuf)); + cp = strtok(tbuf," \n\r"); + PCM[patchcount].loop = false; + if(cp != NULL) { + patchend = axtoi(cp); + cp = strtok(NULL," \n\r"); + if(cp != NULL) { + cp = strtok(NULL," \n\r"); + if(cp != NULL) { + cp = strtok(NULL," \n\r"); + if (cp !=NULL) { + int newpcm = atoi(cp); + PCMReassign[newpcm] = patchcount; + cp = strtok(NULL," \n\r"); + if(cp != NULL) { + if(atoi(cp)==1) PCM[patchcount].loop = true; + cp = strtok(NULL," \n\r"); + if(cp != NULL) { + PCM[patchcount].tune = (float)atoi(cp) / 100.0; + //LOG_MSG("PCM %d tuning at %f", patchcount, PCM[patchcount].tune); + } + } + } + } + } + } + if (patchend==0) break; + + PCM[patchcount].addr = patchstart; + PCM[patchcount].len = patchend - patchstart; + patchcount++; + //printf("Patch %d %d %d %d\n", patchcount, patchstart, patchend, mt32ram.PCM[patchcount].len); + patchstart = patchend; + + } + } else { + // TODO : Fail driver init + return false; + + //exit(0); + } + + fPatch.close(); + + + PCM[53].len = 1950; + + int pat = 0; + int p; + for(p=0;p<54;p++) { + int pr = PCMReassign[p]; + if(!PCM[pr].loop) { + PCMLoopTable[pat] = p; + pat++; + } + if(pat==7) { + PCMLoopTable[pat] = p; + pat++; + } + } + // These are the special combination loop patches + for(p=46;p<=54;p++) { + PCMLoopTable[pat] = -(p - 45); + pat++; + } + + //for(p=0;p<54;p++) LOG_MSG("Loop table %d = %d (%d)", p, PCMLoopTable[p], PCM[p].loop); + + // Generate official PCM list + + // Normal sounds + pat = 0; + for(p=0;p<54;p++) { + PCMList[pat].addr = PCM[PCMReassign[p]].addr; + PCMList[pat].len = PCM[PCMReassign[p]].len; + PCMList[pat].loop = PCM[PCMReassign[p]].loop; + PCMList[pat].aggSound = -1; + PCMList[pat].pcmnum = PCMReassign[p]; + PCMList[pat].ampval = PCM[PCMReassign[p]].ampval; + pat++; + } + + // Drum specific sounds. Not exactly sure yet how these are different + for(p=0;p<20;p++) { + PCMList[pat] = PCMList[p]; + pat++; + } + + // Looped PCM sounds. The last remaining 9 are aggregate sounds; + for(p=0;p<54;p++) { + if(PCMLoopTable[p]>-1) { + PCMList[pat].addr = PCM[PCMReassign[PCMLoopTable[p]]].addr; + PCMList[pat].ampval = PCM[PCMReassign[PCMLoopTable[p]]].ampval; + PCMList[pat].len = PCM[PCMReassign[PCMLoopTable[p]]].len; + PCMList[pat].pcmnum = PCMReassign[PCMLoopTable[p]]; + PCMList[pat].loop = true; + PCMList[pat].aggSound = -1; + } else { + PCMList[pat].addr = 0; + + //Calculate aggregate length + int aggsnd = (-PCMLoopTable[p])-1; + int tmplen = 0; + int sndpos = 0; + while(LoopPatterns[aggsnd][sndpos] != -1) { + tmplen += PCM[LoopPatterns[aggsnd][sndpos]].len; + sndpos++; + } + + PCMList[pat].len = tmplen; + PCMList[pat].loop = true; + PCMList[pat].aggSound = aggsnd; + PCMList[pat].ampval = 0x100; + } + pat++; + + } + + //for(p=0;p<128;p++) LOG_MSG("PCM #%d addr 0x%x len %d loop %d aggSound %d pcmnum %d", p, PCMList[p].addr, PCMList[p].len, PCMList[p].loop, PCMList[p].aggSound, PCMList[p].pcmnum); + + //LOG_MSG("MT-32 Initializing ROM"); + File fIn; + fIn.open("MT32_PCM.ROM"); +#ifdef MT32OUT + File fOut, fOutb; + char tmpc; + fOut.open("mt32out.raw",File::kFileWriteMode); + fOutb.open("mt32out2.raw",File::kFileWriteMode); +#endif + + if(!fIn.isOpen()) { + // TODO : Fail driver init + // printf("MT-32 Init Error - Missing MT32_PCM.ROM\n"); + error("Unable to open MT32_PCM.ROM"); + return false; + + //exit(0); + } + i=0; + //Bit32s maxamp = 0; + while (!fIn.eof()) { + Bit16s s, c1; + + s = fIn.readByte(); + c1 = fIn.readByte(); + + /* + int e,z,u,bit; + + int order[16] = {0, 9,1 ,2, 3, 4, 5, 6, 7, 10, 11, 12,13, 14, 15,8}; + + e=0; + z = 15; + for(u=0;u<15;u++) { + if((order[u]<8) && (order[u]>=0)) { + bit = (s >> (7-order[u])) & 0x1; + } else { + if(order[u]>=8) { + bit = (c1 >> (7-(order[u]-8))) & 0x1; + } else { + bit = 0; + } + } + e = e | (bit << z); + --z; + } + + //if( (e & 0x1) != 0) printf("Last bit = %d\n", e & 0x1); + //Bit16s e = ( ((s & 0x7f) << 4) | ((c1 & 0x40) << 6) | ((s & 0x80) << 6) | ((c1 & 0x3f))) << 2; + if(e<0) e = -32767 - e; + int ut = abs(e); + int dif = 0x7fff - ut; + x = exp(((float)((float)0x8000-(float)dif) / (float)0x1000)); + e = (int)((float)e * (x/3200));*/ + + short e; + int z; + int bit; + int u; + + static const int order[16] = {0, 9,1 ,2, 3, 4, 5, 6, 7, 10, 11, 12,13, 14, 15,8}; + + e=0; + z = 15; + for(u=0;u<15;u++) { + if((order[u]<8) && (order[u]>=0)) { + bit = (s >> (7-order[u])) & 0x1; + } else { + if(order[u]>=8) { + bit = (c1 >> (7-(order[u]-8))) & 0x1; + } else { + bit = 0; + } + } + e = e | (bit << z); + --z; + } + +#ifdef MT32OUT + tmpc = e & 0xff; fOut.write(&tmpc, 1); + tmpc = (e >> 8) & 0x7f; fOut.write(&tmpc, 1); +#endif + // File is encoded in dB, convert to PCM + // MINDB = -96 + // MAXDB = -15 + float testval; + testval = (float)((~e) & 0x7fff); + testval = -(testval / 400.00); + //testval = -(testval / 341.32291666666666666666666666667); + //testval = -(testval / 400.00); + float vol = pow((float)8,(float)(testval / 20)) * 32767; + + if (e>0) vol = -vol; + + romfile[i] = (Bit16s)vol; + +#ifdef MT32OUT + tmpc = (Bit16s)vol & 0xff; fOutb.write(&tmpc, 1); + tmpc = (Bit16s)vol >> 8; fOutb.write(&tmpc, 1); +#endif + + i++; + } + //LOG_MSG("PCM amp = %d", maxamp); +#ifdef MT32OUT + fOutb.close(); + fOut.close(); +#endif + fIn.close(); + int tmp; + for(tmp=0;tmp<16;tmp++) { + if((tmp>=1) && (tmp<=9)) { + chantable[tmp] = tmp-1; + } else { + chantable[tmp] = -1; + } + } + chantable[10] = 8; + for(i=0;i<128;i++) { + mt32ram.params.pSettings[i].timbreGroup = i >> 6; + mt32ram.params.pSettings[i].timbreNum = i & 63; + } + + // For resetting mt32 mid-execution + memcpy(&mt32default, &mt32ram, sizeof(mt32ram)); + + if (!InitTables(baseDir)) return false; + if(myProp.UseDefault) { + InitReverb(0,5,SETRATE); + } else { + InitReverb(myProp.RevType, myProp.RevTime,SETRATE); + } + + for(i=0;i<10;i++) { + mchan[i] = new MidiChannel(SETRATE,i); + + if(i<8) mchan[i]->SetPatch(InitInstr[i],-1); + if(i>8) mchan[i]->SetPatch(InitInstr[i-9],-1); + } + activeChannels = 0; + +#ifdef HAVE_X86 + bool useSSE = false, use3DNow = false; + + use3DNow = Detect3DNow(); + useSSE = DetectSIMD(); + + if (useSSE) debug(1, "MT-32: SSE detected and enabled"); + if (use3DNow) debug(1, "MT-32: 3DNow! detected and enabled"); + + if(use3DNow) { + debug(1, "MT-32 using use SIMD (AMD 3DNow) extensions"); + usefilter = &iir_filter_3dnow; + } + + if(useSSE) { + debug(1, "MT-32 using SIMD (Intel SSE) extensions\n"); + usefilter = &iir_filter_sse; + usingSIMD = true; + } +#endif + + isOpen=true; + isEnabled=false; + +#endif + + return true; +} + +void CSynthMT32::Close(void) { + if (!isOpen) return; + +#ifdef NOMANSLAND + int t, m; + for(t=0;t<4;t++) { + for(m=0;m<256;m++) { + if(waveforms[t][m]!=NULL) free(waveforms[t][m]); + } + } + +#endif + + isOpen=false; + + +} + +void CSynthMT32::PlayMsg(Bit32u msg) { + +#ifdef NOMANSLAND + + int h; + int code = msg & 0xf0; + int chan = msg & 0xf; + int note = (msg & 0xff00) >> 8; + int velocity = (msg & 0xff0000) >> 16; + isEnabled= true; + + //if(chan!=0x9) { + // if(chan==12) return; + // chan = chan & 0x7; + // + //} else { + // chan = 8; + //} + //if (chan==0) return; + //int prechan = chan; + //if(code!=0xf0) LOG_MSG("Playing chan %d, code 0x%x note: 0x%x", chan, code, note); + + chan = chantable[chan]; + //LOG_MSG("Play msg on chan: %d = %d note: %x velocity: %x", chan, msg & 0xf, note, velocity); + if(chan<0) { + //LOG_MSG("Play msg 0x%x on unreg chan: %d = %x", chan, msg & 0xf); + return; + + } + if(chan>8) return; + + + int patch; + Bit32u bend; + + //LOG_MSG("Midi code: 0x%x",msg); + switch (code) { + case 0x80: + //LOG_MSG("Note OFF - Channel %d",chan); + mchan[chan]->StopNote(note,velocity); + break; + case 0x90: + //if(chan!=4) return; + //LOG_MSG("Note ON - Channel %d, Note %d Vel %d",chan, note, velocity); + + if(velocity>0) { + mchan[chan]->PlayNote(note,velocity); + } else { + mchan[chan]->StopNote(note,velocity); + } + + + break; + case 0xb0: // Control change + switch (note) { + case 0x1: // Modulation + //LOG_MSG("Modulation: %d", velocity); + mchan[chan]->SetModulation(velocity); + break; + case 0xb: + //LOG_MSG("Expression set: %d", velocity); + mchan[chan]->SetVolume(velocity); + break; + case 0x7: // Set volume + //if(chan!=3) return; + //LOG_MSG("Volume set: %d", velocity); + mchan[chan]->SetVolume(velocity); + break; + case 0xa: // Pan + mchan[chan]->SetPan(velocity); + break; + case 0x40: // Hold pedal + if(velocity<64) { + mchan[chan]->SetHoldPedal(false); + mchan[chan]->StopPedalHold(); + } else { + mchan[chan]->SetHoldPedal(true); + } + break; + + case 0x7b: // All notes off + + for(h=0;h<MAXPOLY;h++) { + mchan[chan]->StopNote(-1,0); + } + break; + case 0x79: // Reset all controllers + break; + + default: + //LOG_MSG("Control code: 0x%x - vel %x",note, velocity); + break; + } + + break; + case 0xc0: // Patch setting + char currentInstr[32]; + patch = (mt32ram.params.pSettings[note].timbreGroup * 64) + mt32ram.params.pSettings[note].timbreNum; + memset(¤tInstr,0,16); + memcpy(¤tInstr,mt32ram.params.patch[patch].common.name,10); + + //LOG_MSG("Set patch (%s) %d (%d) chan %d (%d) from ng %d, t %d", currentInstr, patch, note, chan, msg & 0xf, mt32ram.params.pSettings[note].timbreGroup, mt32ram.params.pSettings[note].timbreNum); + if((chan>=0) && (chan<8)) mt32ram.params.timTemp[chan] = mt32ram.params.patch[patch]; + mchan[chan]->SetPatch(note,-1); + break; + case 0xe0: // Pitch bender + bend = (velocity << 7) | (note); + //LOG_MSG("Pitch bender %x", bend); + mchan[chan]->SetBend(bend); + break; + default: + //LOG_MSG("Undef Midi code: 0x%x - %x - %x",code, note, velocity); + + break; + } + +#endif + //midiOutShortMsg(m_out, msg); +} + +void CSynthMT32::PlaySysex(Bit8u * sysex,Bit32u len) { + +#ifdef NOMANSLAND + + Bit32u addr; + Bit32u *header; + unsigned int off; + int m; + header = (Bit32u *)(sysex+1); + //int dummy = 0; + Bit32s lens = len; + + + // For some reason commands in IMuseInternal::initMT32 do not have prefix byte + if(READ_LE_UINT32(header) != 0x12161041) { + if(READ_LE_UINT32(sysex) == 0x12161041) { + header = (Bit32u *)sysex; + sysex--; // We don't access sysex[0], so it's safe + } + } + + if(READ_LE_UINT32(header) == 0x12161041) { + addr = (sysex[5] << 16) | (sysex[6] << 8) | (sysex[7]); + //LOG_MSG("Sysex addr: %x", addr); + if (addr<0x30000) { + //LOG_MSG("Channel temp area %x", addr); + } + if ((addr>=0x30000) && (addr<0x40000)) { + off = ((addr & 0x7f00) >> 1) | (addr & 0xff); + for(m=0;m<(lens-10);m++) { + mt32ram.memabs.mt32memory[off+m] = sysex[8+m]; + } + //LOG_MSG("Patch temp %d at %x - len %d", off/16, off % 16, len-10); + + if(initmode>1) { + for(m=0;m<8;m++) { + int tmppat; + for(tmppat=0;tmppat<128;tmppat++) { + if(mt32ram.params.pSettings[tmppat].timbreGroup == mt32ram.params.tmpSettings[m].timbreGroup) { + if(mt32ram.params.pSettings[tmppat].timbreNum == mt32ram.params.tmpSettings[m].timbreNum) { + //LOG_MSG("Setting %d to patch %d", m, tmppat); + this->PlayMsg((tmppat << 8) | 0xc0 | m); + + break; + } + } + } + //LOG_MSG("Patch chan %d - Assign mode %d", m,mt32ram.params.tmpSettings[m].fineTune); + } + } + } + if ((addr>=0x40000) && (addr<0x50000)) { + int toffat = sizeof(mt32ram.patchabs.pTemp) + sizeof(mt32ram.patchabs.rTemp); + off = ((addr & 0x7f00) >> 1) | (addr & 0x7f); + for(m=0;m<(lens-10);m++) { + mt32ram.memabs.mt32memory[off+m+toffat] = sysex[8+m]; + } + int chanoff = off / sizeof(timbreParam); + + //LOG_MSG("Timbre temp off %x offdiv %x - len %d", off, chanoff, len-10); + if(mchan[chanoff]!=NULL) mchan[chanoff]->RefreshPatch(); + } + + if ((addr>=0x50000) && (addr<0x60000)) { + off = (((addr & 0x7f00) >> 1) | (addr & 0xff)) + sizeof(mt32ram.patchabs.pTemp) + sizeof(mt32ram.patchabs.rTemp) + sizeof(mt32ram.patchabs.tTemp); + + for(m=0;m<(lens-10);m++) { + mt32ram.memabs.mt32memory[off+m] = sysex[8+m]; + } + //LOG_MSG("Patch area %d, assigning to %d, patch %d - len %d", off/16, mt32ram.params.pSettings[off/16].timbreGroup, mt32ram.params.pSettings[off/16].timbreNum, len-10); + } + if ((addr>=0x80000) && (addr<0x90000)) { + // Timbre patches + int tc = (addr & 0xfe00) >> 9; + off = ((addr & 0x100) >> 1) | (addr & 0xff); + int calcoff; + int pn=0; + + switch(initmode) { + case 0: + calcoff = tc * sizeof(timbreParam); + pn = tc; + break; + case 1: + calcoff = (tc+ 64) * sizeof(timbreParam); + pn = tc + 64; + break; + default: + calcoff = (tc + 128) * sizeof(timbreParam); + pn = tc + 128; + break; + } + + // Transfer sysex parameter data into MT-32 memory + calcoff += (off + sizeof(mt32ram.patchabs.pTemp) + sizeof(mt32ram.patchabs.rTemp) + sizeof(mt32ram.patchabs.tTemp) + sizeof(mt32ram.patchabs.patchmemory)); + for(m=0;m<(lens-10);m++) { + mt32ram.memabs.mt32memory[calcoff+m] = sysex[8+m]; + } + //LOG_MSG("Loaded patch %s at pn %d", mt32ram.params.patch[pn].common.name, pn); + } + if ((addr>=0x100000) && (addr<=0x1fffff)) { + off = ((addr & 0x100) >> 1) | (addr & 0xff); + for(m=0;m<(lens-10);m++) { + mt32ram.patchabs.systemBank[m+off] = sysex[8+m]; + } + + //LOG_MSG("System Reconfiguration:"); + memset(chantable,-1,sizeof(chantable)); + memset(miditable,-1,sizeof(miditable)); + + for(m=0;m<9;m++) { + //LOG_MSG("Channel %d set to MIDI channel %d",m,mt32ram.params.system.chanAssign[m]); + if(mt32ram.params.system.chanAssign[m]==16) { + mchan[m]->AllStop(); + } else { + chantable[(int)mt32ram.params.system.chanAssign[m]]=m; + miditable[m] = mt32ram.params.system.chanAssign[m]; + } + } + + //LOG_MSG("Master Tune: %f", ((float)mt32ram.params.system.masterTune)*0.2+432.1); + //LOG_MSG("Reverb mode: %d", mt32ram.params.system.reverbMode); + //LOG_MSG("Reverb time: %d", mt32ram.params.system.reverbTime); + //LOG_MSG("Reverb level: %d", mt32ram.params.system.reverbLevel); + + if(((Bit32u)mt32ram.params.system.reverbMode != curRevMode) || ((Bit32u)mt32ram.params.system.reverbTime!=curRevTime)) { + if(myProp.UseDefault) { + InitReverb(mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime,SETRATE); + curRevLevel = mt32ram.params.system.reverbLevel; + } else { + InitReverb(myProp.RevType, myProp.RevTime,SETRATE); + curRevLevel = myProp.RevLevel; + } + } + + + char *rset = mt32ram.params.system.reserveSettings; + //LOG_MSG("Partial reserve: 1=%d 2=%d 3=%d 4=%d 5=%d 6=%d 7=%d 8=%d 9=%d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]); + int x,y,pr; + pr = 0; + for(x=0;x<9;x++) { + for(y=0;y<rset[x];y++) { + PartialReserveTable[pr] = x; + pr++; + } + } + //if(pr != 32) LOG_MSG("Partial Reserve Table with less than 32 partials reserved!"); + rset = mt32ram.params.system.chanAssign; + //LOG_MSG("Chan assign: 1=%d 2=%d 3=%d 4=%d 5=%d 6=%d 7=%d 8=%d 9=%d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]); + //LOG_MSG("Master volume: %d",mt32ram.params.system.masterVol); + Bit16s tv = (Bit16s)((float)mt32ram.params.system.masterVol * 327.0); + mastervolume = tv; + + } + if (addr==0x200000) { + char buf[SYSEX_SIZE]; + memset(&buf,0,SYSEX_SIZE); + memcpy(&buf,&sysex[8],lens-10); + //LOG_MSG("MT-32 LCD Display: %s", buf); + g_system->displayMessageOnOSD(buf); + } + if ((addr & 0xff0000) == 0x7f0000) { + //LOG_MSG("MT-32 Reset"); + for (Bit32u m1=0;m1<MAXPARTIALS;m1++) partTable[m1]->isActive = false; + + memcpy(&mt32ram, &mt32default, sizeof(mt32ram)); + isEnabled = false; + } + + + } else { + // Header not intended for Roland MT-32 + } + +#endif + +} + + +int CSynthMT32::DumpSysex(char *filename) { + File fp; + char tmpc; + fp.open(filename,File::kFileWriteMode); + if(!fp.isOpen()) + return -1; + + int patchnum; + for(patchnum=0;patchnum<64;patchnum++) { + // Sysex header + tmpc = 0xf0; fp.write(&tmpc, 1); + tmpc = 0x41; fp.write(&tmpc, 1); + tmpc = 0x10; fp.write(&tmpc, 1); + tmpc = 0x16; fp.write(&tmpc, 1); + tmpc = 0x12; fp.write(&tmpc, 1); + + int useaddr = patchnum * 256; + char lsb = useaddr & 0x7f; + char isb = (useaddr >> 7) & 0x7f; + char msb = ((useaddr >> 14) & 0x7f) | 0x08; + //Address + fp.write(&msb, 1); + fp.write(&isb, 1); + fp.write(&lsb, 1); + unsigned int checksum = msb + isb + lsb; + + //Data + fp.write(&mt32ram.params.patch[patchnum+128].common,0xe); + fp.write(&mt32ram.params.patch[patchnum+128].partial[0],0x3a); + fp.write(&mt32ram.params.patch[patchnum+128].partial[1],0x3a); + fp.write(&mt32ram.params.patch[patchnum+128].partial[2],0x3a); + fp.write(&mt32ram.params.patch[patchnum+128].partial[3],0x3a); + //Checksum + char *dat = (char *)&mt32ram.params.patch[patchnum+128]; + int ch; + for(ch=0;ch<246;ch++) checksum += *dat++; + checksum = (checksum & 0x7f); + if(checksum) checksum = 0x80 - checksum; + + fp.write(&checksum,1); + + //End of sysex + tmpc = 0xf7; fp.write(&tmpc, 1); + } + fp.close(); + //LOG_MSG("Wrote temp patches to %s", usefile); + + return 0; +} + + + +static Bit16s tmpBuffer[4096]; +static float sndbufl[4096]; +static float sndbufr[4096]; +static float outbufl[4096]; +static float outbufr[4096]; + +#if USE_MMX == 3 +static float multFactor[4] = { 32767.0, 32767.0, 32767.0, 32767.0 }; +#endif + +void CSynthMT32::MT32_CallBack(Bit8u * stream,Bit32u len, int volume) { + +#ifdef NOMANSLAND + Bit32s i,m; + Bit16s *snd, *useBuf; + Bit32u outlen; + snd = (Bit16s *)stream; + memset(stream,0,len*4); + if(!isEnabled) return; + useBuf = snd; + + outlen = len; + + assert(len < 1024); // tmpBuffer is 4096 bytes + /* + partUsage outUsage; + for(i=0;i<32;i++) { + if(partTable[i]->isActive) { + outUsage.active[i] = -1; + } else { + outUsage.active[i] = 0; + } + outUsage.owner[i] = partTable[i]->ownerChan; + outUsage.assign[i] = PartialReserveTable[i]; + } + fwrite(&outUsage,sizeof(outUsage),1,pInfo);*/ + + for(i=0;i<MAXPARTIALS;i++) partTable[i]->age++; + + for(i=0;i<MAXPARTIALS;i++) { + + if(partTable[i]->produceOutput(tmpBuffer,outlen)==true) { +#if USE_MMX == 0 + Bit16s *tmpoff = snd; + int q = 0; + for(m=0;m<(Bit32s)outlen;m++) { + tmpoff[q] += (Bit16s)(((Bit32s)tmpBuffer[q] * (Bit32s)mastervolume)>>15); + q++; + tmpoff[q] += (Bit16s)(((Bit32s)tmpBuffer[q] * (Bit32s)mastervolume)>>15); + q++; + + } +#else + int tmplen = (outlen >> 1) + 4; +#ifdef I_ASM + __asm { + mov ecx, tmplen + mov ax,mastervolume + shl eax,16 + mov ax,mastervolume + movd mm3,eax + movd mm2,eax + psllq mm3, 32 + por mm3,mm2 + mov esi, useBuf + mov edi, snd +mixloop4: + movq mm1, [esi] + movq mm2, [edi] + pmulhw mm1, mm3 + paddw mm1,mm2 + movq [edi], mm1 + + add esi,8 + add edi,8 + + dec ecx + cmp ecx,0 + jg mixloop4 + emms + } +#else + atti386_produceOutput1(tmplen, mastervolume, useBuf, snd); +#endif +#endif + } + } + + if(myProp.UseReverb) { +#if USE_MMX == 3 + if(!usingSIMD) { +#endif + m=0; + for(i=0;i<(Bit32s)len;i++) { + sndbufl[i] = (float)snd[m] / 32767.0; + m++; + sndbufr[i] = (float)snd[m] / 32767.0; + m++; + } + newReverb->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1); + m=0; + for(i=0;i<(Bit32s)len;i++) { + snd[m] = (Bit16s)(outbufl[i] * 32767.0); + m++; + snd[m] = (Bit16s)(outbufr[i] * 32767.0); + m++; + } +#if USE_MMX == 3 + } else { +#ifdef I_ASM + // Use SIMD instructions to quickly convert between integer and floating point + __asm { + mov ecx, len + shr ecx, 1 + add ecx, 4 + push ecx + + mov esi, multFactor + movups xmm1, [esi] + + // One speaker at a time + mov esi, snd + mov edi, sndbufl + +convloop1: + xor eax,eax + mov ax,[snd] + cwde // Sign extended ax + inc snd + inc snd + movd mm1,eax + psrlq mm1, 32 + mov ax,[snd] + inc snd + inc snd + movd mm2,eax + por mm1,mm2 + + dec ecx + jnz convloop1 + + pop ecx + mov esi, snd + mov edi, sndbufr + inc esi +convloop2: + + dec ecx + jnz convloop2 + + } +#else + atti386_produceOutput2(len, snd, sndbufl, sndbufr, multFactor); +#endif + } +#endif + } + + // for(i=0;i<9;i++) { mchan[i]->CheckNoteList(); } + + for(i=0;i<MAXPARTIALS;i++) { partTable[i]->alreadyOutputed = false; } + + +#if MAXPARTIALS == 0 + // Reorganize partials + CPartialMT32 *tmpPartial; + int y; +resetSearch: + for(i=0;i<MAXPARTIALS;i++) { + // Try to relocate partials not assigned to the right channel + if((partTable[i]->isActive) && (partTable[i]->ownerChan != PartialReserveTable[i])) { + for(y=0;y<MAXPARTIALS;y++) { + if((!partTable[y]->isActive) && (PartialReserveTable[y] == partTable[i]->ownerChan)) { + // Found a free channel that can use this partial - swap it out + tmpPartial = partTable[y]; + partTable[y] = partTable[i]; + partTable[i] = tmpPartial; + goto resetSearch; + } + } + } + } +#endif + +#if MONITORPARTIALS == 1 + samplepos+=outlen; + if(samplepos>SETRATE*5) { + samplepos = 0; + int partUse[9]; + memset(partUse,0,sizeof(partUse)); + for(i=0;i<MAXPARTIALS;i++) { + if(partTable[i]->isActive) partUse[partTable[i]->ownerChan]++; + } + //LOG_MSG("C1: %d C2: %d C3: %d C4 %d", partUse[0], partUse[1], partUse[2], partUse[3]); + //LOG_MSG("C5: %d C6: %d C7: %d C8 %d", partUse[4], partUse[5], partUse[6], partUse[7]); + //LOG_MSG("Rythmn: %d", partUse[8]); + + } + +#endif + + +#endif + + +} diff --git a/backends/midi/mt32/synth.h b/backends/midi/mt32/synth.h new file mode 100644 index 0000000000..1978a17a2f --- /dev/null +++ b/backends/midi/mt32/synth.h @@ -0,0 +1,170 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * Based on Tristan's conversion of Canadacow's code + * + * 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. + * + * $Header$ + * + */ + +#if !defined __CSYNTHMT32_H__ +#define __CSYNTHMT32_H__ + +#ifdef HAVE_X86 +#if defined(_MSC_VER) +#define USE_MMX 2 +#define I_ASM +#else +#define USE_MMX 0 +#undef I_ASM +#endif +#else +#define USE_MMX 0 +#endif + +extern const char *rom_path; + +#define AMPENV 0 +#define FILTENV 1 +#define PITCHENV 2 + +// Filter setting +#define FILTER_FLOAT 1 +#define FILTER_64BIT 0 +#define FILTER_INT 0 + +#define FILTERGRAN 512 + +// Amplitude of waveform generator +#define WGAMP (7168) +//#define WGAMP (8192) + +#include "backends/midi/mt32/structures.h" +#include "sound/mixer.h" + +// Function that detects the availablity of SSE SIMD instructions +// On non-MSVC compilers it automatically returns FALSE as inline assembly is required +bool DetectSIMD(); +// Function that detects the availablity of 3DNow instructions +// On non-MSVC compilers it automatically returns FALSE as inline assembly is required +bool Detect3DNow(); + +struct SynthProperties { + // Sample rate to use in mixing + int SampleRate; + // Flag to activate reverb. True = use reverb, False = no reverb + bool UseReverb; + // Flag True to use software set reverb settings, Flag False to set reverb settings in + // following parameters + bool UseDefault; + // When not using the default settings, this specifies one of the 4 reverb types + // 1 = Room 2 = Hall 3 = Plate 4 = Tap + int RevType; + // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement) + int RevTime; + // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement) + int RevLevel; +}; + +#ifndef BOOL + #define BOOL bool +#endif +#ifndef TRUE + #define TRUE true +#endif +#ifndef FALSE + #define FALSE false +#endif + +// This is the specification of the Callback routine used when calling the RecalcWaveforms +// function +typedef void (*recalcStatusCallback)(int percDone); + +// This external function recreates the base waveform file (waveforms.raw) using a specifed +// sampling rate. The callback routine provides interactivity to let the user know what +// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the +// callback routine, no status is reported. +BOOL RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack); + +typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel); +extern iir_filter_type usefilter; + +extern partialFormat PCM[54]; +extern Bit16s romfile[262656]; +extern Bit32s divtable[256]; +extern Bit32s smalldivtable[256]; +extern Bit32u wavtabler[64][256]; +extern Bit32u looptabler[16][16][256]; +extern Bit16s sintable[65536]; +extern Bit32s penvtable[16][128]; +extern Bit32s pulsetable[101]; +extern Bit32s pulseoffset[101]; +extern Bit32s sawtable[128][128]; +extern float filtcoeff[FILTERGRAN][32][16]; +extern Bit32u lfoptable[101][128]; +extern Bit32s ampveltable[128][64]; +extern Bit32s amptable[129]; +extern Bit16s smallnoise[441]; +extern Bit32s samplepos; +extern Bit16s* waveforms[4][256]; +extern Bit32u waveformsize[4][256]; +extern Bit8s LoopPatterns[16][16]; +extern int drumPan[30][2]; +extern float ResonFactor[32]; +extern float ResonInv[32]; + +extern Bit32s getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly); +extern Bit32s getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly); +extern Bit32s getFiltEnvelope(Bit16s wg, dpoly::partialStatus *pStat, dpoly *poly); + +class CSynthMT32 { +private: + + unsigned char initmode; + bool isOpen; + SynthProperties myProp; + + bool InitTables(const char * baseDir); + +public: + CSynthMT32() : isOpen(false) {}; + + // Used to initialized the MT-32. The baseDir parameter points to the location in the + // filesystem where the ROM and data files are located. The second parameter specifies + // properties for the synthesizer, as outlined in the structure above. + // Returns TRUE if initialization was sucessful, otherwise returns FALSE. + bool ClassicOpen(const char *baseDir, SynthProperties useProp); + + // Closes the MT-32 and deallocates any memory used by the synthesizer + void Close(void); + + // Sends a 4-byte MIDI message to the MT-32 for immediate playback + void PlayMsg(Bit32u msg); + + // Sends a string of Sysex commands to the MT-32 for immediate interpretation + void PlaySysex(Bit8u * sysex, Bit32u len); + + // Save the system state to a sysex file specified by filename + int DumpSysex(char *filename); + + // This callback routine is used to have the MT-32 generate samples to the specified + // output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo, + // one sample is 4 bytes) + void MT32_CallBack(Bit8u * stream, Bit32u len, int volume); + +}; + +#endif |