mirror of
https://github.com/LMMS/lmms.git
synced 2026-03-11 18:46:41 -04:00
Always send MIDI pitch bend events to MIDI channel 0 for the time being. Makes MIDI pitch wheel work with ZynAddSubFX.
384 lines
7.9 KiB
C++
384 lines
7.9 KiB
C++
/*
|
|
* remote_zynaddsubfx.cpp - ZynAddSubFX-embedding plugin
|
|
*
|
|
* Copyright (c) 2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
|
*
|
|
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program (see COPYING); if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <lmmsconfig.h>
|
|
#include <queue>
|
|
|
|
#define BUILD_REMOTE_PLUGIN_CLIENT
|
|
#include "engine.h"
|
|
#include "instrument_play_handle.h"
|
|
#include "note_play_handle.h"
|
|
#include "remote_plugin.h"
|
|
#include "remote_zynaddsubfx.h"
|
|
|
|
|
|
std::string __presets_dir;
|
|
|
|
#define main unused_main
|
|
#ifdef LMMS_BUILD_LINUX
|
|
#define ATOM(x) (x)
|
|
#endif
|
|
#include "src/main.C"
|
|
|
|
#undef main
|
|
|
|
#include <FL/x.H>
|
|
|
|
static pthread_t __gui_thread_handle;
|
|
static pthread_mutex_t __gui_mutex;
|
|
static std::queue<remotePluginClient::message> __gui_messages;
|
|
|
|
|
|
class remoteZynAddSubFX : public remotePluginClient
|
|
{
|
|
public:
|
|
remoteZynAddSubFX( int _shm_in, int _shm_out ) :
|
|
remotePluginClient( _shm_in, _shm_out )
|
|
{
|
|
for( int i = 0; i < NumKeys; ++i )
|
|
{
|
|
m_runningNotes[i] = 0;
|
|
}
|
|
setInputCount( 0 );
|
|
sendMessage( IdInitDone );
|
|
waitForMessage( IdInitDone );
|
|
}
|
|
|
|
virtual void updateSampleRate( void )
|
|
{
|
|
SAMPLE_RATE = sampleRate();
|
|
}
|
|
|
|
virtual void updateBufferSize( void )
|
|
{
|
|
SOUND_BUFFER_SIZE = bufferSize();
|
|
}
|
|
|
|
virtual bool processMessage( const message & _m )
|
|
{
|
|
bool gui_message = false;
|
|
switch( _m.id )
|
|
{
|
|
case IdQuit:
|
|
delete master;
|
|
break;
|
|
|
|
case IdShowUI:
|
|
case IdHideUI:
|
|
case IdLoadSettingsFromFile:
|
|
case IdLoadPresetFromFile:
|
|
gui_message = true;
|
|
break;
|
|
|
|
case IdSaveSettingsToFile:
|
|
{
|
|
char * name = strdup( _m.getString().c_str() );
|
|
master->saveXML( name );
|
|
free( name );
|
|
sendMessage( IdSaveSettingsToFile );
|
|
return true;
|
|
break;
|
|
}
|
|
|
|
case IdZasfPresetDirectory:
|
|
__presets_dir = _m.getString();
|
|
for( int i = 0; i < MAX_BANK_ROOT_DIRS; ++i )
|
|
{
|
|
if( config.cfg.bankRootDirList[i] == NULL )
|
|
{
|
|
config.cfg.bankRootDirList[i] = new char[MAX_STRING_SIZE];
|
|
strcpy(config.cfg.bankRootDirList[i], __presets_dir.c_str() );
|
|
break;
|
|
}
|
|
else if( strcmp( config.cfg.bankRootDirList[i],
|
|
__presets_dir.c_str() ) == 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return remotePluginClient::processMessage( _m );
|
|
}
|
|
if( gui_message )
|
|
{
|
|
pthread_mutex_lock( &__gui_mutex );
|
|
__gui_messages.push( _m );
|
|
pthread_mutex_unlock( &__gui_mutex );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual ~remoteZynAddSubFX()
|
|
{
|
|
}
|
|
|
|
// all functions are called while master->mutex is held
|
|
virtual void processMidiEvent( const midiEvent & _e,
|
|
const f_cnt_t /* _offset */ )
|
|
{
|
|
switch( _e.m_type )
|
|
{
|
|
case MidiNoteOn:
|
|
if( _e.velocity() > 0 )
|
|
{
|
|
if( _e.key() <= 0 || _e.key() >= 128 )
|
|
{
|
|
break;
|
|
}
|
|
if( m_runningNotes[_e.key()] > 0 )
|
|
{
|
|
master->NoteOff( 0, _e.key() );
|
|
}
|
|
++m_runningNotes[_e.key()];
|
|
master->NoteOn( 0, _e.key(),
|
|
_e.velocity() );
|
|
break;
|
|
}
|
|
case MidiNoteOff:
|
|
if( _e.key() <= 0 || _e.key() >= 128 )
|
|
{
|
|
break;
|
|
}
|
|
if( --m_runningNotes[_e.key()] <= 0 )
|
|
{
|
|
master->NoteOff( 0, _e.key() );
|
|
}
|
|
break;
|
|
case MidiPitchBend:
|
|
master->SetController( 0,
|
|
C_pitchwheel,
|
|
_e.m_data.m_param[0] +
|
|
_e.m_data.m_param[1]*128-8192 );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
virtual void process( const sampleFrame * _in, sampleFrame * _out )
|
|
{
|
|
REALTYPE outputl[SOUND_BUFFER_SIZE];
|
|
REALTYPE outputr[SOUND_BUFFER_SIZE];
|
|
|
|
master->AudioOut( outputl, outputr );
|
|
|
|
for( fpp_t f = 0; f < SOUND_BUFFER_SIZE; ++f )
|
|
{
|
|
_out[f][0] = outputl[f];
|
|
_out[f][1] = outputr[f];
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
int m_runningNotes[NumKeys];
|
|
|
|
} ;
|
|
|
|
static remoteZynAddSubFX * __remote_zasf = NULL;
|
|
static int __exit = 0;
|
|
|
|
|
|
void * guiThread( void * )
|
|
{
|
|
int e;
|
|
ui = NULL;
|
|
|
|
while( !__exit )
|
|
{
|
|
if( ui )
|
|
{
|
|
Fl::wait( 0.1 );
|
|
}
|
|
else
|
|
{
|
|
#ifdef LMMS_BUILD_WIN32
|
|
Sleep( 100 );
|
|
#else
|
|
usleep( 100*1000 );
|
|
#endif
|
|
}
|
|
pthread_mutex_lock( &__gui_mutex );
|
|
while( __gui_messages.size() )
|
|
{
|
|
remotePluginClient::message m = __gui_messages.front();
|
|
__gui_messages.pop();
|
|
switch( m.id )
|
|
{
|
|
case IdShowUI:
|
|
// we only create GUI
|
|
if( !ui )
|
|
{
|
|
Fl::scheme( "plastic" );
|
|
ui = new MasterUI( master, &e );
|
|
}
|
|
ui->showUI();
|
|
ui->refresh_master_ui();
|
|
break;
|
|
|
|
case IdHideUI:
|
|
if( !ui ) break;
|
|
switch( config.cfg.UserInterfaceMode )
|
|
{
|
|
case 0:
|
|
ui->selectuiwindow->hide();
|
|
break;
|
|
case 1:
|
|
ui->masterwindow->hide();
|
|
break;
|
|
case 2:
|
|
ui->simplemasterwindow->hide();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IdLoadSettingsFromFile:
|
|
{
|
|
char * f = strdup( m.getString().
|
|
c_str() );
|
|
pthread_mutex_lock( &master->mutex );
|
|
master->defaults();
|
|
master->loadXML( f );
|
|
pthread_mutex_unlock( &master->mutex );
|
|
master->applyparameters();
|
|
if( ui ) ui->refresh_master_ui();
|
|
unlink( f );
|
|
free( f );
|
|
pthread_mutex_lock( &master->mutex );
|
|
__remote_zasf->sendMessage(
|
|
IdLoadSettingsFromFile );
|
|
pthread_mutex_unlock( &master->mutex );
|
|
break;
|
|
}
|
|
|
|
case IdLoadPresetFromFile:
|
|
{
|
|
char * f = strdup( m.getString().
|
|
c_str() );
|
|
pthread_mutex_lock( &master->mutex );
|
|
const int npart = ui ?
|
|
ui->npartcounter->value()-1 : 0;
|
|
master->part[npart]->defaultsinstrument();
|
|
master->part[npart]->loadXMLinstrument( f );
|
|
pthread_mutex_unlock( &master->mutex );
|
|
master->applyparameters();
|
|
if( ui )
|
|
{
|
|
ui->npartcounter->do_callback();
|
|
ui->updatepanel();
|
|
ui->refresh_master_ui();
|
|
}
|
|
free( f );
|
|
pthread_mutex_lock( &master->mutex );
|
|
__remote_zasf->sendMessage(
|
|
IdLoadPresetFromFile );
|
|
pthread_mutex_unlock( &master->mutex );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
pthread_mutex_unlock( &__gui_mutex );
|
|
}
|
|
Fl::flush();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
int main( int _argc, char * * _argv )
|
|
{
|
|
if( _argc < 3 )
|
|
{
|
|
fprintf( stderr, "not enough arguments\n" );
|
|
return( -1 );
|
|
}
|
|
|
|
#ifdef LMMS_BUILD_WIN32
|
|
// (non-portable) initialization of statically linked pthread library
|
|
pthread_win32_process_attach_np();
|
|
pthread_win32_thread_attach_np();
|
|
#endif
|
|
|
|
__remote_zasf = new remoteZynAddSubFX( atoi( _argv[1] ),
|
|
atoi( _argv[2] ) );
|
|
|
|
config.init();
|
|
OSCIL_SIZE = config.cfg.OscilSize;
|
|
|
|
config.cfg.GzipCompression = 0;
|
|
|
|
srand( time( NULL ) );
|
|
denormalkillbuf = new REALTYPE[SOUND_BUFFER_SIZE];
|
|
for( int i = 0; i < SOUND_BUFFER_SIZE; ++i )
|
|
{
|
|
denormalkillbuf[i] = (RND-0.5)*1e-16;
|
|
}
|
|
|
|
OscilGen::tmpsmps = new REALTYPE[OSCIL_SIZE];
|
|
newFFTFREQS( &OscilGen::outoscilFFTfreqs, OSCIL_SIZE/2 );
|
|
|
|
pthread_mutex_init( &__gui_mutex, NULL );
|
|
|
|
master = new Master();
|
|
master->swaplr = 0;
|
|
|
|
pthread_create( &__gui_thread_handle, NULL, guiThread, NULL );
|
|
|
|
remotePluginClient::message m;
|
|
while( ( m = __remote_zasf->receiveMessage() ).id != IdQuit )
|
|
{
|
|
pthread_mutex_lock( &master->mutex );
|
|
__remote_zasf->processMessage( m );
|
|
pthread_mutex_unlock( &master->mutex );
|
|
}
|
|
|
|
__exit = 1;
|
|
|
|
delete denormalkillbuf;
|
|
delete OscilGen::tmpsmps;
|
|
deleteFFTFREQS( &OscilGen::outoscilFFTfreqs );
|
|
|
|
pthread_mutex_destroy( &__gui_mutex );
|
|
|
|
#ifdef LMMS_BUILD_WIN32
|
|
pthread_win32_thread_detach_np();
|
|
pthread_win32_process_detach_np();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|