mirror of https://github.com/dcoredump/dexed.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
497 lines
17 KiB
497 lines
17 KiB
/**
|
|
*
|
|
* Copyright (c) 2013 Pascal Gauthier.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include "PluginProcessor.h"
|
|
#include "PluginEditor.h"
|
|
#include "GlobalEditor.h"
|
|
#include "ParamDialog.h"
|
|
#include "SysexComm.h"
|
|
#include "Dexed.h"
|
|
#include "math.h"
|
|
#include "DXLookNFeel.h"
|
|
#include <fstream>
|
|
|
|
#include "msfa/fm_op_kernel.h"
|
|
|
|
using namespace ::std;
|
|
|
|
class AboutBox : public DialogWindow {
|
|
public:
|
|
Image about_png;
|
|
|
|
AboutBox(Component *parent) : DialogWindow("About", Colour(0xFF000000), true) {
|
|
setUsingNativeTitleBar(false);
|
|
setAlwaysOnTop(true);
|
|
about_png = ImageCache::getFromMemory(BinaryData::about_png, BinaryData::about_pngSize);
|
|
setSize( about_png.getWidth(), about_png.getHeight());
|
|
centreAroundComponent (parent, getWidth(), getHeight());
|
|
}
|
|
|
|
void closeButtonPressed() {
|
|
setVisible (false);
|
|
}
|
|
|
|
void paint(Graphics &g) {
|
|
g.drawImage (about_png, 0, 0, about_png.getWidth(), about_png.getHeight(),
|
|
0, 0, about_png.getWidth(), about_png.getHeight());
|
|
g.setColour(Colour(0xFF000000));
|
|
String ver("Version " DEXED_VERSION " ; built date " __DATE__ );
|
|
g.drawSingleLineText(ver, 9, 118);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Ugly but usefull midi monitor to know if you are really sending/receiving something from the DX7
|
|
* If the midi is not configured this component wont show up
|
|
*/
|
|
class MidiMonitor : public Component {
|
|
SysexComm *midi;
|
|
public:
|
|
MidiMonitor(SysexComm *sysexComm) {
|
|
midi = sysexComm;
|
|
}
|
|
|
|
void paint(Graphics &g) {
|
|
if ( ! (midi->isInputActive() || midi->isOutputActive() ) )
|
|
return;
|
|
|
|
g.setColour(DXLookNFeel::dxDarkBrown);
|
|
g.fillRect(0, 0, getWidth(), getHeight());
|
|
g.setColour(Colours::black);
|
|
g.drawSingleLineText("DX7 ", 0, 13);
|
|
|
|
if ( midi->isInputActive() ) {
|
|
g.drawSingleLineText("IN", 27,13);
|
|
if ( midi->inActivity ) {
|
|
g.setColour(Colours::red);
|
|
} else {
|
|
g.setColour(Colours::darkgrey);
|
|
}
|
|
g.fillRect(44, 4, 7, 9);
|
|
|
|
midi->inActivity = false;
|
|
}
|
|
|
|
if ( midi->isOutputActive() ) {
|
|
g.setColour(Colours::black);
|
|
g.drawSingleLineText("OUT", 55, 13);
|
|
if ( midi->outActivity ) {
|
|
g.setColour(Colours::red);
|
|
} else {
|
|
g.setColour(Colours::darkgrey);
|
|
}
|
|
g.fillRect(83, 4, 7, 9);
|
|
|
|
midi->outActivity = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* ownerFilter)
|
|
: AudioProcessorEditor (ownerFilter),
|
|
midiKeyboard (ownerFilter->keyboardState, MidiKeyboardComponent::horizontalKeyboard)
|
|
{
|
|
LookAndFeel::setDefaultLookAndFeel(&dx_lnf);
|
|
|
|
setSize (866, 420);
|
|
|
|
processor = ownerFilter;
|
|
|
|
addAndMakeVisible (cartButton = new TextButton("CART"));
|
|
cartButton->setButtonText ("CART");
|
|
cartButton->addListener (this);
|
|
cartButton->setBounds(5, 6, 50, 18);
|
|
|
|
addAndMakeVisible (loadButton = new TextButton("LOAD"));
|
|
loadButton->setButtonText ("LOAD");
|
|
loadButton->addListener(this);
|
|
loadButton->setBounds(59, 6, 50, 18);
|
|
|
|
addAndMakeVisible(saveButton = new TextButton("SAVE"));
|
|
saveButton->setButtonText ("SAVE");
|
|
saveButton->addListener (this);
|
|
saveButton->setBounds(113, 6, 50, 18);
|
|
|
|
addAndMakeVisible (&programs);
|
|
programs.setEditableText(false);
|
|
programs.setJustificationType (Justification::centredLeft);
|
|
programs.setTextWhenNothingSelected (String::empty);
|
|
programs.setBounds(167, 6, 160, 18);
|
|
rebuildProgramCombobox();
|
|
programs.addListener(this);
|
|
|
|
addAndMakeVisible(storeButton = new TextButton("STORE"));
|
|
storeButton->setButtonText("STORE");
|
|
storeButton->addListener(this);
|
|
storeButton->setBounds(331, 6, 50, 18);
|
|
|
|
addAndMakeVisible(initButton = new TextButton("INIT"));
|
|
initButton->setButtonText("INIT");
|
|
initButton->addListener(this);
|
|
initButton->setBounds(385, 6, 50, 18);
|
|
|
|
addAndMakeVisible(monoButton = new ToggleButton("MONO"));
|
|
monoButton->setButtonText("MONO");
|
|
monoButton->addListener(this);
|
|
monoButton->setBounds(439, 6, 50, 18);
|
|
|
|
addAndMakeVisible(sendButton = new TextButton("SEND"));
|
|
sendButton->setVisible(false);
|
|
sendButton->setButtonText("SEND");
|
|
sendButton->addListener(this);
|
|
sendButton->setBounds(493, 6, 50, 18);
|
|
sendButton->setVisible(processor->sysexComm.isOutputActive());
|
|
|
|
addAndMakeVisible(midiMonitor = new MidiMonitor(&processor->sysexComm));
|
|
midiMonitor->setBounds(645, 6, 110, 18);
|
|
|
|
addAndMakeVisible(settingsButton = new TextButton("PARMS"));
|
|
settingsButton->setButtonText("PARMS");
|
|
settingsButton->addListener(this);
|
|
settingsButton->setBounds(754, 6, 50, 18);
|
|
|
|
addAndMakeVisible(aboutButton = new TextButton("ABOUT"));
|
|
aboutButton->setButtonText("ABOUT");
|
|
aboutButton->addListener(this);
|
|
aboutButton->setBounds(807, 6, 50, 18);
|
|
|
|
// OPERATORS
|
|
addAndMakeVisible(&(operators[0]));
|
|
operators[0].setBounds(7, 38, 280, 90);
|
|
operators[0].bind(processor, 0);
|
|
|
|
addAndMakeVisible(&(operators[1]));
|
|
operators[1].setBounds(294, 38, 280, 90);
|
|
operators[1].bind(processor, 1);
|
|
|
|
addAndMakeVisible(&(operators[2]));
|
|
operators[2].setBounds(579, 38, 280, 90);
|
|
operators[2].bind(processor, 2);
|
|
|
|
addAndMakeVisible(&(operators[3]));
|
|
operators[3].setBounds(7, 133, 280, 90);
|
|
operators[3].bind(processor, 3);
|
|
|
|
addAndMakeVisible(&(operators[4]));
|
|
operators[4].setBounds(294, 133, 280, 90);
|
|
operators[4].bind(processor, 4);
|
|
|
|
addAndMakeVisible(&(operators[5]));
|
|
operators[5].setBounds(579, 133, 280, 90);
|
|
operators[5].bind(processor, 5);
|
|
|
|
// add the midi keyboard component..
|
|
addAndMakeVisible (&midiKeyboard);
|
|
|
|
// The DX7 is a badass on the bass, keep it that way
|
|
midiKeyboard.setLowestVisibleKey(24);
|
|
|
|
const int keyboardHeight = 90;
|
|
midiKeyboard.setBounds (4, getHeight() - keyboardHeight - 4, getWidth() - 8, keyboardHeight);
|
|
|
|
addAndMakeVisible(&global);
|
|
global.setBounds(5,235,855,90);
|
|
global.bind(processor);
|
|
|
|
sendPopup.addItem(1, "Send program to DX7");
|
|
sendPopup.addItem(2, "Send cartridge to DX7");
|
|
|
|
monoButton->setState(processor->isMonoMode() ? Button::ButtonState::buttonDown : Button::ButtonState::buttonNormal);
|
|
|
|
updateUI();
|
|
startTimer(100);
|
|
}
|
|
|
|
DexedAudioProcessorEditor::~DexedAudioProcessorEditor() {
|
|
stopTimer();
|
|
processor->unbindUI();
|
|
}
|
|
|
|
//==============================================================================
|
|
void DexedAudioProcessorEditor::paint (Graphics& g) {
|
|
g.setColour(DXLookNFeel::background);
|
|
g.fillRoundedRectangle(0.0f, 0.0f, (float) getWidth(), (float) getHeight(), 0);
|
|
}
|
|
|
|
void DexedAudioProcessorEditor::buttonClicked(Button *buttonThatWasClicked) {
|
|
if (buttonThatWasClicked == cartButton) {
|
|
int result = processor->cartManager.getCarts()->show();
|
|
if ( result < 1 )
|
|
return;
|
|
|
|
processor->loadBuiltin(result-1);
|
|
processor->setCurrentProgram(0);
|
|
rebuildProgramCombobox();
|
|
programs.setSelectedId(processor->getCurrentProgram()+1, dontSendNotification);
|
|
processor->updateHostDisplay();
|
|
return;
|
|
}
|
|
|
|
if (buttonThatWasClicked == loadButton) {
|
|
FileChooser fc ("Import original DX sysex...", File::nonexistent, "*.syx;*.SYX;*.*", 1);
|
|
|
|
if ( fc.browseForFileToOpen()) {
|
|
String f = fc.getResults().getReference(0).getFullPathName();
|
|
uint8_t syx_data[4104];
|
|
ifstream fp_in(f.toRawUTF8(), ios::binary);
|
|
if (fp_in.fail()) {
|
|
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
|
"Error",
|
|
"Unable to open: " + f);
|
|
return;
|
|
}
|
|
fp_in.read((char *)syx_data, 4104);
|
|
fp_in.close();
|
|
if ( processor->importSysex((char *) &syx_data) ) {
|
|
global.setSystemMessage(String("Unkown sysex format !?"));
|
|
}
|
|
processor->setCurrentProgram(0);
|
|
rebuildProgramCombobox();
|
|
programs.setSelectedId(processor->getCurrentProgram()+1, dontSendNotification);
|
|
processor->updateHostDisplay();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (buttonThatWasClicked == saveButton) {
|
|
FileChooser fc ("Export DX sysex...", File::nonexistent, "*.syx", 1);
|
|
if ( fc.browseForFileToSave(true) ) {
|
|
String f = fc.getResults().getReference(0).getFullPathName();
|
|
char syx_data[4104];
|
|
|
|
exportSysexCart((char *) syx_data, (char *) &processor->sysex, 0);
|
|
|
|
ofstream fp_out(f.toRawUTF8(), ios::binary);
|
|
fp_out.write((char *)syx_data, 4104);
|
|
fp_out.close();
|
|
|
|
if (fp_out.fail()) {
|
|
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
|
"Error",
|
|
"Unable to write: " + f);
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (buttonThatWasClicked == storeButton) {
|
|
storeProgram();
|
|
return;
|
|
}
|
|
|
|
if (buttonThatWasClicked == sendButton) {
|
|
int result = sendPopup.show();
|
|
|
|
if ( result == 1 ) {
|
|
uint8_t raw[167];
|
|
|
|
exportSysexPgm((char *) raw, processor->data, processor->sysexComm.getChl());
|
|
if ( processor->sysexComm.isOutputActive() ) {
|
|
processor->sysexComm.send(MidiMessage(raw, 163));
|
|
}
|
|
global.setSystemMessage(String("Done sending program"));
|
|
return;
|
|
}
|
|
|
|
if ( result == 2 ) {
|
|
uint8_t raw[4104];
|
|
|
|
exportSysexCart((char *) raw, (char *) &processor->sysex, processor->sysexComm.getChl());
|
|
if ( processor->sysexComm.isOutputActive() ) {
|
|
processor->sysexComm.send(MidiMessage(raw, 4104));
|
|
}
|
|
global.setSystemMessage(String("Done sending cartridge"));
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (buttonThatWasClicked == settingsButton) {
|
|
int tp = processor->getEngineType();
|
|
|
|
AlertWindow window("","", AlertWindow::NoIcon, this);
|
|
ParamDialog param;
|
|
param.setColour(AlertWindow::backgroundColourId, Colour(0x32FFFFFF));
|
|
param.setDialogValues(processor->controllers, processor->sysexComm, tp);
|
|
|
|
window.addCustomComponent(¶m);
|
|
window.addButton("OK", 0);
|
|
window.addButton("Cancel" ,1);
|
|
if ( window.runModalLoop() != 0 )
|
|
return;
|
|
|
|
bool ret = param.getDialogValues(processor->controllers, processor->sysexComm, &tp);
|
|
processor->setEngineType(tp);
|
|
processor->savePreference();
|
|
|
|
if ( ret == false ) {
|
|
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Midi Interface", "Error opening midi ports");
|
|
}
|
|
|
|
sendButton->setVisible(processor->sysexComm.isOutputActive());
|
|
return;
|
|
}
|
|
|
|
if (buttonThatWasClicked == initButton ) {
|
|
processor->resetToInitVoice();
|
|
return;
|
|
}
|
|
|
|
if (buttonThatWasClicked == monoButton ) {
|
|
processor->setMonoMode(monoButton->getToggleState());
|
|
monoButton->setState(processor->isMonoMode() ? Button::ButtonState::buttonDown : Button::ButtonState::buttonNormal);
|
|
return;
|
|
}
|
|
|
|
if (buttonThatWasClicked == aboutButton) {
|
|
AboutBox about(this);
|
|
about.runModalLoop();
|
|
return;
|
|
}
|
|
|
|
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Sorry", "Soon !");
|
|
}
|
|
|
|
void DexedAudioProcessorEditor::comboBoxChanged (ComboBox* comboBoxThatHasChanged) {
|
|
processor->setCurrentProgram(programs.getSelectedId()-1);
|
|
processor->updateHostDisplay();
|
|
}
|
|
|
|
void DexedAudioProcessorEditor::timerCallback() {
|
|
if ( processor->forceRefreshUI ) {
|
|
processor->forceRefreshUI = false;
|
|
updateUI();
|
|
}
|
|
|
|
if ( ! processor->peekVoiceStatus() )
|
|
return;
|
|
|
|
for(int i=0;i<6;i++) {
|
|
operators[i].updateGain(sqrt(processor->voiceStatus.amp[5 - i]) / 8196); // TODO: FUGLY !!!! change this sqrt nonsens
|
|
operators[i].updateEnvPos(processor->voiceStatus.ampStep[5 - i]);
|
|
}
|
|
global.updatePitchPos(processor->voiceStatus.pitchStep);
|
|
global.updateVu(processor->vuSignal);
|
|
midiMonitor->repaint();
|
|
}
|
|
|
|
void DexedAudioProcessorEditor::updateUI() {
|
|
for(int i=0;i<processor->ctrl.size();i++) {
|
|
processor->ctrl[i]->updateComponent();
|
|
}
|
|
for(int i=0;i<6;i++) {
|
|
operators[i].updateDisplay();
|
|
}
|
|
rebuildProgramCombobox();
|
|
global.updateDisplay();
|
|
}
|
|
|
|
void DexedAudioProcessorEditor::rebuildProgramCombobox() {
|
|
programs.clear(dontSendNotification);
|
|
for(int i=0;i<processor->getNumPrograms();i++) {
|
|
String id;
|
|
id << (i+1) << ". " << processor->getProgramName(i);
|
|
programs.addItem(id, i+1);
|
|
}
|
|
programs.setSelectedId(processor->getCurrentProgram()+1, dontSendNotification);
|
|
}
|
|
|
|
void DexedAudioProcessorEditor::storeProgram() {
|
|
String currentName(processor->getProgramName(processor->getCurrentProgram()));
|
|
char destSysex[4096];
|
|
File *externalFile = NULL;
|
|
|
|
memcpy(&destSysex, processor->sysex, 4096);
|
|
|
|
while (true) {
|
|
String msg;
|
|
if ( externalFile == NULL ) {
|
|
msg = "Store program to current cartridge";
|
|
} else {
|
|
msg = "Store program to " + externalFile->getFileName();
|
|
}
|
|
|
|
AlertWindow dialog(String("Store Program"), msg, AlertWindow::NoIcon, this);
|
|
dialog.addTextEditor(String("Name"), currentName, String("Name"), false);
|
|
// TODO: fix the name length to 10
|
|
|
|
StringArray programs;
|
|
extractProgramNames((char *) &destSysex, programs);
|
|
|
|
dialog.addComboBox(String("Dest"), programs);
|
|
dialog.addButton("OK", 0, KeyPress(KeyPress::returnKey));
|
|
dialog.addButton("Cancel", 1, KeyPress(KeyPress::escapeKey));
|
|
dialog.addButton("External File", 2, KeyPress());
|
|
int response = dialog.runModalLoop();
|
|
|
|
if ( response == 2 ) {
|
|
FileChooser fc("Destination Sysex", File::nonexistent, "*.syx;*.SYX;*.*", 1);
|
|
|
|
if ( fc.browseForFileToOpen() ) {
|
|
if ( externalFile != NULL )
|
|
delete externalFile;
|
|
|
|
MemoryBlock block;
|
|
externalFile = new File(fc.getResults().getReference(0));
|
|
if ( externalFile->loadFileAsData(block) ) {
|
|
block.copyTo(destSysex, 6, 4096);
|
|
continue;
|
|
}
|
|
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Read error", "Unable to read file");
|
|
}
|
|
}
|
|
|
|
if ( response == 0 ) {
|
|
TextEditor *name = dialog.getTextEditor(String("Name"));
|
|
ComboBox *dest = dialog.getComboBoxComponent(String("Dest"));
|
|
|
|
int programNum = dest->getSelectedItemIndex();
|
|
String programName(name->getText());
|
|
if ( programName.length() > 10 ) {
|
|
int toStrip = programName.length() - 10;
|
|
programName = programName.dropLastCharacters(toStrip);
|
|
}
|
|
|
|
if ( externalFile == NULL ) {
|
|
packProgram((uint8_t *) processor->sysex, (uint8_t *) processor->data, programNum, programName);
|
|
processor->programNames.set(programNum, programName);
|
|
rebuildProgramCombobox();
|
|
processor->setCurrentProgram(programNum);
|
|
processor->updateHostDisplay();
|
|
} else {
|
|
packProgram((uint8_t *) &destSysex, (uint8_t *) processor->data, programNum, programName);
|
|
char sysexFile[4104];
|
|
exportSysexCart((char *) &sysexFile, (char *) &destSysex, 0);
|
|
if ( ! externalFile->replaceWithData(sysexFile, 4104) ) {
|
|
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Write error", "Unable to write file");
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ( externalFile != NULL )
|
|
delete externalFile;
|
|
}
|
|
|