Browse Source

Add juce-opl plugin

tags/2018-04-16
falkTX 5 years ago
parent
commit
a81d629410
47 changed files with 13273 additions and 0 deletions
  1. +4
    -0
      ports/Makefile
  2. +11
    -0
      ports/juce-opl/LV2/premake.lua
  3. +11
    -0
      ports/juce-opl/VST/premake.lua
  4. +66
    -0
      ports/juce-opl/source/ChannelButtonLookAndFeel.cpp
  5. +29
    -0
      ports/juce-opl/source/ChannelButtonLookAndFeel.h
  6. +420
    -0
      ports/juce-opl/source/DROMultiplexer.cpp
  7. +58
    -0
      ports/juce-opl/source/DROMultiplexer.h
  8. +35
    -0
      ports/juce-opl/source/EnumFloatParameter.cpp
  9. +15
    -0
      ports/juce-opl/source/EnumFloatParameter.h
  10. +27
    -0
      ports/juce-opl/source/FloatParameter.cpp
  11. +17
    -0
      ports/juce-opl/source/FloatParameter.h
  12. +11
    -0
      ports/juce-opl/source/InstrumentLoader.h
  13. +36
    -0
      ports/juce-opl/source/IntFloatParameter.cpp
  14. +16
    -0
      ports/juce-opl/source/IntFloatParameter.h
  15. +27
    -0
      ports/juce-opl/source/JuceHeader.h
  16. +40
    -0
      ports/juce-opl/source/JucePluginCharacteristics.h
  17. +147
    -0
      ports/juce-opl/source/OPLLookAndFeel.cpp
  18. +55
    -0
      ports/juce-opl/source/OPLLookAndFeel.h
  19. +3805
    -0
      ports/juce-opl/source/PluginGui.cpp
  20. +300
    -0
      ports/juce-opl/source/PluginGui.h
  21. +958
    -0
      ports/juce-opl/source/PluginProcessor.cpp
  22. +110
    -0
      ports/juce-opl/source/PluginProcessor.h
  23. +34
    -0
      ports/juce-opl/source/SbiLoader.cpp
  24. +13
    -0
      ports/juce-opl/source/SbiLoader.h
  25. +154
    -0
      ports/juce-opl/source/adlib.h
  26. +122
    -0
      ports/juce-opl/source/config.h
  27. +1535
    -0
      ports/juce-opl/source/dbopl.cpp
  28. +257
    -0
      ports/juce-opl/source/dbopl.h
  29. +76
    -0
      ports/juce-opl/source/dosbox.h
  30. +213
    -0
      ports/juce-opl/source/drov1.cpp.bak
  31. +5
    -0
      ports/juce-opl/source/drov1.h
  32. +55
    -0
      ports/juce-opl/source/hardware.h
  33. +311
    -0
      ports/juce-opl/source/hiopl.cpp
  34. +103
    -0
      ports/juce-opl/source/hiopl.h
  35. +78
    -0
      ports/juce-opl/source/inout.h
  36. +50
    -0
      ports/juce-opl/source/itoa.h
  37. +67
    -0
      ports/juce-opl/source/logging.h
  38. +114
    -0
      ports/juce-opl/source/mixer.h
  39. +1018
    -0
      ports/juce-opl/source/nkopl3.cpp
  40. +238
    -0
      ports/juce-opl/source/nkopl3.h
  41. +201
    -0
      ports/juce-opl/source/opl.h
  42. +67
    -0
      ports/juce-opl/source/pic.h
  43. +334
    -0
      ports/juce-opl/source/setup.h
  44. +26
    -0
      ports/juce-opl/source/tests.cpp
  45. +85
    -0
      ports/juce-opl/source/windows.h
  46. +1895
    -0
      ports/juce-opl/source/zdopl.cpp
  47. +24
    -0
      ports/juce-opl/source/zdopl.h

+ 4
- 0
ports/Makefile View File

@@ -54,6 +54,7 @@ else
$(MAKE) -C HiReSam/LV2
# $(MAKE) -C juce-demo-host/LV2
$(MAKE) -C juce-demo-plugin/LV2
$(MAKE) -C juce-opl/LV2
$(MAKE) -C klangfalter/LV2
$(MAKE) -C LUFSMeter/LV2
$(MAKE) -C LUFSMeter/LV2-Multi
@@ -93,6 +94,7 @@ vst: libs
$(MAKE) -C HiReSam/VST
# $(MAKE) -C juce-demo-host/VST
$(MAKE) -C juce-demo-plugin/VST
$(MAKE) -C juce-opl/VST
$(MAKE) -C klangfalter/VST
$(MAKE) -C LUFSMeter/VST
$(MAKE) -C LUFSMeter/VST-Multi
@@ -131,6 +133,7 @@ clean:
$(MAKE) clean -C HiReSam/LV2
$(MAKE) clean -C juce-demo-host/LV2
$(MAKE) clean -C juce-demo-plugin/LV2
$(MAKE) clean -C juce-opl/LV2
$(MAKE) clean -C klangfalter/LV2
$(MAKE) clean -C LUFSMeter/LV2
$(MAKE) clean -C LUFSMeter/LV2-Multi
@@ -165,6 +168,7 @@ clean:
$(MAKE) clean -C HiReSam/LV2
$(MAKE) clean -C juce-demo-host/VST
$(MAKE) clean -C juce-demo-plugin/VST
$(MAKE) clean -C juce-opl/VST
$(MAKE) clean -C klangfalter/VST
$(MAKE) clean -C LUFSMeter/VST
$(MAKE) clean -C LUFSMeter/VST-Multi


+ 11
- 0
ports/juce-opl/LV2/premake.lua View File

@@ -0,0 +1,11 @@
dofile("../../../scripts/make-project.lua")
package = make_juce_lv2_project("JuceOPL")
package.files = {
matchfiles (
"../source/*.cpp",
"../../../libs/juce-plugin/JucePluginMain.cpp"
)
}

+ 11
- 0
ports/juce-opl/VST/premake.lua View File

@@ -0,0 +1,11 @@
dofile("../../../scripts/make-project.lua")
package = make_juce_vst_project("JuceOPL")
package.files = {
matchfiles (
"../source/*.cpp",
"../../../libs/juce-plugin/JucePluginMain.cpp"
)
}

+ 66
- 0
ports/juce-opl/source/ChannelButtonLookAndFeel.cpp View File

@@ -0,0 +1,66 @@
/*
==============================================================================

ChannelButtonLookAndFeel.cpp
Created: 10 Oct 2016 9:38:20pm
Author: bruce

==============================================================================
*/

#include "ChannelButtonLookAndFeel.h"
#include "PluginGui.h"

ChannelButtonLookAndFeel::ChannelButtonLookAndFeel()
{


}

static void drawButtonShape(Graphics& g, const Path& outline, Colour baseColour, float height)
{
const float mainBrightness = baseColour.getBrightness();
const float mainAlpha = baseColour.getFloatAlpha();

g.setFillType(FillType(baseColour));
g.fillPath(outline);

//g.setColour(Colours::white.withAlpha(0.4f * mainAlpha * mainBrightness * mainBrightness));
//g.strokePath(outline, PathStrokeType(1.0f), AffineTransform::translation(0.0f, 1.0f)
// .scaled(1.0f, (height - 1.6f) / height));

//g.setColour(Colours::black.withAlpha(0.4f * mainAlpha));
//g.strokePath(outline, PathStrokeType(1.0f));
}

void ChannelButtonLookAndFeel::drawButtonBackground(Graphics& g, Button& button, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown)
{
Colour baseColour(backgroundColour.withMultipliedSaturation(button.hasKeyboardFocus(true) ? 1.3f : 0.9f)
.withMultipliedAlpha(button.isEnabled() ? 0.9f : 0.5f));

if (isButtonDown || isMouseOverButton)
baseColour = baseColour.contrasting(isButtonDown ? 0.2f : 0.1f);

const bool flatOnLeft = button.isConnectedOnLeft();
const bool flatOnRight = button.isConnectedOnRight();
const bool flatOnTop = button.isConnectedOnTop();
const bool flatOnBottom = button.isConnectedOnBottom();

const float width = button.getWidth() - 1.0f;
const float height = button.getHeight() - 1.0f;

if (width > 0 && height > 0)
{
const float cornerSize = 4.0f;

Path outline;
outline.addRoundedRectangle(0.5f, 0.5f, width, height, cornerSize, cornerSize,
!(flatOnLeft || flatOnTop),
!(flatOnRight || flatOnTop),
!(flatOnLeft || flatOnBottom),
!(flatOnRight || flatOnBottom));

drawButtonShape(g, outline, baseColour, height);
}
}

+ 29
- 0
ports/juce-opl/source/ChannelButtonLookAndFeel.h View File

@@ -0,0 +1,29 @@
/*
==============================================================================

ChannelButtonLookAndFeel.h
Created: 10 Oct 2016 9:38:20pm
Author: bruce

==============================================================================
*/

#ifndef ChannelButtonLookAndFeel_H_INCLUDED
#define ChannelButtonLookAndFeel_H_INCLUDED

#include "JuceHeader.h"

class ChannelButtonLookAndFeel : public LookAndFeel_V3
{

public:
ChannelButtonLookAndFeel();
void drawButtonBackground(Graphics& g, Button& button, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown);

};




#endif // ChannelButtonLookAndFeel_H_INCLUDED

+ 420
- 0
ports/juce-opl/source/DROMultiplexer.cpp View File

@@ -0,0 +1,420 @@
#include "DROMultiplexer.h"
#include "JuceHeader.h"

/// Jeff-Russ added guard against windows.h include if not windows:
#ifdef _WIN32 // covers both 32 and 64-bit
#include <Windows.h>
#else
#include "windows.h"
#endif

/// Jeff-Russ added to replace mising itoa for xcode:
#if __APPLE__ || __linux__
#include "itoa.h"
#endif

// Used by the first recording instance to claim master status
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 */
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
};

// 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,
};

// bass drum uses two operators.
// others use either modulator or carrier.
static Bit32u PERCUSSION_OFFSETS[5][2] = {
{ 0x13, 0x10 }, // bd
{ 0x00, 0x14 }, // sd
{ 0x12, 0x00 }, // tt
{ 0x00, 0x15}, // cy
{ 0x11, 0x00 }, // hh
};

static Bit32u PERCUSSION_CHANNELS[5] = {
7, 7, 8, 8, 9
// or 7, 8, 8, 9, 9?
};

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);
};

HANDLE conout;
DROMultiplexer::DROMultiplexer()
{
InitCaptureVariables();
#ifdef _DEBUG
AllocConsole();
conout = GetStdHandle(STD_OUTPUT_HANDLE);
#endif
}

DROMultiplexer::~DROMultiplexer()
{
#ifdef _DEBUG
FreeConsole();
#endif
}

DROMultiplexer* DROMultiplexer::GetMaster() {
return DROMultiplexer::master;
}

void DROMultiplexer::_CopyOplPercussionSettings(Hiopl* opl, int pIdx) {
// input channel 1 is as good as any..
int op1Off = opl->_GetOffset(1, 1);
int op2Off = opl->_GetOffset(1, 2);
Bit32u inAddr;
Bit32u outAddr;

Bit32u* outOff = PERCUSSION_OFFSETS[pIdx];
// waveform select
int base = 0xe0;
if (outOff[0]) {
inAddr = base + op1Off;
outAddr = base + outOff[0];
_CaptureRegWriteWithDelay(outAddr, opl->_ReadReg(inAddr));
}
if (outOff[1]) {
inAddr = base + op2Off;
outAddr = base + outOff[1];
_CaptureRegWrite(outAddr, opl->_ReadReg(inAddr));
}
// other operator settings
for (base = 0x20; base <= 0x80; base += 0x20) {
if (outOff[0]) {
inAddr = base + op1Off;
outAddr = base + outOff[0];
_CaptureRegWrite(outAddr, opl->_ReadReg(inAddr));
}
if (outOff[1]) {
inAddr = base + op2Off;
outAddr = base + outOff[1];
_CaptureRegWrite(outAddr, opl->_ReadReg(inAddr));
}
}

// channel wide settings
int chInOff = opl->_GetOffset(1);
inAddr = 0xc0 + chInOff;
outAddr = 0xc0 + PERCUSSION_CHANNELS[pIdx];
_CaptureRegWrite(outAddr, 0x30 | opl->_ReadReg(inAddr)); // make sure L+R channels always enabled
}

void DROMultiplexer::_CopyOplChannelSettings(Hiopl* opl, int inCh, int outCh) {
// read all instrument settings and write them all to the file
int op1Off = opl->_GetOffset(inCh, 1);
int op2Off = opl->_GetOffset(inCh, 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(inCh);
inAddr = 0xc0 + chInOff;
outAddr = 0xc0 + CHANNEL_OFFSETS[outCh];
_CaptureRegWrite(outAddr, 0x30 | opl->_ReadReg(inAddr));
}

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);

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(" ");
_DebugOut(itoa(opl->_ReadReg(inAddr), addr, 16));
_DebugOut("\n");
}
}

void DROMultiplexer::TwoOpMelodicNoteOff(Hiopl* opl, int ch) {
const ScopedLock sl(lock);

int chOff = opl->_GetOffset(ch);
OplCh_t key;
key.opl = opl;
key.ch = ch;

int outCh = channelMap[key];
char n[8];
_DebugOut(itoa(outCh, n, 16));
_DebugOut(" note off\n");
// note-off
Bit32u inAddr = 0xb0 + chOff;
Bit32u outAddr = 0xb0 + CHANNEL_OFFSETS[outCh];
_CaptureRegWriteWithDelay(outAddr, opl->_ReadReg(inAddr));
}

void DROMultiplexer::_DebugOut(const 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) {
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, 16));
return i;
}
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 -1;
}

void DROMultiplexer::PercussionChange(Hiopl* opl, int pIdx) {
const ScopedLock sl(lock);
_CopyOplPercussionSettings(opl, pIdx);
Bit8u val = opl->_ReadReg(0xbd);
Bit8u maskOut = 1 << abs(4 - 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);
}
}

void DROMultiplexer::InitCaptureVariables() {
captureHandle = NULL;
captureLengthBytes = 0;
lastWrite = -1;
captureStart = -1;
channelMap.clear();
for (int i = 0; i < MELODIC_CHANNELS; i++) {
channels[i].opl = NULL;
channels[i].ch = -1;
}
OxBD = 0x20; // percussion mode should always be enabled
}

bool DROMultiplexer::StartCapture(const char* filepath, Hiopl *opl) {
captureHandle = fopen(filepath, "wb");
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, OxBD | (opl->_ReadReg(0xbd) & 0xc0)); // enable percmode, copy tremolo and vibrato depth only
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() {
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();
DROMultiplexer::master = NULL;
}

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;
if (0xbd == reg) {
OxBD = value;
}
}

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;
}

+ 58
- 0
ports/juce-opl/source/DROMultiplexer.h View File

@@ -0,0 +1,58 @@
#pragma once
#include <map>
#include "hiopl.h"
#include "JuceHeader.h"

class DROMultiplexer
{
public:
static const int MELODIC_CHANNELS = 15;

DROMultiplexer();
~DROMultiplexer();

void TwoOpMelodicNoteOn(Hiopl* opl, int ch);
void TwoOpMelodicNoteOff(Hiopl* opl, int ch);
void PercussionChange(Hiopl* opl, int perc);

void InitCaptureVariables();
bool IsAnInstanceRecording();
bool IsAnotherInstanceRecording();
bool StartCapture(const char* filepath, Hiopl* opl);
void StopCapture();
static DROMultiplexer* GetMaster();

private:
void _CaptureDelay(Bit16u delayMs);
void _CaptureRegWriteWithDelay(Bit32u reg, Bit8u value);
void _CaptureRegWrite(Bit32u reg, Bit8u value);
void _CaptureOpl3Enable();
int _FindFreeChannel(Hiopl* opl, int inCh);
void _CopyOplChannelSettings(Hiopl* opl, int inCh, int outCh);
void _CopyOplPercussionSettings(Hiopl* opl, int pIdx);
void _DebugOut(const char* str);
static DROMultiplexer* master;
Bit8u OxBD; // cached value of percussion register

FILE* captureHandle;
Bit64s captureStart;
Bit64s lastWrite;
Bit32u captureLengthBytes;
static CriticalSection lock;

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;
};


+ 35
- 0
ports/juce-opl/source/EnumFloatParameter.cpp View File

@@ -0,0 +1,35 @@
#include "EnumFloatParameter.h"


EnumFloatParameter::EnumFloatParameter(String name, StringArray values)
:FloatParameter(name)
{
this->values = values;
}


EnumFloatParameter::~EnumFloatParameter(void)
{
}

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

void EnumFloatParameter::setParameterIndex(int i)
{
this->value = (float)i/(float)values.size();
if (this->value < 0.0f)
this->value = 0.0f;
else if (this->value > 1.0f)
this->value = 1.0f;
}

String EnumFloatParameter::getParameterText(void)
{
return values[this->getParameterIndex()];
}

+ 15
- 0
ports/juce-opl/source/EnumFloatParameter.h View File

@@ -0,0 +1,15 @@
#pragma once
#include "FloatParameter.h"
class EnumFloatParameter :
public FloatParameter
{
public:
EnumFloatParameter(String name, StringArray values);
~EnumFloatParameter(void);
String getParameterText(void);
int getParameterIndex(void);
void setParameterIndex(int);
private:
StringArray values;
};


+ 27
- 0
ports/juce-opl/source/FloatParameter.cpp View File

@@ -0,0 +1,27 @@
#include "FloatParameter.h"


FloatParameter::FloatParameter(String name)
{
this->name = name;
this->value = 0.0f;
}

FloatParameter::~FloatParameter(void)
{
}

float FloatParameter::getParameter(void)
{
return value;
}

void FloatParameter::setParameter(float value)
{
this->value = value;
}

String FloatParameter::getName(void)
{
return name;
}

+ 17
- 0
ports/juce-opl/source/FloatParameter.h View File

@@ -0,0 +1,17 @@
#pragma once
#include "JuceHeader.h"

class FloatParameter
{
public:
FloatParameter(String name);
virtual ~FloatParameter(void);
float getParameter(void);
void setParameter(float value);
String getName(void);
virtual String getParameterText(void) = 0;
protected:
float value;
private:
String name;
};

+ 11
- 0
ports/juce-opl/source/InstrumentLoader.h View File

@@ -0,0 +1,11 @@
#pragma once
#include "PluginProcessor.h"

// Just specifies the interfaces for instrument loaders.

class InstrumentLoader
{
public:
virtual void loadInstrumentData(int n, const unsigned char* data, AdlibBlasterAudioProcessor *proc) = 0;
virtual String getExtension() = 0;
};

+ 36
- 0
ports/juce-opl/source/IntFloatParameter.cpp View File

@@ -0,0 +1,36 @@
#include "IntFloatParameter.h"


IntFloatParameter::IntFloatParameter(String name, int min, int max)
:FloatParameter(name)
{
this->min = min;
this->max = max;
}

IntFloatParameter::~IntFloatParameter(void)
{
}

int IntFloatParameter::getParameterValue(void)
{
int range = max - min;
int i = (int)(this->value * range) + min;
if (i > range)
i = range;
return i;
}

void IntFloatParameter::setParameterValue(int i)
{
this->value = (float)(i - min)/(float)(max - min);
if (this->value < 0.0f)
this->value = 0.0f;
else if (this->value > 1.0f)
this->value = 1.0f;
}

String IntFloatParameter::getParameterText(void)
{
return String(this->getParameterValue());
}

+ 16
- 0
ports/juce-opl/source/IntFloatParameter.h View File

@@ -0,0 +1,16 @@
#pragma once
#include "FloatParameter.h"
class IntFloatParameter :
public FloatParameter
{
public:
IntFloatParameter(String name, int min, int max);
~IntFloatParameter(void);
String getParameterText(void);
int getParameterValue(void);
void setParameterValue(int i);
private:
int min;
int max;
};


+ 27
- 0
ports/juce-opl/source/JuceHeader.h View File

@@ -0,0 +1,27 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
This is the header file that your files should include in order to get all the
JUCE library headers. You should avoid including the JUCE headers directly in
your own source files, because that wouldn't pick up the correct configuration
options for your app.
*/
#ifndef __APPHEADERFILE_9C46E6CC__
#define __APPHEADERFILE_9C46E6CC__
#include "JucePluginMain.h"
using namespace juce;
namespace ProjectInfo
{
const char* const projectName = "JuceOPL";
const char* const versionString = "1.0.0";
const int versionNumber = 0x10000;
}
#endif // __APPHEADERFILE_9C46E6CC__

+ 40
- 0
ports/juce-opl/source/JucePluginCharacteristics.h View File

@@ -0,0 +1,40 @@
/*
IMPORTANT! This file is auto-generated by the Jucer each time you save your
project - if you alter its contents, your changes may be overwritten!
This header file contains configuration options for the plug-in. If you need to change any of
these, it'd be wise to do so using the Jucer, rather than editing this file directly...
*/
#ifndef __PLUGINCHARACTERISTICS_D4EFFF1A__
#define __PLUGINCHARACTERISTICS_D4EFFF1A__
#define JucePlugin_Name "JuceOPL"
#define JucePlugin_Desc "JuceOPL"
#define JucePlugin_Manufacturer "Plainweave Software"
#define JucePlugin_ManufacturerCode 'Pwve'
#define JucePlugin_PluginCode 'JOPL'
#define JucePlugin_MaxNumInputChannels 0
#define JucePlugin_MaxNumOutputChannels 1
#define JucePlugin_PreferredChannelConfigurations {0, 1}
#define JucePlugin_IsSynth 0
#define JucePlugin_IsMidiEffect 0
#define JucePlugin_WantsMidiInput 1
#define JucePlugin_ProducesMidiOutput 0
#define JucePlugin_SilenceInProducesSilenceOut 0
#define JucePlugin_TailLengthSeconds 0
#define JucePlugin_EditorRequiresKeyboardFocus 0
#define JucePlugin_VersionCode 0x10000
#define JucePlugin_VersionString "1.0.0"
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode
#define JucePlugin_VSTCategory kPlugCategEffect
#define JucePlugin_LV2URI "https://bsutherland.github.io/JuceOPLVSTi/"
#define JucePlugin_WantsLV2Latency 0
#define JucePlugin_WantsLV2TimePos 0
#define JucePlugin_WantsLV2State 1
#define JucePlugin_WantsLV2Presets 0
#endif // __PLUGINCHARACTERISTICS_D4EFFF1A__

+ 147
- 0
ports/juce-opl/source/OPLLookAndFeel.cpp View File

@@ -0,0 +1,147 @@
/*
==============================================================================

OPLLookAndFeel.cpp
Created: 10 Oct 2016 9:38:20pm
Author: bruce

==============================================================================
*/

#include "OPLLookAndFeel.h"
#include "PluginGui.h"

const Colour OPLLookAndFeel::DOS_GREEN = Colour(0xff007f00);
const Colour OPLLookAndFeel::DOS_GREEN_DARK = Colour(0xff003f00);

OPLLookAndFeel::OPLLookAndFeel()
{
toggleOff = ImageCache::getFromMemory(PluginGui::toggle_off_sq_png, PluginGui::toggle_off_sq_pngSize), 1.000f, Colour(0x00000000);
toggleOn = ImageCache::getFromMemory(PluginGui::toggle_on_sq_png, PluginGui::toggle_on_sq_pngSize), 1.000f, Colour(0x00000000);
toggleRect = Rectangle<float>((float)toggleOff.getWidth(), (float)toggleOn.getHeight());

// Prevents an ugly white border from being drawn around a component with keyboard focus.
setColour(TextEditor::ColourIds::focusedOutlineColourId, Colours::black);
setColour(Slider::ColourIds::textBoxOutlineColourId, DOS_GREEN);

setColour(TextButton::ColourIds::buttonColourId, DOS_GREEN);
setColour(TextButton::ColourIds::buttonOnColourId, DOS_GREEN);
setColour(TextButton::ColourIds::textColourOnId, Colours::black);
setColour(TextButton::ColourIds::textColourOffId, Colours::black);

}

void OPLLookAndFeel::drawTickBox(Graphics &g,
Component &c,
float x,
float y,
float w,
float h,
bool ticked,
bool isEnabled,
bool isMouseOverButton,
bool isButtonDown
) {
g.drawImage(ticked ? toggleOn : toggleOff, toggleRect.withY(y + 2));
}

// From JuceLookAndFeel_V2
static Colour createBaseColour(Colour buttonColour,
bool hasKeyboardFocus,
bool isMouseOverButton,
bool isButtonDown) noexcept
{
const float sat = hasKeyboardFocus ? 1.3f : 0.9f;
const Colour baseColour(buttonColour.withMultipliedSaturation(sat));

if (isButtonDown) return baseColour.contrasting(0.2f);
if (isMouseOverButton) return baseColour.contrasting(0.1f);

return baseColour;
}

int OPLLookAndFeel::getSliderThumbRadius(Slider& s) {
return 10;
}

// Adapted rom JuceLookAndFeel_V2 - changed round thumb to plain filled rectangle and tweake size.
void OPLLookAndFeel::drawLinearSliderThumb(Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) {
const float sliderRadius = (float)(getSliderThumbRadius(slider));

Colour knobColour(createBaseColour(slider.findColour(Slider::thumbColourId),
slider.hasKeyboardFocus(false) && slider.isEnabled(),
slider.isMouseOverOrDragging() && slider.isEnabled(),
slider.isMouseButtonDown() && slider.isEnabled()));

const float outlineThickness = slider.isEnabled() ? 0.8f : 0.3f;

if (style == Slider::LinearHorizontal || style == Slider::LinearVertical)
{
float kx, ky;
float sw, sh;

if (style == Slider::LinearVertical)
{
sw = sliderRadius * 2.0f;
sh = sliderRadius;
kx = x + width * 0.5f;
ky = sliderPos + sh * 0.5f;
}
else
{
sw = sliderRadius;
sh = sliderRadius * 2.0f;
kx = sliderPos + sw * 0.5f;
ky = y + height * 0.5f;
}

g.setColour(knobColour);
g.fillRect(kx - sliderRadius, ky - sliderRadius, sw, sh);
}
else
{
LookAndFeel_V2::drawLinearSliderThumb(g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}

// Adapted from JuceLookAndFeel_V3 - replace rounded rectangles with regular ones.
void OPLLookAndFeel::drawLinearSliderBackground(Graphics& g, int x, int y, int width, int height,
float /*sliderPos*/,
float /*minSliderPos*/,
float /*maxSliderPos*/,
const Slider::SliderStyle /*style*/, Slider& slider)
{
const float sliderRadius = (float)(getSliderThumbRadius(slider) - 2);

const Colour trackColour(slider.findColour(Slider::trackColourId));
const Colour gradCol1(trackColour.overlaidWith(Colour(slider.isEnabled() ? 0x13000000 : 0x09000000)));
const Colour gradCol2(trackColour.overlaidWith(Colour(0x06000000)));
Path indent;

if (slider.isHorizontal())
{
const float iy = y + height * 0.5f - sliderRadius * 0.5f;

g.setGradientFill(ColourGradient(gradCol1, 0.0f, iy,
gradCol2, 0.0f, iy + sliderRadius, false));

indent.addRectangle(x - sliderRadius * 0.5f, iy, width + sliderRadius, sliderRadius);
}
else
{
const float ix = x + width * 0.5f - sliderRadius * 0.5f;

g.setGradientFill(ColourGradient(gradCol1, ix, 0.0f,
gradCol2, ix + sliderRadius, 0.0f, false));

indent.addRectangle(ix, y - sliderRadius * 0.5f, sliderRadius, height + sliderRadius);
}

g.fillPath(indent);

g.setColour(trackColour.contrasting(0.5f));
g.strokePath(indent, PathStrokeType(0.5f));
}

+ 55
- 0
ports/juce-opl/source/OPLLookAndFeel.h View File

@@ -0,0 +1,55 @@
/*
==============================================================================

OPLLookAndFeel.h
Created: 10 Oct 2016 9:38:20pm
Author: bruce

==============================================================================
*/

#ifndef OPLLOOKANDFEEL_H_INCLUDED
#define OPLLOOKANDFEEL_H_INCLUDED

#include "JuceHeader.h"

class OPLLookAndFeel : public LookAndFeel_V3
{
private:
Image toggleOff;
Image toggleOn;
Rectangle<float> toggleRect;

public:
static const Colour DOS_GREEN;
static const Colour DOS_GREEN_DARK;

OPLLookAndFeel();

void drawTickBox(Graphics &g,
Component &c,
float x,
float y,
float w,
float h,
bool ticked,
bool isEnabled,
bool isMouseOverButton,
bool isButtonDown
);

int getSliderThumbRadius(Slider& s);
void drawLinearSliderThumb(Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider);
void drawLinearSliderBackground(Graphics& g, int x, int y, int width, int height,
float /*sliderPos*/,
float /*minSliderPos*/,
float /*maxSliderPos*/,
const Slider::SliderStyle /*style*/, Slider& slider);
};




#endif // OPLLOOKANDFEEL_H_INCLUDED

+ 3805
- 0
ports/juce-opl/source/PluginGui.cpp
File diff suppressed because it is too large
View File


+ 300
- 0
ports/juce-opl/source/PluginGui.h View File

@@ -0,0 +1,300 @@
/*
==============================================================================
This is an automatically generated GUI class created by the Projucer!
Be careful when adding custom code to these files, as only the code within
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Projucer version: 5.0.2
------------------------------------------------------------------------------
The Projucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
#pragma once
//[Headers] -- You can add your own extra header files here --
#include <array>
#include "JuceHeader.h"
#include "PluginProcessor.h"
//[/Headers]
//==============================================================================
/**
//[Comments]
This is a GUI for the OPL2 VST plugin, created in Juce.
//[/Comments]
*/
class PluginGui : public AudioProcessorEditor,
public FileDragAndDropTarget,
public DragAndDropContainer,
public Timer,
public ComboBoxListener,
public SliderListener,
public ButtonListener
{
public:
//==============================================================================
PluginGui (AdlibBlasterAudioProcessor* ownerFilter);
~PluginGui();
//==============================================================================
//[UserMethods] -- You can add your own custom methods in this section.
void updateFromParameters();
bool isInterestedInFileDrag (const StringArray& files);
void fileDragEnter (const StringArray& files, int x, int y);
void fileDragMove (const StringArray& files, int x, int y);
void fileDragExit (const StringArray& files);
void filesDropped (const StringArray& files, int x, int y);
void timerCallback();
void setRecordButtonState(bool recording);
//[/UserMethods]
void paint (Graphics& g) override;
void resized() override;
void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override;
void sliderValueChanged (Slider* sliderThatWasMoved) override;
void buttonClicked (Button* buttonThatWasClicked) override;
// Binary resources:
static const char* full_sine_png;
static const int full_sine_pngSize;
static const char* half_sine_png;
static const int half_sine_pngSize;
static const char* abs_sine_png;
static const int abs_sine_pngSize;
static const char* quarter_sine_png;
static const int quarter_sine_pngSize;
static const char* camel_sine_png;
static const int camel_sine_pngSize;
static const char* alternating_sine_png;
static const int alternating_sine_pngSize;
static const char* square_png;
static const int square_pngSize;
static const char* logarithmic_saw_png;
static const int logarithmic_saw_pngSize;
static const char* channeloff_png;
static const int channeloff_pngSize;
static const char* channelon_png;
static const int channelon_pngSize;
static const char* toggle_off_sq_png;
static const int toggle_off_sq_pngSize;
static const char* toggle_on_sq_png;
static const int toggle_on_sq_pngSize;
static const char* line_border_horiz_png;
static const int line_border_horiz_pngSize;
static const char* line_border_vert_png;
static const int line_border_vert_pngSize;
static const char* algo_switch_off_png;
static const int algo_switch_off_pngSize;
static const char* algo_switch_on_png;
static const int algo_switch_on_pngSize;
static const char* algo_switch_on2_png;
static const int algo_switch_on2_pngSize;
static const char* algo_switch_on3_png;
static const int algo_switch_on3_pngSize;
static const char* twoopAm_png;
static const int twoopAm_pngSize;
static const char* twoopFm_png;
static const int twoopFm_pngSize;
static const char* bassdrum_png;
static const int bassdrum_pngSize;
static const char* snare_png;
static const int snare_pngSize;
static const char* disabled_png;
static const int disabled_pngSize;
static const char* tom_png;
static const int tom_pngSize;
static const char* hihat_png;
static const int hihat_pngSize;
static const char* cymbal_png;
static const int cymbal_pngSize;
static const char* adlib_png;
static const int adlib_pngSize;
private:
//[UserVariables] -- You can add your own custom variables in this section.
static const uint32 COLOUR_MID = 0xff007f00;
static const uint32 COLOUR_RECORDING = 0xffff0000;
AdlibBlasterAudioProcessor* processor;
std::array<ScopedPointer<TextButton>, Hiopl::CHANNELS> channels;
TooltipWindow tooltipWindow;
File instrumentLoadDirectory;
File instrumentSaveDirectory;
//[/UserVariables]
//==============================================================================
ScopedPointer<GroupComponent> groupComponent2;
ScopedPointer<GroupComponent> groupComponent4;
ScopedPointer<GroupComponent> groupComponent11;
ScopedPointer<GroupComponent> groupComponent10;
ScopedPointer<GroupComponent> groupComponent9;
ScopedPointer<GroupComponent> groupComponent;
ScopedPointer<ComboBox> frequencyComboBox;
ScopedPointer<Label> frequencyLabel;
ScopedPointer<Slider> aSlider;
ScopedPointer<Label> aLabel;
ScopedPointer<Slider> dSlider;
ScopedPointer<Label> dLabel;
ScopedPointer<Slider> sSlider;
ScopedPointer<Label> dLabel2;
ScopedPointer<Slider> rSlider;
ScopedPointer<Label> rLabel;
ScopedPointer<Slider> attenuationSlider;
ScopedPointer<Label> attenuationLabel;
ScopedPointer<Label> dbLabel;
ScopedPointer<ImageButton> sineImageButton;
ScopedPointer<ImageButton> halfsineImageButton;
ScopedPointer<ImageButton> abssineImageButton;
ScopedPointer<ImageButton> quartersineImageButton;
ScopedPointer<Label> waveLabel;
ScopedPointer<ToggleButton> tremoloButton;
ScopedPointer<ToggleButton> vibratoButton;
ScopedPointer<ToggleButton> sustainButton;
ScopedPointer<ToggleButton> keyscaleEnvButton;
ScopedPointer<Label> dbLabel2;
ScopedPointer<ComboBox> frequencyComboBox2;
ScopedPointer<Label> frequencyLabel3;
ScopedPointer<Slider> aSlider2;
ScopedPointer<Label> aLabel2;
ScopedPointer<Slider> dSlider2;
ScopedPointer<Label> dLabel3;
ScopedPointer<Slider> sSlider2;
ScopedPointer<Label> dLabel4;
ScopedPointer<Slider> rSlider2;
ScopedPointer<Label> rLabel2;
ScopedPointer<Slider> attenuationSlider2;
ScopedPointer<Label> attenuationLabel2;
ScopedPointer<Label> dbLabel3;
ScopedPointer<ImageButton> sineImageButton2;
ScopedPointer<ImageButton> halfsineImageButton2;
ScopedPointer<ImageButton> abssineImageButton2;
ScopedPointer<ImageButton> quartersineImageButton2;
ScopedPointer<Label> waveLabel2;
ScopedPointer<ToggleButton> tremoloButton2;
ScopedPointer<ToggleButton> vibratoButton2;
ScopedPointer<ToggleButton> sustainButton2;
ScopedPointer<ToggleButton> keyscaleEnvButton2;
ScopedPointer<Label> frequencyLabel4;
ScopedPointer<GroupComponent> groupComponent3;
ScopedPointer<Slider> tremoloSlider;
ScopedPointer<Label> frequencyLabel5;
ScopedPointer<Label> dbLabel5;
ScopedPointer<Slider> vibratoSlider;
ScopedPointer<Label> frequencyLabel6;
ScopedPointer<Label> dbLabel6;
ScopedPointer<Slider> feedbackSlider;
ScopedPointer<Label> frequencyLabel7;
ScopedPointer<ComboBox> velocityComboBox;
ScopedPointer<ComboBox> velocityComboBox2;
ScopedPointer<Label> attenuationLabel4;
ScopedPointer<ImageButton> alternatingsineImageButton;
ScopedPointer<ImageButton> camelsineImageButton;
ScopedPointer<ImageButton> squareImageButton;
ScopedPointer<ImageButton> logsawImageButton;
ScopedPointer<ImageButton> alternatingsineImageButton2;
ScopedPointer<ImageButton> camelsineImageButton2;
ScopedPointer<ImageButton> squareImageButton2;
ScopedPointer<ImageButton> logsawImageButton2;
ScopedPointer<Label> dbLabel4;
ScopedPointer<ComboBox> keyscaleAttenuationComboBox2;
ScopedPointer<ComboBox> keyscaleAttenuationComboBox;
ScopedPointer<GroupComponent> groupComponent5;
ScopedPointer<Slider> emulatorSlider;
ScopedPointer<Label> emulatorLabel;
ScopedPointer<Label> emulatorLabel2;
ScopedPointer<ToggleButton> recordButton;
ScopedPointer<TextButton> exportButton;
ScopedPointer<TextButton> loadButton;
ScopedPointer<Label> versionLabel;
ScopedPointer<ImageButton> ToggleButtonOffExample;
ScopedPointer<ImageButton> ToggleButtonOnExample;
ScopedPointer<Label> label;
ScopedPointer<Label> label2;
ScopedPointer<ImageButton> LineBorderButton1C;
ScopedPointer<ImageButton> LineBorderButton1A;
ScopedPointer<ImageButton> LineBorderButton1B;
ScopedPointer<Label> label3;
ScopedPointer<ImageButton> LineBorderButton1C2;
ScopedPointer<ImageButton> LineBorderButton1A2;
ScopedPointer<ImageButton> LineBorderButton1B2;
ScopedPointer<ImageButton> LineBorderButton1C3;
ScopedPointer<ImageButton> LineBorderButton1B3;
ScopedPointer<ImageButton> algoSwitchButtonOffEx1;
ScopedPointer<ImageButton> algoSwitchButtonOffEx2;
ScopedPointer<ImageButton> algoSwitchButtonOnEx1;
ScopedPointer<ImageButton> algoSwitchButtonOnEx2;
ScopedPointer<Label> label4;
ScopedPointer<Label> label5;
ScopedPointer<Label> label6;
ScopedPointer<Label> label7;
ScopedPointer<Label> label8;
ScopedPointer<ImageButton> algoSwitchButtonOn2Ex1;
ScopedPointer<ImageButton> algoSwitchButtonOn2Ex2;
ScopedPointer<Label> label9;
ScopedPointer<Label> label10;
ScopedPointer<ImageButton> algoSwitchButtonOn3Ex1;
ScopedPointer<ImageButton> algoSwitchButtonOn3Ex2;
ScopedPointer<Label> label11;
ScopedPointer<Label> label12;
ScopedPointer<ImageButton> TwoOpAMButton;
ScopedPointer<ImageButton> TwoOpFMButton;
ScopedPointer<Label> label13;
ScopedPointer<Label> label14;
ScopedPointer<Label> label15;
ScopedPointer<Label> label16;
ScopedPointer<Label> label17;
ScopedPointer<GroupComponent> groupComponent6;
ScopedPointer<ImageButton> algoSwitchButtonOnEx3;
ScopedPointer<Label> label18;
ScopedPointer<ImageButton> algoSwitchButtonOffEx3;
ScopedPointer<Label> label19;
ScopedPointer<ImageButton> TwoOpAMButton2;
ScopedPointer<Label> label20;
ScopedPointer<Label> label21;
ScopedPointer<Label> label22;
ScopedPointer<ImageButton> algoSwitchButtonOffEx4;
ScopedPointer<Label> label23;
ScopedPointer<ImageButton> algoSwitchButtonOn3Ex3;
ScopedPointer<Label> label24;
ScopedPointer<ImageButton> TwoOpFMButton2;
ScopedPointer<Label> label25;
ScopedPointer<Label> label26;
ScopedPointer<GroupComponent> groupComponent7;
ScopedPointer<ImageButton> algoSwitchButtonOffEx5;
ScopedPointer<Label> label27;
ScopedPointer<ImageButton> algoSwitchButtonOn3Ex4;
ScopedPointer<Label> label28;
ScopedPointer<GroupComponent> groupComponent8;
ScopedPointer<Label> frequencyLabel9;
ScopedPointer<Label> label29;
ScopedPointer<Label> label30;
ScopedPointer<Label> frequencyLabel10;
ScopedPointer<Label> attenuationLabel5;
ScopedPointer<ImageButton> fmButton;
ScopedPointer<ImageButton> additiveButton;
ScopedPointer<ImageButton> bassDrumButton;
ScopedPointer<ImageButton> snareDrumButton;
ScopedPointer<ImageButton> disablePercussionButton;
ScopedPointer<ImageButton> tomTomButton;
ScopedPointer<ImageButton> cymbalButton;
ScopedPointer<ImageButton> hiHatButton;
ScopedPointer<Label> dbLabel7;
ScopedPointer<Label> dbLabel8;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginGui)
};
//[EndFile] You can add extra defines here...
//[/EndFile]

+ 958
- 0
ports/juce-opl/source/PluginProcessor.cpp View File

@@ -0,0 +1,958 @@
#include "PluginProcessor.h"
#include "PluginGui.h"
#include "EnumFloatParameter.h"
#include "IntFloatParameter.h"
#include "SbiLoader.h"

#include <iterator>

const char *AdlibBlasterAudioProcessor::PROGRAM_INDEX = "Program Index";

//==============================================================================
AdlibBlasterAudioProcessor::AdlibBlasterAudioProcessor()
: i_program(-1)
{
// Initalize OPL
velocity = false;
Opl = new Hiopl();
Opl->SetSampleRate(44100);
Opl->EnableWaveformControl();

// Initialize parameters

const String waveforms[] = {"Sine", "Half Sine", "Abs Sine", "Quarter Sine", "Alternating Sine", "Camel Sine", "Square", "Logarithmic Sawtooth"};
params.push_back(new EnumFloatParameter("Carrier Wave",
StringArray(waveforms, sizeof(waveforms)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Wave",
StringArray(waveforms, sizeof(waveforms)/sizeof(String)))
);

const String frq_multipliers[] = {
"x0.5", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x10", "x12", "x12", "x15", "x15"
};
params.push_back(new EnumFloatParameter("Carrier Frequency Multiplier",
StringArray(frq_multipliers, sizeof(frq_multipliers)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Frequency Multiplier",
StringArray(frq_multipliers, sizeof(frq_multipliers)/sizeof(String)))
);

const String levels[] = {"0.00 dB", "0.75 dB", "1.50 dB", "2.25 dB", "3.00 dB", "3.75 dB", "4.50 dB", "5.25 dB", "6.00 dB", "6.75 dB", "7.50 dB", "8.25 dB", "9.00 dB", "9.75 dB", "10.50 dB", "11.25 dB", "12.00 dB", "12.75 dB", "13.50 dB", "14.25 dB", "15.00 dB", "15.75 dB", "16.50 dB", "17.25 dB", "18.00 dB", "18.75 dB", "19.50 dB", "20.25 dB", "21.00 dB", "21.75 dB", "22.50 dB", "23.25 dB", "24.00 dB", "24.75 dB", "25.50 dB", "26.25 dB", "27.00 dB", "27.75 dB", "28.50 dB", "29.25 dB", "30.00 dB", "30.75 dB", "31.50 dB", "32.25 dB", "33.00 dB", "33.75 dB", "34.50 dB", "35.25 dB", "36.00 dB", "36.75 dB", "37.50 dB", "38.25 dB", "39.00 dB", "39.75 dB", "40.50 dB", "41.25 dB", "42.00 dB", "42.75 dB", "43.50 dB", "44.25 dB", "45.00 dB", "45.75 dB", "46.50 dB", "47.25 dB"};
params.push_back(new EnumFloatParameter("Carrier Attenuation",
StringArray(levels, sizeof(levels)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Attenuation",
StringArray(levels, sizeof(levels)/sizeof(String)))
);

const String depth[] = {"Light", "Heavy"};
params.push_back(new EnumFloatParameter("Tremolo Depth",
StringArray(depth, sizeof(depth)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Vibrato Depth",
StringArray(depth, sizeof(depth)/sizeof(String)))
);

const String onoff[] = {"Disable", "Enable"};
params.push_back(new EnumFloatParameter("Carrier Tremolo",
StringArray(onoff, sizeof(onoff)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Carrier Vibrato",
StringArray(onoff, sizeof(onoff)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Carrier Sustain",
StringArray(onoff, sizeof(onoff)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Carrier Keyscale Rate",
StringArray(onoff, sizeof(onoff)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Tremolo",
StringArray(onoff, sizeof(onoff)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Vibrato",
StringArray(onoff, sizeof(onoff)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Sustain",
StringArray(onoff, sizeof(onoff)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Keyscale Rate",
StringArray(onoff, sizeof(onoff)/sizeof(String)))
);

const String ksrs[] = {"None","1.5 dB/8ve","3 dB/8ve","6 dB/8ve"};
params.push_back(new EnumFloatParameter("Carrier Keyscale Level",
StringArray(ksrs, sizeof(ksrs)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Keyscale Level",
StringArray(ksrs, sizeof(ksrs)/sizeof(String)))
);

const String algos[] = {"Frequency Modulation", "Additive"};
params.push_back(new EnumFloatParameter("Algorithm",
StringArray(algos, sizeof(algos)/sizeof(String)))
);

params.push_back(new IntFloatParameter("Modulator Feedback", 0, 7));
params.push_back(new IntFloatParameter("Carrier Attack", 0, 15));
params.push_back(new IntFloatParameter("Carrier Decay", 0, 15));
params.push_back(new IntFloatParameter("Carrier Sustain Level", 0, 15));
params.push_back(new IntFloatParameter("Carrier Release", 0, 15));
params.push_back(new IntFloatParameter("Modulator Attack", 0, 15));
params.push_back(new IntFloatParameter("Modulator Decay", 0, 15));
params.push_back(new IntFloatParameter("Modulator Sustain Level", 0, 15));
params.push_back(new IntFloatParameter("Modulator Release", 0, 15));

const String sensitivitySettings[] = {"None", "Low", "High"};
params.push_back(new EnumFloatParameter("Carrier Velocity Sensitivity",
StringArray(sensitivitySettings, sizeof(sensitivitySettings)/sizeof(String)))
);
params.push_back(new EnumFloatParameter("Modulator Velocity Sensitivity",
StringArray(sensitivitySettings, sizeof(sensitivitySettings)/sizeof(String)))
);

const String emulators[] = {"DOSBox", "ZDoom"};
params.push_back(new EnumFloatParameter("Emulator",
StringArray(emulators, sizeof(emulators)/sizeof(String)))
);

const String percussion[] = { "Off", "Bass drum", "Snare", "Tom", "Cymbal", "Hi-hat" };
params.push_back(new EnumFloatParameter("Percussion Mode",
StringArray(percussion, sizeof(percussion) / sizeof(String)))
);

for(unsigned int i = 0; i < params.size(); i++) {
paramIdxByName[params[i]->getName()] = i;
}

initPrograms();

for(std::map<String,std::vector<float>>::iterator it = programs.begin(); it != programs.end(); ++it) {
program_order.push_back(it->first);
}
setCurrentProgram(0);
for (int i = 0; i < Hiopl::CHANNELS+1; i++) {
active_notes[i] = NO_NOTE;
channel_enabled[i] = true;
}
currentScaledBend = 1.0f;

for (int i = 1; i <= Hiopl::CHANNELS; ++i)
available_channels.push_back(i);
}

void AdlibBlasterAudioProcessor::initPrograms()
{
// these ones from the Syndicate in-game music
const float i_params_0[] = {
0.000000f, 0.330000f, // waveforms
0.066667f, 0.133333f, // frq multipliers
0.142857f, 0.412698f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 1.0f, 0.0f, // tre / vib / sus / ks
0.0f, 0.0f, 1.0f, 1.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.000000f, // feedback
0.5f, 0.3f, 0.3f, 0.3f, // adsr
0.5f, 0.3f, 0.1f, 0.6f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_0 (i_params_0, i_params_0 + sizeof(i_params_0) / sizeof(float));
programs["Mercenary Bass"] = std::vector<float>(v_i_params_0);

const float i_params_19189[] = {
0.000000f, 0.000000f, // waveforms
0.066667f, 0.200000f, // frq multipliers
0.000000f, 0.285714f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 0.0f, 1.0f, // tre / vib / sus / ks
0.0f, 0.0f, 0.0f, 1.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.571429f, // feedback
1.0f, 1.0f, 0.0f, 0.3f, // adsr
1.0f, 0.5f, 0.2f, 0.3f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_19189 (i_params_19189, i_params_19189 + sizeof(i_params_19189) / sizeof(float));
programs["Patrol Bass"] = std::vector<float>(v_i_params_19189);

const float i_params_38377[] = {
0.000000f, 0.160000f, // waveforms
0.066667f, 0.066667f, // frq multipliers
0.000000f, 0.460317f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.0f, 0.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.000000f, // feedback
1.0f, 0.3f, 0.5f, 0.5f, // adsr
1.0f, 0.1f, 0.9f, 1.0f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_38377 (i_params_38377, i_params_38377 + sizeof(i_params_38377) / sizeof(float));
programs["Subdue Bass"] = std::vector<float>(v_i_params_38377);

const float i_params_38392[] = {
0.000000f, 0.000000f, // waveforms
0.000000f, 0.000000f, // frq multipliers
0.000000f, 0.000000f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 1.0f, 0.0f, // tre / vib / sus / ks
0.0f, 0.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.000000f, // feedback
0.1f, 0.1f, 0.7f, 0.1f, // adsr
0.1f, 0.9f, 0.1f, 0.1f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_38392 (i_params_38392, i_params_38392 + sizeof(i_params_38392) / sizeof(float));
programs["Dark Future Sweep"] = std::vector<float>(v_i_params_38392);

const float i_params_39687[] = {
0.000000f, 0.000000f, // waveforms
0.066667f, 0.333333f, // frq multipliers
0.000000f, 0.301587f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.0f, 0.0f, 0.0f, 1.0f, // tre / vib / sus / ks
0.000000f, 1.000000f, // KSR/8ve
0.000000f, // algorithm
0.571429f, // feedback
1.0f, 0.3f, 0.1f, 0.3f, // adsr
1.0f, 0.7f, 0.0f, 0.4f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_39687 (i_params_39687, i_params_39687 + sizeof(i_params_39687) / sizeof(float));
programs["Sinister Bass"] = std::vector<float>(v_i_params_39687);

const float i_params_76784[] = {
0.000000f, 0.330000f, // waveforms
0.066667f, 0.133333f, // frq multipliers
0.000000f, 0.428571f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 1.0f, 0.0f, // tre / vib / sus / ks
0.0f, 0.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.000000f, // feedback
1.0f, 0.3f, 0.4f, 0.4f, // adsr
1.0f, 0.4f, 0.5f, 0.3f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_76784 (i_params_76784, i_params_76784 + sizeof(i_params_76784) / sizeof(float));
programs["Buzcut Bass"] = std::vector<float>(v_i_params_76784);

const float i_params_97283[] = {
0.000000f, 0.330000f, // waveforms
0.133333f, 0.400000f, // frq multipliers
0.000000f, 0.365079f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 0.0f, 1.0f, // tre / vib / sus / ks
0.0f, 0.0f, 0.0f, 1.0f, // tre / vib / sus / ks
0.000000f, 0.660000f, // KSR/8ve
0.000000f, // algorithm
0.000000f, // feedback
0.6f, 0.7f, 0.0f, 0.2f, // adsr
0.6f, 0.7f, 0.1f, 0.1f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_97283 (i_params_97283, i_params_97283 + sizeof(i_params_97283) / sizeof(float));
programs["Death Toll Bell"] = std::vector<float>(v_i_params_97283);

// The start of the Dune 2 introduction
const float i_params_3136[] = {
0.000000f, 0.330000f, // waveforms
0.133333f, 0.133333f, // frq multipliers
0.000000f, 0.333333f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.0f, 0.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.000000f, 0.330000f, // KSR/8ve
0.000000f, // algorithm
0.571429f, // feedback
1.0f, 0.1f, 0.1f, 0.3f, // adsr
1.0f, 0.4f, 0.2f, 0.3f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_3136 (i_params_3136, i_params_3136 + sizeof(i_params_3136) / sizeof(float));
programs["Westwood Chime"] = std::vector<float>(v_i_params_3136);

const float i_params_7254[] = {
0.000000f, 0.160000f, // waveforms
0.066667f, 0.066667f, // frq multipliers
0.253968f, 0.476190f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
1.0f, 1.0f, 1.0f, 1.0f, // tre / vib / sus / ks
1.0f, 1.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.000000f, 0.330000f, // KSR/8ve
0.000000f, // algorithm
0.571429f, // feedback
0.1f, 0.1f, 0.1f, 0.1f, // adsr
0.2f, 0.1f, 0.1f, 0.0f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_7254 (i_params_7254, i_params_7254 + sizeof(i_params_7254) / sizeof(float));
programs["Desert Pipe"] = std::vector<float>(v_i_params_7254);

const float i_params_20108[] = {
0.000000f, 0.000000f, // waveforms
0.400000f, 0.066667f, // frq multipliers
0.238095f, 0.000000f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
1.0f, 1.0f, 1.0f, 0.0f, // tre / vib / sus / ks
0.0f, 1.0f, 1.0f, 0.0f, // tre / vib / sus / ks
0.000000f, 0.330000f, // KSR/8ve
0.000000f, // algorithm
0.000000f, // feedback
0.1f, 0.1f, 0.1f, 0.1f, // adsr
0.1f, 0.1f, 0.1f, 0.1f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_20108 (i_params_20108, i_params_20108 + sizeof(i_params_20108) / sizeof(float));
programs["Y2180 Strings"] = std::vector<float>(v_i_params_20108);

const float i_params_27550[] = {
0.500000f, 0.000000f, // waveforms
0.000000f, 0.066667f, // frq multipliers
0.238095f, 0.793651f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 1.0f, 0.0f, 0.0f, // tre / vib / sus / ks
0.0f, 0.0f, 1.0f, 1.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.571429f, // feedback
1.0f, 0.0f, 1.0f, 1.0f, // adsr
0.9f, 0.1f, 0.0f, 1.0f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_27550 (i_params_27550, i_params_27550 + sizeof(i_params_27550) / sizeof(float));
programs["Emperor Chord"] = std::vector<float>(v_i_params_27550);

const float i_params_harpsi[] = {
0.330000f, 0.160000f, // waveforms
0.066667f, 0.200000f, // frq multipliers
0.142857f, 0.260000f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 1.0f, 0.0f, // tre / vib / sus / ks
0.0f, 1.0f, 1.0f, 1.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.000000f, // feedback
0.85f, 0.3f, 0.3f, 0.3f, // adsr
0.85f, 0.3f, 0.1f, 0.6f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_harpsi (i_params_harpsi, i_params_harpsi + sizeof(i_params_harpsi) / sizeof(float));
programs["Harpsi"] = std::vector<float>(v_i_params_harpsi);

const float i_params_tromba[] = {
0.000000f, 0.160000f, // waveforms
0.066667f, 0.000000f, // frq multipliers
0.142857f, 0.220000f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
0.0f, 0.0f, 1.0f, 0.0f, // tre / vib / sus / ks
1.0f, 0.0f, 1.0f, 1.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.500000f, // feedback
0.45f, 0.3f, 0.3f, 0.3f, // adsr
0.45f, 0.45f, 0.1f, 0.6f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_tromba (i_params_tromba, i_params_tromba + sizeof(i_params_tromba) / sizeof(float));
programs["Tromba"] = std::vector<float>(v_i_params_tromba);

const float i_params_bassdrum[] = {
0.000000f, 0.500000f, // waveforms
0.000000f, 0.000000f, // frq multipliers
0.000000f, 0.090000f, // attenuation
0.000000f, 0.000000f, // tremolo / vibrato depth
1.0f, 1.0f, 1.0f, 0.0f, // tre / vib / sus / ks
1.0f, 1.0f, 1.0f, 1.0f, // tre / vib / sus / ks
0.000000f, 0.000000f, // KSR/8ve
0.000000f, // algorithm
0.500000f, // feedback
1.00f, 0.5f, 0.3f, 0.4f, // adsr
1.00f, 0.75f, 0.5f, 0.5f, // adsr
0.0f, 0.0f, // velocity sensitivity
0.0f, // emulator
0.0f, // percussion mode
};
std::vector<float> v_i_params_bassdrum (i_params_bassdrum, i_params_bassdrum + sizeof(i_params_bassdrum) / sizeof(float));
programs["bassdrum"] = std::vector<float>(v_i_params_bassdrum);

}

void AdlibBlasterAudioProcessor::applyPitchBend()
{ // apply the currently configured pitch bend to all active notes.
for (int i = 1; i <= Hiopl::CHANNELS; i++) {
if (NO_NOTE != active_notes[i]) {
float f = (float)MidiMessage::getMidiNoteInHertz(active_notes[i]);
f *= currentScaledBend;
Opl->SetFrequency(i, f);
}
}
}

AdlibBlasterAudioProcessor::~AdlibBlasterAudioProcessor()
{
for (unsigned int i=0; i < params.size(); ++i)
delete params[i];
delete Opl;
}

//==============================================================================
const String AdlibBlasterAudioProcessor::getName() const
{
return JucePlugin_Name;
}

int AdlibBlasterAudioProcessor::getNumParameters()
{
return (int)params.size();
}

float AdlibBlasterAudioProcessor::getParameter (int index)
{
return params[index]->getParameter();
}

void AdlibBlasterAudioProcessor::setIntParameter (String name, int value)
{
int i = paramIdxByName[name];
IntFloatParameter* p = (IntFloatParameter*)params[i];
p->setParameterValue(value);
setParameter(i, p->getParameter());
}

void AdlibBlasterAudioProcessor::setEnumParameter (String name, int index)
{
int i = paramIdxByName[name];
EnumFloatParameter* p = (EnumFloatParameter*)params[i];
p->setParameterIndex(index);
setParameter(i, p->getParameter());
}

int AdlibBlasterAudioProcessor::getIntParameter (String name)
{
int i = paramIdxByName[name];
IntFloatParameter* p = (IntFloatParameter*)params[i];
return p->getParameterValue();
}

int AdlibBlasterAudioProcessor::getEnumParameter (String name)
{
int i = paramIdxByName[name];
EnumFloatParameter* p = (EnumFloatParameter*)params[i];
return p->getParameterIndex();
}

bool AdlibBlasterAudioProcessor::getBoolParameter(String name)
{
return 0 != getEnumParameter(name);
}

// Parameters which apply directly to the OPL
void AdlibBlasterAudioProcessor::setParameter (int index, float newValue)
{
FloatParameter* p = params[index];
p->setParameter(newValue);
String name = p->getName();
int osc = 2; // Carrier
if (name.startsWith("Modulator")) {
osc = 1;
}
if (name.endsWith("Wave")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetWaveform(c, osc, (Waveform)((EnumFloatParameter*)p)->getParameterIndex());
} else if (name.endsWith("Attenuation")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetAttenuation(c, osc, ((EnumFloatParameter*)p)->getParameterIndex());
} else if (name.endsWith("Frequency Multiplier")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetFrequencyMultiple(c, osc, (FreqMultiple)((EnumFloatParameter*)p)->getParameterIndex());
} else if (name.endsWith("Attack")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetEnvelopeAttack(c, osc, ((IntFloatParameter*)p)->getParameterValue());
} else if (name.endsWith("Decay")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetEnvelopeDecay(c, osc, ((IntFloatParameter*)p)->getParameterValue());
} else if (name.endsWith("Sustain Level")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetEnvelopeSustain(c, osc, ((IntFloatParameter*)p)->getParameterValue());
} else if (name.endsWith("Release")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetEnvelopeRelease(c, osc, ((IntFloatParameter*)p)->getParameterValue());
} else if (name.endsWith("Feedback")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetModulatorFeedback(c, ((IntFloatParameter*)p)->getParameterValue());
} else if (name.endsWith("Keyscale Level")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->SetKsl(c, osc, ((EnumFloatParameter*)p)->getParameterIndex());
} else if (name.endsWith("Keyscale Rate")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->EnableKsr(c, osc, ((EnumFloatParameter*)p)->getParameterIndex() > 0);
} else if (name.endsWith("Sustain")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->EnableSustain(c, osc, ((EnumFloatParameter*)p)->getParameterIndex() > 0);
} else if (name.endsWith("Tremolo")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->EnableTremolo(c, osc, ((EnumFloatParameter*)p)->getParameterIndex() > 0);
} else if (name.endsWith("Vibrato")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->EnableVibrato(c, osc, ((EnumFloatParameter*)p)->getParameterIndex() > 0);
} else if (name.endsWith("Algorithm")) {
for(int c=1;c<=Hiopl::CHANNELS;c++) Opl->EnableAdditiveSynthesis(c, ((EnumFloatParameter*)p)->getParameterIndex() > 0);
} else if (name.startsWith("Tremolo Depth")) {
Opl->TremoloDepth(((EnumFloatParameter*)p)->getParameterIndex() > 0);
} else if (name.startsWith("Vibrato Depth")) {
Opl->VibratoDepth(((EnumFloatParameter*)p)->getParameterIndex() > 0);
} else if (name.startsWith("Emulator")) {
Opl->SetEmulator((Emulator)((EnumFloatParameter*)p)->getParameterIndex());
} else if (name.startsWith("Percussion")) {
Opl->SetPercussionMode(((EnumFloatParameter*)p)->getParameterIndex() > 0);
}
}

void AdlibBlasterAudioProcessor::loadInstrumentFromFile(String filename)
{
FILE* f = fopen(filename.toUTF8(), "rb");
unsigned char buf[MAX_INSTRUMENT_FILE_SIZE_BYTES];
int n = (int)fread(buf, 1, MAX_INSTRUMENT_FILE_SIZE_BYTES, f);
fclose(f);
SbiLoader* loader = new SbiLoader();
loader->loadInstrumentData(n, buf, this);
delete loader;
updateGuiIfPresent();
}

void AdlibBlasterAudioProcessor::saveInstrumentToFile(String filename)
{
// http://www.shikadi.net/moddingwiki/SBI_Format
const Bit32u sbi_registers[] = {
0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xe0, 0xe3, 0xc0
};
FILE* f = fopen(filename.toUTF8(), "wb");
if (f) {
fwrite("SBI\x1d", 1, 4, f);
fwrite("JuceOPLVSTi instrument \0", 1, 32, f);
for (int i = 0; i < 11; i++) {
Bit8u regVal = Opl->_ReadReg(sbi_registers[i]);
fwrite(&regVal, 1, 1, f);
}
fwrite(" ", 1, 5, f);
fclose(f);
}
}

// Used to configure parameters from .SBI instrument file
void AdlibBlasterAudioProcessor::setParametersByRegister(int register_base, int op, uint8 value)
{
const String operators[] = {"Modulator", "Carrier"};
register_base &= 0xF0;
switch (register_base) {
case 0x20:
setEnumParameter(operators[op] + " Tremolo", (value & 0x80) ? 1 : 0);
setEnumParameter(operators[op] + " Vibrato", (value & 0x40) ? 1 : 0);
setEnumParameter(operators[op] + " Sustain", (value & 0x20) ? 1 : 0);
setEnumParameter(operators[op] + " Keyscale Rate", (value & 0x10) ? 1 : 0);
setEnumParameter(operators[op] + " Frequency Multiplier", value & 0x0f);
break;
case 0x40:
setEnumParameter(operators[op] + " Keyscale Level", (value & 0xc0) >> 6);
setEnumParameter(operators[op] + " Attenuation", value & 0x3f);
break;
case 0x60:
setIntParameter(operators[op] + " Attack", (value & 0xf0) >> 4);
setIntParameter(operators[op] + " Decay", value & 0x0f);
break;
case 0x80:
setIntParameter(operators[op] + " Sustain Level", (value & 0xf0) >> 4);
setIntParameter(operators[op] + " Release", value & 0x0f);
break;
case 0xC0:
setIntParameter("Modulator Feedback", (value & 0xe) >> 1);
setEnumParameter("Algorithm", value & 0x1);
break;
case 0xE0:
setEnumParameter(operators[op] + " Wave", value & 0x7);
break;
default:
break;
}
}

const String AdlibBlasterAudioProcessor::getParameterName (int index)
{
return params[index]->getName();
}

const String AdlibBlasterAudioProcessor::getParameterText (int index)
{
return params[index]->getParameterText();
}

const String AdlibBlasterAudioProcessor::getInputChannelName (int channelIndex) const
{
return String (channelIndex + 1);
}

const String AdlibBlasterAudioProcessor::getOutputChannelName (int channelIndex) const
{
return String (channelIndex + 1);
}

bool AdlibBlasterAudioProcessor::isInputChannelStereoPair (int index) const
{
return false;
}

bool AdlibBlasterAudioProcessor::isOutputChannelStereoPair (int index) const
{
return true; //// Jeff-Russ changed to true for AU version. for vsti make it false
}

bool AdlibBlasterAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}

bool AdlibBlasterAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}

bool AdlibBlasterAudioProcessor::silenceInProducesSilenceOut() const
{
return false;
}

double AdlibBlasterAudioProcessor::getTailLengthSeconds() const
{
return 0.0;
}

int AdlibBlasterAudioProcessor::getNumPrograms()
{
return (int)programs.size();
}

int AdlibBlasterAudioProcessor::getCurrentProgram()
{
return i_program;
}

void AdlibBlasterAudioProcessor::updateGuiIfPresent()
{
PluginGui* gui = (PluginGui*)getActiveEditor();
if (gui) {
gui->updateFromParameters();
}
}

void AdlibBlasterAudioProcessor::setCurrentProgram (int index)
{
if (i_program==index)
return;

i_program = index;
std::vector<float> &v_params = programs[getProgramName(index)];
for (unsigned int i = 0; i < params.size() && i < v_params.size(); i++) {
setParameter(i, v_params[i]);
}
updateGuiIfPresent();
}

const String AdlibBlasterAudioProcessor::getProgramName (int index)
{
return program_order[index];
}

void AdlibBlasterAudioProcessor::changeProgramName (int index, const String& newName)
{
}

//==============================================================================
void AdlibBlasterAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
Opl->SetSampleRate((int)sampleRate);
Opl->EnableWaveformControl();
// Use this method as the place to do any pre-playback
// initialisation that you need..
}

void AdlibBlasterAudioProcessor::releaseResources()
{
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.