From f544caf18b936803cf1e2a84fcc5096c2c2d96c0 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 2 May 2014 14:51:19 -0700 Subject: [PATCH] Key changing dimension, 24-bit samples, locking Support for 24-bit samples. It just so happens that the only GIG file I have that is 24-bit also has the key changing dimension set, so I implemented that so I could compare the output sound with that of Linux Sampler. It still sounds different, brighter in LMMS. Not sure if that has to do with gain, ADSR, or incorrect 24-bit decoding. I'm guessing ADSR. There were many crashing issues when playing notes when changing patches/instruments. More locking, less crashing. However, this also means that it is quite slow when playing a lot of notes with large samples and converting sample rates. Linux Sampler amazingly can handle 36 or so keys being pressed at once whereas LMMS nowhere near that many at the moment. --- plugins/gig_player/gig_player.cpp | 136 +++++++++++++++++------------- plugins/gig_player/gig_player.h | 2 + 2 files changed, 81 insertions(+), 57 deletions(-) diff --git a/plugins/gig_player/gig_player.cpp b/plugins/gig_player/gig_player.cpp index 071911fb4..2a334c1eb 100644 --- a/plugins/gig_player/gig_player.cpp +++ b/plugins/gig_player/gig_player.cpp @@ -84,6 +84,7 @@ gigInstrument::gigInstrument( InstrumentTrack * _instrument_track ) : m_instance( NULL ), m_instrument( NULL ), m_RandomSeed( 0 ), + m_currentKeyDimension( 0 ), m_filename( "" ), m_lastMidiPitch( -1 ), m_lastMidiPitchRange( -1 ), @@ -193,20 +194,14 @@ void gigInstrument::freeInstance() // No more references if( m_instance->refCount <= 0 ) { - qDebug() << "Really deleting " << m_filename; - s_instances.remove( m_filename ); delete m_instance; } - // Just remove our reference - else - { - qDebug() << "un-referencing " << m_filename; - } - s_instancesMutex.unlock(); + s_instancesMutex.unlock(); m_instance = NULL; } + m_synthMutex.unlock(); } @@ -230,10 +225,7 @@ void gigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) // Increment Reference if( s_instances.contains( relativePath ) ) { - qDebug() << "Using existing reference to " << relativePath; - m_instance = s_instances[ relativePath ]; - m_instance->refCount++; } @@ -258,15 +250,11 @@ void gigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) m_synthMutex.unlock(); if( succeeded ) - { m_filename = relativePath; - emit fileChanged(); - } else - { m_filename = ""; - emit fileChanged(); - } + + emit fileChanged(); delete[] gigAscii; @@ -291,8 +279,13 @@ void gigInstrument::updatePatch() QString gigInstrument::getCurrentPatchName() { + m_synthMutex.lock(); + if( !m_instance ) + { + m_synthMutex.unlock(); return ""; + } int iBankSelected = m_bankNum.value(); int iProgSelected = m_patchNum.value(); @@ -311,12 +304,14 @@ QString gigInstrument::getCurrentPatchName() if( name == "" ) name = ""; + m_synthMutex.unlock(); return name; } pInstrument = m_instance->gig.GetNextInstrument(); } + m_synthMutex.unlock(); return ""; } @@ -366,7 +361,7 @@ void gigInstrument::play( sampleFrame * _working_buffer ) const fpp_t frames = engine::mixer()->framesPerPeriod(); // Initialize to zeros - for (int i = 0; i < frames; ++i) + for( int i = 0; i < frames; ++i ) { _working_buffer[i][0] = 0; _working_buffer[i][1] = 0; @@ -432,7 +427,7 @@ void gigInstrument::deleteNotePluginData( NotePlayHandle * _n ) { noteRelease = i->release; - float fadeOut = 0.5; // Seconds + float fadeOut = 0.25; // Seconds int len = std::min( int( floor( fadeOut * engine::mixer()->processingSampleRate() ) ), i->size-i->position ); int endPoint = i->position+len; @@ -456,7 +451,7 @@ void gigInstrument::deleteNotePluginData( NotePlayHandle * _n ) m_synthMutex.unlock(); - if (noteRelease) + if( noteRelease ) { // Add the release notes if available const int baseVelocity = instrumentTrack()->midiPort()->baseVelocity(); @@ -497,14 +492,11 @@ Dimension gigInstrument::getDimensions( gig::Region* pRegion, int velocity, bool 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.release = true; 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? + dim.DimValues[i] = (uint) (m_currentKeyDimension * pRegion->pDimensionDefinitions[i].zones); break; case gig::dimension_roundrobin: case gig::dimension_roundrobinkeyboard: @@ -558,47 +550,56 @@ Dimension gigInstrument::getDimensions( gig::Region* pRegion, int velocity, bool gigNote gigInstrument::sampleToNote( gig::Sample* pSample, int midiNote, float attenuation, bool release ) { - if( !pSample ) + if( !pSample || pSample->Channels == 0 ) return gigNote(); gig::buffer_t buf = pSample->LoadSampleData(); gigNote note( pSample->SamplesTotal, release ); note.midiNote = midiNote; + if( pSample->Channels > 2 ) + qDebug() << "Warning: only using first two channels of GIG file"; + // 24 Bit - // TODO: implement this if( pSample->BitDepth == 24 ) { - qDebug() << "Error: not 16 bit... not implemented"; - /*int n = pSample->SamplesTotal * pSample->Channels; + uint8_t* pInt = static_cast( buf.pStart ); - 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);*/ + for( int i = 0; i < pSample->SamplesTotal/pSample->Channels; ++i ) + { + int32_t valueLeft = (pInt[3*pSample->Channels*i]<<8) | + (pInt[3*pSample->Channels*i+1]<<16) | + (pInt[3*pSample->Channels*i+2]<<24); + + note.note[i][0] = 1.0/0x100000000*attenuation*valueLeft; + + if( pSample->Channels == 1 ) + { + note.note[i][1] = note.note[i][0]; + } + else + { + int32_t valueRight = (pInt[3*pSample->Channels*i+3]<<8) | + (pInt[3*pSample->Channels*i+4]<<16) | + (pInt[3*pSample->Channels*i+5]<<24); + + note.note[i][1] = 1.0/0x100000000*attenuation*valueRight; + } + } } // 16 Bit else { - int16_t* pInt = reinterpret_cast( buf.pStart ); + int16_t* pInt = static_cast( buf.pStart ); - if( pSample->Channels <= 2 ) + for( int i = 0; i < pSample->SamplesTotal/pSample->Channels; ++i ) { - for( int i = 0; i < pSample->SamplesTotal/pSample->Channels; ++i ) - { - note.note[i][0] = 1.0/0x10000*pInt[pSample->Channels*i] * attenuation; + note.note[i][0] = 1.0/0x10000*pInt[pSample->Channels*i] * attenuation; - 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] * attenuation; - } - } - else - { - qDebug() << "Error: not stereo... not implemented"; + 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] * attenuation; } } @@ -609,7 +610,6 @@ gigNote gigInstrument::sampleToNote( gig::Sample* pSample, int midiNote, float a int outputRate = engine::mixer()->processingSampleRate(); if( sampleRate != outputRate ) { - qDebug() << "Warning: converting sample rate"; gigNote converted = convertSampleRate( note, sampleRate, outputRate ); return converted; } @@ -628,6 +628,8 @@ void gigInstrument::getInstrument() int iBankSelected = m_bankNum.value(); int iProgSelected = m_patchNum.value(); + m_synthMutex.lock(); + if( m_instance ) { gig::Instrument* pInstrument = m_instance->gig.GetFirstInstrument(); @@ -645,6 +647,8 @@ void gigInstrument::getInstrument() m_instrument = pInstrument; } + + m_synthMutex.unlock(); } @@ -662,7 +666,10 @@ gigNote gigInstrument::convertSampleRate( gigNote& old, int oldRate, int newRate src_data.output_frames = note.size; src_data.src_ratio = (double) note.size / old.size; src_data.end_of_input = 0; + + m_srcMutex.lock(); int error = src_process( m_srcState, &src_data ); + m_srcMutex.unlock(); if( error ) { @@ -682,7 +689,7 @@ gigNote gigInstrument::convertSampleRate( gigNote& old, int oldRate, int newRate void gigInstrument::updateSampleRate() { - m_synthMutex.lock(); + m_srcMutex.lock(); if( m_srcState != NULL ) src_delete( m_srcState ); @@ -693,7 +700,7 @@ void gigInstrument::updateSampleRate() if( m_srcState == NULL || error ) qCritical( "error while creating libsamplerate data structure in gigInstrument::updateSampleRate()" ); - m_synthMutex.unlock(); + m_srcMutex.unlock(); } @@ -701,10 +708,21 @@ void gigInstrument::updateSampleRate() // Find the sample we want to play (based on velocity, etc.) void gigInstrument::addNotes( int midiNote, int velocity, bool release ) { + m_synthMutex.lock(); + if( m_instrument ) { gig::Region* pRegion = m_instrument->GetFirstRegion(); + // Change key dimension, e.g. change samples based on what key is pressed + // in a certain range. + if( midiNote >= m_instrument->DimensionKeyRange.low && midiNote + <= m_instrument->DimensionKeyRange.high ) + m_currentKeyDimension = float( midiNote - + m_instrument->DimensionKeyRange.low ) / ( + m_instrument->DimensionKeyRange.high - + m_instrument->DimensionKeyRange.low + 1 ); + while( pRegion ) { Dimension dim = getDimensions( pRegion, velocity, release ); @@ -718,26 +736,30 @@ void gigInstrument::addNotes( int midiNote, int velocity, bool release ) if( midiNote >= keyLow && midiNote <= keyHigh ) { - float attenuation; + float attenuation = pDimRegion->GetVelocityAttenuation( velocity );; float length = (double) pSample->SamplesTotal / engine::mixer()->processingSampleRate(); + //qDebug() << "Playing sample " << QString::fromStdString( pSample->pInfo->Name ); + + // TODO: sample panning? looping? crossfade different layers? + if (release) - attenuation = 1 - 0.01053 * (256 >> pDimRegion->ReleaseTriggerDecay) * length; + attenuation *= 1 - 0.01053 * (256 >> pDimRegion->ReleaseTriggerDecay) * length; else - attenuation = pDimRegion->SampleAttenuation; + attenuation *= pDimRegion->SampleAttenuation; gigNote note = sampleToNote( pSample, midiNote, attenuation, dim.release ); - m_synthMutex.lock(); m_notes.push_back(note); - m_synthMutex.unlock(); } } pRegion = m_instrument->GetNextRegion(); } } + + m_synthMutex.unlock(); } @@ -789,7 +811,6 @@ gigInstrumentView::gigInstrumentView( Instrument * _instrument, QWidget * _paren toolTip::add( m_patchDialogButton, tr( "Choose the patch" ) ); - // LCDs m_bankNumLcd = new LcdSpinBox( 3, "21pink", this ); m_bankNumLcd->move(131, 62); @@ -827,6 +848,7 @@ gigInstrumentView::~gigInstrumentView() + void gigInstrumentView::modelChanged() { gigInstrument * k = castModel(); diff --git a/plugins/gig_player/gig_player.h b/plugins/gig_player/gig_player.h index 00285a26f..d05342bf2 100644 --- a/plugins/gig_player/gig_player.h +++ b/plugins/gig_player/gig_player.h @@ -164,12 +164,14 @@ private: gigInstance* m_instance; gig::Instrument* m_instrument; uint32_t m_RandomSeed; + float m_currentKeyDimension; QString m_filename; // Protect synth when we are re-creating it. QMutex m_synthMutex; QMutex m_loadMutex; + QMutex m_srcMutex; sample_rate_t m_internalSampleRate; int m_lastMidiPitch;