From a1034a065e52d1953b3f7e0a753b30210b99a901 Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Fri, 10 Jun 2016 18:07:19 +0300 Subject: [PATCH] Another update - Added more chords - Fixed window size - Ability to reset instrument state to previous - States of Deep Tremolo and Deep Vibrato, and recently opened/saved file now are remembering - Ability to drop file into the window to open it - Ability to open file from command line argument - Better poliphony: now used all 18 2-operator channels and 6 4-operator channels - Question on saving a modified file --- FileFormats/junlevizion.cpp | 2 - audio.cpp | 15 +- bank.cpp | 16 +- bank.h | 2 + bank_editor.cpp | 215 +++- bank_editor.h | 22 + bank_editor.ui | 1952 ++++++++++++++++++----------------- main.cpp | 5 + opl/generator.cpp | 202 +++- opl/generator.h | 9 +- version.h | 7 +- 11 files changed, 1378 insertions(+), 1069 deletions(-) diff --git a/FileFormats/junlevizion.cpp b/FileFormats/junlevizion.cpp index 52574d7..d5cfdba 100644 --- a/FileFormats/junlevizion.cpp +++ b/FileFormats/junlevizion.cpp @@ -1,5 +1,3 @@ - -#include /* * OPL Bank Editor by Wohlstand, a free tool for music bank editing * Copyright (c) 2016 Vitaly Novichkov diff --git a/audio.cpp b/audio.cpp index 89912da..d3093b2 100644 --- a/audio.cpp +++ b/audio.cpp @@ -53,6 +53,20 @@ void BankEditor::initAudio() connect(ui->testMinor, SIGNAL(pressed()), m_generator, SLOT(PlayMinorChord())); connect(ui->testMinor, SIGNAL(released()), m_generator, SLOT(NoteOffAllChans())); + connect(ui->testAugmented, SIGNAL(pressed()), m_generator, SLOT(PlayAugmentedChord())); + connect(ui->testAugmented, SIGNAL(released()), m_generator, SLOT(NoteOffAllChans())); + + connect(ui->testDiminished, SIGNAL(pressed()), m_generator, SLOT(PlayDiminishedChord())); + connect(ui->testDiminished, SIGNAL(released()), m_generator, SLOT(NoteOffAllChans())); + + connect(ui->testMajor7, SIGNAL(pressed()), m_generator, SLOT(PlayMajor7Chord())); + connect(ui->testMajor7, SIGNAL(released()), m_generator, SLOT(NoteOffAllChans())); + + connect(ui->testMinor7, SIGNAL(pressed()), m_generator, SLOT(PlayMinor7Chord())); + connect(ui->testMinor7, SIGNAL(released()), m_generator, SLOT(NoteOffAllChans())); + + connect(ui->shutUp, SIGNAL(clicked()), m_generator, SLOT(Silence())); + connect(ui->noteToTest, SIGNAL(valueChanged(int)), m_generator, SLOT(changeNote(int))); m_generator->changeNote(ui->noteToTest->value()); @@ -68,7 +82,6 @@ void BankEditor::initAudio() m_pushTimer.start(4); } - void BankEditor::pushTimerExpired() { if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) diff --git a/bank.cpp b/bank.cpp index 8ad9b19..e627bb9 100644 --- a/bank.cpp +++ b/bank.cpp @@ -26,11 +26,25 @@ FmBank::FmBank() FmBank::FmBank(const FmBank &fb) { - int size=sizeof(Instrument)*128; + int size = sizeof(Instrument)*128; memcpy(Ins_Melodic, fb.Ins_Melodic, size); memcpy(Ins_Percussion, fb.Ins_Percussion, size); } +bool FmBank::operator==(const FmBank &fb) +{ + int size = sizeof(Instrument)*128; + bool res = true; + res &= (memcmp(Ins_Melodic, fb.Ins_Melodic, size)==0); + res &= (memcmp(Ins_Percussion, fb.Ins_Percussion, size)==0); + return res; +} + +bool FmBank::operator!=(const FmBank &fb) +{ + return !this->operator==(fb); +} + void FmBank::reset() { int size=sizeof(Instrument)*128; diff --git a/bank.h b/bank.h index 3fff0cd..992caae 100644 --- a/bank.h +++ b/bank.h @@ -32,6 +32,8 @@ class FmBank public: FmBank(); FmBank(const FmBank &fb); + bool operator==(const FmBank &fb); + bool operator!=(const FmBank &fb); /** * @brief Set everything to zero diff --git a/bank_editor.cpp b/bank_editor.cpp index 1b7bb00..063cd92 100644 --- a/bank_editor.cpp +++ b/bank_editor.cpp @@ -18,6 +18,9 @@ #include #include +#include + +#include #include "bank_editor.h" #include "ui_bank_editor.h" @@ -31,6 +34,7 @@ BankEditor::BankEditor(QWidget *parent) : { memset(&m_clipboard, 0, sizeof(FmBank::Instrument)); m_curInst = NULL; + m_curInstBackup = NULL; m_lock = false; ui->setupUi(this); @@ -43,7 +47,13 @@ BankEditor::BankEditor(QWidget *parent) : m_buffer.resize(8192); m_buffer.fill(0, 8192); + + this->setWindowFlags(Qt::WindowTitleHint|Qt::WindowSystemMenuHint| + Qt::WindowCloseButtonHint|Qt::WindowMinimizeButtonHint); + this->setFixedSize(this->window()->width(), this->window()->height()); + initAudio(); + loadSettings(); } BankEditor::~BankEditor() @@ -56,63 +66,165 @@ BankEditor::~BankEditor() delete ui; } +void BankEditor::loadSettings() +{ + QApplication::setOrganizationName(_COMPANY); + QApplication::setOrganizationDomain(_PGE_URL); + QApplication::setApplicationName("OPL FM Banks Editor"); + QSettings setup; + ui->deepTremolo->setChecked(setup.value("deep-tremolo", false).toBool()); + ui->deepVibrato->setChecked(setup.value("deep-vibrato", false).toBool()); + m_recentPath = setup.value("recent-path").toString(); +} +void BankEditor::saveSettings() +{ + QSettings setup; + setup.setValue("deep-tremolo", ui->deepTremolo->isChecked()); + setup.setValue("deep-vibrato", ui->deepVibrato->isChecked()); + setup.setValue("recent-path", m_recentPath); +} -void BankEditor::on_actionNew_triggered() +void BankEditor::closeEvent(QCloseEvent *event) { - ui->currentFile->setText(tr("")); - m_bank.reset(); - on_instruments_currentItemChanged(NULL, NULL); + if(m_bank != m_bankBackup) + { + QMessageBox::StandardButton res = QMessageBox::question(this, tr("File is not saved"), tr("File is modified and not saved. Do you want to save it?"), QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel); + if((res==QMessageBox::Cancel) || (res==QMessageBox::NoButton)) + { + event->ignore(); + return; + } + else + if(res==QMessageBox::Yes) + { + if(!saveFileAs()) + { + event->ignore(); + return; + } + } + } + saveSettings(); } -void BankEditor::on_actionOpen_triggered() +void BankEditor::dragEnterEvent(QDragEnterEvent *e) { - QString fileToOpen; - fileToOpen = QFileDialog::getOpenFileName(this, "Open bank file", m_recentPath, "JunleVision bank (*.op3)"); - if(fileToOpen.isEmpty()) - return; + if (e->mimeData()->hasUrls()) + { + e->acceptProposedAction(); + } +} + +void BankEditor::dropEvent(QDropEvent *e) +{ + this->raise(); + this->setFocus(Qt::ActiveWindowFocusReason); + + foreach(const QUrl &url, e->mimeData()->urls()) + { + const QString &fileName = url.toLocalFile(); + if(openFile(fileName)) + break; //Only first valid file! + } +} + + +bool BankEditor::openFile(QString filePath) +{ + if(filePath.endsWith("op3", Qt::CaseInsensitive)) + { + int err = JunleVizion::loadFile(filePath, m_bank); + if(err != JunleVizion::ERR_OK) + { + QString errText; + switch(err) + { + case JunleVizion::ERR_BADFORMAT: errText = "Bad file format"; break; + case JunleVizion::ERR_NOFILE: errText = "Can't open file"; break; + } + QMessageBox::warning(this, "Can't open bank file!", "Can't open bank file because "+errText, QMessageBox::Ok); + return false; + } else { + m_recentPath = filePath; + if(!ui->instruments->selectedItems().isEmpty()) + on_instruments_currentItemChanged(ui->instruments->selectedItems().first(), NULL); + else + on_instruments_currentItemChanged(NULL, NULL); + ui->currentFile->setText(filePath); + m_bankBackup = m_bank; + return true; + } + } + return false; +} - int err = JunleVizion::loadFile(fileToOpen, m_bank); - if(err != JunleVizion::ERR_OK) +bool BankEditor::saveFile(QString filePath) +{ + if(filePath.endsWith("op3", Qt::CaseInsensitive)) { - QString errText; - switch(err) + int err = JunleVizion::saveFile(filePath, m_bank); + if(err != JunleVizion::ERR_OK) { - case JunleVizion::ERR_BADFORMAT: errText = "Bad file format"; break; - case JunleVizion::ERR_NOFILE: errText = "Can't open file"; break; + QString errText; + switch(err) + { + case JunleVizion::ERR_NOFILE: errText = "Can't open file for write!"; break; + } + QMessageBox::warning(this, "Can't save bank file!", "Can't save bank file because "+errText, QMessageBox::Ok); + return false; + } else { + ui->currentFile->setText(filePath); + m_recentPath = filePath; + m_bankBackup = m_bank; + return true; } - QMessageBox::warning(this, "Can't open bank file!", "Can't open bank file because "+errText, QMessageBox::Ok); - } else { - m_recentPath = fileToOpen; - if(!ui->instruments->selectedItems().isEmpty()) - on_instruments_currentItemChanged(ui->instruments->selectedItems().first(), NULL); - else - on_instruments_currentItemChanged(NULL, NULL); - ui->currentFile->setText(fileToOpen); } + return false; } -void BankEditor::on_actionSave_triggered() +bool BankEditor::saveFileAs() { QString fileToSave; fileToSave = QFileDialog::getSaveFileName(this, "Save bank file", m_recentPath, "JunleVision bank (*.op3)"); if(fileToSave.isEmpty()) - return; + return false; + return saveFile(fileToSave); +} - int err = JunleVizion::saveFile(fileToSave, m_bank); - if(err != JunleVizion::ERR_OK) +void BankEditor::flushInstrument() +{ + loadInstrument(); + if( m_curInst && ui->percussion->isChecked() ) { - QString errText; - switch(err) - { - case JunleVizion::ERR_NOFILE: errText = "Can't open file for write!"; break; - } - QMessageBox::warning(this, "Can't save bank file!", "Can't save bank file because "+errText, QMessageBox::Ok); - } else { - ui->currentFile->setText(fileToSave); + ui->noteToTest->setValue( m_curInst->percNoteNum ); } + sendPatch(); +} + +void BankEditor::on_actionNew_triggered() +{ + ui->currentFile->setText(tr("")); + m_bank.reset(); + m_bankBackup.reset(); + on_instruments_currentItemChanged(NULL, NULL); +} + +void BankEditor::on_actionOpen_triggered() +{ + QString fileToOpen; + fileToOpen = QFileDialog::getOpenFileName(this, "Open bank file", m_recentPath, "JunleVision bank (*.op3)"); + if(fileToOpen.isEmpty()) + return; + + openFile(fileToOpen); +} + +void BankEditor::on_actionSave_triggered() +{ + saveFileAs(); } void BankEditor::on_actionExit_triggered() @@ -132,13 +244,27 @@ void BankEditor::on_actionPaste_triggered() if(!m_curInst) return; memcpy(m_curInst, &m_clipboard, sizeof(FmBank::Instrument)); + flushInstrument(); +} - loadInstrument(); - if( m_curInst && ui->percussion->isChecked() ) +void BankEditor::on_actionReset_current_instrument_triggered() +{ + if(!m_curInstBackup || !m_curInst) + return; //Some pointer is Null!!! + + if( memcmp(m_curInst, m_curInstBackup, sizeof(FmBank::Instrument))==0 ) + return; //Nothing to do + + if( QMessageBox::Yes == QMessageBox::question(this, + tr("Reset instrument to initial state"), + tr("This instrument will be reseted to initial state " + "(sice this file loaded or saved).\n" + "Are you wish to continue?"), + QMessageBox::Yes|QMessageBox::No) ) { - ui->noteToTest->setValue( m_curInst->percNoteNum ); + memcpy(m_curInst, m_curInstBackup, sizeof(FmBank::Instrument)); + flushInstrument(); } - sendPatch(); } @@ -165,18 +291,14 @@ void BankEditor::on_instruments_currentItemChanged(QListWidgetItem *current, QLi ui->curInsInfo->setText(QString("%1 - %2").arg(current->data(Qt::UserRole).toInt()).arg(current->text())); setCurrentInstrument(current->data(Qt::UserRole).toInt(), ui->percussion->isChecked() ); } - loadInstrument(); - if( m_curInst && ui->percussion->isChecked() ) - { - ui->noteToTest->setValue( m_curInst->percNoteNum ); - } - sendPatch(); + flushInstrument(); } void BankEditor::setCurrentInstrument(int num, bool isPerc) { m_curInst = isPerc ? &m_bank.Ins_Percussion[num] : &m_bank.Ins_Melodic[num]; + m_curInstBackup = isPerc ? &m_bankBackup.Ins_Percussion[num] : &m_bankBackup.Ins_Melodic[num]; } void BankEditor::loadInstrument() @@ -319,3 +441,6 @@ void BankEditor::setDrums() } } + + + diff --git a/bank_editor.h b/bank_editor.h index 4745287..1b12f3c 100644 --- a/bank_editor.h +++ b/bank_editor.h @@ -39,6 +39,9 @@ class BankEditor : public QMainWindow explicit BankEditor(QWidget *parent = 0); ~BankEditor(); + void loadSettings(); + void saveSettings(); + void initAudio(); //! Path for currently opened file @@ -47,16 +50,30 @@ class BankEditor : public QMainWindow //! Currently loaded FM bank FmBank m_bank; + //! Backup of currently loaded FM bank + FmBank m_bankBackup; + //! Backup for melodic note while percusive mode is enabled int m_recentMelodicNote; //! Currently selected instrument FmBank::Instrument* m_curInst; + //! Currently selected instrument + FmBank::Instrument* m_curInstBackup; + //! Clipboard FmBank::Instrument m_clipboard; + bool openFile(QString filePath); + bool saveFile(QString filePath); + bool saveFileAs(); + /* ************** Help functions ************** */ + /** + * @brief Loads current instrument into GUI controlls and sends it to generator + */ + void flushInstrument(); /** * @brief Sets current instrument to editand test * @param num Number of instrument (from 0 to 127) @@ -207,7 +224,12 @@ private slots: void on_op4_eg_toggled(bool checked); void on_op4_ksr_toggled(bool checked); + void on_actionReset_current_instrument_triggered(); +protected: + void closeEvent(QCloseEvent *event); + void dragEnterEvent(QDragEnterEvent *e); + void dropEvent(QDropEvent *e); private: Ui::BankEditor *ui; diff --git a/bank_editor.ui b/bank_editor.ui index 35f70d9..eccc163 100644 --- a/bank_editor.ui +++ b/bank_editor.ui @@ -7,9 +7,12 @@ 0 0 920 - 774 + 775 + + true + Bank editor for Yamaha OPL3/OPL2 sound generator @@ -75,7 +78,7 @@ 240 50 541 - 601 + 600 @@ -84,971 +87,901 @@ QFrame::Raised - - - - 260 - 330 - 281 - 17 - - - - Enable 4-operator / Double-voice mode - - - - - false - - - - 280 - 360 - 253 - 231 - - - - Modulator 2 - - - - - - 15 - - - - - - - 15 - - - - - - - Sustain - - - - - - - 15 - - - - - - - Release - - - - - - - Wave shape: - - - - - + + + + + Feedback 1 + + + + + + + 7 + + + + + + + Connection 1 + + - - 0 - Sine - + + + + 0 + 18 + + + + AM + + - - 1 - Half-Sine - + + + + 0 + 18 + + + + FM + + + true + + - - - 2 - Absolute Sine - + + + + + + + Percussion + + + + + + Note number: + + - - - 3 - Quarter-Sine - + + + + 127 + + - - - 4 - 1/Sine, Sielence - + + + + + + + Carrier 1 + + + + + + Release + + - - - 5 - 1/Ans-Sine, Sielence - + + + + 15 + + - - - 6 - Square - + + + + 15 + + - - - 7 - Saw - - - - - - - - Tremolo (AM) - - - - - - - Sustaining voice (EG) - - - - - - - Vibrato (VIB) - - - - - - - Envelope scale (KSR) - - - - - - - Frequency multiplication - - - - - - - 15 - - - - - - - Attack - - - - - - - Decay - - - - - - - 15 - - - - - - - 63 - - - - - - - Level: - - - - - - - 3 - - - - - - - Key Scale Level - - - - - - - - - 10 - 60 - 253 - 231 - - - - Carrier 1 - - - - - - Release - - - - - - - 15 - - - - - - - 15 - - - - - - - Sustaining voice (EG) - - - - - - - 15 - - - - - - - 15 - - - - - - - Sustain - - - - - - - Wave shape: - - - - - - - 15 - - - - - - - Frequency multiplication - - - - - - - Envelope scale (KSR) - - - - - - - Tremolo (AM) - - - - - - - - 0 - Sine - + + + + Sustaining voice (EG) + + - - - 1 - Half-Sine - + + + + 15 + + - - - 2 - Absolute Sine - + + + + 15 + + - - - 3 - Quarter-Sine - + + + + Sustain + + - - - 4 - 1/Sine, Sielence - + + + + Wave shape: + + - - - 5 - 1/Ans-Sine, Sielence - + + + + 15 + + - - - 6 - Square - + + + + Frequency multiplication + + - - - 7 - Saw - - - - - - - - Vibrato (VIB) - - - - - - - Attack - - - - - - - Decay - - - - - - - 63 - - - - - - - Level: - - - - - - - 3 - - - - - - - Key Scale Level - - - - - - - - false - - - - 130 - 310 - 121 - 50 - - - - Connection 2 - - - - - - - 0 - 18 - - - - AM - - - - - - - - 0 - 18 - - - - FM - - - true - - - - - - - - - 80 - 30 - 42 - 22 - - - - 7 - - - - - false - - - - 10 - 360 - 253 - 231 - - - - Carrier 2 - - - - - - 15 - - - - - - - Frequency multiplication - - - - - - - Envelope scale (KSR) - - - - - - - Tremolo (AM) - - - - - - - Vibrato (VIB) - - - - - - - Sustaining voice (EG) - - - - - - - Attack - - - - - - - 15 - - - - - - - Sustain - - - - - - - Decay - - - - - - - 15 - - - - - - - 15 - - - - - - - Release - - - - - - - 15 - - - - - - - Wave shape: - - - - - - - - 0 - Sine - + + + + Envelope scale (KSR) + + - - - 1 - Half-Sine - + + + + Tremolo (AM) + + - - - 2 - Absolute Sine - + + + + + 0 - Sine + + + + + 1 - Half-Sine + + + + + 2 - Absolute Sine + + + + + 3 - Quarter-Sine + + + + + 4 - 1/Sine, Sielence + + + + + 5 - 1/Ans-Sine, Sielence + + + + + 6 - Square + + + + + 7 - Saw + + + - - - 3 - Quarter-Sine - + + + + Vibrato (VIB) + + - - - 4 - 1/Sine, Sielence - + + + + Attack + + - - - 5 - 1/Ans-Sine, Sielence - + + + + Decay + + - - - 6 - Square - + + + + 63 + + - - - 7 - Saw - - - - - - - - 63 - - - - - - - Level: - - - - - - - 3 - - - - - - - Key Scale Level - - - - - - - - - 280 - 60 - 253 - 231 - - - - Modulator 1 - - - - - - Attack - - - - - - - 15 - - - - - - - Decay - - - - - - - 15 - - - - - - - Sustain - - - - - - - 15 - - - - - - - Release - - - - - - - 15 - - - - - - - Wave shape: - - - - - - - - 0 - Sine - + + + + Level: + + - - - 1 - Half-Sine - + + + + 3 + + - - - 2 - Absolute Sine - + + + + Key Scale Level + + - - - 3 - Quarter-Sine - + + + + + + + Modulator 1 + + + + + + Attack + + - - - 4 - 1/Sine, Sielence - + + + + 15 + + - - - 5 - 1/Ans-Sine, Sielence - + + + + Decay + + + + + + + 15 + + + + + + + Sustain + + + + + + + 15 + + + + + + + Release + + + + + + + 15 + + + + + + + Wave shape: + + + + + + + 0 - Sine + + + + + 1 - Half-Sine + + + + + 2 - Absolute Sine + + + + + 3 - Quarter-Sine + + + + + 4 - 1/Sine, Sielence + + + + + 5 - 1/Ans-Sine, Sielence + + + + + 6 - Square + + + + + 7 - Saw + + + + + + + + Tremolo (AM) + + + + + + + Sustaining voice (EG) + + + + + + + Vibrato (VIB) + + + + + + + Envelope scale (KSR) + + + + + + + Frequency multiplication + + + + + + + 15 + + + + + + + 63 + + + + + + + Level: + + + + + + + 3 + + + + + + + Key Scale Level + + + + + + + + + + false + + + Feedback 2 + + + + + + + false + + + 7 + + + + + + + false + + + Connection 2 + + - - 6 - Square - + + + + 0 + 18 + + + + AM + + - - 7 - Saw - - - - - - - - Tremolo (AM) - - - - - - - Sustaining voice (EG) - - - - - - - Vibrato (VIB) - - - - - - - Envelope scale (KSR) - - - - - - - Frequency multiplication - - - - - - - 15 - - - - - - - 63 - - - - - - - Level: - - - - - - - 3 - - - - - - - Key Scale Level - - - - - - - - - 130 - 10 - 121 - 50 - - - - Connection 1 - - - - - - - 0 - 18 - - - - AM - - - - - - - - 0 - 18 - - - - FM - - - true - - - - - - - - false - - - - 80 - 330 - 42 - 22 - - - - 7 - - - - - - 20 - 30 - 61 - 20 - - - - Feedback 1 - - - - - false - - - - 20 - 330 - 61 - 20 - - - - Feedback 2 - - - - - - 270 - 0 - 157 - 63 - - - - Percussion - - - - - - Note number: - - - - - - - 127 - - - - - + + + + 0 + 18 + + + + FM + + + true + + + + + + + + + + Enable 4-operator / Double-voice mode + + + + + + + false + + + Carrier 2 + + + + + + 15 + + + + + + + Frequency multiplication + + + + + + + Envelope scale (KSR) + + + + + + + Tremolo (AM) + + + + + + + Vibrato (VIB) + + + + + + + Sustaining voice (EG) + + + + + + + Attack + + + + + + + 15 + + + + + + + Sustain + + + + + + + Decay + + + + + + + 15 + + + + + + + 15 + + + + + + + Release + + + + + + + 15 + + + + + + + Wave shape: + + + + + + + + 0 - Sine + + + + + 1 - Half-Sine + + + + + 2 - Absolute Sine + + + + + 3 - Quarter-Sine + + + + + 4 - 1/Sine, Sielence + + + + + 5 - 1/Ans-Sine, Sielence + + + + + 6 - Square + + + + + 7 - Saw + + + + + + + + 63 + + + + + + + Level: + + + + + + + 3 + + + + + + + Key Scale Level + + + + + + + + + + false + + + Modulator 2 + + + + + + 15 + + + + + + + 15 + + + + + + + Sustain + + + + + + + 15 + + + + + + + Release + + + + + + + Wave shape: + + + + + + + + 0 - Sine + + + + + 1 - Half-Sine + + + + + 2 - Absolute Sine + + + + + 3 - Quarter-Sine + + + + + 4 - 1/Sine, Sielence + + + + + 5 - 1/Ans-Sine, Sielence + + + + + 6 - Square + + + + + 7 - Saw + + + + + + + + Tremolo (AM) + + + + + + + Sustaining voice (EG) + + + + + + + Vibrato (VIB) + + + + + + + Envelope scale (KSR) + + + + + + + Frequency multiplication + + + + + + + 15 + + + + + + + Attack + + + + + + + Decay + + + + + + + 15 + + + + + + + 63 + + + + + + + Level: + + + + + + + 3 + + + + + + + Key Scale Level + + + + + + + @@ -1067,27 +1000,14 @@ 790 - 40 + 150 121 - 321 + 501 Testing - - - - 10 - 180 - 121 - 22 - - - - Deep Vibrato - - @@ -1101,7 +1021,7 @@ 127 - 54 + 60 @@ -1114,20 +1034,7 @@ - Play major chord - - - - - - 10 - 160 - 121 - 22 - - - - Deep Tremolo + Major chord @@ -1140,7 +1047,7 @@ - Play minor chord + Minor chord @@ -1169,6 +1076,71 @@ Test note #(0...127) + + + + 10 + 220 + 101 + 23 + + + + Major 7-chord + + + + + + 10 + 250 + 101 + 23 + + + + Minor 7-chord + + + + + + 10 + 160 + 101 + 23 + + + + Augmented chord + + + + + + 10 + 190 + 101 + 23 + + + + Augmented chord + + + + + + 10 + 470 + 101 + 23 + + + + Shut up! + + @@ -1205,6 +1177,45 @@ <Untitled> + + + + 790 + 60 + 121 + 80 + + + + Global OPL3 flags + + + + + 10 + 20 + 101 + 22 + + + + Deep Tremolo + + + + + + 10 + 40 + 101 + 22 + + + + Deep Vibrato + + + @@ -1212,7 +1223,7 @@ 0 0 920 - 27 + 21 @@ -1238,6 +1249,8 @@ + + @@ -1293,6 +1306,11 @@ Ctrl+V + + + Reset current instrument + + diff --git a/main.cpp b/main.cpp index 356c5dc..dc17292 100644 --- a/main.cpp +++ b/main.cpp @@ -18,6 +18,7 @@ #include "bank_editor.h" #include +#include int main(int argc, char *argv[]) { @@ -25,5 +26,9 @@ int main(int argc, char *argv[]) BankEditor w; w.show(); + QStringList args = a.arguments(); + if(args.size()>1) + w.openFile(args[1]); + return a.exec(); } diff --git a/opl/generator.cpp b/opl/generator.cpp index 8936060..1f46bc5 100644 --- a/opl/generator.cpp +++ b/opl/generator.cpp @@ -38,11 +38,32 @@ static const unsigned short Channels[23] = 0x100,0x101,0x102, 0x103,0x104,0x105, 0x106,0x107,0x108, // 9..17 (secondary set) 0x006,0x007,0x008,0xFFF,0xFFF }; // <- hw percussions, 0xFFF = no support for pitch/pan +#define USED_CHANNELS_2OP 18 +#define USED_CHANNELS_2OP_PS4 9 +#define USED_CHANNELS_4OP 6 + +//! Regular 2-operator channels map +static const int channels[USED_CHANNELS_2OP] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17 + }; + +//! Pseudo 4-operators 2-operator channels map 1 +static const int channels1[USED_CHANNELS_2OP_PS4] = {0, 2, 4, 6, 8, 10, 12, 14, 16}; +//! Pseudo 4-operators 2-operator channels map 1 +static const int channels2[USED_CHANNELS_2OP_PS4] = {1, 3, 5, 7, 9, 11, 13, 15, 17}; + +//! 4-operator channels map 1 +int channels1_4op[USED_CHANNELS_4OP] = {0, 1, 2, 9, 10, 11}; +//! 4-operator channels map 1 +int channels2_4op[USED_CHANNELS_4OP] = {3, 4, 5, 12, 13, 14}; + Generator::Generator(int sampleRate, QObject *parent) : QIODevice(parent) { - note = 57; + note = 60; m_patch = { // ,---------+-------- Wave select settings @@ -83,35 +104,19 @@ Generator::Generator(int sampleRate, 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions }; - chip.Init(sampleRate); + chip.Init( sampleRate ); for(unsigned a=0; a< 18; ++a) chip.WriteReg(0xB0+Channels[a], 0x00); for(unsigned a=0; a (USED_CHANNELS_2OP_PS4-1)) + chanPs4op=0; + } + else + { + adlchannel[0] = channels[chan2op]; + adlchannel[1] = channels[chan2op]; + /* Rotating channels to have nicer poliphony on key spam */ + chan2op++; + if(chan2op > (USED_CHANNELS_2OP-1)) + chan2op=0; + } + } + else + if(natural_4op) + { + adlchannel[0] = channels1_4op[chan4op]; + adlchannel[1] = channels2_4op[chan4op]; + + /* Rotating channels to have nicer poliphony on key spam */ + chan4op++; + if(chan4op > (USED_CHANNELS_4OP-1)) + chan4op=0; } m_ins[adlchannel[0]] = i[0]; @@ -272,13 +308,16 @@ void Generator::PlayNoteF(int noteID, int chan2op1, int chan2op2, int chan4op1, double phase = 0.0; Patch(adlchannel[0], i[0]); - Patch(adlchannel[1], i[1]); + if( pseudo_4op || natural_4op) + Patch(adlchannel[1], i[1]); Pan(adlchannel[0], 0x30); - Pan(adlchannel[1], 0x30); + if( pseudo_4op || natural_4op ) + Pan(adlchannel[1], 0x30); Touch_Real(adlchannel[0], 63); - Touch_Real(adlchannel[1], 63); + if( pseudo_4op || natural_4op) + Touch_Real(adlchannel[1], 63); bend = 0.0 + m_patch.OPS[i[0]].finetune; NoteOn(adlchannel[0], 172.00093 * std::exp(0.057762265 * (tone + bend + phase))); @@ -290,6 +329,38 @@ void Generator::PlayNoteF(int noteID, int chan2op1, int chan2op2, int chan4op1, } } +void Generator::switch4op(bool enabled) +{ + if(enabled) + { + //Enable 4-operators mode + chip.WriteReg(0x104, 0x3F); + unsigned fours = 6; + unsigned nextfour = 0; + for(unsigned a=0; a2) chan=0; +void Generator::PlayNote() +{ + PlayNoteF(note); } void Generator::PlayMajorChord() { - PlayNoteF(note, 7, 6, 1,4); - PlayNoteF(note+4, 15, 8, 2,5); - PlayNoteF(note-5, 17, 16, 9,12); + PlayNoteF( note-12 ); + PlayNoteF( note ); + PlayNoteF( note+4 ); + PlayNoteF( note-5 ); } void Generator::PlayMinorChord() { - PlayNoteF(note, 7, 6, 1, 4); - PlayNoteF(note+3, 15, 8, 2, 5); - PlayNoteF(note-5, 17, 16, 9, 12); + PlayNoteF( note-12 ); + PlayNoteF( note ); + PlayNoteF( note+3 ); + PlayNoteF( note-5 ); +} + +void Generator::PlayAugmentedChord() +{ + PlayNoteF( note-12 ); + PlayNoteF( note ); + PlayNoteF( note+4 ); + PlayNoteF( note-4 ); +} + +void Generator::PlayDiminishedChord() +{ + PlayNoteF( note-12 ); + PlayNoteF( note ); + PlayNoteF( note+3 ); + PlayNoteF( note-6 ); +} + +void Generator::PlayMajor7Chord() +{ + PlayNoteF( note-12 ); + PlayNoteF( note-2 ); + PlayNoteF( note ); + PlayNoteF( note+4 ); + PlayNoteF( note-5 ); +} + +void Generator::PlayMinor7Chord() +{ + PlayNoteF( note-12 ); + PlayNoteF( note-2 ); + PlayNoteF( note ); + PlayNoteF( note+3 ); + PlayNoteF( note-5 ); } + + void Generator::changePatch(const FmBank::Instrument &instrument, bool isDrum) { //Shutup everything - for(unsigned c=0; c < NUM_OF_CHANNELS; ++c) - { - NoteOff(c); - } + Silence(); + switch4op( instrument.en_4op ); m_patch.OPS[0].modulator_E862 = (uint(instrument.OP[MODULATOR1].waveform) << 24) diff --git a/opl/generator.h b/opl/generator.h index 4ac4b49..a2bca18 100644 --- a/opl/generator.h +++ b/opl/generator.h @@ -82,14 +82,21 @@ class Generator : public QIODevice void Touch(unsigned c, unsigned volume); void Patch(unsigned c, unsigned i); void Pan(unsigned c, unsigned value); - void PlayNoteF(int noteID, int chan2op1 = 8, int chan2op2 = 7, int chan4op1 = 1, int chan4op2 = 4); + void PlayNoteF(int noteID); + void switch4op(bool enabled); public slots: void Silence(); void NoteOffAllChans(); + void PlayNote(); void PlayMajorChord(); void PlayMinorChord(); + void PlayAugmentedChord(); + void PlayDiminishedChord(); + void PlayMajor7Chord(); + void PlayMinor7Chord(); + void changePatch(const FmBank::Instrument &instrument, bool isDrum=false); void changeNote(int newnote); void changeDeepTremolo(bool enabled); diff --git a/version.h b/version.h index dcf0788..544f382 100644 --- a/version.h +++ b/version.h @@ -19,7 +19,12 @@ #ifndef VERSION_H #define VERSION_H +#define _COMPANY "Wohlhabend Networks" + +#define _PGE_URL "wohlsoft.ru" + #define PROGRAM_NAME "FM Bank Editor by Wohlstand" -#define VERSION "1.1.1" + +#define VERSION "1.1.2" #endif // VERSION_H