From d9b45113210c007f948c7559672b6c340eb5fd45 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 20 Oct 2014 20:45:33 -0700 Subject: [PATCH] Mutex for note output, delete note after fade out Hopefully the separate mutex for playing the samples reduces the glitching. Deleting notes after fading out instead of after the entire sample finished playing (with many zeros after the fade out) will reduce the number of notes playing at the same time which should allow for more actually-heard notes to be played. Also, moved delete note code from release function into the rendering the notes to the output function. This seemed to fix notes occasionally not being released. --- plugins/gig_player/CMakeLists.txt | 2 +- plugins/gig_player/gig_player.cpp | 118 +++++++++++++++++------------- plugins/gig_player/gig_player.h | 3 + 3 files changed, 71 insertions(+), 52 deletions(-) diff --git a/plugins/gig_player/CMakeLists.txt b/plugins/gig_player/CMakeLists.txt index b989479f5..b786c0fe4 100644 --- a/plugins/gig_player/CMakeLists.txt +++ b/plugins/gig_player/CMakeLists.txt @@ -1,6 +1,6 @@ if(LMMS_HAVE_GIG) INCLUDE(BuildPlugin) - INCLUDE_DIRECTORIES(${GIG_INCLUDE_DIRS}{) + INCLUDE_DIRECTORIES(${GIG_INCLUDE_DIRS}) # Required for not crashing loading files with libgig SET(GCC_COVERAGE_COMPILE_FLAGS "-fexceptions -std=c++11") diff --git a/plugins/gig_player/gig_player.cpp b/plugins/gig_player/gig_player.cpp index 8b2e2edef..26a085473 100644 --- a/plugins/gig_player/gig_player.cpp +++ b/plugins/gig_player/gig_player.cpp @@ -1,9 +1,14 @@ /* - * gig_player.cpp - a gig player using libgig + * gig_player.cpp - a gig player using libgig (based on sf2 player plugin) * * Copyright (c) 2008 Paul Giblock * Copyright (c) 2009-2014 Tobias Doerffel * + * Based partially on some code from LinuxSampler (also GPL): + * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck + * Copyright (C) 2005-2008 Christian Schoenebeck + * Copyright (C) 2009-2010 Christian Schoenebeck and Grigor Iliev + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program is free software; you can redistribute it and/or @@ -367,24 +372,47 @@ void gigInstrument::play( sampleFrame * _working_buffer ) _working_buffer[i][1] = 0; } - m_synthMutex.lock(); + m_notesMutex.lock(); - for( int i = 0; i < frames; ++i ) + // Fill with portions of the note samples + for( std::list::iterator note = m_notes.begin(); note != m_notes.end(); ++note ) { - for( std::list::iterator note = m_notes.begin(); note != m_notes.end(); ++note ) + if( note->size != 0 ) { - if( note->position < note->size && note->size > 0 ) + for( int i = 0; i < frames && note->position < note->noteEnd; ++i, ++note->position ) { - _working_buffer[i][0] += note->note[note->position][0]*m_gain.value(); - _working_buffer[i][1] += note->note[note->position][1]*m_gain.value(); - ++note->position; + _working_buffer[i][0] += note->note[note->position][0]; + _working_buffer[i][1] += note->note[note->position][1]; } } } - m_synthMutex.unlock(); + m_notesMutex.unlock(); + + // Set gain properly based on volume control + for( int i = 0; i < frames; ++i) + { + _working_buffer[i][0] *= m_gain.value(); + _working_buffer[i][1] *= m_gain.value(); + } instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL ); + + m_notesMutex.lock(); + + // Delete ended notes + for( std::list::iterator note = m_notes.begin(); note != m_notes.end(); ++note ) + { + if( note->size == 0 || note->position >= note->noteEnd ) + { + note = m_notes.erase(note); + + if (note == m_notes.end()) + break; + } + } + + m_notesMutex.unlock(); } @@ -393,28 +421,7 @@ void gigInstrument::play( sampleFrame * _working_buffer ) void gigInstrument::deleteNotePluginData( NotePlayHandle * _n ) { GIGPluginData * pluginData = static_cast( _n->m_pluginData ); - m_synthMutex.lock(); - - // Continue looping starting at the beginning. Erasing - // invalidates the iterator. - bool deleted = false; - - // Delete ended notes - do - { - deleted = false; - - for( std::list::iterator i = m_notes.begin(); i != m_notes.end(); ++i ) - { - if( i->position >= i->size ) - { - m_notes.erase(i); - deleted = true; - break; - } - } - } - while( deleted ); + m_notesMutex.lock(); // Is the note supposed to have a release sample played? bool noteRelease = false; @@ -423,33 +430,30 @@ void gigInstrument::deleteNotePluginData( NotePlayHandle * _n ) // TODO: implement proper ADSR from GIG specs for( std::list::iterator i = m_notes.begin(); i != m_notes.end(); ++i ) { - if( i->midiNote == pluginData->midiNote ) + if( i->midiNote == pluginData->midiNote && !i->fadeOut ) { noteRelease = i->release; float fadeOut = i->releaseTime; // Seconds - int len = std::min( int( floor( fadeOut * engine::mixer()->processingSampleRate() ) ), + int len = std::min( int( std::floor( fadeOut * engine::mixer()->processingSampleRate() ) ), i->size-i->position ); int endPoint = i->position+len; + i->noteEnd = endPoint; // Delete note after it becomes zero + i->fadeOut = true; // We're now fading this thing out + if (len < 0) - break; + continue; 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(); + m_notesMutex.unlock(); if( noteRelease ) { @@ -537,7 +541,7 @@ Dimension gigInstrument::getDimensions( gig::Region* pRegion, int velocity, bool dim.DimValues[i] = 0; break; default: - qDebug() << "Error: Unknown dimension"; + qDebug() << "gigInstrument: Unknown dimension"; dim.DimValues[i] = 0; } } @@ -559,7 +563,7 @@ gigNote gigInstrument::sampleToNote( gig::Sample* pSample, int midiNote, note.midiNote = midiNote; if( pSample->Channels > 2 ) - qDebug() << "Warning: only using first two channels of GIG file"; + qDebug() << "gigInstrument: only using first two channels of GIG file"; // 24 Bit if( pSample->BitDepth == 24 ) @@ -569,8 +573,8 @@ gigNote gigInstrument::sampleToNote( gig::Sample* pSample, int midiNote, 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); + (pInt[3*pSample->Channels*i+1]<<16) | + (pInt[3*pSample->Channels*i+2]<<24); note.note[i][0] = 1.0/0x100000000*attenuation*valueLeft; @@ -668,6 +672,7 @@ gigNote gigInstrument::convertSampleRate( gigNote& old, int oldRate, int newRate src_data.src_ratio = (double) note.size / old.size; src_data.end_of_input = 0; + // TODO: should we have multiple sample rate converters? m_srcMutex.lock(); int error = src_process( m_srcState, &src_data ); m_srcMutex.unlock(); @@ -740,8 +745,6 @@ void gigInstrument::addNotes( int midiNote, int velocity, bool release ) 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) @@ -752,7 +755,10 @@ void gigInstrument::addNotes( int midiNote, int velocity, bool release ) gigNote note = sampleToNote( pSample, midiNote, attenuation, dim.release, pDimRegion->EG1Release ); + m_notesMutex.lock(); m_notes.push_back(note); + m_notesMutex.unlock(); + break; // Only grab one sample to play } } @@ -970,8 +976,10 @@ gigNote::gigNote() : midiNote( -1 ), note( NULL ), size( 0 ), + noteEnd( 0 ), release( false ), - releaseTime( 0 ) + releaseTime( 0 ), + fadeOut( false ) { } @@ -979,8 +987,10 @@ gigNote::gigNote(int size, bool release, float releaseTime ) : position( 0 ), midiNote( -1 ), size( size ), + noteEnd( size ), release( release ), - releaseTime( releaseTime ) + releaseTime( releaseTime ), + fadeOut( false ) { if( size > 0 ) note = new sampleFrame[size]; @@ -998,8 +1008,10 @@ gigNote::gigNote( const gigNote& g ) : midiNote( g.midiNote ), note( NULL ), size( g.size ), + noteEnd( g.noteEnd ), release( g.release ), - releaseTime( g.releaseTime ) + releaseTime( g.releaseTime ), + fadeOut( g.fadeOut ) { if (size > 0) { @@ -1019,8 +1031,10 @@ gigNote::gigNote( gigNote&& g ) : midiNote( g.midiNote ), note( NULL ), size( 0 ), + noteEnd( 0 ), release( g.release ), - releaseTime( g.releaseTime ) + releaseTime( g.releaseTime ), + fadeOut( g.fadeOut ) { *this = std::move( g ); } @@ -1034,11 +1048,13 @@ gigNote& gigNote::operator=( gigNote&& g ) delete[] note; size = g.size; + noteEnd = g.noteEnd; note = g.note; position = g.position; midiNote = g.midiNote; release = g.release; releaseTime = g.releaseTime; + fadeOut = g.fadeOut; g.size = 0; g.note = NULL; diff --git a/plugins/gig_player/gig_player.h b/plugins/gig_player/gig_player.h index ff210e3d4..08fb136af 100644 --- a/plugins/gig_player/gig_player.h +++ b/plugins/gig_player/gig_player.h @@ -98,8 +98,10 @@ public: int midiNote; sampleFrame* note; int size; // Don't try changing this... + int noteEnd; // Delete note before end of note if released and faded out early bool release; // Whether to trigger a release sample on key up float releaseTime; // After letting up, time to fade out + bool fadeOut; // Whether we have started fading out yet }; @@ -173,6 +175,7 @@ private: QMutex m_synthMutex; QMutex m_loadMutex; QMutex m_srcMutex; + QMutex m_notesMutex; sample_rate_t m_internalSampleRate; int m_lastMidiPitch;