From a18061bdab4a5764cf16a778b2d446789116a3da Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 29 Apr 2014 22:53:50 -0700 Subject: [PATCH] GIG Player now uses correct velocity and changes sample rate properly Fixed crashes when loading invalid file, but this required exceptions since libgig throws exceptions, hence the changes to CMakeLists.txt. If the sample rate is changed (e.g. on exporting at 48k when the samples are at 44.1k), when playing the note convert each note individually to the correct sample rate instead of converting the output from the notes to the correct sample rate. Save the instrument so we don't have to search the entire GIG file every time we play a note. This should make it work better on computers that don't have tons of RAM. Currently there is a 0.5 second fade out time when the note is released. I still have to implement the release-triggered samples. --- plugins/gig_player/CMakeLists.txt | 5 + plugins/gig_player/gig_player.cpp | 487 +++++++++++++++++++----------- plugins/gig_player/gig_player.h | 23 +- 3 files changed, 341 insertions(+), 174 deletions(-) diff --git a/plugins/gig_player/CMakeLists.txt b/plugins/gig_player/CMakeLists.txt index 7e32e5340..f808f15fb 100644 --- a/plugins/gig_player/CMakeLists.txt +++ b/plugins/gig_player/CMakeLists.txt @@ -1,6 +1,11 @@ if(LMMS_HAVE_GIG) INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${GIG_INCLUDE_DIRS}{) + + # Required for not crashing loading files with libgig + SET(GCC_COVERAGE_COMPILE_FLAGS "-fexceptions") + add_definitions(${GCC_COVERAGE_COMPILE_FLAGS}) + LINK_DIRECTORIES(${GIG_LIBRARY_DIRS}) LINK_LIBRARIES(${GIG_LIBRARIES}) BUILD_PLUGIN(gigplayer gig_player.cpp gig_player.h patches_dialog.cpp patches_dialog.h patches_dialog.ui MOCFILES gig_player.h patches_dialog.h UICFILES patches_dialog.ui EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/gig_player/gig_player.cpp b/plugins/gig_player/gig_player.cpp index cf5df5edf..15dc4616a 100644 --- a/plugins/gig_player/gig_player.cpp +++ b/plugins/gig_player/gig_player.cpp @@ -27,8 +27,6 @@ #include #include #include -#include // sin, cos, ... -#include // memset #include "FileDialog.h" #include "gig_player.h" @@ -84,6 +82,8 @@ gigInstrument::gigInstrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &gigplayer_plugin_descriptor ), m_srcState( NULL ), m_instance( NULL ), + m_instrument( NULL ), + m_RandomSeed( 0 ), m_filename( "" ), m_lastMidiPitch( -1 ), m_lastMidiPitchRange( -1 ), @@ -95,12 +95,11 @@ gigInstrument::gigInstrument( InstrumentTrack * _instrument_track ) : InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); engine::mixer()->addPlayHandle( iph ); + updateSampleRate(); + connect( &m_bankNum, SIGNAL( dataChanged() ), this, SLOT( updatePatch() ) ); connect( &m_patchNum, SIGNAL( dataChanged() ), this, SLOT( updatePatch() ) ); - //connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) ); - - // Gain - //connect( &m_gain, SIGNAL( dataChanged() ), this, SLOT( updateGain() ) ); + connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) ); } @@ -151,6 +150,7 @@ void gigInstrument::loadFile( const QString & _file ) { openFile( _file, false ); updatePatch(); + updateSampleRate(); } } @@ -195,7 +195,6 @@ void gigInstrument::freeInstance() { qDebug() << "Really deleting " << m_filename; - // Need we do more to delete the gig instance? s_instances.remove( m_filename ); delete m_instance; } @@ -216,6 +215,7 @@ void gigInstrument::freeInstance() void gigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) { emit fileLoading(); + bool succeeded = false; // Used for loading file char * gigAscii = qstrdup( qPrintable( SampleBuffer::tryToMakeAbsolute( _gigFile ) ) ); @@ -240,19 +240,31 @@ void gigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) // Add to map, if doesn't exist. else { - // Grab this sf from the top of the stack and add to list - m_instance = new gigInstance( _gigFile ); - s_instances.insert( relativePath, m_instance ); + try + { + // Grab this sf from the top of the stack and add to list + m_instance = new gigInstance( _gigFile ); + s_instances.insert( relativePath, m_instance ); + succeeded = true; + } + catch( ... ) + { + m_instance = NULL; + succeeded = false; + } } s_instancesMutex.unlock(); m_synthMutex.unlock(); - // TODO: only do this if successfully loaded - if( true ) + if( succeeded ) { m_filename = relativePath; - + emit fileChanged(); + } + else + { + m_filename = ""; emit fileChanged(); } @@ -261,6 +273,7 @@ void gigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) if( updateTrackName ) { instrumentTrack()->setName( QFileInfo( _gigFile ).baseName() ); + updatePatch(); } } @@ -270,11 +283,7 @@ void gigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) void gigInstrument::updatePatch() { if( m_bankNum.value() >= 0 && m_patchNum.value() >= 0 ) - { - // TODO: are there patches in GIG files? - //fluid_synth_program_select( m_synth, m_channel, m_fontId, - // m_bankNum.value(), m_patchNum.value() ); - } + getInstrument(); } @@ -282,21 +291,24 @@ void gigInstrument::updatePatch() QString gigInstrument::getCurrentPatchName() { - if (!m_instance) + if( !m_instance ) return ""; int iBankSelected = m_bankNum.value(); int iProgSelected = m_patchNum.value(); gig::Instrument* pInstrument = m_instance->gig.GetFirstInstrument(); - while (pInstrument) { + + while( pInstrument ) + { int iBank = pInstrument->MIDIBank; int iProg = pInstrument->MIDIProgram; - if (iBank == iBankSelected && iProg == iProgSelected) { + if( iBank == iBankSelected && iProg == iProgSelected ) + { QString name = QString::fromStdString(pInstrument->pInfo->Name); - if (name == "") + if( name == "" ) name = ""; return name; @@ -330,144 +342,47 @@ void gigInstrument::playNote( NotePlayHandle * _n, sampleFrame * ) pluginData->midiNote = midiNote; pluginData->lastPanning = -1; pluginData->lastVelocity = 127; - _n->m_pluginData = pluginData; - // TODO: Start the note here - //const int baseVelocity = instrumentTrack()->midiPort()->baseVelocity(); - //fluid_synth_noteon( m_synth, m_channel, midiNote, _n->midiVelocity( baseVelocity ) ); - - if (m_instance) + if( m_instance ) { - // Find instrument - int iBankSelected = m_bankNum.value(); - int iProgSelected = m_patchNum.value(); + if( !m_instrument ) + getInstrument(); - gig::Instrument* pInstrument = m_instance->gig.GetFirstInstrument(); - while (pInstrument) { - int iBank = pInstrument->MIDIBank; - int iProg = pInstrument->MIDIProgram; - - if (iBank == iBankSelected && iProg == iProgSelected) { - break; - } - - pInstrument = m_instance->gig.GetNextInstrument(); - } - - // Find sample - if (pInstrument) + // Find the sample we want to play (based on velocity, etc.) + if( m_instrument ) { - gig::Region* pRegion = pInstrument->GetFirstRegion(); + gig::Region* pRegion = m_instrument->GetFirstRegion(); - while (pRegion) { - gig::Sample* pSample = pRegion->GetSample(); + while( pRegion ) + { + const int baseVelocity = instrumentTrack()->midiPort()->baseVelocity(); + const uint velocity = _n->midiVelocity( baseVelocity ); - if (pSample) + Dimension dim = getDimensions( pRegion, velocity, false ); + gig::DimensionRegion* pDimRegion = pRegion->GetDimensionRegionByValue( dim.DimValues ); + gig::Sample* pSample = pDimRegion->pSample; + + if( pSample ) { - int rate = pSample->SamplesPerSecond; - QString name = QString::fromStdString(pSample->pInfo->Name); int keyLow = pRegion->KeyRange.low; int keyHigh = pRegion->KeyRange.high; - if (rate != engine::mixer()->processingSampleRate()) + if( midiNote >= keyLow && midiNote <= keyHigh ) { - qDebug() << "Warning: wrong sample rate, conversion not implemented"; - } - - if (midiNote >= keyLow && midiNote <= keyHigh) - { - /*qDebug() << "Playing note " << midiNote << " between " << keyLow - << " and " << keyHigh - << " from sample " << name << " at " << rate << " Hz of size " - << pSample->SamplesTotal;*/ - - gig::buffer_t buf = pSample->LoadSampleData(); - gigNote note(pSample->SamplesTotal); - - if (pSample->BitDepth == 24) - { - qDebug() << "Error: not 16 bit... not implemented"; - /*int n = pSample->SamplesTotal * pSample->Channels; - - for (int i = n-1; i>=0; i-=pSample->Channels) - for (int j = 0; j < pSample->Channels; ++j) - note.note[i][j] = - 1.0/0x800000*(pWave[i*3+j] - | pWave[i*3+1+3*j]<<8 - | pWave[i*3+2+3*j]<<16);*/ - } - else - { - int16_t* pInt = reinterpret_cast(buf.pStart); - - if (pSample->Channels <= 2) - { - for (int i = 0; i < pSample->SamplesTotal/pSample->Channels; ++i) - { - note.note[i][0] = 1.0/0x10000*pInt[pSample->Channels*i]; - - if (pSample->Channels == 1) - note.note[i][1] = note.note[i][0]; - else - note.note[i][1] = 1.0/0x10000*pInt[pSample->Channels*i+1]; - } - } - else - { - qDebug() << "Error: not stereo... not implemented"; - } - } + gigNote note = sampleToNote(pSample, midiNote); m_synthMutex.lock(); m_notes.push_back(note); m_synthMutex.unlock(); - - pSample->ReleaseSampleData(); } } - pRegion = pInstrument->GetNextRegion(); + pRegion = m_instrument->GetNextRegion(); } } } - else - { - // TODO: eventually get rid of this - - // By default just play a sine wave instead of a GIG file. - double freq = _n->unpitchedFrequency(); - //int size = ceil(engine::mixer()->processingSampleRate()/freq); - int size = 1.0/2*engine::mixer()->processingSampleRate(); - float scale = 0.25; // Default max volume for one note - gigNote note(size); - note.midiNote = midiNote; - - for (int i = 0; i < size; ++i) - { - note.note[i][0] = sin(1.0*i*freq*2*M_PI/engine::mixer()->processingSampleRate()) - * (1.0-1.0/size*i)*scale; - note.note[i][1] = note.note[i][0]; - } - - m_synthMutex.lock(); - m_notes.push_back(note); - m_synthMutex.unlock(); - } } - - /*GIGPluginData * pluginData = static_cast( - _n->m_pluginData ); - - const float currentVelocity = _n->volumeLevel( tfp ) * instrumentTrack()->midiPort()->baseVelocity(); - if( pluginData->lastVelocity != currentVelocity ) - { - m_synthMutex.lock(); - // TODO: set the new velocity to currentvelocity - m_synthMutex.unlock(); - - pluginData->lastVelocity = currentVelocity; - }*/ } @@ -482,31 +397,23 @@ void gigInstrument::play( sampleFrame * _working_buffer ) // Initialize to zeros for (int i = 0; i < frames; ++i) { - _working_buffer[i][0] = float(); - _working_buffer[i][1] = float(); + _working_buffer[i][0] = 0; + _working_buffer[i][1] = 0; } m_synthMutex.lock(); - const int currentMidiPitch = instrumentTrack()->midiPitch(); + /*const int currentMidiPitch = instrumentTrack()->midiPitch(); if( m_lastMidiPitch != currentMidiPitch ) - { m_lastMidiPitch = currentMidiPitch; - // TODO: pitch bend... - //fluid_synth_pitch_bend( m_synth, m_channel, m_lastMidiPitch ); - } - const int currentMidiPitchRange = instrumentTrack()->midiPitchRange(); if( m_lastMidiPitchRange != currentMidiPitchRange ) - { - m_lastMidiPitchRange = currentMidiPitchRange; - // TODO: pitch bend... - //fluid_synth_pitch_wheel_sens( m_synth, m_channel, m_lastMidiPitchRange ); - } + m_lastMidiPitchRange = currentMidiPitchRange;*/ - if( m_internalSampleRate < engine::mixer()->processingSampleRate() && + /*if( m_internalSampleRate < engine::mixer()->processingSampleRate() && m_srcState != NULL ) { + const fpp_t frames = engine::mixer()->framesPerPeriod(); const fpp_t f = frames * m_internalSampleRate / engine::mixer()->processingSampleRate(); #ifdef __GNUC__ sampleFrame tmp[f]; @@ -514,7 +421,7 @@ void gigInstrument::play( sampleFrame * _working_buffer ) sampleFrame * tmp = new sampleFrame[f]; #endif - qDebug() << "Different internal sample rate"; + qDebug() << "Warning: playing at different sample rate"; // TODO: get sample, interleave left/right channels SRC_DATA src_data; @@ -538,10 +445,7 @@ void gigInstrument::play( sampleFrame * _working_buffer ) } } else - { - // TODO: get sample, interleave left/right channels - //fluid_synth_write_float( m_synth, frames, _working_buffer, 0, 2, _working_buffer, 1, 2 ); - + {*/ for( int i = 0; i < frames; ++i ) { for( std::list::iterator note = m_notes.begin(); note != m_notes.end(); ++note ) @@ -554,7 +458,7 @@ void gigInstrument::play( sampleFrame * _working_buffer ) } } } - } + //} m_synthMutex.unlock(); @@ -566,19 +470,19 @@ void gigInstrument::play( sampleFrame * _working_buffer ) void gigInstrument::deleteNotePluginData( NotePlayHandle * _n ) { - //GIGPluginData * pluginData = static_cast( _n->m_pluginData ); + GIGPluginData * pluginData = static_cast( _n->m_pluginData ); m_synthMutex.lock(); - // Delete ended notes. Continue looping starting at the beginning. Erasing + // Continue looping starting at the beginning. Erasing // invalidates the iterator. bool deleted = false; + // Delete ended notes do { deleted = false; - std::list::iterator i = m_notes.begin(); - while( i != m_notes.end() ) + for( std::list::iterator i = m_notes.begin(); i != m_notes.end(); ++i ) { if( i->position >= i->size ) { @@ -586,15 +490,44 @@ void gigInstrument::deleteNotePluginData( NotePlayHandle * _n ) deleted = true; break; } - - ++i; } } while( deleted ); + // TODO: instead of fading out, use getDimensions(region, velocity, release = true) + // to see if we should create a new "note" for the release of the note, e.g. + // the piano releasing a key sound + + // Fade out the note we want to end + for( std::list::iterator i = m_notes.begin(); i != m_notes.end(); ++i ) + { + if( i->midiNote == pluginData->midiNote ) + { + float fadeOut = 0.5; // Seconds + int len = std::min( int( floor( fadeOut * engine::mixer()->processingSampleRate() ) ), + i->size-i->position ); + int endPoint = i->position+len; + + if (len <= 0) + break; + + for( int k = i->position, j = 0; k < endPoint; ++k, ++j ) + { + i->note[k][0] *= 1.0-1.0/len*j; + i->note[k][1] *= 1.0-1.0/len*j; + } + + for( int k = endPoint; k < i->size; ++k ) + { + i->note[k][0] = 0; + i->note[k][1] = 0; + } + } + } + m_synthMutex.unlock(); - //delete pluginData; + delete pluginData; } @@ -608,6 +541,225 @@ PluginView * gigInstrument::instantiateView( QWidget * _parent ) +Dimension gigInstrument::getDimensions( gig::Region* pRegion, int velocity, bool release ) +{ + Dimension dim; + + if( !pRegion ) + return dim; + + for( int i = pRegion->Dimensions - 1; i >= 0; --i ) + { + switch( pRegion->pDimensionDefinitions[i].dimension ) + { + case gig::dimension_layer: + //DimValues[i] = iLayer; + dim.DimValues[i] = 0; + break; + case gig::dimension_velocity: + dim.DimValues[i] = velocity; + break; + case gig::dimension_releasetrigger: + //VoiceType = (ReleaseTriggerVoice) ? Voice::type_release_trigger : (!iLayer) ? Voice::type_release_trigger_required : Voice::type_normal; + dim.DimValues[i] = (uint) release; + break; + case gig::dimension_keyboard: + //dim.DimValues[i] = (uint) (pEngineChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones); + dim.DimValues[i] = 0; + // TODO: may be useful? + break; + case gig::dimension_roundrobin: + case gig::dimension_roundrobinkeyboard: + //dim.DimValues[i] = (uint) pEngineChannel->pMIDIKeyInfo[MIDIKey].RoundRobinIndex; // incremented for each note on + dim.DimValues[i] = 0; + // TODO: implement this + break; + case gig::dimension_random: + m_RandomSeed = m_RandomSeed * 1103515245 + 12345; // classic pseudo random number generator + dim.DimValues[i] = (uint) m_RandomSeed >> (32 - pRegion->pDimensionDefinitions[i].bits); // highest bits are most random + break; + case gig::dimension_samplechannel: + case gig::dimension_channelaftertouch: + case gig::dimension_modwheel: + case gig::dimension_breath: + case gig::dimension_foot: + case gig::dimension_portamentotime: + case gig::dimension_effect1: + case gig::dimension_effect2: + case gig::dimension_genpurpose1: + case gig::dimension_genpurpose2: + case gig::dimension_genpurpose3: + case gig::dimension_genpurpose4: + case gig::dimension_sustainpedal: + case gig::dimension_portamento: + case gig::dimension_sostenutopedal: + case gig::dimension_softpedal: + case gig::dimension_genpurpose5: + case gig::dimension_genpurpose6: + case gig::dimension_genpurpose7: + case gig::dimension_genpurpose8: + case gig::dimension_effect1depth: + case gig::dimension_effect2depth: + case gig::dimension_effect3depth: + case gig::dimension_effect4depth: + case gig::dimension_effect5depth: + dim.DimValues[i] = 0; + break; + case gig::dimension_none: + qDebug() << "Error: dimension=none"; + break; + default: + qDebug() << "Error: Unknown dimension"; + } + } + + return dim; +} + + + + +gigNote gigInstrument::sampleToNote( gig::Sample* pSample, int midiNote ) +{ + if( !pSample ) + return gigNote(); + + gig::buffer_t buf = pSample->LoadSampleData(); + gigNote note( pSample->SamplesTotal ); + note.midiNote = midiNote; + + // 24 Bit + // TODO: implement this + if( pSample->BitDepth == 24 ) + { + qDebug() << "Error: not 16 bit... not implemented"; + /*int n = pSample->SamplesTotal * pSample->Channels; + + for (int i = n-1; i>=0; i-=pSample->Channels) + for (int j = 0; j < pSample->Channels; ++j) + note.note[i][j] = + 1.0/0x800000*(pWave[i*3+j] + | pWave[i*3+1+3*j]<<8 + | pWave[i*3+2+3*j]<<16);*/ + } + // 16 Bit + else + { + int16_t* pInt = reinterpret_cast( buf.pStart ); + + if( pSample->Channels <= 2 ) + { + for( int i = 0; i < pSample->SamplesTotal/pSample->Channels; ++i ) + { + note.note[i][0] = 1.0/0x10000*pInt[pSample->Channels*i]; + + if( pSample->Channels == 1 ) + note.note[i][1] = note.note[i][0]; + else + note.note[i][1] = 1.0/0x10000*pInt[pSample->Channels*i+1]; + } + } + else + { + qDebug() << "Error: not stereo... not implemented"; + } + } + + pSample->ReleaseSampleData(); + + // Convert sample rate + int sampleRate = pSample->SamplesPerSecond; + int outputRate = engine::mixer()->processingSampleRate(); + if( sampleRate != outputRate ) + { + qDebug() << "Warning: converting sample rate"; + gigNote converted = convertSampleRate( note, sampleRate, outputRate ); + return converted; + } + else + { + return note; + } +} + + + + +void gigInstrument::getInstrument() +{ + // Find instrument + int iBankSelected = m_bankNum.value(); + int iProgSelected = m_patchNum.value(); + + if( m_instance ) + { + gig::Instrument* pInstrument = m_instance->gig.GetFirstInstrument(); + + while( pInstrument ) + { + int iBank = pInstrument->MIDIBank; + int iProg = pInstrument->MIDIProgram; + + if( iBank == iBankSelected && iProg == iProgSelected ) + break; + + pInstrument = m_instance->gig.GetNextInstrument(); + } + + m_instrument = pInstrument; + } +} + + + + +gigNote gigInstrument::convertSampleRate( gigNote& old, int oldRate, int newRate ) +{ + gigNote note( ceil( (double)old.size*newRate/oldRate ) ); + note.midiNote = old.midiNote; + + SRC_DATA src_data; + src_data.data_in = old.note[0]; + src_data.data_out = note.note[0]; + src_data.input_frames = old.size; + src_data.output_frames = note.size; + src_data.src_ratio = (double) note.size / old.size; + src_data.end_of_input = 0; + int error = src_process( m_srcState, &src_data ); + + if( error ) + { + qCritical( "gigInstrument: error while resampling: %s", src_strerror( error ) ); + } + + if( src_data.output_frames_gen > note.size ) + { + qCritical( "gigInstrument: not enough frames: %ld / %d", src_data.output_frames_gen, old.size ); + } + + return note; +} + + + + +void gigInstrument::updateSampleRate() +{ + m_synthMutex.lock(); + + if( m_srcState != NULL ) + src_delete( m_srcState ); + + int error; + m_srcState = src_new( engine::mixer()->currentQualitySettings().libsrcInterpolation(), DEFAULT_CHANNELS, &error ); + + if( m_srcState == NULL || error ) + qCritical( "error while creating libsamplerate data structure in gigInstrument::updateSampleRate()" ); + + m_synthMutex.unlock(); +} + + @@ -615,7 +767,6 @@ class gigKnob : public knob { public: gigKnob( QWidget * _parent ) : -// knob( knobStyled, _parent ) knob( knobBright_26, _parent ) { setFixedSize( 31, 38 ); @@ -703,13 +854,10 @@ void gigInstrumentView::modelChanged() m_gainKnob->setModel( &k->m_gain ); - connect( k, SIGNAL( fileChanged() ), this, SLOT( updateFilename() ) ); - connect( k, SIGNAL( fileLoading() ), this, SLOT( invalidateFile() ) ); updateFilename(); - } @@ -727,7 +875,6 @@ void gigInstrumentView::updateFilename() m_patchDialogButton->setEnabled( !i->m_filename.isEmpty() ); updatePatchName(); - update(); } @@ -741,7 +888,6 @@ void gigInstrumentView::updatePatchName() QString patch = i->getCurrentPatchName(); m_patchLabel->setText( fm.elidedText( patch, Qt::ElideLeft, m_patchLabel->width() ) ); - update(); } @@ -807,7 +953,6 @@ void gigInstrumentView::showFileDialog() void gigInstrumentView::showPatchDialog() { - // TODO: does it have patches? gigInstrument * k = castModel(); patchesDialog pd( this ); pd.setup( k->m_instance, 1, k->instrumentTrack()->name(), &k->m_bankNum, &k->m_patchNum, m_patchLabel ); diff --git a/plugins/gig_player/gig_player.h b/plugins/gig_player/gig_player.h index c18991abe..1ee6c0cc4 100644 --- a/plugins/gig_player/gig_player.h +++ b/plugins/gig_player/gig_player.h @@ -65,6 +65,19 @@ public: +struct Dimension +{ + Dimension() + { + for( int i = 0; i < 8; ++i) + DimValues[i] = 0; + } + + uint DimValues[8]; +}; + + + class gigNote { public: @@ -166,6 +179,7 @@ public: public slots: void openFile( const QString & _gigFile, bool updateTrackName = true ); void updatePatch(); + void updateSampleRate(); private: @@ -176,12 +190,11 @@ private: SRC_STATE * m_srcState; gigInstance* m_instance; + gig::Instrument* m_instrument; + uint32_t m_RandomSeed; QString m_filename; - // Protect the array of active notes - QMutex m_notesRunningMutex; - // Protect synth when we are re-creating it. QMutex m_synthMutex; QMutex m_loadMutex; @@ -198,6 +211,10 @@ private: private: void freeInstance(); + void getInstrument(); + gigNote sampleToNote( gig::Sample* pSample, int midiNote ); + Dimension getDimensions( gig::Region* pRegion, int velocity, bool release ); + gigNote convertSampleRate( gigNote& old, int oldRate, int newRate ); friend class gigInstrumentView;