aboutsummaryrefslogtreecommitdiff
path: root/sound/softsynth/ym2612.cpp
diff options
context:
space:
mode:
authorMax Horn2004-12-25 18:34:44 +0000
committerMax Horn2004-12-25 18:34:44 +0000
commitfec3df209601b812034fafed53ef74b7ee732512 (patch)
tree14572de096e66b3720faf67dbd9d3cf3f3926422 /sound/softsynth/ym2612.cpp
parent0d2fa6ecf02d5745db90d78c78e546b3fe62d373 (diff)
downloadscummvm-rg350-fec3df209601b812034fafed53ef74b7ee732512.tar.gz
scummvm-rg350-fec3df209601b812034fafed53ef74b7ee732512.tar.bz2
scummvm-rg350-fec3df209601b812034fafed53ef74b7ee732512.zip
Moved the softsynth midi drivers into a sound/softsynth; amongst other things, this fixes bug #1083058
svn-id: r16316
Diffstat (limited to 'sound/softsynth/ym2612.cpp')
-rw-r--r--sound/softsynth/ym2612.cpp907
1 files changed, 907 insertions, 0 deletions
diff --git a/sound/softsynth/ym2612.cpp b/sound/softsynth/ym2612.cpp
new file mode 100644
index 0000000000..b9ad9b35ca
--- /dev/null
+++ b/sound/softsynth/ym2612.cpp
@@ -0,0 +1,907 @@
+/* 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 "sound/softsynth/emumidi.h"
+
+#include <math.h>
+
+
+////////////////////////////////////////
+//
+// Miscellaneous
+//
+////////////////////////////////////////
+
+static int *sintbl = 0;
+static int *powtbl = 0;
+static int *frequencyTable = 0;
+static int *keycodeTable = 0;
+static int *keyscaleTable = 0;
+static int *attackOut = 0;
+
+////////////////////////////////////////
+//
+// Class declarations
+//
+////////////////////////////////////////
+
+class Operator2612;
+class Voice2612;
+class MidiChannel_YM2612;
+class MidiDriver_YM2612;
+
+class Operator2612 {
+protected:
+ Voice2612 *_owner;
+ enum State { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
+ State _state;
+ int32 _currentLevel;
+ int _frequency;
+ uint32 _phase;
+ int _lastOutput;
+ int _feedbackLevel;
+ int _detune;
+ int _multiple;
+ int32 _totalLevel;
+ int _keyScale;
+ int _velocity;
+ int _specifiedTotalLevel;
+ int _specifiedAttackRate;
+ int _specifiedDecayRate;
+ int _specifiedSustainLevel;
+ int _specifiedSustainRate;
+ int _specifiedReleaseRate;
+ int _tickCount;
+ int _attackTime;
+ int32 _decayRate;
+ int32 _sustainLevel;
+ int32 _sustainRate;
+ int32 _releaseRate;
+
+public:
+ Operator2612 (Voice2612 *owner);
+ ~Operator2612();
+ void feedbackLevel(int level);
+ void setInstrument(byte const *instrument);
+ void velocity(int velo);
+ void keyOn();
+ void keyOff();
+ void frequency(int freq);
+ void nextTick(const int *phaseShift, int *outbuf, int buflen);
+ bool inUse() { return (_state != _s_ready); }
+};
+
+class Voice2612 {
+public:
+ Voice2612 *next;
+ uint16 _rate;
+
+protected:
+ Operator2612 *_opr[4];
+ int _velocity;
+ int _control7;
+ int _note;
+ int _frequencyOffs;
+ int _frequency;
+ int _algorithm;
+
+ int *_buffer;
+ int _buflen;
+
+public:
+ Voice2612();
+ ~Voice2612();
+ void setControlParameter(int control, int value);
+ void setInstrument(byte const *instrument);
+ void velocity(int velo);
+ void nextTick(int *outbuf, int buflen);
+ void noteOn(int n, int onVelo);
+ bool noteOff(int note);
+ void pitchBend(int value);
+ void recalculateFrequency();
+};
+
+class MidiChannel_YM2612 : public MidiChannel {
+protected:
+ uint16 _rate;
+ Voice2612 *_voices;
+ Voice2612 *_next_voice;
+
+public:
+ void removeAllVoices();
+ void nextTick(int *outbuf, int buflen);
+ void rate(uint16 r);
+
+public:
+ MidiChannel_YM2612();
+ virtual ~MidiChannel_YM2612();
+
+ // MidiChannel interface
+ MidiDriver *device() { return 0; }
+ byte getNumber() { return 0; }
+ void release() { }
+ void send(uint32 b) { }
+ void noteOff(byte note);
+ void noteOn(byte note, byte onVelo);
+ void programChange(byte program) { }
+ void pitchBend(int16 value);
+ void controlChange(byte control, byte value);
+ void pitchBendFactor(byte value) { }
+ void sysEx_customInstrument(uint32 type, byte *instr);
+};
+
+class MidiDriver_YM2612 : public MidiDriver_Emulated {
+protected:
+ MidiChannel_YM2612 *_channel[16];
+
+ int _next_voice;
+ int _volume;
+
+protected:
+ static void createLookupTables();
+ void nextTick(int16 *buf1, int buflen);
+ int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
+ void rate(uint16 r);
+
+ void generateSamples(int16 *buf, int len);
+
+public:
+ MidiDriver_YM2612(SoundMixer *mixer);
+ virtual ~MidiDriver_YM2612();
+
+ int open();
+ void close();
+ void send(uint32 b);
+ void send(byte channel, uint32 b); // Supports higher than channel 15
+ 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 _mixer->getOutputRate(); }
+};
+
+////////////////////////////////////////
+//
+// Operator2612 implementation
+//
+////////////////////////////////////////
+
+Operator2612::Operator2612 (Voice2612 *owner) :
+ _owner (owner),
+ _state (_s_ready),
+ _currentLevel ((int32)0x7f << 15),
+ _phase (0),
+ _lastOutput (0),
+ _feedbackLevel (0),
+ _detune (0),
+ _multiple (1),
+ _keyScale (0),
+ _specifiedTotalLevel (127),
+ _specifiedAttackRate (0),
+ _specifiedDecayRate (0),
+ _specifiedSustainRate (0),
+ _specifiedReleaseRate (15) {
+ velocity(0);
+}
+
+Operator2612::~Operator2612()
+{ }
+
+void Operator2612::velocity(int velo) {
+ _velocity = velo;
+ _totalLevel = ((int32)_specifiedTotalLevel << 15) +
+ ((int32)(127-_velocity) << 13);
+ _sustainLevel = ((int32)_specifiedSustainLevel << 17);
+}
+
+void Operator2612::feedbackLevel(int level) {
+ _feedbackLevel = level;
+}
+
+void Operator2612::setInstrument(byte const *instrument) {
+ _detune = (instrument[8] >> 4) & 7;
+ _multiple = instrument[8] & 15;
+ _specifiedTotalLevel = instrument[12] & 127;
+ _keyScale = (instrument[16] >> 6) & 3;
+ _specifiedAttackRate = instrument[16] & 31;
+ _specifiedDecayRate = instrument[20] & 31;
+ _specifiedSustainRate = instrument[24] & 31;
+ _specifiedSustainLevel = (instrument[28] >> 4) & 15;
+ _specifiedReleaseRate = instrument[28] & 15;
+ _state = _s_ready; // 本物ではどうなのかな?
+ velocity(_velocity);
+}
+
+void Operator2612::keyOn() {
+ _state = _s_attacking;
+ _tickCount = 0;
+ _phase = 0; // どうも、実際こうらしい
+ _currentLevel = ((int32)0x7f << 15); // これも、実際こうらしい
+}
+
+void Operator2612::keyOff() {
+ if (_state != _s_ready)
+ _state = _s_releasing;
+}
+
+void Operator2612::frequency(int freq) {
+ double value; // Use for intermediate computations to avoid int64 arithmetic
+ int r;
+
+ _frequency = freq / _owner->_rate;
+
+ r = _specifiedAttackRate;
+ if (r != 0) {
+ r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
+ if (r >= 64)
+ r = 63; // するべきなんだろうとは思うんだけど (赤p.207)
+ }
+
+ r = 63 - r;
+ if (_specifiedTotalLevel >= 128)
+ value = 0;
+ else {
+ value = powtbl[(r&3) << 7];
+ value *= 1 << (r >> 2);
+ value *= 41; // r == 20 のとき、0-96[db] が 10.01[ms] == 41.00096
+ value /= 1 << (15 + 5);
+ value *= 127 - _specifiedTotalLevel;
+ value /= 127;
+ }
+ _attackTime = (int32) value; // 1 秒 == (1 << 12)
+ if (_attackTime > 0)
+ _attackTime = (1 << (12+10)) / (_owner->_rate * _attackTime);
+
+ r = _specifiedDecayRate;
+ if (r != 0) {
+ r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
+ if (r >= 64)
+ r = 63;
+ }
+ value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
+ _decayRate = (int32) value / _owner->_rate;
+
+ r = _specifiedSustainRate;
+ if (r != 0) {
+ r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
+ if (r >= 64)
+ r = 63;
+ }
+ value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
+ _sustainRate = (int32) value / _owner->_rate;
+
+ r = _specifiedReleaseRate;
+ if (r != 0) {
+ r = r * 2 + 1; // (Translated) I cannot know whether the timing is a good choice or not
+ r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
+ // KS による補正はあるらしい。赤p.206 では記述されてないけど。
+ if (r >= 64)
+ r = 63;
+ }
+ value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
+ _releaseRate = (int32) value / _owner->_rate;
+}
+
+void Operator2612::nextTick(const int *phasebuf, int *outbuf, int buflen) {
+ if (_state == _s_ready)
+ return;
+ if (_state == _s_attacking && _attackTime <= 0) {
+ _currentLevel = 0;
+ _state = _s_decaying;
+ }
+
+ int32 levelIncrement = 0;
+ int32 target = 0;
+ State next_state = _s_ready;
+ const int32 zero_level = ((int32)0x7f << 15);
+ const int phaseIncrement = (_multiple > 0) ? (_frequency * _multiple) : (_frequency / 2);
+
+ int32 output = _lastOutput;
+ int32 level = _currentLevel + _totalLevel;
+
+ while (buflen) {
+ switch (_state) {
+ case _s_ready:
+ return;
+ break;
+ case _s_attacking:
+ next_state = _s_attacking;
+ break;
+ case _s_decaying:
+ levelIncrement = _decayRate;
+ target = _sustainLevel + _totalLevel;
+ next_state = _s_sustaining;
+ break;
+ case _s_sustaining:
+ levelIncrement = _sustainRate;
+ target = zero_level + _totalLevel;
+ next_state = _s_ready;
+ break;
+ case _s_releasing:
+ levelIncrement = _releaseRate;
+ target = zero_level + _totalLevel;
+ next_state = _s_ready;
+ break;
+ }
+
+ bool switching = false;
+ do {
+ if (next_state == _s_attacking) {
+ // Attack phase
+ ++_tickCount;
+ int i = (int) (_tickCount * _attackTime);
+ if (i >= 1024) {
+ level = _totalLevel;
+ _state = _s_decaying;
+ switching = true;
+ } else {
+ level = (attackOut[i] << (31 - 8 - 16)) + _totalLevel;
+ }
+ } else {
+ // Decay, Sustain and Release phases
+ level += levelIncrement;
+ if (level >= target) {
+ level = target;
+ _state = next_state;
+ switching = true;
+ }
+ }
+
+ if (level < zero_level) {
+ int phaseShift = *phasebuf >> 2; // 正しい変調量は? 3 じゃ小さすぎで 2 じゃ大きいような。
+ if (_feedbackLevel)
+ phaseShift += (output << (_feedbackLevel - 1)) / 1024;
+ output = sintbl[((_phase >> 7) + phaseShift) & 0x7ff];
+ output >>= (level >> 18); // 正しい減衰量は?
+ // Here is the original code, which requires 64-bit ints
+// output *= powtbl[511 - ((level>>25)&511)];
+// output >>= 16;
+// output >>= 1;
+ // And here's our 32-bit trick for doing it. (Props to Fingolfin!)
+ // Result varies from original code by max of 1.
+// int powVal = powtbl[511 - ((level>>9)&511)];
+// int outputHI = output / 256;
+// int powHI = powVal / 256;
+// output = (outputHI * powHI) / 2 + (outputHI * (powVal % 256) + powHI * (output % 256)) / 512;
+ // And here's the even faster code.
+ // Result varies from original code by max of 8.
+ output = ((output >> 4) * (powtbl[511-((level>>9)&511)] >> 3)) / 1024;
+
+ _phase += phaseIncrement;
+ _phase &= 0x3ffff;
+ } else
+ output = 0;
+
+ *outbuf += output;
+ --buflen;
+ ++phasebuf;
+ ++outbuf;
+ } while (buflen && !switching);
+ }
+ _lastOutput = output;
+ _currentLevel = level - _totalLevel;
+}
+
+////////////////////////////////////////
+//
+// Voice2612 implementation
+//
+////////////////////////////////////////
+
+Voice2612::Voice2612() {
+ next = 0;
+ _control7 = 127;
+ _note = 40;
+ _frequency = 440;
+ _frequencyOffs = 0x2000;
+ _algorithm = 7;
+
+ _buffer = 0;
+ _buflen = 0;
+
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); ++i)
+ _opr[i] = new Operator2612 (this);
+ velocity(0);
+}
+
+Voice2612::~Voice2612() {
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); ++i)
+ delete _opr[i];
+ free(_buffer);
+}
+
+void Voice2612::velocity(int velo) {
+ _velocity = velo;
+#if 0
+ int v = (velo * _control7) >> 7; // これだと精度良くないですね
+#else
+ int v = velo + (_control7 - 127) * 4;
+#endif
+ bool iscarrier[8][4] = {
+ { false, false, false, true, }, //0
+ { false, false, false, true, }, //1
+ { false, false, false, true, }, //2
+ { false, false, false, true, }, //3
+ { false, true, false, true, }, //4
+ { false, true, true, true, }, //5
+ { false, true, true, true, }, //6
+ { true, true, true, true, }, //7
+ };
+ int opr;
+ for (opr = 0; opr < 4; opr++)
+ if (iscarrier[_algorithm][opr])
+ _opr[opr]->velocity(v);
+ else
+ _opr[opr]->velocity(127);
+}
+
+void Voice2612::setControlParameter(int control, int value) {
+ switch (control) {
+ case 7:
+ _control7 = value;
+ velocity(_velocity);
+ break;
+ case 123:
+ // All notes off
+ noteOff(_note);
+ };
+}
+
+void Voice2612::setInstrument(byte const *instrument) {
+ if (instrument == NULL)
+ return;
+
+ _algorithm = instrument[32] & 7;
+ _opr[0]->feedbackLevel((instrument[32] >> 3) & 7);
+ _opr[1]->feedbackLevel(0);
+ _opr[2]->feedbackLevel(0);
+ _opr[3]->feedbackLevel(0);
+ _opr[0]->setInstrument(instrument + 0);
+ _opr[1]->setInstrument(instrument + 2);
+ _opr[2]->setInstrument(instrument + 1);
+ _opr[3]->setInstrument(instrument + 3);
+}
+
+void Voice2612::nextTick(int *outbuf, int buflen) {
+ if (_velocity == 0)
+ return;
+
+ if (_buflen < buflen) {
+ free(_buffer);
+ _buflen = buflen;
+ _buffer = (int *) malloc(sizeof(int) * buflen * 2);
+ }
+
+ int *buf1 = _buffer;
+ int *buf2 = _buffer + buflen;
+ memset(_buffer, 0, sizeof(int) * buflen * 2);
+
+ switch (_algorithm) {
+ case 0:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, buf1, buflen);
+ memset (buf2, 0, sizeof (int) * buflen);
+ _opr[2]->nextTick(buf1, buf2, buflen);
+ _opr[3]->nextTick(buf2, outbuf, buflen);
+ break;
+ case 1:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf1, buf2, buflen);
+ _opr[2]->nextTick(buf2, buf1, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 2:
+ _opr[1]->nextTick(buf1, buf2, buflen);
+ _opr[2]->nextTick(buf2, buf1, buflen);
+ memset(buf2, 0, sizeof(int) * buflen);
+ _opr[0]->nextTick(buf2, buf1, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 3:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, buf1, buflen);
+ memset(buf2, 0, sizeof(int) * buflen);
+ _opr[2]->nextTick(buf2, buf1, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 4:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, outbuf, buflen);
+ _opr[2]->nextTick(buf1, buf1, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 5:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, outbuf, buflen);
+ _opr[2]->nextTick(buf2, outbuf, buflen);
+ _opr[3]->nextTick(buf2, outbuf, buflen);
+ break;
+ case 6:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, outbuf, buflen);
+ _opr[2]->nextTick(buf1, outbuf, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 7:
+ _opr[0]->nextTick(buf1, outbuf, buflen);
+ _opr[1]->nextTick(buf1, outbuf, buflen);
+ _opr[2]->nextTick(buf1, outbuf, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ };
+}
+
+void Voice2612::noteOn(int n, int onVelo) {
+ _note = n;
+ velocity(onVelo);
+ recalculateFrequency();
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); i++)
+ _opr[i]->keyOn();
+}
+
+bool Voice2612::noteOff(int note) {
+ if (_note != note)
+ return false;
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); i++)
+ _opr[i]->keyOff();
+ return true;
+}
+
+void Voice2612::pitchBend(int value) {
+ _frequencyOffs = value;
+ recalculateFrequency();
+}
+
+void Voice2612::recalculateFrequency() {
+ // MIDI とも違うし....
+ // どういう仕様なんだろうか?
+ // と思ったら、なんと、これ (↓) が正解らしい。
+ int32 basefreq = frequencyTable[_note];
+ int cfreq = frequencyTable[_note - (_note % 12)];
+ int oct = _note / 12;
+ int fnum = (int) (((double)basefreq * (1 << 13)) / cfreq); // OPL の fnum と同じようなもの。
+ fnum += _frequencyOffs - 0x2000;
+ if (fnum < 0x2000) {
+ fnum += 0x2000;
+ oct--;
+ }
+ if (fnum >= 0x4000) {
+ fnum -= 0x2000;
+ oct++;
+ }
+
+ // _frequency は最終的にバイアス 256*1024 倍
+ _frequency = (int) ((frequencyTable[oct*12] * (double)fnum) / 8);
+
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); i++)
+ _opr[i]->frequency(_frequency);
+}
+
+////////////////////////////////////////
+//
+// MidiChannel_YM2612
+//
+////////////////////////////////////////
+
+MidiChannel_YM2612::MidiChannel_YM2612() {
+ _voices = 0;
+ _next_voice = 0;
+}
+
+MidiChannel_YM2612::~MidiChannel_YM2612() {
+ removeAllVoices();
+}
+
+void MidiChannel_YM2612::removeAllVoices() {
+ if (!_voices)
+ return;
+ Voice2612 *last, *voice = _voices;
+ for (; voice; voice = last) {
+ last = voice->next;
+ delete voice;
+ }
+ _voices = _next_voice = 0;
+}
+
+void MidiChannel_YM2612::noteOn(byte note, byte onVelo) {
+ if (!_voices)
+ return;
+ _next_voice = _next_voice ? _next_voice : _voices;
+ _next_voice->noteOn(note, onVelo);
+ _next_voice = _next_voice->next;
+}
+
+void MidiChannel_YM2612::noteOff(byte note) {
+ if (!_voices)
+ return;
+ if (_next_voice == _voices)
+ _next_voice = 0;
+ Voice2612 *voice = _next_voice;
+ do {
+ if (!voice)
+ voice = _voices;
+ if (voice->noteOff(note)) {
+ _next_voice = voice;
+ break;
+ }
+ voice = voice->next;
+ } while (voice != _next_voice);
+}
+
+void MidiChannel_YM2612::controlChange(byte control, byte value) {
+ // いいのかこれで?
+ if (control == 121) {
+ // Reset controller
+ removeAllVoices();
+ } else {
+ Voice2612 *voice = _voices;
+ for (; voice; voice = voice->next)
+ voice->setControlParameter(control, value);
+ }
+}
+
+void MidiChannel_YM2612::sysEx_customInstrument(uint32 type, byte *fmInst) {
+ if (type != 'EUP ')
+ return;
+ Voice2612 *voice = new Voice2612;
+ voice->next = _voices;
+ _voices = voice;
+ voice->_rate = _rate;
+ voice->setInstrument(fmInst);
+}
+
+void MidiChannel_YM2612::pitchBend(int16 value) {
+ // いいのかこれで?
+ Voice2612 *voice = _voices;
+ for (; voice; voice = voice->next)
+ voice->pitchBend(value);
+}
+
+void MidiChannel_YM2612::nextTick(int *outbuf, int buflen) {
+ Voice2612 *voice = _voices;
+ for (; voice; voice = voice->next)
+ voice->nextTick(outbuf, buflen);
+}
+
+void MidiChannel_YM2612::rate(uint16 r) {
+ _rate = r;
+ Voice2612 *voice = _voices;
+ for (; voice; voice = voice->next)
+ voice->_rate = r;
+}
+
+////////////////////////////////////////
+//
+// MidiDriver_YM2612
+//
+////////////////////////////////////////
+
+MidiDriver_YM2612::MidiDriver_YM2612(SoundMixer *mixer)
+ : MidiDriver_Emulated(mixer) {
+ _next_voice = 0;
+
+ createLookupTables();
+ _volume = 256;
+ int i;
+ for (i = 0; i < ARRAYSIZE(_channel); i++)
+ _channel[i] = new MidiChannel_YM2612;
+ rate(getRate());
+}
+
+MidiDriver_YM2612::~MidiDriver_YM2612() {
+ int i;
+ for (i = 0; i < ARRAYSIZE(_channel); i++)
+ delete _channel[i];
+ delete sintbl;
+ delete powtbl;
+ delete frequencyTable;
+ delete keycodeTable;
+ delete keyscaleTable;
+ delete attackOut;
+ sintbl = powtbl = frequencyTable = keycodeTable = keyscaleTable = attackOut = 0;
+}
+
+int MidiDriver_YM2612::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ MidiDriver_Emulated::open();
+
+ _mixer->setupPremix(this);
+ return 0;
+}
+
+void MidiDriver_YM2612::close() {
+ if (!_isOpen)
+ return;
+ _isOpen = false;
+
+ // Detach the premix callback handler
+ _mixer->setupPremix(0);
+}
+
+void MidiDriver_YM2612::send(uint32 b) {
+ send(b & 0xF, b & 0xFFFFFFF0);
+}
+
+void MidiDriver_YM2612::send(byte chan, uint32 b) {
+ //byte param3 = (byte) ((b >> 24) & 0xFF);
+ byte param2 = (byte) ((b >> 16) & 0xFF);
+ byte param1 = (byte) ((b >> 8) & 0xFF);
+ byte cmd = (byte) (b & 0xF0);
+ if (chan > ARRAYSIZE(_channel))
+ return;
+
+ switch (cmd) {
+ case 0x80:// Note Off
+ _channel[chan]->noteOff(param1);
+ break;
+ case 0x90: // Note On
+ _channel[chan]->noteOn(param1, param2);
+ break;
+ case 0xA0: // Aftertouch
+ break; // Not supported.
+ case 0xB0: // Control Change
+ _channel[chan]->controlChange(param1, param2);
+ break;
+ case 0xC0: // Program Change
+ _channel[chan]->programChange(param1);
+ break;
+ case 0xD0: // Channel Pressure
+ break; // Not supported.
+ case 0xE0: // Pitch Bend
+ _channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000);
+ break;
+ case 0xF0: // SysEx
+ // We should never get here! SysEx information has to be
+ // sent via high-level semantic methods.
+ warning("MidiDriver_YM2612: Receiving SysEx command on a send() call");
+ break;
+
+ default:
+ warning("MidiDriver_YM2612: Unknown send() command 0x%02X", cmd);
+ }
+}
+
+void MidiDriver_YM2612::sysEx(byte *msg, uint16 length) {
+ if (msg[0] != 0x7C || msg[1] >= ARRAYSIZE(_channel))
+ return;
+ _channel[msg[1]]->sysEx_customInstrument('EUP ', &msg[2]);
+}
+
+void MidiDriver_YM2612::generateSamples(int16 *data, int len) {
+ memset(data, 0, 2 * sizeof(int16) * len);
+ nextTick(data, len);
+}
+
+void MidiDriver_YM2612::nextTick(int16 *buf1, int buflen) {
+ int *buf0 = (int *)buf1;
+
+ int i;
+ for (i = 0; i < ARRAYSIZE(_channel); i++)
+ _channel[i]->nextTick(buf0, buflen);
+
+ for (i = 0; i < buflen; ++i)
+ buf1[i*2+1] = buf1[i*2] = ((buf0[i] * volume()) >> 10) & 0xffff;
+}
+
+void MidiDriver_YM2612::rate(uint16 r)
+{
+ int i;
+ for (i = 0; i < ARRAYSIZE(_channel); i++)
+ _channel[i]->rate(r);
+}
+
+void MidiDriver_YM2612::createLookupTables() {
+ {
+ int i;
+ sintbl = new int [2048];
+ for (i = 0; i < 2048; i++)
+ sintbl[i] = (int)(0xffff * sin(i/2048.0*2.0*PI));
+ }
+
+ {
+ int i;
+ powtbl = new int [1025];
+ for (i = 0; i <= 1024; i++)
+ powtbl[i] = (int)(0x10000 * pow(2.0, (i-512)/512.0));
+ }
+
+ {
+ int i;
+ int block;
+
+ static int fnum[] = {
+ 0x026a, 0x028f, 0x02b6, 0x02df,
+ 0x030b, 0x0339, 0x036a, 0x039e,
+ 0x03d5, 0x0410, 0x044e, 0x048f,
+ };
+
+ // (int)(880.0 * 256.0 * pow(2.0, (note-0x51)/12.0)); // バイアス 256 倍
+ // 0x45 が 440Hz (a4)、0x51 が 880Hz (a5) らしい
+ frequencyTable = new int [120];
+ for (block = -1; block < 9; block++) {
+ for (i = 0; i < 12; i++) {
+ double freq = fnum[i] * (166400.0 / 3) * pow(2.0, block-21);
+ frequencyTable[(block+1)*12+i] = (int)(256.0 * freq);
+ }
+ }
+
+ keycodeTable = new int [120];
+ // detune 量の計算や KS による rate 変換に使うんじゃないかな
+ for (block = -1; block < 9; block++) {
+ for (i = 0; i < 12; i++) {
+ // see p.204
+ int f8 = (fnum[i] >> 7) & 1;
+ int f9 = (fnum[i] >> 8) & 1;
+ int f10 = (fnum[i] >> 9) & 1;
+ int f11 = (fnum[i] >> 10) & 1;
+ int n4 = f11;
+ int n3 = f11&(f10|f9|f8) | (~f11&f10&f9&f8);
+ int note = n4*2 + n3;
+ // see p.207
+ keycodeTable[(block+1)*12+i] = block*4 + note;
+ }
+ }
+ }
+
+ {
+ int freq;
+ keyscaleTable = new int [8192];
+ keyscaleTable[0] = 0;
+ for (freq = 1; freq < 8192; freq++) {
+ keyscaleTable[freq] = (int)(log((double)freq) / 9.03 * 32.0) - 1;
+ // 8368[Hz] (o9c) で 32くらい。9.03 =:= ln 8368
+ }
+ }
+
+ {
+ int i;
+ attackOut = new int [1024];
+ for (i = 0; i < 1024; i++)
+ attackOut[i] = (int)(((0x7fff+0x03a5)*30.0) / (30.0+i)) - 0x03a5;
+ }
+}
+
+////////////////////////////////////////
+//
+// MidiDriver_YM2612 factory
+//
+////////////////////////////////////////
+
+MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer) {
+ return new MidiDriver_YM2612(mixer);
+}