From d0a821947af2765615f13d39e5940707e19ac51a Mon Sep 17 00:00:00 2001 From: Garrett Date: Sun, 9 Nov 2014 12:22:45 -0800 Subject: [PATCH] Fixed resampling issues When providing extra frames, libsamplerate stores the extras internally and outputs them in the next period. But, when it has enough internally to output a whole period, it just outputs the internal buffer while not using any more input frames. Now I provide some extra frames, check to see how many frames we used actually used, and update the sample positions and ADSR accordingly. --- plugins/GigPlayer/GigPlayer.cpp | 69 +++++++++++++++++++++++++++------ plugins/GigPlayer/GigPlayer.h | 3 +- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index bf7abf468..e475efa98 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -72,6 +72,12 @@ Plugin::Descriptor PLUGIN_EXPORT gigplayer_plugin_descriptor = +// Margins for extra samples to provide to libsamplerate to reduce glitching +const f_cnt_t GIGMARGIN[] = { 128, 64, 64, 4, 4 }; + + + + struct GIGPluginData { int midiNote; @@ -340,6 +346,7 @@ void GigInstrument::playNote( NotePlayHandle * _n, sampleFrame * ) void GigInstrument::play( sampleFrame * _working_buffer ) { const fpp_t frames = engine::mixer()->framesPerPeriod(); + const int interpolation = engine::mixer()->currentQualitySettings().libsrcInterpolation(); // Initialize to zeros std::memset( &_working_buffer[0][0], 0, DEFAULT_CHANNELS * frames * sizeof( float ) ); @@ -364,6 +371,9 @@ void GigInstrument::play( sampleFrame * _working_buffer ) // How many frames we'll be grabbing from the sample int samples = frames; + // How many we actually used from the sample + int used = frames; + for( QList::iterator note = m_notes.begin(); note != m_notes.end(); ++note ) { // Process notes in the KeyUp state, adding release samples if desired @@ -450,7 +460,10 @@ void GigInstrument::play( sampleFrame * _working_buffer ) // Read a different number of samples depending on the sample rate, but // resample it to always output the right number of frames - samples = round( (double) frames * oldRate / newRate ); + samples = frames * oldRate / newRate; + + // We need a bit of margin so we don't get glitching + samples += GIGMARGIN[interpolation]; // Buffer for the resampled data convertBuf = new sampleFrame[samples]; @@ -494,9 +507,6 @@ void GigInstrument::play( sampleFrame * _working_buffer ) buf.NullExtensionSize = allocationsize - buf.Size; std::memset( (int8_t*) buf.pStart + buf.Size, 0, buf.NullExtensionSize ); - // Save the new position in the sample - sample->pos = sample->sample->GetPos(); - // Convert from 16 or 24 bit into 32-bit float if( sample->sample->BitDepth == 24 ) // 24 bit { @@ -550,10 +560,13 @@ void GigInstrument::play( sampleFrame * _working_buffer ) // Cleanup delete[] (int8_t*) buf.pStart; - // Apply ADSR + // Apply ADSR using a copy so if we don't use these samples when + // resampling, the ADSR doesn't get messed up + ADSR copy = sample->adsr; + for( int i = 0; i < samples; ++i ) { - double amplitude = sample->adsr.value(); + double amplitude = copy.value(); m_sampleData[i][0] *= amplitude; m_sampleData[i][1] *= amplitude; } @@ -583,14 +596,37 @@ void GigInstrument::play( sampleFrame * _working_buffer ) { // If an error occurred, it's better to render nothing than have some // screeching high-volume noise - if( !convertSampleRate( *convertBuf, *_working_buffer, samples, frames, oldRate, newRate ) ) + if( !convertSampleRate( *convertBuf, *_working_buffer, samples, frames, + oldRate, newRate, used ) ) { - std::memset( &_working_buffer[0][0], 0, DEFAULT_CHANNELS * frames * sizeof( float ) ); + std::memset( &_working_buffer[0][0], 0, + DEFAULT_CHANNELS * frames * sizeof( float ) ); } delete[] convertBuf; } + // Update the note positions with how many samples we actually used + for( QList::iterator note = m_notes.begin(); note != m_notes.end(); ++note ) + { + // Only process the notes if we're in a playing state + if( !( note->state == PlayingKeyDown || + note->state == PlayingKeyUp ) ) + { + continue; + } + + for( QList::iterator sample = note->samples.begin(); + sample != note->samples.end(); ++sample ) + { + if( sample->sample != NULL ) + { + sample->pos += used; + sample->adsr.inc( used ); + } + } + } + m_notesMutex.unlock(); m_synthMutex.unlock(); @@ -822,20 +858,22 @@ void GigInstrument::getInstrument() bool GigInstrument::convertSampleRate( sampleFrame & oldBuf, sampleFrame & newBuf, - int oldSize, int newSize, int oldRate, int newRate ) + int oldSize, int newSize, int oldRate, int newRate, int& used ) { SRC_DATA src_data; src_data.data_in = &oldBuf[0]; src_data.data_out = &newBuf[0]; src_data.input_frames = oldSize; src_data.output_frames = newSize; - src_data.src_ratio = (double) newSize / oldSize; + src_data.src_ratio = (double) newRate / oldRate; src_data.end_of_input = 0; m_srcMutex.lock(); int error = src_process( m_srcState, &src_data ); m_srcMutex.unlock(); + used = src_data.input_frames_used; + if( error != 0 ) { qCritical( "GigInstrument: error while resampling: %s", src_strerror( error ) ); @@ -848,7 +886,7 @@ bool GigInstrument::convertSampleRate( sampleFrame & oldBuf, sampleFrame & newBu return false; } - if( src_data.input_frames_used == 0 || src_data.output_frames_gen == 0 ) + if( src_data.output_frames_gen == 0 ) { qCritical( "GigInstrument: could not resample, no frames generated" ); return false; @@ -1196,6 +1234,15 @@ double ADSR::value() return currentAmplitude; } +// Increment internal positions a certain number of times +void ADSR::inc( int num ) +{ + for( int i = 0; i < num; ++i ) + { + value(); + } +} + diff --git a/plugins/GigPlayer/GigPlayer.h b/plugins/GigPlayer/GigPlayer.h index 51f2b2b7f..03a207650 100644 --- a/plugins/GigPlayer/GigPlayer.h +++ b/plugins/GigPlayer/GigPlayer.h @@ -115,6 +115,7 @@ public: void keyup(); // We will begin releasing starting now bool done(); // Is this sample done playing? double value(); // What's the current amplitude + void inc( int num ); // Increment internal positions by num } ; @@ -273,7 +274,7 @@ private: // Convert sample rates bool convertSampleRate( sampleFrame & oldBuf, sampleFrame & newBuf, - int oldSize, int newSize, int oldRate, int newRate ); + int oldSize, int newSize, int oldRate, int newRate, int& used ); friend class GigInstrumentView;