diff --git a/JuceOPLVSTi.jucer b/JuceOPLVSTi.jucer index 942e45b..b9c40a3 100644 --- a/JuceOPLVSTi.jucer +++ b/JuceOPLVSTi.jucer @@ -75,7 +75,7 @@ + externalLibraries="" toolset=""> diff --git a/Source/DROMultiplexer.cpp b/Source/DROMultiplexer.cpp index 8292c83..40a6f67 100644 --- a/Source/DROMultiplexer.cpp +++ b/Source/DROMultiplexer.cpp @@ -66,7 +66,7 @@ static Bit32u CHANNEL_OFFSETS[15]= { // bass drum uses two operators. // others use either modulator or carrier. -static Bit32u PERCUSSION_OPERATORS[5][2] = { +static Bit32u PERCUSSION_OFFSETS[5][2] = { { 0x13, 0x10 }, // bd { 0x00, 0x14 }, // sd { 0x12, 0x00 }, // tt @@ -114,7 +114,7 @@ void DROMultiplexer::_CopyOplPercussionSettings(Hiopl* opl, int pIdx) { Bit32u inAddr; Bit32u outAddr; - Bit32u* outOff = PERCUSSION_OPERATORS[pIdx]; + Bit32u* outOff = PERCUSSION_OFFSETS[pIdx]; // waveform select int base = 0xe0; if (outOff[0]) { @@ -181,32 +181,35 @@ void DROMultiplexer::_CopyOplChannelSettings(Hiopl* opl, int inCh, int outCh) { void DROMultiplexer::TwoOpMelodicNoteOn(Hiopl* opl, int inCh) { const ScopedLock sl(lock); - + // find a free channel and mark it as used char addr[16]; int outCh = _FindFreeChannel(opl, inCh); - _DebugOut(" <- "); - _DebugOut(itoa((int)opl, addr, 16)); - _DebugOut(" "); - for (int i = 0; i < MELODIC_CHANNELS; i++) { - Hiopl* tmpOpl = channels[i].opl; - _DebugOut(NULL == tmpOpl ? "-" : tmpOpl->GetState(channels[i].ch)); - } - //_DebugOut("\n"); - _CopyOplChannelSettings(opl, inCh, outCh); + if (outCh >= 0) { + _DebugOut(" <- "); + _DebugOut(itoa((int)opl, addr, 16)); + _DebugOut(" "); + for (int i = 0; i < MELODIC_CHANNELS; i++) { + Hiopl* tmpOpl = channels[i].opl; + _DebugOut(NULL == tmpOpl ? "-" : tmpOpl->GetState(channels[i].ch)); + } + //_DebugOut("\n"); + _CopyOplChannelSettings(opl, inCh, outCh); - // note frequency - int chInOff = opl->_GetOffset(inCh); - int inAddr = 0xa0 + chInOff; - int outAddr = 0xa0 + CHANNEL_OFFSETS[outCh]; - _CaptureRegWrite(outAddr, opl->_ReadReg(inAddr)); - _DebugOut(itoa(opl->_ReadReg(inAddr), addr, 16)); - // note-on - inAddr = 0xb0 + chInOff; - outAddr = 0xb0 + CHANNEL_OFFSETS[outCh]; - _CaptureRegWrite(outAddr, opl->_ReadReg(inAddr)); - _DebugOut(itoa(opl->_ReadReg(inAddr), addr, 16)); - _DebugOut("\n"); + // note frequency + int chInOff = opl->_GetOffset(inCh); + int inAddr = 0xa0 + chInOff; + int outAddr = 0xa0 + CHANNEL_OFFSETS[outCh]; + _CaptureRegWrite(outAddr, opl->_ReadReg(inAddr)); + _DebugOut(itoa(opl->_ReadReg(inAddr), addr, 16)); + // note-on + inAddr = 0xb0 + chInOff; + outAddr = 0xb0 + CHANNEL_OFFSETS[outCh]; + _CaptureRegWrite(outAddr, opl->_ReadReg(inAddr)); + _DebugOut(" "); + _DebugOut(itoa(opl->_ReadReg(inAddr), addr, 16)); + _DebugOut("\n"); + } } void DROMultiplexer::TwoOpMelodicNoteOff(Hiopl* opl, int ch) { @@ -248,8 +251,21 @@ int DROMultiplexer::_FindFreeChannel(Hiopl* opl, int inCh) { } i += 1; } + // fall back to a released channel for same opl instance + i = 0; + while (i < MELODIC_CHANNELS) { + if (opl == channels[i].opl && 'R' == opl->GetState(channels[i].ch)[0]) { + channels[i].opl = opl; + channels[i].ch = inCh; + channelMap[channels[i]] = i; + char n[8]; + _DebugOut(itoa(i, n, 16)); + return i; + } + i += 1; + } _DebugOut("Could not find free channel!"); - return 0; + return -1; } void DROMultiplexer::PercussionChange(Hiopl* opl, int pIdx) { @@ -260,6 +276,17 @@ void DROMultiplexer::PercussionChange(Hiopl* opl, int pIdx) { if (0 == (val & maskOut)) { // note-off _CaptureRegWriteWithDelay(0xbd, 0xBD & (0xe0 | ~maskOut)); } else { // note-on + char addr[16]; + // note frequency + for (int i = 0; i < i; i++) { + Bit32u outOff = PERCUSSION_OFFSETS[pIdx][i]; + if (0x0 != outOff) { + int inAddr = 0xa0 + opl->_GetOffset(1); // any channel is fine, they should have all been written + int outAddr = 0xa0 + outOff; + _CaptureRegWrite(outAddr, opl->_ReadReg(inAddr)); + _DebugOut(itoa(opl->_ReadReg(inAddr), addr, 16)); + } + } _CaptureRegWriteWithDelay(0xbd, OxBD | maskOut); } } diff --git a/readme.md b/readme.md index 58b3775..74340e9 100644 --- a/readme.md +++ b/readme.md @@ -2,11 +2,11 @@ This VST instrument provides an emulated OPL sound chip. It provides all features of the OPL2, and some features of the OPL3. -See here for Windows binaries, screenshots etc: http://bsutherland.github.io/JuceOPLVSTi/ +See here for binaries, screenshots etc: http://bsutherland.github.io/JuceOPLVSTi/ ## What's an OPL? ## -The OPL is a digital sound synthesis chip developed by Yamaha in the mid 1980s. Among other products, it was used in sound cards for PC, including the Adlib card. +The OPL is a digital sound synthesis chip developed by Yamaha in the mid 1980s. Among other products, it was used in sound cards for PC, including the Ad Lib card and early Sound Blaster series. At a technical level: the emulator has channels comprised of 2 oscillators each. Each pair of oscillators is usually combined via phase modulation (basically frequency modulation). Each oscillator can produce one of eight waveforms (sine, half sine, absolute sine, quarter sine, alternating sine, camel sine, square, logarithmic sawtooth), and has an ADSR envelope controlling its amplitude. The unusual waveforms give it a characteristic sound. @@ -20,6 +20,7 @@ Each instance of the plugin emulates an entire OPL chip, but polyphony is implem Some documentation which may be useful: +- [Introduction to FM Synthesis](https://www.youtube.com/watch?v=ziFv00PegJg) (not specific to the OPL, but a great primer) - [OPL2 on Wikipedia](http://en.wikipedia.org/wiki/Yamaha_YM3812) - [Original Yamaha datasheet](http://www.alldatasheet.com/datasheet-pdf/pdf/84281/YAMAHA/YM3812.html) - [AdLib programming guide](http://www.shipbrook.net/jeff/sb.html) Dates back to 1991! @@ -35,9 +36,9 @@ 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! +Also, some [much more detailed notes](http://midibox.org/forums/topic/18625-opl3-percussion-mode-map/) 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? ## @@ -55,7 +56,7 @@ The code I wrote is essentially a device driver for the emulated OPL, implementi ## Building ## -So far I've only built under Windows. Thanks to Juce, it should be possible to build under OSX and Linux too. **Volunteers to build OSX/Linux releases would be appreciated** (XCode required for OSX). +So far I've only built under Windows. Thanks to the hard work of others, OSX and Linux builds should be coming very soon! ### Windows Build Instructions ###