From 9d7c34098ce230d89b56b7bd887ad08bcb45b20c Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 6 Sep 2017 17:57:24 +0900 Subject: [PATCH 1/9] Fix Vibed when the "P" parameter has non-zero value (#3789) --- plugins/vibed/vibrating_string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vibed/vibrating_string.h b/plugins/vibed/vibrating_string.h index 6d620ace0..c21ed43cf 100644 --- a/plugins/vibed/vibrating_string.h +++ b/plugins/vibed/vibrating_string.h @@ -130,7 +130,7 @@ private: offset = ( m_randomize / 2.0f - m_randomize ) * r; _dl->data[i] = _scale * - _values[_dl->length - i] + + _values[_dl->length - i - 1] + offset; } for( int i = _pick; i < _dl->length; i++ ) From fe98a9aa431c1e463d698ece014c60bb99693196 Mon Sep 17 00:00:00 2001 From: DomClark Date: Thu, 24 Aug 2017 22:09:38 +0100 Subject: [PATCH 2/9] Fix some VST deadlocks/hangs --- include/MainWindow.h | 8 ++ include/RemotePlugin.h | 8 ++ plugins/vst_base/RemoteVstPlugin.cpp | 199 ++++++++++++++++++++------- plugins/vst_base/VstPlugin.cpp | 20 +-- src/gui/MainWindow.cpp | 12 +- 5 files changed, 186 insertions(+), 61 deletions(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index 69b430d33..1a58f868c 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "ConfigManager.h" #include "SubWindow.h" @@ -248,4 +249,11 @@ signals: } ; +class AutoSaveThread : public QThread +{ + Q_OBJECT +public: + void run(); +} ; + #endif diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index d016c5acd..725f51c2c 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -1072,6 +1072,14 @@ RemotePluginBase::message RemotePluginBase::waitForMessage( const message & _wm, bool _busy_waiting ) { +#ifndef BUILD_REMOTE_PLUGIN_CLIENT + if( _busy_waiting ) + { + // No point processing events outside of the main thread + _busy_waiting = QThread::currentThread() == + QCoreApplication::instance()->thread(); + } +#endif while( !isInvalid() ) { #ifndef BUILD_REMOTE_PLUGIN_CLIENT diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 03e160a8d..74af607c5 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -44,6 +44,10 @@ #ifdef LMMS_BUILD_LINUX +#ifndef NOMINMAX +#define NOMINMAX +#endif + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -67,6 +71,7 @@ #include +#include #include @@ -113,7 +118,7 @@ class RemoteVstPlugin; RemoteVstPlugin * __plugin = NULL; -DWORD __GuiThreadID = 0; +HWND __MessageHwnd = NULL; @@ -244,8 +249,38 @@ public: pthread_mutex_unlock( &m_pluginLock ); } + inline bool isProcessing() const + { + return m_processing; + } + + inline void setProcessing( bool processing ) + { + m_processing = processing; + } + + inline void queueMessage( const message & m ) { + m_messageList.push( m ); + } + + inline bool shouldGiveIdle() const + { + return m_shouldGiveIdle; + } + + inline void setShouldGiveIdle( bool shouldGiveIdle ) + { + m_shouldGiveIdle = shouldGiveIdle; + } + + void idle(); + void processUIThreadMessages(); + static DWORD WINAPI processingThread( LPVOID _param ); - static DWORD WINAPI guiEventLoop( LPVOID _param ); + static bool setupMessageWindow(); + static DWORD WINAPI guiEventLoop(); + static LRESULT CALLBACK messageWndProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ); private: @@ -303,6 +338,10 @@ private: bool m_initialized; pthread_mutex_t m_pluginLock; + bool m_processing; + + std::queue m_messageList; + bool m_shouldGiveIdle; float * * m_inputs; @@ -348,6 +387,9 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : m_windowHeight( 0 ), m_initialized( false ), m_pluginLock(), + m_processing( false ), + m_messageList(), + m_shouldGiveIdle( false ), m_inputs( NULL ), m_outputs( NULL ), m_midiEvents(), @@ -1418,8 +1460,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, SHOW_CALLBACK ("amc: audioMasterIdle\n" ); // call application idle routine (this will // call effEditIdle for all open editors too) - PostThreadMessage( __GuiThreadID, - WM_USER, GiveIdle, 0 ); + PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 ); return 0; case audioMasterPinConnected: @@ -1720,8 +1761,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, case audioMasterUpdateDisplay: SHOW_CALLBACK( "amc: audioMasterUpdateDisplay\n" ); // something has changed, update 'multi-fx' display - PostThreadMessage( __GuiThreadID, - WM_USER, GiveIdle, 0 ); + PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 ); return 0; #if kVstVersion > 2 @@ -1754,6 +1794,43 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, +void RemoteVstPlugin::idle() +{ + if( isProcessing() ) + { + setShouldGiveIdle( true ); + return; + } + setProcessing( true ); + pluginDispatch( effEditIdle ); + setShouldGiveIdle( false ); + setProcessing( false ); + // We might have received a message whilst idling + processUIThreadMessages(); +} + + + + +void RemoteVstPlugin::processUIThreadMessages() +{ + setProcessing( true ); + while( m_messageList.size() ) + { + processMessage( m_messageList.front() ); + m_messageList.pop(); + if( shouldGiveIdle() ) + { + pluginDispatch( effEditIdle ); + setShouldGiveIdle( false ); + } + } + setProcessing( false ); +} + + + + DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) { RemoteVstPlugin * _this = static_cast( _param ); @@ -1767,7 +1844,7 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) } else { - PostThreadMessage( __GuiThreadID, + PostMessage( __MessageHwnd, WM_USER, ProcessPluginMessage, (LPARAM) new message( m ) ); @@ -1775,7 +1852,7 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) } // notify GUI thread about shutdown - PostThreadMessage( __GuiThreadID, WM_USER, ClosePlugin, 0 ); + PostMessage( __MessageHwnd, WM_USER, ClosePlugin, 0 ); return 0; } @@ -1783,61 +1860,37 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) -DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param ) +bool RemoteVstPlugin::setupMessageWindow() { - RemoteVstPlugin * _this = static_cast( _param ); - HMODULE hInst = GetModuleHandle( NULL ); if( hInst == NULL ) { - _this->debugMessage( "guiEventLoop(): can't get " + __plugin->debugMessage( "setupMessageWindow(): can't get " "module handle\n" ); - return -1; + return false; } - HWND timerWindow = CreateWindowEx( 0, "LVSL", "dummy", + __MessageHwnd = CreateWindowEx( 0, "LVSL", "dummy", 0, 0, 0, 0, 0, NULL, NULL, hInst, NULL ); + SetWindowLongPtr( __MessageHwnd, GWLP_WNDPROC, + reinterpret_cast( RemoteVstPlugin::messageWndProc ) ); // install GUI update timer - SetTimer( timerWindow, 1000, 50, NULL ); + SetTimer( __MessageHwnd, 1000, 50, NULL ); + return true; +} + + + + +DWORD WINAPI RemoteVstPlugin::guiEventLoop() +{ MSG msg; - - bool quit = false; - while( quit == false && GetMessage( &msg, NULL, 0, 0 ) ) + while( GetMessage( &msg, NULL, 0, 0 ) > 0 ) { TranslateMessage( &msg ); DispatchMessage( &msg ); - - if( msg.message == WM_TIMER && _this->isInitialized() ) - { - // give plugin some idle-time for GUI-update - _this->pluginDispatch( effEditIdle ); - } - else if( msg.message == WM_USER ) - { - switch( msg.wParam ) - { - case ProcessPluginMessage: - { - message * m = (message *) msg.lParam; - _this->processMessage( *m ); - delete m; - break; - } - - case GiveIdle: - _this->pluginDispatch( effEditIdle ); - break; - - case ClosePlugin: - quit = true; - break; - - default: - break; - } - } } return 0; @@ -1846,6 +1899,49 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param ) +LRESULT CALLBACK RemoteVstPlugin::messageWndProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ) +{ + if( uMsg == WM_TIMER && __plugin->isInitialized() ) + { + // give plugin some idle-time for GUI-update + __plugin->idle(); + return 0; + } + else if( uMsg == WM_USER ) + { + switch( wParam ) + { + case ProcessPluginMessage: + { + message * m = (message *) lParam; + __plugin->queueMessage( *m ); + delete m; + if( !__plugin->isProcessing() ) + { + __plugin->processUIThreadMessages(); + } + return 0; + } + + case GiveIdle: + __plugin->idle(); + return 0; + + case ClosePlugin: + PostQuitMessage(0); + return 0; + + default: + break; + } + } + return DefWindowProc( hwnd, uMsg, wParam, lParam ); +} + + + + int main( int _argc, char * * _argv ) { #ifdef SYNC_WITH_SHM_FIFO @@ -1893,7 +1989,10 @@ int main( int _argc, char * * _argv ) if( __plugin->isInitialized() ) { - __GuiThreadID = GetCurrentThreadId(); + if( RemoteVstPlugin::setupMessageWindow() == false ) + { + return -1; + } if( CreateThread( NULL, 0, RemoteVstPlugin::processingThread, __plugin, 0, NULL ) == NULL ) { @@ -1901,7 +2000,7 @@ int main( int _argc, char * * _argv ) "processingThread\n" ); return -1; } - RemoteVstPlugin::guiEventLoop( __plugin ); + RemoteVstPlugin::guiEventLoop(); } diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 22c9f7c20..9873d28e5 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -389,7 +389,7 @@ int VstPlugin::currentProgram() { lock(); sendMessage( message( IdVstCurrentProgram ) ); - waitForMessage( IdVstCurrentProgram ); + waitForMessage( IdVstCurrentProgram, true ); unlock(); return m_currentProgram; @@ -401,7 +401,7 @@ const QMap & VstPlugin::parameterDump() { lock(); sendMessage( IdVstGetParameterDump ); - waitForMessage( IdVstParameterDump ); + waitForMessage( IdVstParameterDump, true ); unlock(); return m_parameterDump; @@ -528,7 +528,7 @@ void VstPlugin::openPreset( ) QSTR_TO_STDSTR( QDir::toNativeSeparators( ofd.selectedFiles()[0] ) ) ) ); - waitForMessage( IdLoadPresetFile ); + waitForMessage( IdLoadPresetFile, true ); unlock(); } } @@ -540,7 +540,7 @@ void VstPlugin::setProgram( int index ) { lock(); sendMessage( message( IdVstSetProgram ).addInt( index ) ); - waitForMessage( IdVstSetProgram ); + waitForMessage( IdVstSetProgram, true ); unlock(); } @@ -551,7 +551,7 @@ void VstPlugin::rotateProgram( int offset ) { lock(); sendMessage( message( IdVstRotateProgram ).addInt( offset ) ); - waitForMessage( IdVstRotateProgram ); + waitForMessage( IdVstRotateProgram, true ); unlock(); } @@ -562,7 +562,7 @@ void VstPlugin::loadProgramNames() { lock(); sendMessage( message( IdVstProgramNames ) ); - waitForMessage( IdVstProgramNames ); + waitForMessage( IdVstProgramNames, true ); unlock(); } @@ -599,7 +599,7 @@ void VstPlugin::savePreset( ) QSTR_TO_STDSTR( QDir::toNativeSeparators( fns ) ) ) ); - waitForMessage( IdSavePresetFile ); + waitForMessage( IdSavePresetFile, true ); unlock(); } } @@ -611,7 +611,7 @@ void VstPlugin::setParam( int i, float f ) { lock(); sendMessage( message( IdVstSetParameter ).addInt( i ).addFloat( f ) ); - //waitForMessage( IdVstSetParameter ); + //waitForMessage( IdVstSetParameter, true ); unlock(); } @@ -640,7 +640,7 @@ void VstPlugin::loadChunk( const QByteArray & _chunk ) QSTR_TO_STDSTR( QDir::toNativeSeparators( tf.fileName() ) ) ). addInt( _chunk.size() ) ); - waitForMessage( IdLoadSettingsFromFile ); + waitForMessage( IdLoadSettingsFromFile, true ); unlock(); } } @@ -659,7 +659,7 @@ QByteArray VstPlugin::saveChunk() addString( QSTR_TO_STDSTR( QDir::toNativeSeparators( tf.fileName() ) ) ) ); - waitForMessage( IdSaveSettingsToFile ); + waitForMessage( IdSaveSettingsToFile, true ); unlock(); a = tf.readAll(); } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 63b931823..f65c96066 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1541,7 +1541,9 @@ void MainWindow::autoSave() "enablerunningautosave" ).toInt() || ! Engine::getSong()->isPlaying() ) ) { - Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile()); + AutoSaveThread * ast = new AutoSaveThread(); + connect( ast, SIGNAL( finished() ), ast, SLOT( deleteLater() ) ); + ast->start(); autoSaveTimerReset(); // Reset timer } else @@ -1553,3 +1555,11 @@ void MainWindow::autoSave() } } } + + + + +void AutoSaveThread::run() +{ + Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile()); +} From 7429cb8155238dbc839bac45d6400740bb63e72d Mon Sep 17 00:00:00 2001 From: DomClark Date: Thu, 7 Sep 2017 22:11:11 +0100 Subject: [PATCH 3/9] Fix segfault on VST plugin I/O change --- include/RemotePlugin.h | 10 ++++++ plugins/vst_base/RemoteVstPlugin.cpp | 54 +++++++++++++++++++++++++--- src/core/RemotePlugin.cpp | 6 ++++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 725f51c2c..b3ac4b676 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -424,6 +424,7 @@ enum RemoteMessageIDs IdChangeSharedMemoryKey, IdChangeInputCount, IdChangeOutputCount, + IdChangeInputOutputCount, IdShowUI, IdHideUI, IdSaveSettingsToString, @@ -919,6 +920,15 @@ public: sendMessage( message( IdChangeOutputCount ).addInt( _i ) ); } + void setInputOutputCount( int i, int o ) + { + m_inputCount = i; + m_outputCount = o; + sendMessage( message( IdChangeInputOutputCount ) + .addInt( i ) + .addInt( o ) ); + } + virtual int inputCount() const { return m_inputCount; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 74af607c5..67ce71ea8 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -249,6 +249,26 @@ public: pthread_mutex_unlock( &m_pluginLock ); } + inline void lockShm() + { + pthread_mutex_lock( &m_shmLock ); + } + + inline void unlockShm() + { + pthread_mutex_unlock( &m_shmLock ); + } + + inline bool isShmValid() + { + return m_shmValid; + } + + inline void setShmIsValid( bool valid ) + { + m_shmValid = valid; + } + inline bool isProcessing() const { return m_processing; @@ -347,6 +367,9 @@ private: float * * m_inputs; float * * m_outputs; + pthread_mutex_t m_shmLock; + bool m_shmValid; + typedef std::vector VstMidiEventList; VstMidiEventList m_midiEvents; @@ -392,6 +415,8 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : m_shouldGiveIdle( false ), m_inputs( NULL ), m_outputs( NULL ), + m_shmLock(), + m_shmValid( false ), m_midiEvents(), m_bpm( 0 ), m_currentSamplePos( 0 ), @@ -402,6 +427,7 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : { pthread_mutex_init( &m_pluginLock, NULL ); + pthread_mutex_init( &m_shmLock, NULL ); __plugin = this; @@ -500,6 +526,7 @@ RemoteVstPlugin::~RemoteVstPlugin() delete[] m_inputs; delete[] m_outputs; + pthread_mutex_destroy( &m_shmLock ); pthread_mutex_destroy( &m_pluginLock ); } @@ -836,6 +863,16 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) // now we're ready to fetch sound from VST-plugin + lock(); + lockShm(); + + if( !isShmValid() ) + { + unlockShm(); + unlock(); + return; + } + for( int i = 0; i < inputCount(); ++i ) { m_inputs[i] = &((float *) _in)[i * bufferSize()]; @@ -847,8 +884,6 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) memset( m_outputs[i], 0, bufferSize() * sizeof( float ) ); } - lock(); - #ifdef OLD_VST_SDK if( m_plugin->flags & effFlagsCanReplacing ) { @@ -864,6 +899,7 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) } #endif + unlockShm(); unlock(); m_currentSamplePos += bufferSize(); @@ -1380,14 +1416,19 @@ void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) void RemoteVstPlugin::updateInOutCount() { + lockShm(); + + setShmIsValid( false ); + + unlockShm(); + delete[] m_inputs; delete[] m_outputs; m_inputs = NULL; m_outputs = NULL; - setInputCount( inputCount() ); - setOutputCount( outputCount() ); + setInputOutputCount( inputCount(), outputCount() ); char buf[64]; sprintf( buf, "inputs: %d output: %d\n", inputCount(), outputCount() ); @@ -1842,6 +1883,11 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) { _this->processMessage( m ); } + else if( m.id == IdChangeSharedMemoryKey ) + { + _this->processMessage( m ); + _this->setShmIsValid( true ); + } else { PostMessage( __MessageHwnd, diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index bb7e39c6a..c7756552c 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -481,6 +481,12 @@ bool RemotePlugin::processMessage( const message & _m ) resizeSharedProcessingMemory(); break; + case IdChangeInputOutputCount: + m_inputCount = _m.getInt( 0 ); + m_outputCount = _m.getInt( 1 ); + resizeSharedProcessingMemory(); + break; + case IdDebugMessage: fprintf( stderr, "RemotePlugin::DebugMessage: %s", _m.getString( 0 ).c_str() ); From cfb7a89f0e4c3f2401b8d32715d00eaaea4236f6 Mon Sep 17 00:00:00 2001 From: DomClark Date: Sun, 10 Sep 2017 08:06:38 +0100 Subject: [PATCH 4/9] Fix empty VST tracks creating noise in playback (#3798) --- plugins/vestige/vestige.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 650af5c52..0985f5e2e 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -32,6 +32,7 @@ #include #include +#include "BufferManager.h" #include "Engine.h" #include "gui_templates.h" #include "InstrumentPlayHandle.h" @@ -281,16 +282,19 @@ void vestigeInstrument::loadFile( const QString & _file ) void vestigeInstrument::play( sampleFrame * _buf ) { m_pluginMutex.lock(); + + const fpp_t frames = Engine::mixer()->framesPerPeriod(); + if( m_plugin == NULL ) { + BufferManager::clear( _buf, frames ); + m_pluginMutex.unlock(); return; } m_plugin->process( NULL, _buf ); - const fpp_t frames = Engine::mixer()->framesPerPeriod(); - instrumentTrack()->processAudioBuffer( _buf, frames, NULL ); m_pluginMutex.unlock(); From f26a02da04f7dad8e7ebf6281cb8c3ae76265bbd Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Tue, 12 Sep 2017 10:28:38 +0200 Subject: [PATCH 5/9] Suppress midi in events on export (#3778) --- src/tracks/InstrumentTrack.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 71cc43fb5..1ec873dc4 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -237,6 +237,11 @@ MidiEvent InstrumentTrack::applyMasterKey( const MidiEvent& event ) void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset ) { + if( Engine::getSong()->isExporting() ) + { + return; + } + bool eventHandled = false; switch( event.type() ) From c0682c94a437e63fba52be6b03e50623b3b669b9 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 14 Sep 2017 08:09:54 +0900 Subject: [PATCH 6/9] Fix MIDI export (#3733) * Re-enable MIDI export * Fix logic for processing BB tracks and BB notes * Consider master pitch and base note in MIDI export. * Cut BB notes at the end of BB pattern. --- README.md | 2 +- include/ExportFilter.h | 4 +- plugins/CMakeLists.txt | 2 +- plugins/MidiExport/MidiExport.cpp | 282 ++++++++++++++++++++++++------ plugins/MidiExport/MidiExport.h | 42 ++++- plugins/MidiExport/MidiFile.hpp | 5 +- src/core/Song.cpp | 7 +- src/gui/MainWindow.cpp | 5 +- 8 files changed, 278 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 9c65c4be7..1061ecff6 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Features * Many powerful instrument and effect-plugins out of the box * Full user-defined track-based automation and computer-controlled automation sources * Compatible with many standards such as SoundFont2, VST(i), LADSPA, GUS Patches, and full MIDI support -* MIDI file importing +* MIDI file importing and exporting Building --------- diff --git a/include/ExportFilter.h b/include/ExportFilter.h index f27bc0c82..35416f492 100644 --- a/include/ExportFilter.h +++ b/include/ExportFilter.h @@ -39,7 +39,9 @@ public: virtual ~ExportFilter() {} - virtual bool tryExport( const TrackContainer::TrackList &tracks, int tempo, const QString &filename ) = 0; + virtual bool tryExport(const TrackContainer::TrackList &tracks, + const TrackContainer::TrackList &tracksBB, + int tempo, int masterPitch, const QString &filename ) = 0; protected: virtual void saveSettings( QDomDocument &, QDomElement & ) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 8a464e388..24c15e391 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -56,7 +56,7 @@ IF("${PLUGIN_LIST}" STREQUAL "") LadspaEffect lb302 MidiImport - # MidiExport - temporarily disabled, MIDI export is broken + MidiExport MultitapEcho monstro nes diff --git a/plugins/MidiExport/MidiExport.cpp b/plugins/MidiExport/MidiExport.cpp index b838353d2..1e20e9d40 100644 --- a/plugins/MidiExport/MidiExport.cpp +++ b/plugins/MidiExport/MidiExport.cpp @@ -1,7 +1,8 @@ /* - * MidiExport.cpp - support for importing MIDI files + * MidiExport.cpp - support for Exporting MIDI files * - * Author: Mohamed Abdel Maksoud + * Copyright (c) 2015 Mohamed Abdel Maksoud + * Copyright (c) 2017 Hyunjin Song * * This file is part of LMMS - https://lmms.io * @@ -30,8 +31,10 @@ #include #include "MidiExport.h" -#include "Engine.h" + +#include "lmms_math.h" #include "TrackContainer.h" +#include "BBTrack.h" #include "InstrumentTrack.h" @@ -44,7 +47,8 @@ Plugin::Descriptor PLUGIN_EXPORT midiexport_plugin_descriptor = "MIDI Export", QT_TRANSLATE_NOOP( "pluginBrowser", "Filter for exporting MIDI-files from LMMS" ), - "Mohamed Abdel Maksoud ", + "Mohamed Abdel Maksoud and " + "Hyunjin Song ", 0x0100, Plugin::ExportFilter, NULL, @@ -68,99 +72,269 @@ MidiExport::~MidiExport() -bool MidiExport::tryExport( const TrackContainer::TrackList &tracks, int tempo, const QString &filename ) +bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, + const TrackContainer::TrackList &tracks_BB, + int tempo, int masterPitch, const QString &filename) { QFile f(filename); f.open(QIODevice::WriteOnly); QDataStream midiout(&f); InstrumentTrack* instTrack; + BBTrack* bbTrack; QDomElement element; int nTracks = 0; - const int BUFFER_SIZE = 50*1024; uint8_t buffer[BUFFER_SIZE]; uint32_t size; - for( const Track* track : tracks ) if( track->type() == Track::InstrumentTrack ) nTracks++; + for (const Track* track : tracks) if (track->type() == Track::InstrumentTrack) nTracks++; + for (const Track* track : tracks_BB) if (track->type() == Track::InstrumentTrack) nTracks++; // midi header MidiFile::MIDIHeader header(nTracks); size = header.writeToBuffer(buffer); midiout.writeRawData((char *)buffer, size); - // midi tracks - for( Track* track : tracks ) - { - DataFile dataFile( DataFile::SongProject ); - MidiFile::MIDITrack mtrack; - - if( track->type() != Track::InstrumentTrack ) continue; + std::vector>> plists; + + // midi tracks + for (Track* track : tracks) + { + DataFile dataFile(DataFile::SongProject); + MTrack mtrack; + + if (track->type() == Track::InstrumentTrack) + { + + mtrack.addName(track->name().toStdString(), 0); + //mtrack.addProgramChange(0, 0); + mtrack.addTempo(tempo, 0); + + instTrack = dynamic_cast(track); + element = instTrack->saveState(dataFile, dataFile.content()); + + int base_pitch = 0; + double base_volume = 1.0; + int base_time = 0; + + MidiNoteVector pat; + + for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) + { + + if (n.nodeName() == "instrumenttrack") + { + QDomElement it = n.toElement(); + // transpose +12 semitones, workaround for #1857 + base_pitch = (69 - it.attribute("basenote", "57").toInt()); + if (it.attribute("usemasterpitch", "1").toInt()) + { + base_pitch += masterPitch; + } + base_volume = it.attribute("volume", "100").toDouble()/100.0; + } + + if (n.nodeName() == "pattern") + { + base_time = n.toElement().attribute("pos", "0").toInt(); + writePattern(pat, n, base_pitch, base_volume, base_time); + } + + } + ProcessBBNotes(pat, INT_MAX); + writePatternToTrack(mtrack, pat); + size = mtrack.writeToBuffer(buffer); + midiout.writeRawData((char *)buffer, size); + } + + if (track->type() == Track::BBTrack) + { + bbTrack = dynamic_cast(track); + element = bbTrack->saveState(dataFile, dataFile.content()); + + std::vector> plist; + for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) + { + + if (n.nodeName() == "bbtco") + { + QDomElement it = n.toElement(); + int pos = it.attribute("pos", "0").toInt(); + int len = it.attribute("len", "0").toInt(); + plist.push_back(std::pair(pos, pos+len)); + } + } + std::sort(plist.begin(), plist.end()); + plists.push_back(plist); + + } + } // for each track + + // midi tracks in BB tracks + for (Track* track : tracks_BB) + { + DataFile dataFile(DataFile::SongProject); + MTrack mtrack; + + auto itr = plists.begin(); + std::vector> st; + + if (track->type() != Track::InstrumentTrack) continue; - //qDebug() << "exporting " << track->name(); - - mtrack.addName(track->name().toStdString(), 0); //mtrack.addProgramChange(0, 0); mtrack.addTempo(tempo, 0); - - instTrack = dynamic_cast( track ); - element = instTrack->saveState( dataFile, dataFile.content() ); - - // instrumentTrack - // - instrumentTrack - // - pattern - int base_pitch = 0; + + instTrack = dynamic_cast(track); + element = instTrack->saveState(dataFile, dataFile.content()); + + int base_pitch = 0; double base_volume = 1.0; - int base_time = 0; - - - for(QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) + + for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { - //QDomText txt = n.toText(); - //qDebug() << ">> child node " << n.nodeName(); - if (n.nodeName() == "instrumenttrack") { - // TODO interpret pan="0" fxch="0" usemasterpitch="1" pitchrange="1" pitch="0" basenote="57" QDomElement it = n.toElement(); - base_pitch = it.attribute("pitch", "0").toInt(); - base_volume = it.attribute("volume", "100").toDouble()/100.0; + // transpose +12 semitones, workaround for #1857 + base_pitch = (69 - it.attribute("basenote", "57").toInt()); + if (it.attribute("usemasterpitch", "1").toInt()) + { + base_pitch += masterPitch; + } + base_volume = it.attribute("volume", "100").toDouble() / 100.0; } - + if (n.nodeName() == "pattern") { - base_time = n.toElement().attribute("pos", "0").toInt(); - // TODO interpret steps="12" muted="0" type="1" name="Piano1" len="2592" - for(QDomNode nn = n.firstChild(); !nn.isNull(); nn = nn.nextSibling()) + std::vector> &plist = *itr; + + MidiNoteVector nv, pat; + writePattern(pat, n, base_pitch, base_volume, 0); + + // workaround for nested BBTCOs + int pos = 0; + int len = n.toElement().attribute("steps", "1").toInt() * 12; + for (auto it = plist.begin(); it != plist.end(); ++it) { - QDomElement note = nn.toElement(); - if (note.attribute("len", "0") == "0" || note.attribute("vol", "0") == "0") continue; - #if 0 - qDebug() << ">>>> key " << note.attribute( "key", "0" ) - << " " << note.attribute("len", "0") << " @" - << note.attribute("pos", "0"); - #endif - mtrack.addNote( - note.attribute("key", "0").toInt()+base_pitch - , 100 * base_volume * (note.attribute("vol", "100").toDouble()/100) - , (base_time+note.attribute("pos", "0").toDouble())/48 - , (note.attribute("len", "0")).toDouble()/48); + while (!st.empty() && st.back().second <= it->first) + { + writeBBPattern(pat, nv, len, st.back().first, pos, st.back().second); + pos = st.back().second; + st.pop_back(); + } + + if (!st.empty() && st.back().second <= it->second) + { + writeBBPattern(pat, nv, len, st.back().first, pos, it->first); + pos = it->first; + while (!st.empty() && st.back().second <= it->second) + { + st.pop_back(); + } + } + + st.push_back(*it); + pos = it->first; } + + while (!st.empty()) + { + writeBBPattern(pat, nv, len, st.back().first, pos, st.back().second); + pos = st.back().second; + st.pop_back(); + } + + ProcessBBNotes(nv, pos); + writePatternToTrack(mtrack, nv); + ++itr; } - } size = mtrack.writeToBuffer(buffer); midiout.writeRawData((char *)buffer, size); - } // for each track - + } + return true; } +void MidiExport::writePattern(MidiNoteVector &pat, QDomNode n, + int base_pitch, double base_volume, int base_time) +{ + // TODO interpret steps="12" muted="0" type="1" name="Piano1" len="2592" + for (QDomNode nn = n.firstChild(); !nn.isNull(); nn = nn.nextSibling()) + { + QDomElement note = nn.toElement(); + if (note.attribute("len", "0") == "0") continue; + // TODO interpret pan="0" fxch="0" pitchrange="1" + MidiNote mnote; + mnote.pitch = qMax(0, qMin(127, note.attribute("key", "0").toInt() + base_pitch)); + mnote.volume = qMin(qRound(base_volume * note.attribute("vol", "100").toDouble()), 127); + mnote.time = base_time + note.attribute("pos", "0").toInt(); + mnote.duration = note.attribute("len", "0").toInt(); + pat.push_back(mnote); + } +} + + + +void MidiExport::writePatternToTrack(MTrack &mtrack, MidiNoteVector &nv) +{ + for (auto it = nv.begin(); it != nv.end(); ++it) + { + mtrack.addNote(it->pitch, it->volume, it->time / 48.0, it->duration / 48.0); + } +} + + + +void MidiExport::writeBBPattern(MidiNoteVector &src, MidiNoteVector &dst, + int len, int base, int start, int end) +{ + if (start >= end) { return; } + start -= base; + end -= base; + std::sort(src.begin(), src.end()); + for (auto it = src.begin(); it != src.end(); ++it) + { + for (int time = it->time + ceil((start - it->time) / len) + * len; time < end; time += len) + { + MidiNote note; + note.duration = it->duration; + note.pitch = it->pitch; + note.time = base + time; + note.volume = it->volume; + dst.push_back(note); + } + } +} + + + +void MidiExport::ProcessBBNotes(MidiNoteVector &nv, int cutPos) +{ + std::sort(nv.begin(), nv.end()); + int cur = INT_MAX, next = INT_MAX; + for (auto it = nv.rbegin(); it != nv.rend(); ++it) + { + if (it->time < cur) + { + next = cur; + cur = it->time; + } + if (it->duration < 0) + { + it->duration = qMin(qMin(-it->duration, next - cur), cutPos - it->time); + } + } +} + + void MidiExport::error() { diff --git a/plugins/MidiExport/MidiExport.h b/plugins/MidiExport/MidiExport.h index 279f369f6..3c36eeb8f 100644 --- a/plugins/MidiExport/MidiExport.h +++ b/plugins/MidiExport/MidiExport.h @@ -1,7 +1,8 @@ /* * MidiExport.h - support for Exporting MIDI-files * - * Author: Mohamed Abdel Maksoud + * Copyright (c) 2015 Mohamed Abdel Maksoud + * Copyright (c) 2017 Hyunjin Song * * This file is part of LMMS - https://lmms.io * @@ -31,25 +32,52 @@ #include "MidiFile.hpp" +const int BUFFER_SIZE = 50*1024; +typedef MidiFile::MIDITrack MTrack; + +struct MidiNote +{ + int time; + uint8_t pitch; + int duration; + uint8_t volume; + + inline bool operator<(const MidiNote &b) const + { + return this->time < b.time; + } +} ; + +typedef std::vector MidiNoteVector; +typedef std::vector::iterator MidiNoteIterator; + + class MidiExport: public ExportFilter { // Q_OBJECT public: - MidiExport( ); + MidiExport(); ~MidiExport(); - virtual PluginView * instantiateView( QWidget * ) + virtual PluginView *instantiateView(QWidget *) { - return( NULL ); + return nullptr; } - virtual bool tryExport( const TrackContainer::TrackList &tracks, int tempo, const QString &filename ); + virtual bool tryExport(const TrackContainer::TrackList &tracks, + const TrackContainer::TrackList &tracks_BB, + int tempo, int masterPitch, const QString &filename); private: - + void writePattern(MidiNoteVector &pat, QDomNode n, + int base_pitch, double base_volume, int base_time); + void writePatternToTrack(MTrack &mtrack, MidiNoteVector &nv); + void writeBBPattern(MidiNoteVector &src, MidiNoteVector &dst, + int len, int base, int start, int end); + void ProcessBBNotes(MidiNoteVector &nv, int cutPos); - void error( void ); + void error(); } ; diff --git a/plugins/MidiExport/MidiFile.hpp b/plugins/MidiExport/MidiFile.hpp index 0e2bfbe5b..a1f91de2f 100644 --- a/plugins/MidiExport/MidiFile.hpp +++ b/plugins/MidiExport/MidiFile.hpp @@ -156,8 +156,10 @@ struct Event writeBigEndian4(int(60000000.0 / tempo), fourbytes); //printf("tempo of %x translates to ", tempo); + /* for (int i=0; i<3; i++) printf("%02x ", fourbytes[i+1]); printf("\n"); + */ buffer[size++] = fourbytes[1]; buffer[size++] = fourbytes[2]; buffer[size++] = fourbytes[3]; @@ -186,7 +188,8 @@ struct Event // events are sorted by their time inline bool operator < (const Event& b) const { - return this->time < b.time; + return this->time < b.time || + (this->time == b.time && this->type > b.type); } }; diff --git a/src/core/Song.cpp b/src/core/Song.cpp index a576bbcbd..01071668c 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -1444,14 +1444,15 @@ void Song::exportProjectMidi() // instantiate midi export plugin TrackContainer::TrackList tracks; - tracks += Engine::getSong()->tracks(); - tracks += Engine::getBBTrackContainer()->tracks(); + TrackContainer::TrackList tracks_BB; + tracks = Engine::getSong()->tracks(); + tracks_BB = Engine::getBBTrackContainer()->tracks(); ExportFilter *exf = dynamic_cast (Plugin::instantiate("midiexport", NULL, NULL)); if (exf==NULL) { qDebug() << "failed to load midi export filter!"; return; } - exf->tryExport(tracks, Engine::getSong()->getTempo(), export_filename); + exf->tryExport(tracks, tracks_BB, getTempo(), m_masterPitchModel.value(), export_filename); } } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index f65c96066..9bb4bfea6 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -300,12 +300,11 @@ void MainWindow::finalize() SLOT( exportProjectTracks() ), Qt::CTRL + Qt::SHIFT + Qt::Key_E ); - // temporarily disabled broken MIDI export - /*project_menu->addAction( embed::getIconPixmap( "midi_file" ), + project_menu->addAction( embed::getIconPixmap( "midi_file" ), tr( "Export &MIDI..." ), Engine::getSong(), SLOT( exportProjectMidi() ), - Qt::CTRL + Qt::Key_M );*/ + Qt::CTRL + Qt::Key_M ); // Prevent dangling separator at end of menu per https://bugreports.qt.io/browse/QTBUG-40071 #if !(defined(LMMS_BUILD_APPLE) && (QT_VERSION >= 0x050000) && (QT_VERSION < 0x050600)) From 4e3c6b0940a367c1e204e8e57b288f8e2e6e7db7 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 19 Sep 2017 04:48:33 +0900 Subject: [PATCH 7/9] Fix recording of single streamed instruments(regression in #3774) (#3803) * Revert "same note layering when sustain pedal is pressed (#3774)" This reverts commit e387e7744521d933b9e8455623eef55119b8403d. * Fix recording of sustained notes --- include/InstrumentTrack.h | 1 + src/core/NotePlayHandle.cpp | 21 +++++++++++---------- src/tracks/InstrumentTrack.cpp | 24 +++++++++++++++++++++++- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 6d2e42c3d..5ef604def 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -237,6 +237,7 @@ private: MidiPort m_midiPort; NotePlayHandle* m_notes[NumKeys]; + NotePlayHandleList m_sustainedNotes; int m_runningMidiNotes[NumKeys]; QMutex m_midiNotesMutex; diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 0dff48fc0..84d888fee 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -252,17 +252,8 @@ void NotePlayHandle::play( sampleFrame * _working_buffer ) if( m_released && (!instrumentTrack()->isSustainPedalPressed() || m_releaseStarted) ) { - if (m_releaseStarted == false) - { + m_releaseStarted = true; - if( m_origin == OriginMidiInput ) - { - setLength( MidiTime( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); - m_instrumentTrack->midiNoteOff( *this ); - } - - m_releaseStarted = true; - } f_cnt_t todo = framesThisPeriod; // if this note is base-note for arpeggio, always set @@ -389,6 +380,16 @@ void NotePlayHandle::noteOff( const f_cnt_t _s ) MidiTime::fromFrames( _s, Engine::framesPerTick() ), _s ); } + + // inform attached components about MIDI finished (used for recording in Piano Roll) + if (!instrumentTrack()->isSustainPedalPressed()) + { + if( m_origin == OriginMidiInput ) + { + setLength( MidiTime( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); + m_instrumentTrack->midiNoteOff( *this ); + } + } } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 1ec873dc4..3c53e4d4c 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -278,6 +278,12 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti // be deleted later automatically) Engine::mixer()->requestChangeInModel(); m_notes[event.key()]->noteOff( offset ); + if (isSustainPedalPressed() && + m_notes[event.key()]->origin() == + m_notes[event.key()]->OriginMidiInput) + { + m_sustainedNotes << m_notes[event.key()]; + } m_notes[event.key()] = NULL; Engine::mixer()->doneChangeInModel(); } @@ -307,8 +313,24 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti { m_sustainPedalPressed = true; } - else + else if (isSustainPedalPressed()) { + for (NotePlayHandle* nph : m_sustainedNotes) + { + if (nph && nph->isReleased()) + { + if( nph->origin() == + nph->OriginMidiInput) + { + nph->setLength( + MidiTime( static_cast( + nph->totalFramesPlayed() / + Engine::framesPerTick() ) ) ); + midiNoteOff( *nph ); + } + } + } + m_sustainedNotes.clear(); m_sustainPedalPressed = false; } } From 8a39302571c96e130dd6db1f466011760fd7f1ec Mon Sep 17 00:00:00 2001 From: flynn16 Date: Thu, 21 Sep 2017 01:12:41 +0800 Subject: [PATCH 8/9] Enable HiDPI Scaling (Qt 5.6+ only) [stable-1.2] (#3814) Enables HiDPI Scaling in GuiApplication.cpp, environmental variable and mac manifest --- cmake/apple/lmms.plist.in | 2 ++ cmake/linux/lmms.desktop | 2 +- src/gui/GuiApplication.cpp | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/apple/lmms.plist.in b/cmake/apple/lmms.plist.in index 638b4af1a..10ff7a996 100644 --- a/cmake/apple/lmms.plist.in +++ b/cmake/apple/lmms.plist.in @@ -143,6 +143,8 @@ + NSPrincipalClass + NSApplication NSHighResolutionCapable True diff --git a/cmake/linux/lmms.desktop b/cmake/linux/lmms.desktop index 0d236d446..6094ccfe1 100644 --- a/cmake/linux/lmms.desktop +++ b/cmake/linux/lmms.desktop @@ -8,7 +8,7 @@ Comment=easy music production for everyone! Comment[ca]=Producció fàcil de música per a tothom! Comment[fr]=Production facile de musique pour tout le monde ! Icon=lmms -Exec=env QT_X11_NO_NATIVE_MENUBAR=1 lmms %f +Exec=env QT_X11_NO_NATIVE_MENUBAR=1 QT_AUTO_SCREEN_SCALE_FACTOR=1 lmms %f Terminal=false Type=Application Categories=Qt;AudioVideo;Audio;Midi; diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index 5315a66a9..e82d95b9a 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -40,6 +40,7 @@ #include "SongEditor.h" #include +#include #include #include @@ -53,6 +54,11 @@ GuiApplication* GuiApplication::instance() GuiApplication::GuiApplication() { + // enable HiDPI scaling before showing anything (Qt 5.6+ only) + #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); + #endif + // prompt the user to create the LMMS working directory (e.g. ~/lmms) if it doesn't exist if ( !ConfigManager::inst()->hasWorkingDir() && QMessageBox::question( NULL, From fb5a58a5260c552004a26d6d8cd672d4056fa25b Mon Sep 17 00:00:00 2001 From: DomClark Date: Wed, 20 Sep 2017 23:00:19 +0100 Subject: [PATCH 9/9] Don't use VST-provided memory when loading chunks (#3805) --- include/aeffectx.h | 1 + plugins/vst_base/RemoteVstPlugin.cpp | 20 ++++---------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/include/aeffectx.h b/include/aeffectx.h index b398c88e3..138e356c1 100644 --- a/include/aeffectx.h +++ b/include/aeffectx.h @@ -101,6 +101,7 @@ const int effEditOpen = 14; const int effEditClose = 15; const int effEditIdle = 19; const int effEditTop = 20; +const int effSetChunk = 24; const int effProcessEvents = 25; const int effGetEffectName = 45; const int effGetVendorString = 47; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 67ce71ea8..09f8569e9 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -1385,20 +1385,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) { - char * buf = NULL; - - void * chunk = NULL; - // various plugins need this in order to not crash when setting - // chunk (also we let the plugin allocate "safe" memory this way) - const int actualLen = pluginDispatch( 23, 0, 0, &chunk ); - - // allocated buffer big enough? - if( _len > actualLen ) - { - // no, then manually allocate a buffer - buf = new char[_len]; - chunk = buf; - } + char * chunk = new char[_len]; const int fd = open( _file.c_str(), O_RDONLY | O_BINARY ); if ( ::read( fd, chunk, _len ) != _len ) @@ -1406,9 +1393,10 @@ void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) fprintf( stderr, "Error loading chunk from file.\n" ); } close( fd ); - pluginDispatch( 24, 0, _len, chunk ); - delete[] buf; + pluginDispatch( effSetChunk, 0, _len, chunk ); + + delete[] chunk; }