From 086860e6348342a817cce27a89db0a176886581c Mon Sep 17 00:00:00 2001 From: Paul Giblock Date: Wed, 16 Apr 2008 04:07:32 +0000 Subject: [PATCH] Fixed sf2-player reference sharing for good git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@926 0778d3d1-df1d-0410-868b-ea421aaaa00d --- ChangeLog | 8 ++ plugins/sf2_player/sf2_player.cpp | 201 +++++++++++++----------------- plugins/sf2_player/sf2_player.h | 10 +- 3 files changed, 95 insertions(+), 124 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5c5504b28..83bc6d700 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2008-04-16 Paul Giblock + + * plugins/sf2_player/sf2_player.cpp: + * plugins/sf2_player/sf2_player.h: + rewrote reference-sharing. Should return near-100% memory on unload, and + should properly handle samplerate changes. Now we can render large + sf2-based projects without consuming a gig of ram :) + 2008-04-15 Tobias Doerffel * src/gui/file_browser.cpp: diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index c0736c3ea..3c23e6ffc 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -77,6 +77,7 @@ int (* sf2Instrument::s_origFree)( fluid_sfont_t * ); sf2Instrument::sf2Instrument( instrumentTrack * _instrument_track ) : instrument( _instrument_track, &sf2player_plugin_descriptor ), m_srcState( NULL ), + m_font( NULL ), m_fontId( 0 ), m_filename( "" ), m_bankNum( -1, -1, 999, 1, this ), @@ -95,15 +96,6 @@ sf2Instrument::sf2Instrument( instrumentTrack * _instrument_track ) : // everytime we load a new soundfont. m_synth = new_fluid_synth( m_settings ); - // Install our callbacks - fluid_sfloader_t * pLoader - = (fluid_sfloader_t *) malloc( sizeof( fluid_sfloader_t ) ); - - pLoader->data = (void *) this; - pLoader->load = sf2Instrument::sfloaderLoad; - pLoader->free = sf2Instrument::sfloaderFree; - fluid_synth_add_sfloader( m_synth, pLoader ); - instrumentPlayHandle * iph = new instrumentPlayHandle( this ); engine::getMixer()->addPlayHandle( iph ); @@ -117,7 +109,6 @@ sf2Instrument::sf2Instrument( instrumentTrack * _instrument_track ) : connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) ); - } @@ -125,12 +116,14 @@ sf2Instrument::sf2Instrument( instrumentTrack * _instrument_track ) : sf2Instrument::~sf2Instrument() { engine::getMixer()->removePlayHandles( getInstrumentTrack() ); + freeFont(); delete_fluid_synth( m_synth ); delete_fluid_settings( m_settings ); if( m_srcState != NULL ) { src_delete( m_srcState ); } + } @@ -161,43 +154,85 @@ QString sf2Instrument::nodeName( void ) const +void sf2Instrument::freeFont( void ) +{ + QTextStream cout( stdout, QIODevice::WriteOnly ); + + m_synthMutex.lock(); + + if ( m_font != NULL ) + { + --(m_font->refCount); + + // No more references + if( m_font->refCount <= 0 ) + { + cout << "Really deleting " << m_filename << endl; + + sf2Instrument::s_fonts.remove( m_filename ); + fluid_synth_sfunload( m_synth, m_fontId, TRUE ); + + delete m_font; + } + + // Just remove our reference + else + { + cout << "un-referencing " << m_filename << endl; + + fluid_synth_remove_sfont( m_synth, m_font->fluidFont ); + } + + m_font = NULL; + } + m_synthMutex.unlock(); +} + + + void sf2Instrument::openFile( const QString & _sf2File ) { emit fileLoading(); - // Remove synth from this fluidSynth (but not necessarily memory) - if ( m_filename != "" ) - { - // Recreate synth - m_synthMutex.lock(); - delete_fluid_synth( m_synth ); - m_synth = new_fluid_synth( m_settings ); - m_synthMutex.unlock(); - - // No need to explicitly delete font. Deleting fluidSynth calls free - // on all fonts. This, causes sfloaderFreeFont to run. - } - char * sf2Ascii = qstrdup( qPrintable( _sf2File ) ); - m_fontId = fluid_synth_sfload( m_synth, sf2Ascii, TRUE ); - QString sf2Key( sf2Ascii ); + // free reference to soundfont if one is selected + freeFont(); + + m_synthMutex.lock(); + + // Increment Reference + if( s_fonts.contains( _sf2File ) ) + { + QTextStream cout( stdout, QIODevice::WriteOnly ); + cout << "Using existing reference to " << _sf2File << endl; + + m_font = s_fonts[ _sf2File ]; + + m_font->refCount++; + + m_fontId = fluid_synth_add_sfont( m_synth, m_font->fluidFont ); + } // Add to map, if doesn't exist. - // We can't do this in callback because fluid hasn't created the sfont yet - if( !s_fonts.contains( sf2Key ) && fluid_synth_sfcount( m_synth ) > 0 ) + else { - // Grab this sf from the top of the stack - fluid_sfont_t * sfont = fluid_synth_get_sfont( m_synth, 0 ); + m_fontId = fluid_synth_sfload( m_synth, sf2Ascii, TRUE ); - // Hold on to real free function for executing later. - s_origFree = sfont->free; - sfont->free = sf2Instrument::sfloaderFreeFont; - - // Finally, add font - s_fonts.insert( sf2Key, new sf2Font( sfont ) ); + if( fluid_synth_sfcount( m_synth ) > 0 ) + { + // Grab this sf from the top of the stack and add to list + m_font = new sf2Font( fluid_synth_get_sfont( m_synth, 0 ) ); + s_fonts.insert( _sf2File, m_font ); + } + else + { + // TODO: Couldn't load file! + } } + m_synthMutex.unlock(); + if( m_fontId >= 0 ) { m_patchNum.setValue( 0 ); @@ -230,26 +265,30 @@ void sf2Instrument::updateSampleRate( void ) fluid_settings_getnum( m_settings, "synth.sample-rate", &tempRate ); m_internalSampleRate = static_cast( tempRate ); - if( m_filename != "" ) + if( m_font ) { // Now, delete the old one and replace m_synthMutex.lock(); + fluid_synth_remove_sfont( m_synth, m_font->fluidFont ); delete_fluid_synth( m_synth ); - + // New synth m_synth = new_fluid_synth( m_settings ); - - // Load sfont, should be in memory and increment refCount - char * sf2Ascii = qstrdup( qPrintable( m_filename ) ); - m_fontId = fluid_synth_sfload( m_synth, sf2Ascii, TRUE ); - delete[] sf2Ascii; - -// openFile( m_filename ); + m_fontId = fluid_synth_add_sfont( m_synth, m_font->fluidFont ); m_synthMutex.unlock(); - + // synth program change (set bank and patch) updatePatch(); } + else + { + // Recreate synth with no soundfonts + m_synthMutex.lock(); + delete_fluid_synth( m_synth ); + m_synth = new_fluid_synth( m_settings ); + m_synthMutex.unlock(); + } + if( m_internalSampleRate < engine::getMixer()->sampleRate() ) { m_synthMutex.lock(); @@ -313,12 +352,6 @@ void sf2Instrument::play( bool _try_parallelizing, m_synthMutex.lock(); - // TODO: Toby can correctly interpolate for HQ mode. - // m_internalSampleRate is fluid's true sampling rate (basically 44100, 88200, or 96000) - // also, it looks like we will need to only get a portion of the frames and need - // to account properly for accumulated error (i.e: using frames*internalRate/mixerRate - // will not work unless the value is whole. Probably only a big deal for small period-sizes? - if( m_internalSampleRate < engine::getMixer()->sampleRate() && m_srcState != NULL ) { @@ -359,72 +392,6 @@ void sf2Instrument::play( bool _try_parallelizing, } - -int sf2Instrument::sfloaderFree( fluid_sfloader_t * _loader ) -{ - if( _loader != NULL ) - { - free( _loader ); - } - return 0; -} - - - -int sf2Instrument::sfloaderFreeFont( fluid_sfont_t * _sfont ) -{ - QString key = QString( _sfont->get_name(_sfont) ); - - sf2Font * f = sf2Instrument::s_fonts[key]; - - --(f->refCount); - - // No more references - if( f->refCount <= 0 ) - { - QTextStream cout( stdout, QIODevice::WriteOnly ); - cout << "Really deleting " << key << endl; - - sf2Instrument::s_fonts.remove( key ); - delete f; - - // Let fluidsynth free the font, as if we were not here - return s_origFree( _sfont ); - } - - // Make fluid think we freed the sfont - return 0; -} - - - -fluid_sfont_t * sf2Instrument::sfloaderLoad( - fluid_sfloader_t * _loader, const char *_filename ) -{ - if( _loader == NULL ) - { - return NULL; - } - - QString filename = _filename; - - if( sf2Instrument::s_fonts.contains( filename ) ) - { - QTextStream cout( stdout, QIODevice::WriteOnly ); - cout << "Using existing reference to " << filename << endl; - - sf2Font * font = sf2Instrument::s_fonts[ filename ]; - - font->refCount++; - return font->fluidFont; - } - - // fluidsynth will call next (default) loader... - return NULL; -} - - - void sf2Instrument::deleteNotePluginData( notePlayHandle * _n ) { int * midiNote = static_cast( _n->m_pluginData ); diff --git a/plugins/sf2_player/sf2_player.h b/plugins/sf2_player/sf2_player.h index 504dda9d1..0b7ee38f8 100644 --- a/plugins/sf2_player/sf2_player.h +++ b/plugins/sf2_player/sf2_player.h @@ -96,6 +96,8 @@ private: fluid_settings_t* m_settings; fluid_synth_t* m_synth; + sf2Font* m_font; + int m_fontId; QString m_filename; @@ -113,13 +115,7 @@ private: lcdSpinBoxModel m_patchNum; private: - // Our special callback functions - static int sfloaderFree( fluid_sfloader_t * _loader ); - static fluid_sfont_t * sfloaderLoad( - fluid_sfloader_t * _loader, const char * _filename ); - - static int sfloaderFreeFont( fluid_sfont_t * _soundFont ); - + void freeFont( void ); friend class sf2InstrumentView;