2
0
Fork 0

DRO output from multiple plugins. Channel mapping needs testing and debugging.

This commit is contained in:
Bruce Sutherland 2015-01-06 22:25:51 +09:00
parent 45d0d9549c
commit a16025e39c
5 changed files with 151 additions and 26 deletions

View file

@ -23,6 +23,44 @@ static Bit8u dro_opl3_enable[] = {
0x02 // switch back to regular OPL2 registers 0x02 // switch back to regular OPL2 registers
}; };
// offsets for the 15 two-operator melodic channels
// http://www.shikadi.net/moddingwiki/OPL_chip
static Bit32u OPERATOR_OFFSETS[15][2] = {
{0x000, 0x003}, // 0, 3
{0x001, 0x004}, // 1, 4
{0x002, 0x005}, // 2, 5
{0x008, 0x00b}, // 6, 9
{0x009, 0x00c}, // 7, 10
{0x00a, 0x00d}, // 8, 11
{0x100, 0x103}, // 18, 21
{0x101, 0x104}, // 19, 22
{0x102, 0x105}, // 20, 23
{0x108, 0x10b}, // 24, 27
{0x109, 0x10c}, // 25, 28
{0x10a, 0x10d}, // 26, 29
{0x110, 0x113}, // 30, 33
{0x111, 0x114}, // 31, 34
{0x112, 0x115}, // 32, 35
};
static Bit32u CHANNEL_OFFSETS[15]= {
0x0,
0x1,
0x2,
0x3,
0x4,
0x5,
0x100,
0x101,
0x102,
0x103,
0x104,
0x105,
0x106,
0x107,
0x108,
};
INLINE void host_writed(Bit8u *off, Bit32u val) { INLINE void host_writed(Bit8u *off, Bit32u val) {
off[0] = (Bit8u)(val); off[0] = (Bit8u)(val);
off[1] = (Bit8u)(val >> 8); off[1] = (Bit8u)(val >> 8);
@ -32,26 +70,96 @@ INLINE void host_writed(Bit8u *off, Bit32u val) {
DROMultiplexer::DROMultiplexer() DROMultiplexer::DROMultiplexer()
{ {
for (int i = 0; i < MELODIC_CHANNELS; ++i) {
channels[i].opl = NULL;
channels[i].ch = -1;
}
} }
DROMultiplexer::~DROMultiplexer() DROMultiplexer::~DROMultiplexer()
{ {
} }
DROMultiplexer* DROMultiplexer::GetMaster() {
return DROMultiplexer::master;
}
void DROMultiplexer::TwoOpMelodicNoteOn(Hiopl* opl, int ch) { void DROMultiplexer::TwoOpMelodicNoteOn(Hiopl* opl, int ch) {
// find a free channel and mark it as used
int outCh = _FindFreeChannel();
channels[outCh].opl = opl;
channels[outCh].ch = ch;
channelMap[channels[outCh]] = outCh;
// read all instrument settings and write them all to the file
int op1Off = opl->_GetOffset(ch, 1);
int op2Off = opl->_GetOffset(ch, 2);
Bit32u inAddr;
Bit32u outAddr;
// waveform select
int base = 0xe0;
inAddr = base + op1Off;
outAddr = base + OPERATOR_OFFSETS[outCh][0];
_CaptureRegWriteWithDelay(outAddr, opl->_ReadReg(inAddr));
inAddr = base + op2Off;
outAddr = base + OPERATOR_OFFSETS[outCh][1];
_CaptureRegWrite(outAddr, opl->_ReadReg(inAddr));
// other operator settings
for (base = 0x20; base <= 0x80; base += 0x20) {
inAddr = base + op1Off;
outAddr = base + OPERATOR_OFFSETS[outCh][0];
_CaptureRegWrite(outAddr, opl->_ReadReg(inAddr));
inAddr = base + op2Off;
outAddr = base + OPERATOR_OFFSETS[outCh][1];
_CaptureRegWrite(outAddr, opl->_ReadReg(inAddr));
}
// channel wide settings
int chInOff = opl->_GetOffset(ch);
inAddr = 0xc0 + chInOff;
outAddr = 0xc0 + CHANNEL_OFFSETS[outCh];
_CaptureRegWrite(outAddr, 0x30 | opl->_ReadReg(inAddr));
// note frequency
inAddr = 0xa0 + chInOff;
outAddr = 0xa0 + CHANNEL_OFFSETS[outCh];
_CaptureRegWrite(outAddr, opl->_ReadReg(inAddr));
// note-on
inAddr = 0xb0 + chInOff;
outAddr = 0xb0 + CHANNEL_OFFSETS[outCh];
_CaptureRegWrite(outAddr, opl->_ReadReg(inAddr));
} }
void DROMultiplexer::TwoOpMelodicNoteOff(Hiopl* opl, int ch) { void DROMultiplexer::TwoOpMelodicNoteOff(Hiopl* opl, int ch) {
int chOff = opl->_GetOffset(ch);
OplCh_t key;
key.opl = opl;
key.ch = ch;
int outCh = channelMap[key];
// note-off
Bit32u inAddr = 0xb0 + chOff;
Bit32u outAddr = 0xb0 + CHANNEL_OFFSETS[outCh];
_CaptureRegWriteWithDelay(outAddr, opl->_ReadReg(inAddr));
}
int DROMultiplexer::_FindFreeChannel() {
int i = 0;
while (i < MELODIC_CHANNELS) {
Hiopl* opl = channels[i].opl;
if (NULL == opl || !opl->IsActive(channels[i].ch)) {
return i;
}
i += 1;
}
// TODO: handle the fact that there are no free channels :(
return 0;
} }
void DROMultiplexer::PercussionHit(Hiopl* opl) { void DROMultiplexer::PercussionHit(Hiopl* opl) {
} }
void DROMultiplexer::InitCaptureVariables() { void DROMultiplexer::InitCaptureVariables() {
captureHandle = NULL; captureHandle = NULL;
captureLengthBytes = 0; captureLengthBytes = 0;
@ -107,6 +215,7 @@ void DROMultiplexer::StopCapture() {
fclose(captureHandle); fclose(captureHandle);
} }
InitCaptureVariables(); InitCaptureVariables();
DROMultiplexer::master = NULL;
} }
void DROMultiplexer::_CaptureDelay(Bit16u delayMs) { void DROMultiplexer::_CaptureDelay(Bit16u delayMs) {

View file

@ -1,9 +1,12 @@
#pragma once #pragma once
#include <map>
#include "hiopl.h" #include "hiopl.h"
class DROMultiplexer class DROMultiplexer
{ {
public: public:
static const int MELODIC_CHANNELS = 15;
DROMultiplexer(); DROMultiplexer();
~DROMultiplexer(); ~DROMultiplexer();
@ -16,18 +19,34 @@ public:
bool IsAnotherInstanceRecording(); bool IsAnotherInstanceRecording();
void StartCapture(const char* filepath, Hiopl* opl); void StartCapture(const char* filepath, Hiopl* opl);
void StopCapture(); void StopCapture();
static DROMultiplexer* GetMaster();
//private: private:
void _CaptureDelay(Bit16u delayMs); void _CaptureDelay(Bit16u delayMs);
void _CaptureRegWriteWithDelay(Bit32u reg, Bit8u value); void _CaptureRegWriteWithDelay(Bit32u reg, Bit8u value);
void _CaptureRegWrite(Bit32u reg, Bit8u value); void _CaptureRegWrite(Bit32u reg, Bit8u value);
void _CaptureOpl3Enable(); void _CaptureOpl3Enable();
int _FindFreeChannel();
static DROMultiplexer* master; static DROMultiplexer* master;
FILE* captureHandle; FILE* captureHandle;
Bit64s captureStart; Bit64s captureStart;
Bit64s lastWrite; Bit64s lastWrite;
Bit32u captureLengthBytes; Bit32u captureLengthBytes;
typedef struct oplch {
Hiopl* opl;
int ch;
bool operator<(const oplch &o) const {
return opl < o.opl ||
(opl == o.opl && ch < o.ch);
};
bool operator==(const oplch &o) const {
return opl == o.opl && ch == o.ch;
};
} OplCh_t;
OplCh_t channels[MELODIC_CHANNELS];
std::map<OplCh_t, int> channelMap;
}; };

View file

@ -6,11 +6,6 @@
const char *JuceOplvstiAudioProcessor::PROGRAM_INDEX = "Program Index"; const char *JuceOplvstiAudioProcessor::PROGRAM_INDEX = "Program Index";
static DROMultiplexer* plexer = NULL;
void regWriteCallback(Bit32u reg, Bit8u val) {
if (NULL != plexer) plexer->_CaptureRegWriteWithDelay(reg, val);
}
//============================================================================== //==============================================================================
JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor() JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor()
: i_program(-1) : i_program(-1)
@ -21,7 +16,6 @@ JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor()
Opl->SetSampleRate(44100); Opl->SetSampleRate(44100);
Opl->EnableWaveformControl(); Opl->EnableWaveformControl();
dro = new DROMultiplexer(); dro = new DROMultiplexer();
plexer = dro;
recordingFile = NULL; recordingFile = NULL;
@ -159,7 +153,6 @@ bool JuceOplvstiAudioProcessor::isAnyInstanceRecording() {
void JuceOplvstiAudioProcessor::startRecording(File *outputFile) { void JuceOplvstiAudioProcessor::startRecording(File *outputFile) {
recordingFile = outputFile; recordingFile = outputFile;
dro->StartCapture(outputFile->getFullPathName().toUTF8(), Opl); dro->StartCapture(outputFile->getFullPathName().toUTF8(), Opl);
Opl->regWriteCallback = &regWriteCallback;
} }
void JuceOplvstiAudioProcessor::stopRecording() { void JuceOplvstiAudioProcessor::stopRecording() {
@ -776,6 +769,9 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf
Opl->KeyOn(ch, noteHz); Opl->KeyOn(ch, noteHz);
active_notes[ch] = n; active_notes[ch] = n;
applyPitchBend(); applyPitchBend();
if (isAnyInstanceRecording()) {
dro->GetMaster()->TwoOpMelodicNoteOn(Opl, ch);
}
} }
} }
else if (midi_message.isNoteOff()) { else if (midi_message.isNoteOff()) {
@ -804,6 +800,9 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf
Opl->KeyOff(ch); Opl->KeyOff(ch);
active_notes[ch] = NO_NOTE; active_notes[ch] = NO_NOTE;
} }
if (isAnyInstanceRecording()) {
dro->GetMaster()->TwoOpMelodicNoteOff(Opl, ch);
}
} }
} }
else if (midi_message.isPitchWheel()) { else if (midi_message.isPitchWheel()) {

View file

@ -76,8 +76,6 @@ void Hiopl::_WriteReg(Bit32u reg, Bit8u value, Bit8u mask) {
zdoom->WriteReg(reg, value); zdoom->WriteReg(reg, value);
//} //}
regCache[reg] = value; regCache[reg] = value;
//_CaptureRegWriteWithDelay(reg, value);
if (NULL != regWriteCallback) regWriteCallback(reg, value);
} }
Bit8u Hiopl::_ReadReg(Bit32u reg) { Bit8u Hiopl::_ReadReg(Bit32u reg) {
@ -172,7 +170,6 @@ void Hiopl::SetModulatorFeedback(int ch, int level) {
void Hiopl::SetPercussionMode(bool enable) { void Hiopl::SetPercussionMode(bool enable) {
_WriteReg(0xbd, enable ? 0x20 : 0x0, 0x20); _WriteReg(0xbd, enable ? 0x20 : 0x0, 0x20);
// TODO: handle capture mode.. channels reduced from 18 -> 15
} }
void Hiopl::HitPercussion(Drum drum) { void Hiopl::HitPercussion(Drum drum) {
@ -193,14 +190,15 @@ void Hiopl::KeyOff(int ch) {
_ClearRegBits(0xb0+offset, 0x20); _ClearRegBits(0xb0+offset, 0x20);
} }
//void Hiopl::EnvelopeOffCallback(int ch) { bool Hiopl::IsActive(int ch) {
// // check carrier envelope state
//} return DBOPL::Operator::State::OFF != adlib->chip.chan[ch - 1].op[1].state;
}
void Hiopl::SetFrequency(int ch, float frqHz, bool keyOn) { void Hiopl::SetFrequency(int ch, float frqHz, bool keyOn) {
unsigned int fnum, block; unsigned int fnum, block;
int offset = this->_GetOffset(ch); int offset = this->_GetOffset(ch);
// ZDoom emulator seems to be tuned down by two semitones for some reason. // ZDoom emulator seems to be tuned down by two semitones for some reason. Sample rate difference?
if (ZDOOM == emulator) { if (ZDOOM == emulator) {
frqHz *= 1.122461363636364f; frqHz *= 1.122461363636364f;
} }

View file

@ -25,12 +25,8 @@ enum Drum
BDRUM=0x10, SNARE=0x8, TOM=0x4, CYMBAL=0x2, HIHAT=0x1 BDRUM=0x10, SNARE=0x8, TOM=0x4, CYMBAL=0x2, HIHAT=0x1
}; };
typedef void(*RegWriteCallback_t)(Bit32u reg, Bit8u val);
class Hiopl { class Hiopl {
public: public:
RegWriteCallback_t regWriteCallback = NULL;
static const int CHANNELS = 9; static const int CHANNELS = 9;
static const int OSCILLATORS = 2; static const int OSCILLATORS = 2;
Hiopl(int buflen, Emulator emulator = ZDOOM); Hiopl(int buflen, Emulator emulator = ZDOOM);
@ -62,10 +58,16 @@ class Hiopl {
void SetEnvelopeDecay(int ch, int osc, int t); void SetEnvelopeDecay(int ch, int osc, int t);
void SetEnvelopeSustain(int ch, int osc, int level); void SetEnvelopeSustain(int ch, int osc, int level);
void SetEnvelopeRelease(int ch, int osc, int t); void SetEnvelopeRelease(int ch, int osc, int t);
// Get register address offset for operator settings for the specified channel and operator.
int _GetOffset(int ch, int osc);
// Get register address offset for channel-wide register settings for the specified channel.
int _GetOffset(int ch);
void SetModulatorFeedback(int ch, int level); void SetModulatorFeedback(int ch, int level);
void KeyOn(int ch, float frqHz); void KeyOn(int ch, float frqHz);
void KeyOff(int ch); void KeyOff(int ch);
// Return false if no note is active on the channel (ie release is complete)
bool IsActive(int ch);
void SetFrequency(int ch, float frqHz, bool keyOn=false); void SetFrequency(int ch, float frqHz, bool keyOn=false);
void _WriteReg(Bit32u reg, Bit8u value, Bit8u mask=0x0); void _WriteReg(Bit32u reg, Bit8u value, Bit8u mask=0x0);
void _ClearRegBits(Bit32u reg, Bit8u mask); void _ClearRegBits(Bit32u reg, Bit8u mask);
@ -75,12 +77,10 @@ class Hiopl {
~Hiopl(); ~Hiopl();
private: private:
Emulator emulator; Emulator emulator;
Adlib::Handler *adlib; DBOPL::Handler *adlib;
OPLEmul *zdoom; OPLEmul *zdoom;
Bit8u regCache[256]; Bit8u regCache[256];
bool _CheckParams(int ch, int osc); bool _CheckParams(int ch, int osc);
int _GetOffset(int ch, int osc);
int _GetOffset(int ch);
void _milliHertzToFnum(unsigned int milliHertz, unsigned int *fnum, unsigned int *block, unsigned int conversionFactor=49716); void _milliHertzToFnum(unsigned int milliHertz, unsigned int *fnum, unsigned int *block, unsigned int conversionFactor=49716);
void _ClearRegisters(); void _ClearRegisters();