From 925122545ec2b975809fd0ba48202e862de71265 Mon Sep 17 00:00:00 2001 From: "Raine M. Ekman" Date: Sat, 10 Jan 2015 21:23:14 +0200 Subject: [PATCH 1/3] Fix issue #1292, beautify code a bit, and revert an initialization change I can't grasp the reason for. --- plugins/opl2/opl2instrument.cpp | 53 +++++++++++++++++++-------------- plugins/opl2/opl2instrument.h | 7 +++-- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index d1613a1aa..0b8084349 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -56,6 +56,7 @@ #include "embed.cpp" #include "math.h" +#include "debug.h" #include "Knob.h" #include "LcdSpinBox.h" @@ -92,7 +93,7 @@ Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data ) QMutex opl2instrument::emulatorMutex; // Weird ordering of voice parameters -const unsigned int adlib_opadd[9] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12}; +const unsigned int adlib_opadd[OPL2_VOICES] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12}; opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &OPL2_plugin_descriptor ), @@ -141,9 +142,6 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); Engine::mixer()->addPlayHandle( iph ); - // Voices are laid out in a funny way... - // adlib_opadd = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12}; - // Create an emulator - samplerate, 16 bit, mono emulatorMutex.lock(); theEmulator = new CTemuopl(Engine::mixer()->processingSampleRate(), true, false); @@ -153,9 +151,9 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : emulatorMutex.unlock(); //Initialize voice values - voiceNote[0] = 0; - voiceLRU[0] = 0; - for(int i=1; i<9; ++i) { + // voiceNote[0] = 0; + // voiceLRU[0] = 0; + for(int i=0; iinit(); theEmulator->write(0x01,0x20); emulatorMutex.unlock(); - for(int i=1; i<9; ++i) { + for(int i=0; i0; --i) { if( voiceLRU[i-1] != OPL2_NO_VOICE ) { break; } } voiceLRU[i] = v; +#ifdef false + printf("%d %d %d %d %d %d %d %d %d <-- \n", voiceLRU[0],voiceLRU[1],voiceLRU[2], + voiceLRU[3],voiceLRU[4],voiceLRU[5],voiceLRU[6],voiceLRU[7],voiceLRU[8]); +#endif return i; } @@ -307,11 +314,11 @@ bool opl2instrument::handleMidiEvent( const MidiEvent& event, const MidiTime& ti break; case MidiNoteOff: key = event.key() +12; - for(voice=0; voice<9; ++voice) { + for(voice=0; voicewrite(0xA0+voice, fnums[key] & 0xff); theEmulator->write(0xB0+voice, (fnums[key] & 0x1f00) >> 8 ); - voiceNote[voice] = OPL2_VOICE_FREE; + voiceNote[voice] |= OPL2_VOICE_FREE; pushVoice(voice); } } @@ -323,7 +330,7 @@ bool opl2instrument::handleMidiEvent( const MidiEvent& event, const MidiTime& ti if( velocities[key] != 0) { velocities[key] = vel; } - for(voice=0; voice<9; ++voice) { + for(voice=0; voicewrite(0xA0+v, fnums[voiceNote[v] ] & 0xff); - theEmulator->write(0xB0+v, 32 + ((fnums[voiceNote[v]] & 0x1f00) >> 8) ); - } + // Update pitch of all voices (also released ones) + for( int v=0; vwrite(0xA0+v, fnums[vn] & 0xff); + theEmulator->write(0xB0+v, (playing ? 32 : 0) + ((fnums[vn] & 0x1f00) >> 8) ); } break; case MidiControlChange: @@ -363,7 +370,9 @@ bool opl2instrument::handleMidiEvent( const MidiEvent& event, const MidiTime& ti } break; default: +#ifdef LMMS_DEBUG printf("Midi CC %02x %02x\n", event.controllerNumber(), event.controllerValue() ); +#endif break; } break; @@ -476,7 +485,7 @@ void opl2instrument::loadSettings( const QDomElement & _this ) } // Load a patch into the emulator -void opl2instrument::loadPatch(unsigned char inst[14]) { +void opl2instrument::loadPatch(const unsigned char inst[14]) { emulatorMutex.lock(); for(int v=0; v<9; ++v) { theEmulator->write(0x20+adlib_opadd[v],inst[0]); // op1 AM/VIB/EG/KSR/Multiplier @@ -526,7 +535,7 @@ void opl2instrument::loadGMPatch() { // Update patch from the models to the chip emulation void opl2instrument::updatePatch() { - unsigned char *inst = midi_fm_instruments[0]; + unsigned char inst[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; inst[0] = ( op1_trem_mdl.value() ? 128 : 0 ) + ( op1_vib_mdl.value() ? 64 : 0 ) + ( op1_perc_mdl.value() ? 0 : 32 ) + // NB. This envelope mode is "perc", not "sus" @@ -564,7 +573,7 @@ void opl2instrument::updatePatch() { // have to do this, as the level knobs might've changed for( int voice = 0; voice < 9 ; ++voice) { - if(voiceNote[voice]!=OPL2_VOICE_FREE) { + if(voiceNote[voice] && OPL2_VOICE_FREE == 0) { setVoiceVelocity(voice, velocities[voiceNote[voice]] ); } } diff --git a/plugins/opl2/opl2instrument.h b/plugins/opl2/opl2instrument.h index 7cd644942..f42ff7352 100644 --- a/plugins/opl2/opl2instrument.h +++ b/plugins/opl2/opl2instrument.h @@ -33,8 +33,11 @@ #include "Knob.h" #include "PixmapButton.h" -#define OPL2_VOICE_FREE 255 +// This one is a flag, MIDI notes take 7 low bits +#define OPL2_VOICE_FREE 128 #define OPL2_NO_VOICE 255 +#define OPL2_VOICES 9 + // The "normal" range for LMMS pitchbends #define DEFAULT_BEND_CENTS 100 @@ -58,7 +61,7 @@ public: void saveSettings( QDomDocument & _doc, QDomElement & _this ); void loadSettings( const QDomElement & _this ); - void loadPatch(unsigned char inst[14]); + void loadPatch(const unsigned char inst[14]); void tuneEqual(int center, float Hz); IntModel m_patchModel; From 63e23e015878b00b3ea20acee5546027e508a92f Mon Sep 17 00:00:00 2001 From: "Raine M. Ekman" Date: Sun, 11 Jan 2015 19:19:52 +0200 Subject: [PATCH 2/3] Added loading of SBI files. (e.g. from http://cd.textfiles.com/soundsensations/SYNTH/SBINS/ ) --- plugins/opl2/opl2instrument.cpp | 124 ++++++++++++++++++++++++++++---- plugins/opl2/opl2instrument.h | 2 + 2 files changed, 114 insertions(+), 12 deletions(-) diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index 0b8084349..ecf5f251d 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -50,6 +50,9 @@ #include "InstrumentTrack.h" #include +#include +#include +#include #include "opl.h" #include "temuopl.h" @@ -76,7 +79,7 @@ Plugin::Descriptor PLUGIN_EXPORT OPL2_plugin_descriptor = 0x0100, Plugin::Instrument, new PluginPixmapLoader( "logo" ), - NULL, + "sbi", NULL }; @@ -158,6 +161,8 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : voiceLRU[i] = i; } + storedname = displayName(); + updatePatch(); // Can the buffer size change suddenly? I bet that would break lots of stuff @@ -166,7 +171,7 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : // Some kind of sane defaults pitchbend = 0; - pitchBendRange = 100; + pitchBendRange = 100; // cents RPNcoarse = RPNfine = 255; tuneEqual(69, 440); @@ -338,10 +343,8 @@ bool opl2instrument::handleMidiEvent( const MidiEvent& event, const MidiTime& ti break; case MidiPitchBend: // Update fnumber table - // Pitchbend should be in the range 0...16383 but the new range knob gets it wrong. - // tmp_pb = (2*BEND_CENTS)*((float)event.m_data.m_param[0]/16383)-BEND_CENTS; - // Something like 100 cents = 8192, but offset by 8192 so the +/-100 cents range goes from 0...16383? + // Neutral = 8192, full downbend = 0, full upbend = 16383 tmp_pb = ( event.pitchBend()-8192 ) * pitchBendRange / 8192; if( tmp_pb != pitchbend ) { @@ -377,7 +380,10 @@ bool opl2instrument::handleMidiEvent( const MidiEvent& event, const MidiTime& ti } break; default: +#ifdef LMMS_DEBUG printf("Midi event type %d\n",event.type()); +#endif + break; } emulatorMutex.unlock(); return true; @@ -528,11 +534,6 @@ void opl2instrument::loadGMPatch() { loadPatch(inst); } -// -/* void opl2instrument::loadSBIFile() { - - } */ - // Update patch from the models to the chip emulation void opl2instrument::updatePatch() { unsigned char inst[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -546,9 +547,9 @@ void opl2instrument::updatePatch() { ( op2_perc_mdl.value() ? 0 : 32 ) + // NB. This envelope mode is "perc", not "sus" ( op2_ksr_mdl.value() ? 16 : 0 ) + ((int)op2_mul_mdl.value() & 0x0f); - inst[2] = ( (int)op1_scale_mdl.value() & 0x03 << 6 ) + + inst[2] = ( ((int)op1_scale_mdl.value() & 0x03) << 6 ) + (63 - ( (int)op1_lvl_mdl.value() & 0x3f ) ); - inst[3] = ( (int)op2_scale_mdl.value() & 0x03 << 6 ) + + inst[3] = ( ((int)op2_scale_mdl.value() & 0x03) << 6 ) + (63 - ( (int)op2_lvl_mdl.value() & 0x3f ) ); inst[4] = ((15 - ((int)op1_a_mdl.value() & 0x0f ) ) << 4 )+ (15 - ( (int)op1_d_mdl.value() & 0x0f ) ); @@ -577,9 +578,108 @@ void opl2instrument::updatePatch() { setVoiceVelocity(voice, velocities[voiceNote[voice]] ); } } +#ifdef LMMS_DEBUG + printf("UPD: %02x %02x %02x %02x %02x -- %02x %02x %02x %02x %02x %02x\n", + inst[0], inst[1], inst[2], inst[3], inst[4], + inst[5], inst[6], inst[7], inst[8], inst[9], inst[10]); +#endif + + loadPatch(inst); } +// Load an SBI file into the knob models +void opl2instrument::loadFile( const QString& file ) { + // http://cd.textfiles.com/soundsensations/SYNTH/SBINS/ + // http://cd.textfiles.com/soundsensations/SYNTH/SBI1198/1198SBI.ZIP + if( !file.isEmpty() && QFileInfo( file ).exists() ) + { + QFile sbifile(file); + if (!sbifile.open(QIODevice::ReadOnly )) { + printf("Can't open file\n"); + return; + } + + QByteArray sbidata = sbifile.read(52); + if( !sbidata.startsWith("SBI\0x1a") ) { + printf("No SBI signature\n"); + return; + } + if( sbidata.size() != 52 ) { + printf("SBI size error: expected 52, got %d\n",sbidata.size() ); + } + + // Minimum size of SBI if we ignore "reserved" bytes at end + // https://courses.engr.illinois.edu/ece390/resources/sound/cmf.txt.html + if( sbidata.size() < 47 ) { + return; + } + + + // If user has changed track name... let's hope my logic is valid. + QString sbiname = sbidata.mid(4, 32); + if( instrumentTrack()->displayName() == storedname ) { + instrumentTrack()->setName(sbiname); + storedname = sbiname; + } + +#ifdef LMMS_DEBUG + printf("SBI: %02x %02x %02x %02x %02x -- %02x %02x %02x %02x %02x %02x\n", + (unsigned char)sbidata[36], (unsigned char)sbidata[37], (unsigned char)sbidata[38], (unsigned char)sbidata[39], (unsigned char)sbidata[40], + (unsigned char)sbidata[41], (unsigned char)sbidata[42], (unsigned char)sbidata[43], (unsigned char)sbidata[44], (unsigned char)sbidata[45], (unsigned char)sbidata[46]); +#endif + // Modulator Sound Characteristic (Mult, KSR, EG, VIB, AM) + op1_trem_mdl.setValue( (sbidata[36] & 0x80 ) == 0x80 ? true : false ); + op1_vib_mdl.setValue( (sbidata[36] & 0x40 ) == 0x40 ? true : false ); + op1_perc_mdl.setValue( (sbidata[36] & 0x20 ) == 0x20 ? false : true ); + op1_ksr_mdl.setValue( (sbidata[36] & 0x10 ) == 0x10 ? true : false ); + op1_mul_mdl.setValue( sbidata[36] & 0x0f ); + + // Carrier Sound Characteristic + op2_trem_mdl.setValue( (sbidata[37] & 0x80 ) == 0x80 ? true : false ); + op2_vib_mdl.setValue( (sbidata[37] & 0x40 ) == 0x40 ? true : false ); + op2_perc_mdl.setValue( (sbidata[37] & 0x20 ) == 0x20 ? false : true ); + op2_ksr_mdl.setValue( (sbidata[37] & 0x10 ) == 0x10 ? true : false ); + op2_mul_mdl.setValue( sbidata[37] & 0x0f ); + + // Modulator Scaling/Output Level + op1_scale_mdl.setValue( (sbidata[38] & 0xc0 ) >> 6 ); + op1_lvl_mdl.setValue( 63 - (sbidata[38] & 0x3f) ); + + // Carrier Scaling/Output Level + op2_scale_mdl.setValue( (sbidata[39] & 0xc0) >> 6 ); + op2_lvl_mdl.setValue( 63 - (sbidata[39] & 0x3f) ); + + // Modulator Attack/Decay + op1_a_mdl.setValue( 15 - ( ( sbidata[40] & 0xf0 ) >> 4 ) ); + op1_d_mdl.setValue( 15 - ( sbidata[40] & 0x0f ) ); + + // Carrier Attack/Decay + op2_a_mdl.setValue( 15 - ( ( sbidata[41] & 0xf0 ) >> 4 ) ); + op2_d_mdl.setValue( 15 - ( sbidata[41] & 0x0f ) ); + + // Modulator Sustain/Release + op1_s_mdl.setValue( 15 - ( ( sbidata[42] & 0xf0 ) >> 4 ) ); + op1_r_mdl.setValue( 15 - ( sbidata[42] & 0x0f ) ); + + // Carrier Sustain/Release + op2_s_mdl.setValue( 15 - ( ( sbidata[43] & 0xf0 ) >> 4 ) ); + op2_r_mdl.setValue( 15 - ( sbidata[43] & 0x0f ) ); + + // Modulator Wave Select + op1_waveform_mdl.setValue( sbidata[44] & 0x03 ); + + // Carrier Wave Select + op2_waveform_mdl.setValue( sbidata[45] & 0x03 ); + + // Feedback/Connection + fm_mdl.setValue( (sbidata[46] & 0x01) == 0x01 ? false : true ); + feedback_mdl.setValue( ( (sbidata[46] & 0x0e ) >> 1 ) ); + } +} + + + opl2instrumentView::opl2instrumentView( Instrument * _instrument, diff --git a/plugins/opl2/opl2instrument.h b/plugins/opl2/opl2instrument.h index f42ff7352..b1cce9c3b 100644 --- a/plugins/opl2/opl2instrument.h +++ b/plugins/opl2/opl2instrument.h @@ -63,6 +63,7 @@ public: void loadSettings( const QDomElement & _this ); void loadPatch(const unsigned char inst[14]); void tuneEqual(int center, float Hz); + virtual void loadFile( const QString& file ); IntModel m_patchModel; @@ -114,6 +115,7 @@ private slots: private: Copl *theEmulator; + QString storedname; fpp_t frameCount; short *renderbuffer; int voiceNote[9]; From 4cfed0434ce805e2cf2cf0fe37e3aa8b813635af Mon Sep 17 00:00:00 2001 From: "Raine M. Ekman" Date: Sun, 11 Jan 2015 20:51:22 +0200 Subject: [PATCH 3/3] ...and don't name the track after the name in the SBI file if it's empty. --- plugins/opl2/opl2instrument.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index ecf5f251d..b84edf0b9 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -578,7 +578,7 @@ void opl2instrument::updatePatch() { setVoiceVelocity(voice, velocities[voiceNote[voice]] ); } } -#ifdef LMMS_DEBUG +#ifdef false printf("UPD: %02x %02x %02x %02x %02x -- %02x %02x %02x %02x %02x %02x\n", inst[0], inst[1], inst[2], inst[3], inst[4], inst[5], inst[6], inst[7], inst[8], inst[9], inst[10]); @@ -615,15 +615,14 @@ void opl2instrument::loadFile( const QString& file ) { return; } - - // If user has changed track name... let's hope my logic is valid. QString sbiname = sbidata.mid(4, 32); - if( instrumentTrack()->displayName() == storedname ) { + // If user has changed track name... let's hope my logic is valid. + if( sbiname.size() > 0 && instrumentTrack()->displayName() == storedname ) { instrumentTrack()->setName(sbiname); storedname = sbiname; } -#ifdef LMMS_DEBUG +#ifdef false printf("SBI: %02x %02x %02x %02x %02x -- %02x %02x %02x %02x %02x %02x\n", (unsigned char)sbidata[36], (unsigned char)sbidata[37], (unsigned char)sbidata[38], (unsigned char)sbidata[39], (unsigned char)sbidata[40], (unsigned char)sbidata[41], (unsigned char)sbidata[42], (unsigned char)sbidata[43], (unsigned char)sbidata[44], (unsigned char)sbidata[45], (unsigned char)sbidata[46]);