scummvm/backends/midi/ym2612.cpp

908 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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 "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 generate_samples(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; // <20><>ʪ<EFBFBD>ǤϤɤ<CFA4><C9A4>ʤΤ<CAA4><CEA4><EFBFBD>?
velocity(_velocity);
}
void Operator2612::keyOn() {
_state = _s_attacking;
_tickCount = 0;
_phase = 0; // <20>ɤ<EFBFBD><C9A4><EFBFBD>ºݤ<C2BA><DDA4><EFBFBD><EFBFBD><EFBFBD><E9A4B7>
_currentLevel = ((int32)0x7f << 15); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ºݤ<C2BA><DDA4><EFBFBD><EFBFBD><EFBFBD><E9A4B7>
}
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; // <20><><EFBFBD><EFBFBD><EFBFBD>٤<EFBFBD><D9A4>ʤ<EFBFBD><CAA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȥϻפ<CFBB><D7A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><>p.207)
}
r = 63 - r;
if (_specifiedTotalLevel >= 128)
value = 0;
else {
value = powtbl[(r&3) << 7];
value *= 1 << (r >> 2);
value *= 41; // r == 20 <20>ΤȤ<CEA4><C8A4><EFBFBD>0-96[db] <20><> 10.01[ms] == 41.00096
value /= 1 << (15 + 5);
value *= 127 - _specifiedTotalLevel;
value /= 127;
}
_attackTime = (int32) value; // 1 <20><> == (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 <20>ˤ<EFBFBD><CBA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϥ<EFBFBD><CFA4><EFBFBD><EFBFBD><EFBFBD><E9A4B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p.206 <20>Ǥϵ<C7A4><CFB5>Ҥ<EFBFBD><D2A4><EFBFBD><EFBFBD>Ƥʤ<C6A4><CAA4><EFBFBD><EFBFBD>ɡ<EFBFBD>
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; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD>̤<EFBFBD>? 3 <20><><EFBFBD><EFBFBD><E3BEAE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 2 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E7A4AD><EFBFBD><EFBFBD>ʡ<EFBFBD>
if (_feedbackLevel)
phaseShift += (output << (_feedbackLevel - 1)) / 1024;
output = sintbl[((_phase >> 7) + phaseShift) & 0x7ff];
output >>= (level >> 18); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̤<EFBFBD>?
// 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; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɤ<EFBFBD><C9A4>ʤ<EFBFBD><CAA4>Ǥ<EFBFBD><C7A4><EFBFBD>
#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 <20>Ȥ<EFBFBD><C8A4><EFBFBD><E3A4A6>....
// <20>ɤ<EFBFBD><C9A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͤʤ<CDA4><CAA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
// <20>Ȼפä<D7A4><C3A4><EFBFBD>ʤ<EFBFBD><CAA4>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD> (<28><>) <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E9A4B7><EFBFBD><EFBFBD>
int32 basefreq = frequencyTable[_note];
int cfreq = frequencyTable[_note - (_note % 12)];
int oct = _note / 12;
int fnum = (int) (((double)basefreq * (1 << 13)) / cfreq); // OPL <20><> fnum <20><>Ʊ<EFBFBD><C6B1><EFBFBD><EFBFBD>ʤ<EFBFBD><CAA4>Ρ<EFBFBD>
fnum += _frequencyOffs - 0x2000;
if (fnum < 0x2000) {
fnum += 0x2000;
oct--;
}
if (fnum >= 0x4000) {
fnum -= 0x2000;
oct++;
}
// _frequency <20>Ϻǽ<CFBA>Ū<EFBFBD>˥Х<CBA5><D0A5><EFBFBD><EFBFBD><EFBFBD> 256*1024 <20><>
_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) {
// <20><><EFBFBD><EFBFBD><EFBFBD>Τ<EFBFBD><CEA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
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) {
// <20><><EFBFBD><EFBFBD><EFBFBD>Τ<EFBFBD><CEA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
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::generate_samples(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)); // <20>Х<EFBFBD><D0A5><EFBFBD><EFBFBD><EFBFBD> 256 <20><>
// 0x45 <20><> 440Hz (a4)<29><>0x51 <20><> 880Hz (a5) <20><EFBFBD><E9A4B7>
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 <20>̤η׻<CEB7><D7BB><EFBFBD> KS <20>ˤ<EFBFBD><CBA4><EFBFBD> rate <20>Ѵ<EFBFBD><D1B4>˻Ȥ<CBBB><C8A4>󤸤<EFBFBD><F3A4B8A4>ʤ<EFBFBD><CAA4><EFBFBD><EFBFBD><EFBFBD>
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) <20><> 32<33><32><EFBFBD><EFBFBD><E9A4A4>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);
}