From 63e23e015878b00b3ea20acee5546027e508a92f Mon Sep 17 00:00:00 2001 From: "Raine M. Ekman" Date: Sun, 11 Jan 2015 19:19:52 +0200 Subject: [PATCH] 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];