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.
This commit is contained in:
Garrett
2014-10-20 20:45:33 -07:00
parent 5b7eb30756
commit d9b4511321
3 changed files with 71 additions and 52 deletions

View File

@@ -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")

View File

@@ -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 <drfaygo/at/gmail/dot/com>
* Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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<gigNote>::iterator note = m_notes.begin(); note != m_notes.end(); ++note )
{
for( std::list<gigNote>::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<gigNote>::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<GIGPluginData *>( _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<gigNote>::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<gigNote>::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;

View File

@@ -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;