2
0
Fork 0

Work in progress - refactoring for multiple instance DRO recording, bug fix.

This commit is contained in:
Bruce Sutherland 2015-01-06 12:13:44 +09:00
parent ecfc83df47
commit 45d0d9549c
11 changed files with 281 additions and 178 deletions

View file

@ -11,6 +11,11 @@
buildVST3="0" buildRTAS="0" buildAAX="0" includeBinaryInAppConfig="1">
<MAINGROUP id="TOefyq" name="JuceOPLVSTi">
<GROUP id="{DCA755EB-7953-0641-E719-95C7850E5B2B}" name="Source">
<FILE id="o0sULY" name="DROMultiplexer.cpp" compile="1" resource="0"
file="Source/DROMultiplexer.cpp"/>
<FILE id="cULQN7" name="DROMultiplexer.h" compile="0" resource="0"
file="Source/DROMultiplexer.h"/>
<FILE id="KRj0DZ" name="tests.cpp" compile="1" resource="0" file="Source/tests.cpp"/>
<FILE id="hjHmNq" name="zdopl.cpp" compile="1" resource="0" file="Source/zdopl.cpp"/>
<FILE id="TQyCKv" name="zdopl.h" compile="0" resource="0" file="Source/zdopl.h"/>
<FILE id="LVSdHL" name="InstrumentLoader.h" compile="0" resource="0"
@ -69,7 +74,8 @@
</MODULES>
<JUCEOPTIONS JUCE_QUICKTIME="disabled"/>
<EXPORTFORMATS>
<VS2013 targetFolder="Builds/VisualStudio2013" vstFolder="C:\code\audio\vstsdk2.4">
<VS2013 targetFolder="Builds/VisualStudio2013" vstFolder="C:\code\audio\vstsdk2.4"
externalLibraries="">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="32-bit"
isDebug="1" optimisation="1" targetName="JuceOPLVSTi"/>

157
Source/DROMultiplexer.cpp Normal file
View file

@ -0,0 +1,157 @@
#include "DROMultiplexer.h"
#include "JuceHeader.h"
// Used by the first recording instance to claim master status
// TODO: develop the logic for recording data from other (non-master) plugins
DROMultiplexer* DROMultiplexer::master = NULL;
static Bit8u dro_header[] = {
'D', 'B', 'R', 'A', /* 0x00, Bit32u ID */
'W', 'O', 'P', 'L', /* 0x04, Bit32u ID */
0x0, 0x00, /* 0x08, Bit16u version low */
0x1, 0x00, /* 0x09, Bit16u version high */
0x0, 0x0, 0x0, 0x0, /* 0x0c, Bit32u total milliseconds */
0x0, 0x0, 0x0, 0x0, /* 0x10, Bit32u total data */
0x0, 0x0, 0x0, 0x0 /* 0x14, Bit32u Type 0=opl2,1=opl3,2=dual-opl2 */
};
static Bit8u dro_opl3_enable[] = {
0x03, // switch to extended register bank
0x05, // register 0x105
0x01, // value 0x1
0x02 // switch back to regular OPL2 registers
};
INLINE void host_writed(Bit8u *off, Bit32u val) {
off[0] = (Bit8u)(val);
off[1] = (Bit8u)(val >> 8);
off[2] = (Bit8u)(val >> 16);
off[3] = (Bit8u)(val >> 24);
};
DROMultiplexer::DROMultiplexer()
{
}
DROMultiplexer::~DROMultiplexer()
{
}
void DROMultiplexer::TwoOpMelodicNoteOn(Hiopl* opl, int ch) {
}
void DROMultiplexer::TwoOpMelodicNoteOff(Hiopl* opl, int ch) {
}
void DROMultiplexer::PercussionHit(Hiopl* opl) {
}
void DROMultiplexer::InitCaptureVariables() {
captureHandle = NULL;
captureLengthBytes = 0;
lastWrite = -1;
captureStart = -1;
}
void DROMultiplexer::StartCapture(const char* filepath, Hiopl *opl) {
DROMultiplexer::master = this;
lastWrite = -1;
captureLengthBytes = 0;
captureStart = Time::currentTimeMillis();
captureHandle = fopen(filepath, "wb");
fwrite(dro_header, 1, sizeof(dro_header), captureHandle);
for (int i = 0; i <= 0xff; i++) {
_CaptureRegWrite(i, 0);
}
_CaptureOpl3Enable();
for (Bit8u i = 0x20; i <= 0x35; i++) {
_CaptureRegWrite(i, opl->_ReadReg(i));
}
for (Bit8u i = 0x40; i <= 0x55; i++) {
_CaptureRegWrite(i, opl->_ReadReg(i));
}
for (Bit8u i = 0x60; i <= 0x75; i++) {
_CaptureRegWrite(i, opl->_ReadReg(i));
}
for (Bit8u i = 0x80; i <= 0x95; i++) {
_CaptureRegWrite(i, opl->_ReadReg(i));
}
_CaptureRegWrite(0xbd, opl->_ReadReg(0xbd));
for (Bit8u i = 0xc0; i <= 0xc8; i++) {
_CaptureRegWrite(i, opl->_ReadReg(i) | 0x30); // enable L + R channels
}
for (Bit8u i = 0xe0; i <= 0xf5; i++) {
_CaptureRegWrite(i, opl->_ReadReg(i));
}
}
void DROMultiplexer::StopCapture() {
if (NULL != captureHandle) {
Bit16u finalDelay = (Bit16u)(Time::currentTimeMillis() - lastWrite);
_CaptureDelay(finalDelay);
Bit32u lengthMilliseconds = (Bit32u)(finalDelay + Time::currentTimeMillis() - captureStart);
host_writed(&dro_header[0x0c], lengthMilliseconds);
host_writed(&dro_header[0x10], captureLengthBytes);
//if (opl.raw.opl3 && opl.raw.dualopl2) host_writed(&dro_header[0x14],0x1);
//else if (opl.raw.dualopl2) host_writed(&dro_header[0x14],0x2);
//else host_writed(&dro_header[0x14],0x0);
host_writed(&dro_header[0x14], 0x1); // OPL3
fseek(captureHandle, 0, 0);
fwrite(dro_header, 1, sizeof(dro_header), captureHandle);
fclose(captureHandle);
}
InitCaptureVariables();
}
void DROMultiplexer::_CaptureDelay(Bit16u delayMs) {
Bit8u delay[3];
delay[0] = 0x01;
delay[1] = delayMs & 0xff;
delay[2] = (delayMs >> 8) & 0xff;
fwrite(delay, 1, 3, captureHandle);
captureLengthBytes += 3;
}
void DROMultiplexer::_CaptureRegWrite(Bit32u reg, Bit8u value) {
if (reg <= 0x4) {
Bit8u escape = 0x4;
fwrite(&escape, 1, 1, captureHandle);
captureLengthBytes += 1;
}
Bit8u regAndVal[2];
regAndVal[0] = (Bit8u)reg;
regAndVal[1] = value;
fwrite(regAndVal, 1, 2, captureHandle);
captureLengthBytes += 2;
}
void DROMultiplexer::_CaptureRegWriteWithDelay(Bit32u reg, Bit8u value) {
if (NULL != captureHandle) {
Bit64s t = Time::currentTimeMillis();
if (lastWrite >= 0) {
// Delays of over 65 seconds will be truncated, but that kind of delay is a bit silly anyway..
_CaptureDelay((Bit16u)(t - lastWrite));
}
_CaptureRegWrite(reg, value);
lastWrite = t;
}
}
void DROMultiplexer::_CaptureOpl3Enable() {
fwrite(dro_opl3_enable, 1, 4, captureHandle);
captureLengthBytes += 4;
}
bool DROMultiplexer::IsAnInstanceRecording() {
return NULL != DROMultiplexer::master;
}
bool DROMultiplexer::IsAnotherInstanceRecording() {
return this->IsAnInstanceRecording() && this != DROMultiplexer::master;
}

33
Source/DROMultiplexer.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include "hiopl.h"
class DROMultiplexer
{
public:
DROMultiplexer();
~DROMultiplexer();
void TwoOpMelodicNoteOn(Hiopl* opl, int ch);
void TwoOpMelodicNoteOff(Hiopl* opl, int ch);
void PercussionHit(Hiopl* opl);
void InitCaptureVariables();
bool IsAnInstanceRecording();
bool IsAnotherInstanceRecording();
void StartCapture(const char* filepath, Hiopl* opl);
void StopCapture();
//private:
void _CaptureDelay(Bit16u delayMs);
void _CaptureRegWriteWithDelay(Bit32u reg, Bit8u value);
void _CaptureRegWrite(Bit32u reg, Bit8u value);
void _CaptureOpl3Enable();
static DROMultiplexer* master;
FILE* captureHandle;
Bit64s captureStart;
Bit64s lastWrite;
Bit32u captureLengthBytes;
};

View file

@ -14,7 +14,7 @@ EnumFloatParameter::~EnumFloatParameter(void)
int EnumFloatParameter::getParameterIndex(void)
{
int i = (int)(this->value * values.size());
int i = (int)(this->value * values.size() + 0.5f);
if (i >= values.size())
i = values.size() - 1;
return i;

View file

@ -6,6 +6,11 @@
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()
: i_program(-1)
@ -15,6 +20,8 @@ JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor()
Opl = new Hiopl(44100); // 1 second at 44100
Opl->SetSampleRate(44100);
Opl->EnableWaveformControl();
dro = new DROMultiplexer();
plexer = dro;
recordingFile = NULL;
@ -146,16 +153,17 @@ bool JuceOplvstiAudioProcessor::isThisInstanceRecording() {
}
bool JuceOplvstiAudioProcessor::isAnyInstanceRecording() {
return Opl->IsAnInstanceRecording();
return dro->IsAnInstanceRecording();
}
void JuceOplvstiAudioProcessor::startRecording(File *outputFile) {
recordingFile = outputFile;
Opl->StartCapture(outputFile->getFullPathName().toUTF8());
dro->StartCapture(outputFile->getFullPathName().toUTF8(), Opl);
Opl->regWriteCallback = &regWriteCallback;
}
void JuceOplvstiAudioProcessor::stopRecording() {
Opl->StopCapture();
dro->StopCapture();
recordingFile = NULL;
}
@ -446,6 +454,8 @@ JuceOplvstiAudioProcessor::~JuceOplvstiAudioProcessor()
{
for (unsigned int i=0; i < params.size(); ++i)
delete params[i];
//delete Opl;
//delete dro;
}
//==============================================================================
@ -703,6 +713,8 @@ void JuceOplvstiAudioProcessor::releaseResources()
// spare memory, etc.
}
static const Drum DRUM_INDEX[] = { BDRUM, SNARE, TOM, CYMBAL, HIHAT };
void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
buffer.clear(0, 0, buffer.getNumSamples());
@ -720,11 +732,10 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf
int ch;
if (perc > 0) {
static const Drum drumIndex[] = { BDRUM, SNARE, TOM, CYMBAL, HIHAT };
for (int i = 1; i <= Hiopl::CHANNELS; i++) {
Opl->SetFrequency(i, noteHz, false);
}
Opl->HitPercussion(drumIndex[perc - 1]);
Opl->HitPercussion(DRUM_INDEX[perc - 1]);
} else {
if (!available_channels.empty())
{
@ -766,7 +777,6 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf
active_notes[ch] = n;
applyPitchBend();
}
}
else if (midi_message.isNoteOff()) {
if (perc > 0) {

View file

@ -13,6 +13,7 @@
#include <deque>
#include "../JuceLibraryCode/JuceHeader.h"
#include "hiopl.h"
#include "DROMultiplexer.h"
#include "FloatParameter.h"
@ -87,6 +88,7 @@ public:
private:
Hiopl *Opl;
DROMultiplexer *dro;
std::vector<FloatParameter*> params;
std::map<String, int> paramIdxByName;
std::map<String, std::vector<float>> programs;

View file

@ -529,6 +529,9 @@ void Operator::WriteE0( const Chip* chip, Bit8u val ) {
INLINE void Operator::SetState( Bit8u s ) {
state = s;
volHandler = VolumeHandlerTable[ s ];
if (OFF == s) {
// TODO: callback
}
}
INLINE bool Operator::Silent() const {

View file

@ -5,20 +5,8 @@
// A wrapper around the DOSBox and ZDoom OPL emulators.
// Used by the first recording instance to claim master status
// TODO: develop the logic for recording data from other (non-master) plugins
Hiopl* Hiopl::master = NULL;
bool Hiopl::IsAnInstanceRecording() {
return NULL != Hiopl::master;
}
bool Hiopl::IsAnotherInstanceRecording() {
return this->IsAnInstanceRecording() && this != Hiopl::master;
}
Hiopl::Hiopl(int buflen, Emulator emulator) {
InitCaptureVariables();
//InitCaptureVariables();
adlib = new DBOPL::Handler();
zdoom = JavaOPLCreate(false);
@ -88,7 +76,12 @@ void Hiopl::_WriteReg(Bit32u reg, Bit8u value, Bit8u mask) {
zdoom->WriteReg(reg, value);
//}
regCache[reg] = value;
_CaptureRegWriteWithDelay(reg, value);
//_CaptureRegWriteWithDelay(reg, value);
if (NULL != regWriteCallback) regWriteCallback(reg, value);
}
Bit8u Hiopl::_ReadReg(Bit32u reg) {
return regCache[reg];
}
void Hiopl::_ClearRegBits(Bit32u reg, Bit8u mask) {
@ -179,6 +172,7 @@ void Hiopl::SetModulatorFeedback(int ch, int level) {
void Hiopl::SetPercussionMode(bool enable) {
_WriteReg(0xbd, enable ? 0x20 : 0x0, 0x20);
// TODO: handle capture mode.. channels reduced from 18 -> 15
}
void Hiopl::HitPercussion(Drum drum) {
@ -199,6 +193,10 @@ void Hiopl::KeyOff(int ch) {
_ClearRegBits(0xb0+offset, 0x20);
}
//void Hiopl::EnvelopeOffCallback(int ch) {
//
//}
void Hiopl::SetFrequency(int ch, float frqHz, bool keyOn) {
unsigned int fnum, block;
int offset = this->_GetOffset(ch);
@ -250,145 +248,6 @@ void Hiopl::_milliHertzToFnum(unsigned int milliHertz,
return;
}
static Bit8u dro_header[]={
'D','B','R','A', /* 0x00, Bit32u ID */
'W','O','P','L', /* 0x04, Bit32u ID */
0x0,0x00, /* 0x08, Bit16u version low */
0x1,0x00, /* 0x09, Bit16u version high */
0x0,0x0,0x0,0x0, /* 0x0c, Bit32u total milliseconds */
0x0,0x0,0x0,0x0, /* 0x10, Bit32u total data */
0x0,0x0,0x0,0x0 /* 0x14, Bit32u Type 0=opl2,1=opl3,2=dual-opl2 */
};
static Bit8u dro_opl3_enable[]={
0x03, // switch to extended register bank
0x05, // register 0x105
0x01, // value 0x1
0x02 // switch back to regular OPL2 registers
};
void Hiopl::StartCapture(const char* filepath) {
Hiopl::master = this;
lastWrite = -1;
captureLengthBytes = 0;
captureStart = Time::currentTimeMillis();
captureHandle = fopen(filepath, "wb");
fwrite(dro_header, 1, sizeof(dro_header), captureHandle);
for (int i = 0; i <= 0xff; i++) {
_CaptureRegWrite(i, 0);
}
//_CaptureRegWrite(0x1, 0x20);
/*
for (int ch = 1; ch <= CHANNELS; ch++) {
int offset1 = this->_GetOffset(ch, 1);
int offset2 = this->_GetOffset(ch, 2);
int offset0 = this->_GetOffset(ch);
_CaptureRegWrite(0x20 + offset1, regCache[0x20 + offset1]);
_CaptureRegWrite(0x20 + offset2, regCache[0x20 + offset2]);
_CaptureRegWrite(0x40 + offset1, regCache[0x40 + offset1]);
_CaptureRegWrite(0x40 + offset2, regCache[0x40 + offset2]);
_CaptureRegWrite(0x60 + offset1, regCache[0x60 + offset1]);
_CaptureRegWrite(0x60 + offset2, regCache[0x60 + offset2]);
_CaptureRegWrite(0x80 + offset1, regCache[0x80 + offset1]);
_CaptureRegWrite(0x80 + offset2, regCache[0x80 + offset2]);
_CaptureRegWrite(0xe0 + offset1, regCache[0xe0 + offset1]);
_CaptureRegWrite(0xe0 + offset2, regCache[0xe0 + offset2]);
_CaptureRegWrite(0xc0 + offset0, regCache[0xc0 + offset0]);
}
*/
_CaptureOpl3Enable();
for (Bit8u i = 0x20; i <= 0x35; i++) {
_CaptureRegWrite(i, regCache[i]);
}
for (Bit8u i = 0x40; i <= 0x55; i++) {
_CaptureRegWrite(i, regCache[i]);
}
for (Bit8u i = 0x60; i <= 0x75; i++) {
_CaptureRegWrite(i, regCache[i]);
}
for (Bit8u i = 0x80; i <= 0x95; i++) {
_CaptureRegWrite(i, regCache[i]);
}
_CaptureRegWrite(0xbd, regCache[0xbd]);
for (Bit8u i = 0xc0; i <= 0xc8; i++) {
_CaptureRegWrite(i, regCache[i] | 0x30); // enable L + R channels
}
for (Bit8u i = 0xe0; i <= 0xf5; i++) {
_CaptureRegWrite(i, regCache[i]);
}
}
INLINE void host_writed(Bit8u *off, Bit32u val) {
off[0]=(Bit8u)(val);
off[1]=(Bit8u)(val >> 8);
off[2]=(Bit8u)(val >> 16);
off[3]=(Bit8u)(val >> 24);
};
void Hiopl::InitCaptureVariables() {
captureHandle = NULL;
captureLengthBytes = 0;
lastWrite = -1;
captureStart = -1;
}
void Hiopl::StopCapture() {
if (NULL != captureHandle) {
Bit16u finalDelay = (Bit16u)(Time::currentTimeMillis() - lastWrite);
_CaptureDelay(finalDelay);
Bit32u lengthMilliseconds = (Bit32u)(finalDelay + Time::currentTimeMillis() - captureStart);
host_writed(&dro_header[0x0c], lengthMilliseconds);
host_writed(&dro_header[0x10], captureLengthBytes);
//if (opl.raw.opl3 && opl.raw.dualopl2) host_writed(&dro_header[0x14],0x1);
//else if (opl.raw.dualopl2) host_writed(&dro_header[0x14],0x2);
//else host_writed(&dro_header[0x14],0x0);
host_writed(&dro_header[0x14], 0x1); // OPL3
fseek(captureHandle, 0, 0);
fwrite(dro_header, 1, sizeof(dro_header), captureHandle);
fclose(captureHandle);
}
InitCaptureVariables();
}
void Hiopl::_CaptureDelay(Bit16u delayMs) {
Bit8u delay[3];
delay[0] = 0x01;
delay[1] = delayMs & 0xff;
delay[2] = (delayMs >> 8) & 0xff;
fwrite(delay, 1, 3, captureHandle);
captureLengthBytes += 3;
}
void Hiopl::_CaptureRegWrite(Bit32u reg, Bit8u value) {
if (reg <= 0x4) {
Bit8u escape = 0x4;
fwrite(&escape, 1, 1, captureHandle);
captureLengthBytes += 1;
}
Bit8u regAndVal[2];
regAndVal[0] = (Bit8u)reg;
regAndVal[1] = value;
fwrite(regAndVal, 1, 2, captureHandle);
captureLengthBytes += 2;
}
void Hiopl::_CaptureRegWriteWithDelay(Bit32u reg, Bit8u value) {
if (NULL != captureHandle) {
Bit64s t = Time::currentTimeMillis();
if (lastWrite >= 0) {
// Delays of over 65 seconds will be truncated, but that kind of delay is a bit silly anyway..
_CaptureDelay((Bit16u)(t - lastWrite));
}
_CaptureRegWrite(reg, value);
lastWrite = t;
}
}
void Hiopl::_CaptureOpl3Enable() {
fwrite(dro_opl3_enable, 1, 4, captureHandle);
captureLengthBytes += 4;
}
Hiopl::~Hiopl() {
};
@ -406,3 +265,14 @@ int Hiopl::_GetOffset(int ch) {
assert(_CheckParams(ch));
return ch - 1;
}
/*
2. Two-operator Melodic and Percussion Mode
ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Channel ³ 0 1 2 3 4 5 BD SD TT CY HH 9 10 11 12 13 14 15 16 17 ³
ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
³ Operator 1 ³ 0 1 2 6 7 8 12 16 14 17 13 18 19 20 24 25 26 30 31 32 ³
³ Operator 2 ³ 3 4 5 9 10 11 15 21 22 23 27 28 29 33 34 35 ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
*/

View file

@ -1,3 +1,4 @@
#pragma once
#include <map>
#include "adlib.h"
@ -24,9 +25,12 @@ enum Drum
BDRUM=0x10, SNARE=0x8, TOM=0x4, CYMBAL=0x2, HIHAT=0x1
};
typedef void(*RegWriteCallback_t)(Bit32u reg, Bit8u val);
class Hiopl {
public:
RegWriteCallback_t regWriteCallback = NULL;
static const int CHANNELS = 9;
static const int OSCILLATORS = 2;
Hiopl(int buflen, Emulator emulator = ZDOOM);
@ -65,12 +69,8 @@ class Hiopl {
void SetFrequency(int ch, float frqHz, bool keyOn=false);
void _WriteReg(Bit32u reg, Bit8u value, Bit8u mask=0x0);
void _ClearRegBits(Bit32u reg, Bit8u mask);
void InitCaptureVariables();
bool IsAnInstanceRecording();
bool IsAnotherInstanceRecording();
void StartCapture(const char* filepath);
void StopCapture();
// Read the last value written to the specified register address (cached)
Bit8u _ReadReg(Bit32u reg);
~Hiopl();
private:
@ -83,16 +83,8 @@ class Hiopl {
int _GetOffset(int ch);
void _milliHertzToFnum(unsigned int milliHertz, unsigned int *fnum, unsigned int *block, unsigned int conversionFactor=49716);
void _ClearRegisters();
void _CaptureDelay(Bit16u delayMs);
void _CaptureRegWriteWithDelay(Bit32u reg, Bit8u value);
void _CaptureRegWrite(Bit32u reg, Bit8u value);
void _CaptureOpl3Enable();
std::map<int, int> _op1offset;
std::map<int, int> _op2offset;
static Hiopl* master;
FILE* captureHandle;
Bit64s captureStart;
Bit64s lastWrite;
Bit32u captureLengthBytes;
};

26
Source/tests.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "../JuceLibraryCode/JuceHeader.h"
#include "EnumFloatParameter.h"
class EnumFloatParameterTest : public UnitTest
{
public:
EnumFloatParameterTest() : UnitTest("EnumFloatParameterTest") {}
void runTest()
{
beginTest("Test enumerated parameters");
const String percussion[] = { "Off", "Bass drum", "Snare", "Tom", "Cymbal", "Hi-hat" };
int n = sizeof(percussion) / sizeof(String);
EnumFloatParameter* efp = new EnumFloatParameter("Percussion Mode", StringArray(percussion, n));
expect(true);
for (int i = 0; i < n; i++) {
efp->setParameterIndex(i);
expect(i == efp->getParameterIndex());
}
}
};
static EnumFloatParameterTest enumTest;

View file

@ -35,6 +35,10 @@ Percussion mode is now supported! This mode is not very well documented, even in
- Cymbal: Uses carrier settings. Half-sine recommended.
- Hi-hat: Uses modulator settings. Half-sine recommended.
Also, a link to some much more detailed notes on percussion mode based on experimentation with real hardware!
http://midibox.org/forums/topic/18625-opl3-percussion-mode-map/
## How did you create the instrument programs? ##
To figure out the parameters used by the original games, I just added a printf to the DOSBox OPL emulator, compiled DOSBox, ran the games, and captured their output as raw register writes with timestamps.