diff --git a/Source/DROMultiplexer.cpp b/Source/DROMultiplexer.cpp index 30bce3a..fee7a79 100644 --- a/Source/DROMultiplexer.cpp +++ b/Source/DROMultiplexer.cpp @@ -1,11 +1,14 @@ #include "DROMultiplexer.h" #include "JuceHeader.h" +#include // 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; +// Mutex between plugin instances +CriticalSection DROMultiplexer::lock; + static Bit8u dro_header[] = { 'D', 'B', 'R', 'A', /* 0x00, Bit32u ID */ 'W', 'O', 'P', 'L', /* 0x04, Bit32u ID */ @@ -68,32 +71,51 @@ INLINE void host_writed(Bit8u *off, Bit32u val) { off[3] = (Bit8u)(val >> 24); }; +HANDLE conout; DROMultiplexer::DROMultiplexer() { - for (int i = 0; i < MELODIC_CHANNELS; ++i) { + for (int i = 0; i < MELODIC_CHANNELS; i++) { channels[i].opl = NULL; channels[i].ch = -1; } +#ifdef _DEBUG + AllocConsole(); + conout = GetStdHandle(STD_OUTPUT_HANDLE); +#endif } DROMultiplexer::~DROMultiplexer() { +#ifdef _DEBUG + FreeConsole(); +#endif } DROMultiplexer* DROMultiplexer::GetMaster() { return DROMultiplexer::master; } -void DROMultiplexer::TwoOpMelodicNoteOn(Hiopl* opl, int ch) { +void DROMultiplexer::TwoOpMelodicNoteOn(Hiopl* opl, int inCh) { + const ScopedLock sl(lock); + + for (int i = 1; i <= Hiopl::CHANNELS; i++) { + char s[2]; + s[0] = opl->GetState(i); + s[1] = '\0'; + _DebugOut(s); + } + _DebugOut(" "); + // find a free channel and mark it as used - int outCh = _FindFreeChannel(); - channels[outCh].opl = opl; - channels[outCh].ch = ch; - channelMap[channels[outCh]] = outCh; + char addr[16]; + int outCh = _FindFreeChannel(opl, inCh); + _DebugOut(" <- "); + _DebugOut(itoa((int)opl, addr, 16)); + _DebugOut("\n"); // read all instrument settings and write them all to the file - int op1Off = opl->_GetOffset(ch, 1); - int op2Off = opl->_GetOffset(ch, 2); + int op1Off = opl->_GetOffset(inCh, 1); + int op2Off = opl->_GetOffset(inCh, 2); Bit32u inAddr; Bit32u outAddr; // waveform select @@ -115,7 +137,7 @@ void DROMultiplexer::TwoOpMelodicNoteOn(Hiopl* opl, int ch) { } // channel wide settings - int chInOff = opl->_GetOffset(ch); + int chInOff = opl->_GetOffset(inCh); inAddr = 0xc0 + chInOff; outAddr = 0xc0 + CHANNEL_OFFSETS[outCh]; _CaptureRegWrite(outAddr, 0x30 | opl->_ReadReg(inAddr)); @@ -127,10 +149,11 @@ void DROMultiplexer::TwoOpMelodicNoteOn(Hiopl* opl, int ch) { inAddr = 0xb0 + chInOff; outAddr = 0xb0 + CHANNEL_OFFSETS[outCh]; _CaptureRegWrite(outAddr, opl->_ReadReg(inAddr)); - } void DROMultiplexer::TwoOpMelodicNoteOff(Hiopl* opl, int ch) { + const ScopedLock sl(lock); + int chOff = opl->_GetOffset(ch); OplCh_t key; key.opl = opl; @@ -143,20 +166,33 @@ void DROMultiplexer::TwoOpMelodicNoteOff(Hiopl* opl, int ch) { _CaptureRegWriteWithDelay(outAddr, opl->_ReadReg(inAddr)); } -int DROMultiplexer::_FindFreeChannel() { +void DROMultiplexer::_DebugOut(char* str) { +#ifdef _DEBUG + DWORD count; + count = strlen(str); + WriteConsole(conout, str, count, &count, NULL); +#endif +} + +int DROMultiplexer::_FindFreeChannel(Hiopl* opl, int inCh) { int i = 0; while (i < MELODIC_CHANNELS) { - Hiopl* opl = channels[i].opl; - if (NULL == opl || !opl->IsActive(channels[i].ch)) { + if (NULL == channels[i].opl || !channels[i].opl->IsActive(channels[i].ch)) { + channels[i].opl = opl; + channels[i].ch = inCh; + channelMap[channels[i]] = i; + char n[8]; + _DebugOut(itoa(i, n, 10)); return i; } i += 1; } - // TODO: handle the fact that there are no free channels :( + _DebugOut("Could not find free channel!"); return 0; } void DROMultiplexer::PercussionHit(Hiopl* opl) { + const ScopedLock sl(lock); } @@ -165,38 +201,42 @@ void DROMultiplexer::InitCaptureVariables() { captureLengthBytes = 0; lastWrite = -1; captureStart = -1; + // channelMap.clear(); } -void DROMultiplexer::StartCapture(const char* filepath, Hiopl *opl) { - DROMultiplexer::master = this; - lastWrite = -1; - captureLengthBytes = 0; - captureStart = Time::currentTimeMillis(); +bool DROMultiplexer::StartCapture(const char* filepath, Hiopl *opl) { 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)); + if (captureHandle) { + DROMultiplexer::master = this; + lastWrite = -1; + captureLengthBytes = 0; + captureStart = Time::currentTimeMillis(); + 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)); + } } + return (NULL != captureHandle); } void DROMultiplexer::StopCapture() { diff --git a/Source/DROMultiplexer.h b/Source/DROMultiplexer.h index 7d5bed4..d42208c 100644 --- a/Source/DROMultiplexer.h +++ b/Source/DROMultiplexer.h @@ -1,6 +1,7 @@ #pragma once #include #include "hiopl.h" +#include "../JuceLibraryCode/JuceHeader.h" class DROMultiplexer { @@ -17,7 +18,7 @@ public: void InitCaptureVariables(); bool IsAnInstanceRecording(); bool IsAnotherInstanceRecording(); - void StartCapture(const char* filepath, Hiopl* opl); + bool StartCapture(const char* filepath, Hiopl* opl); void StopCapture(); static DROMultiplexer* GetMaster(); @@ -26,13 +27,15 @@ private: void _CaptureRegWriteWithDelay(Bit32u reg, Bit8u value); void _CaptureRegWrite(Bit32u reg, Bit8u value); void _CaptureOpl3Enable(); - int _FindFreeChannel(); + int _FindFreeChannel(Hiopl* opl, int inCh); + void _DebugOut(char* str); static DROMultiplexer* master; FILE* captureHandle; Bit64s captureStart; Bit64s lastWrite; Bit32u captureLengthBytes; + static CriticalSection lock; typedef struct oplch { Hiopl* opl; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 73fda29..0d5491f 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -152,7 +152,9 @@ bool JuceOplvstiAudioProcessor::isAnyInstanceRecording() { void JuceOplvstiAudioProcessor::startRecording(File *outputFile) { recordingFile = outputFile; - dro->StartCapture(outputFile->getFullPathName().toUTF8(), Opl); + if (!dro->StartCapture(outputFile->getFullPathName().toUTF8(), Opl)) { + juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::InfoIcon, "Could not open specified file for writing!", "OK"); + } } void JuceOplvstiAudioProcessor::stopRecording() { @@ -447,8 +449,8 @@ JuceOplvstiAudioProcessor::~JuceOplvstiAudioProcessor() { for (unsigned int i=0; i < params.size(); ++i) delete params[i]; - //delete Opl; - //delete dro; + delete Opl; + delete dro; } //============================================================================== diff --git a/Source/dbopl.cpp b/Source/dbopl.cpp index 9566516..a35e054 100644 --- a/Source/dbopl.cpp +++ b/Source/dbopl.cpp @@ -529,9 +529,6 @@ 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 { diff --git a/Source/hiopl.cpp b/Source/hiopl.cpp index aacb61a..80a374b 100644 --- a/Source/hiopl.cpp +++ b/Source/hiopl.cpp @@ -190,6 +190,17 @@ void Hiopl::KeyOff(int ch) { _ClearRegBits(0xb0+offset, 0x20); } +static const char STATE[] = { + '-', + 'R', + 'S', + 'D', + 'A', +}; +char Hiopl::GetState(int ch) { + return STATE[adlib->chip.chan[ch - 1].op[1].state]; +} + bool Hiopl::IsActive(int ch) { // check carrier envelope state return DBOPL::Operator::State::OFF != adlib->chip.chan[ch - 1].op[1].state; diff --git a/Source/hiopl.h b/Source/hiopl.h index 407ee2a..716fdd5 100644 --- a/Source/hiopl.h +++ b/Source/hiopl.h @@ -68,6 +68,7 @@ class Hiopl { void KeyOff(int ch); // Return false if no note is active on the channel (ie release is complete) bool IsActive(int ch); + char GetState(int ch); void SetFrequency(int ch, float frqHz, bool keyOn=false); void _WriteReg(Bit32u reg, Bit8u value, Bit8u mask=0x0); void _ClearRegBits(Bit32u reg, Bit8u mask);