diff --git a/ChangeLog b/ChangeLog index 2d11462c..910775b0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,10 @@ Paul Davis Jackdmp changes log --------------------------- +2009-04-08 Stephane Letz + + * Native MIDI backend (JackCoreMidiDriver, JackWinMMEDriver) in progress. + 2009-04-03 Stephane Letz * Simplify JackClient RT code, jack_thread_wait API marked deprecated." diff --git a/common/JackAudioAdapterInterface.cpp b/common/JackAudioAdapterInterface.cpp index 47425d40..ece82fc7 100644 --- a/common/JackAudioAdapterInterface.cpp +++ b/common/JackAudioAdapterInterface.cpp @@ -78,7 +78,8 @@ namespace Jack fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); - fprintf(file, "set title \"Audio adapter timing\"\n"); + fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" + ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); @@ -109,7 +110,8 @@ namespace Jack fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); - fprintf(file, "set title \"Audio adapter timing\"\n"); + fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" + ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"resampling ratio\"\n"); fprintf(file, "plot "); @@ -140,7 +142,8 @@ namespace Jack fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); - fprintf(file, "set title \"Audio adapter timing\"\n"); + fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" + ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index 943571f0..fc753123 100644 --- a/common/JackAudioDriver.cpp +++ b/common/JackAudioDriver.cpp @@ -1,6 +1,6 @@ /* Copyright (C) 2001 Paul Davis -Copyright (C) 2004-2008 GramefClientControl. +Copyright (C) 2004-2008 Grame. 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 diff --git a/common/JackClient.cpp b/common/JackClient.cpp index 310b3c78..5bc45334 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -120,12 +120,12 @@ void JackClient::SetupDriverSync(bool freewheel) jack_log("JackClient::SetupDriverSync driver sem in flush mode"); fSynchroTable[AUDIO_DRIVER_REFNUM].SetFlush(true); fSynchroTable[FREEWHEEL_DRIVER_REFNUM].SetFlush(true); - fSynchroTable[LOOPBACK_DRIVER_REFNUM].SetFlush(true); + fSynchroTable[MIDI_DRIVER_REFNUM].SetFlush(true); } else { jack_log("JackClient::SetupDriverSync driver sem in normal mode"); fSynchroTable[AUDIO_DRIVER_REFNUM].SetFlush(false); fSynchroTable[FREEWHEEL_DRIVER_REFNUM].SetFlush(false); - fSynchroTable[LOOPBACK_DRIVER_REFNUM].SetFlush(false); + fSynchroTable[MIDI_DRIVER_REFNUM].SetFlush(false); } } diff --git a/common/JackConnectionManager.cpp b/common/JackConnectionManager.cpp index 5108ddcd..08fa92ca 100644 --- a/common/JackConnectionManager.cpp +++ b/common/JackConnectionManager.cpp @@ -58,8 +58,8 @@ bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const || ref2 == AUDIO_DRIVER_REFNUM || ref1 == FREEWHEEL_DRIVER_REFNUM || ref2 == FREEWHEEL_DRIVER_REFNUM - || ref1 == LOOPBACK_DRIVER_REFNUM - || ref2 == LOOPBACK_DRIVER_REFNUM) { + || ref1 == MIDI_DRIVER_REFNUM + || ref2 == MIDI_DRIVER_REFNUM) { return false; } else if (ref1 == ref2) { // Same refnum return true; diff --git a/common/JackConstants.h b/common/JackConstants.h index 5d4a136e..fbc552a7 100644 --- a/common/JackConstants.h +++ b/common/JackConstants.h @@ -47,10 +47,10 @@ #define CLIENT_NUM 64 #endif -#define AUDIO_DRIVER_REFNUM 0 // Audio driver is initialized first, it will get the refnum 0 -#define FREEWHEEL_DRIVER_REFNUM 1 // Freewheel driver is initialized second, it will get the refnum 1 -#define LOOPBACK_DRIVER_REFNUM 2 // Loopback driver is initialized third, it will get the refnum 2 -#define REAL_REFNUM LOOPBACK_DRIVER_REFNUM + 1 // Real clients start at LOOPBACK_DRIVER_REFNUM + 1 +#define AUDIO_DRIVER_REFNUM 0 // Audio driver is initialized first, it will get the refnum 0 +#define FREEWHEEL_DRIVER_REFNUM 1 // Freewheel driver is initialized second, it will get the refnum 1 +#define MIDI_DRIVER_REFNUM 2 // Loopback driver is initialized third, it will get the refnum 2 +#define REAL_REFNUM MIDI_DRIVER_REFNUM + 1 // Real clients start at MIDI_DRIVER_REFNUM + 1 #define JACK_DEFAULT_SERVER_NAME "default" diff --git a/common/JackControlAPI.cpp b/common/JackControlAPI.cpp index 0bec8906..8a0a36b6 100644 --- a/common/JackControlAPI.cpp +++ b/common/JackControlAPI.cpp @@ -42,6 +42,7 @@ #include "JackControlAPI.h" #include "JackLockedEngine.h" #include "JackConstants.h" +#include "JackDriverLoader.h" using namespace Jack; @@ -95,6 +96,7 @@ struct jackctl_driver jack_driver_desc_t * desc_ptr; JSList * parameters; JSList * set_parameters; + JackDriverInfo* info; }; struct jackctl_internal @@ -1161,4 +1163,23 @@ EXPORT bool jackctl_server_unload_internal( } } +EXPORT bool jackctl_server_load_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) +{ + if (server_ptr->engine != NULL) { + driver_ptr->info = server_ptr->engine->AddSlave(driver_ptr->desc_ptr, driver_ptr->set_parameters); + return (driver_ptr->info != 0); + } else { + return false; + } +} + +EXPORT bool jackctl_server_unload_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) +{ + if (server_ptr->engine != NULL) { + server_ptr->engine->RemoveSlave(driver_ptr->info); + return true; + } else { + return false; + } +} diff --git a/common/JackControlAPI.h b/common/JackControlAPI.h index 7fc73243..54be6f3f 100644 --- a/common/JackControlAPI.h +++ b/common/JackControlAPI.h @@ -219,6 +219,13 @@ EXPORT bool jackctl_server_load_internal( EXPORT bool jackctl_server_unload_internal( jackctl_server * server, jackctl_internal * internal); + +EXPORT bool jackctl_server_load_slave(jackctl_server_t * server, + jackctl_driver_t * driver); + +EXPORT bool jackctl_server_unload_slave(jackctl_server_t * server, + jackctl_driver_t * driver); + #if 0 { /* Adjust editor indent */ diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp index 682b6f78..f5098771 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -68,7 +68,7 @@ int JackDriver::Open() int refnum = -1; if (fEngine->ClientInternalOpen(fClientControl.fName, &refnum, &fEngineControl, &fGraphManager, this, false) != 0) { - jack_error("Cannot allocate internal client for audio driver"); + jack_error("Cannot allocate internal client for driver"); return -1; } @@ -79,6 +79,46 @@ int JackDriver::Open() return 0; } +int JackDriver::Open (bool capturing, + bool playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + jack_log("JackDriver::Open capture_driver_name = %s", capture_driver_name); + jack_log("JackDriver::Open playback_driver_name = %s", playback_driver_name); + int refnum = -1; + + if (fEngine->ClientInternalOpen(fClientControl.fName, &refnum, &fEngineControl, &fGraphManager, this, false) != 0) { + jack_error("Cannot allocate internal client for driver"); + return -1; + } + + fClientControl.fRefNum = refnum; + fClientControl.fActive = true; + fCaptureLatency = capture_latency; + fPlaybackLatency = playback_latency; + + assert(strlen(capture_driver_name) < JACK_CLIENT_NAME_SIZE); + assert(strlen(playback_driver_name) < JACK_CLIENT_NAME_SIZE); + + strcpy(fCaptureDriverName, capture_driver_name); + strcpy(fPlaybackDriverName, playback_driver_name); + + fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec + if (!fEngineControl->fTimeOut) + fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); + + //fGraphManager->SetBufferSize(fEngineControl->fBufferSize); + fGraphManager->DirectConnect(fClientControl.fRefNum, fClientControl.fRefNum); // Connect driver to itself for "sync" mode + SetupDriverSync(fClientControl.fRefNum, false); + return 0; +} + int JackDriver::Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, @@ -96,7 +136,7 @@ int JackDriver::Open(jack_nframes_t buffer_size, int refnum = -1; if (fEngine->ClientInternalOpen(fClientControl.fName, &refnum, &fEngineControl, &fGraphManager, this, false) != 0) { - jack_error("Cannot allocate internal client for audio driver"); + jack_error("Cannot allocate internal client for driver"); return -1; } diff --git a/common/JackDriver.h b/common/JackDriver.h index a6027905..9c99d7c4 100644 --- a/common/JackDriver.h +++ b/common/JackDriver.h @@ -50,6 +50,17 @@ class SERVER_EXPORT JackDriverInterface {} virtual int Open() = 0; + + virtual int Open (bool capturing, + bool playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) = 0; + virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, @@ -142,6 +153,17 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface int ProcessSlaves(); virtual int Open(); + + virtual int Open (bool capturing, + bool playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, diff --git a/common/JackDriverLoader.cpp b/common/JackDriverLoader.cpp index 37ac7479..52c4aeed 100644 --- a/common/JackDriverLoader.cpp +++ b/common/JackDriverLoader.cpp @@ -800,7 +800,8 @@ Jack::JackDriverClientInterface* JackDriverInfo::Open(jack_driver_desc_t* driver jack_error("no initialize function in shared object %s\n", driver_desc->file); return NULL; } - - return fInitialize(engine, synchro, params); + + fBackend = fInitialize(engine, synchro, params); + return fBackend; } diff --git a/common/JackDriverLoader.h b/common/JackDriverLoader.h index c72183ac..f68fb041 100644 --- a/common/JackDriverLoader.h +++ b/common/JackDriverLoader.h @@ -42,6 +42,7 @@ class JackDriverInfo driverInitialize fInitialize; DRIVER_HANDLE fHandle; + Jack::JackDriverClientInterface* fBackend; public: @@ -54,6 +55,11 @@ class JackDriverInfo } Jack::JackDriverClientInterface* Open(jack_driver_desc_t* driver_desc, Jack::JackLockedEngine*, Jack::JackSynchro*, const JSList*); + + Jack::JackDriverClientInterface* GetBackend() + { + return fBackend; + } }; diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp new file mode 100644 index 00000000..b5bfd4c4 --- /dev/null +++ b/common/JackMidiDriver.cpp @@ -0,0 +1,181 @@ +/* +Copyright (C) 2009 Grame. + +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 +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 +(at your option) any later version. + +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackSystemDeps.h" +#include "JackMidiDriver.h" +#include "JackTime.h" +#include "JackError.h" +#include "JackEngineControl.h" +#include "JackPort.h" +#include "JackGraphManager.h" +#include "JackException.h" +#include + +namespace Jack +{ + +JackMidiDriver::JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) + : JackDriver(name, alias, engine, table), + fCaptureChannels(0), + fPlaybackChannels(0) +{ + for (int i = 0; i < DRIVER_PORT_NUM; i++) { + fRingBuffer[i] = NULL; + } +} + +JackMidiDriver::~JackMidiDriver() +{ + for (int i = 0; i < fCaptureChannels; i++) { + if (fRingBuffer[i]) + jack_ringbuffer_free(fRingBuffer[i]); + } +} + +int JackMidiDriver::Open(bool capturing, + bool playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + fCaptureChannels = inchannels; + fPlaybackChannels = outchannels; + + for (int i = 0; i < fCaptureChannels; i++) { + fRingBuffer[i] = jack_ringbuffer_create(sizeof(float) * BUFFER_SIZE_MAX); + } + + return JackDriver::Open(capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); +} + +int JackMidiDriver::Attach() +{ + JackPort* port; + jack_port_id_t port_index; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + unsigned long port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + int i; + + jack_log("JackMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); + + for (i = 0; i < fCaptureChannels; i++) { + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); + snprintf(name, sizeof(name) - 1, "%s:capture_%d", fClientControl.fName, i + 1); + if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { + jack_error("driver: cannot register port for %s", name); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + fCapturePortList[i] = port_index; + jack_log("JackMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); + } + + port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; + + for (i = 0; i < fPlaybackChannels; i++) { + snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); + snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); + if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { + jack_error("driver: cannot register port for %s", name); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + fPlaybackPortList[i] = port_index; + jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); + } + + return 0; +} + +int JackMidiDriver::Detach() +{ + int i; + jack_log("JackMidiDriver::Detach"); + + for (i = 0; i < fCaptureChannels; i++) { + fGraphManager->ReleasePort(fClientControl.fRefNum, fCapturePortList[i]); + } + + for (i = 0; i < fPlaybackChannels; i++) { + fGraphManager->ReleasePort(fClientControl.fRefNum, fPlaybackPortList[i]); + } + + return 0; +} + +int JackMidiDriver::Read() +{ + return 0; +} + +int JackMidiDriver::Write() +{ + return 0; +} + +int JackMidiDriver::ProcessNull() +{ + return 0; +} + +int JackMidiDriver::Process() +{ + // Read input buffers for the current cycle + if (Read() < 0) { + jack_error("JackMidiDriver::Process: read error, skip cycle"); + return 0; // Skip cycle, but continue processing... + } + + fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); + if (fEngineControl->fSyncMode) { + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0) { + jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); + return -1; + } + } + + // Write output buffers for the current cycle + if (Write() < 0) { + jack_error("JackMidiDriver::Process: write error, skip cycle"); + return 0; // Skip cycle, but continue processing... + } + + return 0; +} + +JackMidiBuffer* JackMidiDriver::GetInputBuffer(int port_index) +{ + assert(fCapturePortList[port_index]); + return (JackMidiBuffer*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize); +} + +JackMidiBuffer* JackMidiDriver::GetOutputBuffer(int port_index) +{ + assert(fPlaybackPortList[port_index]); + return (JackMidiBuffer*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize); +} + +} // end of namespace diff --git a/common/JackMidiDriver.h b/common/JackMidiDriver.h new file mode 100644 index 00000000..7c5bc5e0 --- /dev/null +++ b/common/JackMidiDriver.h @@ -0,0 +1,79 @@ +/* +Copyright (C) 2009 Grame + +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; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackMidiDriver__ +#define __JackMidiDriver__ + +#include "JackDriver.h" +#include "JackMidiPort.h" +#include "JackLockedEngine.h" +#include "ringbuffer.h" + +namespace Jack +{ + +/*! +\brief The base class for MIDI drivers: drivers with MIDI ports. +*/ + +class SERVER_EXPORT JackMidiDriver : public JackDriver +{ + + protected: + + int fCaptureChannels; + int fPlaybackChannels; + + jack_ringbuffer_t* fRingBuffer[DRIVER_PORT_NUM]; + + jack_port_id_t fCapturePortList[DRIVER_PORT_NUM]; + jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM]; + + JackMidiBuffer* GetInputBuffer(int port_index); + JackMidiBuffer* GetOutputBuffer(int port_index); + + public: + + JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); + virtual ~JackMidiDriver(); + + virtual int Open(bool capturing, + bool playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + + virtual int Process(); + virtual int ProcessNull(); + + virtual int Attach(); + virtual int Detach(); + + virtual int Read(); + virtual int Write(); + +}; + +} // end of namespace + +#endif diff --git a/common/JackMidiPort.cpp b/common/JackMidiPort.cpp index 41dcb151..fb933c85 100644 --- a/common/JackMidiPort.cpp +++ b/common/JackMidiPort.cpp @@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. namespace Jack { -void JackMidiBuffer::Reset(jack_nframes_t nframes) +SERVER_EXPORT void JackMidiBuffer::Reset(jack_nframes_t nframes) { /* This line ate 1 hour of my life... dsbaikov */ this->nframes = nframes; @@ -37,7 +37,7 @@ void JackMidiBuffer::Reset(jack_nframes_t nframes) mix_index = 0; } -jack_shmsize_t JackMidiBuffer::MaxEventSize() const +SERVER_EXPORT jack_shmsize_t JackMidiBuffer::MaxEventSize() const { assert (((jack_shmsize_t) - 1) < 0); // jack_shmsize_t should be signed jack_shmsize_t left = buffer_size - (sizeof(JackMidiBuffer) + sizeof(JackMidiEvent) * (event_count + 1) + write_pos); @@ -48,7 +48,7 @@ jack_shmsize_t JackMidiBuffer::MaxEventSize() const return left; } -jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time, jack_shmsize_t size) +SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time, jack_shmsize_t size) { jack_shmsize_t space = MaxEventSize(); if (space == 0 || size > space) { diff --git a/common/JackMidiPort.h b/common/JackMidiPort.h index 0b22944c..0dbfcb9a 100644 --- a/common/JackMidiPort.h +++ b/common/JackMidiPort.h @@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "types.h" #include "JackConstants.h" +#include "JackPlatformPlug.h" #include /** Type for raw event data contained in @ref jack_midi_event_t. */ @@ -42,7 +43,7 @@ struct jack_midi_event_t namespace Jack { -struct JackMidiEvent +struct SERVER_EXPORT JackMidiEvent { // Most MIDI events are < 4 bytes in size, so we can save a lot, storing them inplace. enum { INLINE_SIZE_MAX = sizeof(jack_shmsize_t) }; diff --git a/common/JackServer.cpp b/common/JackServer.cpp index 8dcf2bfb..a1f60edf 100644 --- a/common/JackServer.cpp +++ b/common/JackServer.cpp @@ -22,7 +22,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackServerGlobals.h" #include "JackTime.h" #include "JackFreewheelDriver.h" -#include "JackLoopbackDriver.h" #include "JackThreadedDriver.h" #include "JackGlobals.h" #include "JackLockedEngine.h" @@ -50,10 +49,8 @@ JackServer::JackServer(bool sync, bool temporary, long timeout, bool rt, long pr fEngineControl = new JackEngineControl(sync, temporary, timeout, rt, priority, verbose, server_name); fEngine = new JackLockedEngine(fGraphManager, GetSynchroTable(), fEngineControl); fFreewheelDriver = new JackThreadedDriver(new JackFreewheelDriver(fEngine, GetSynchroTable())); - fLoopbackDriver = new JackLoopbackDriver(fEngine, GetSynchroTable()); fAudioDriver = NULL; fFreewheel = false; - fLoopback = loopback; JackServerGlobals::fInstance = this; // Unique instance JackServerGlobals::fUserCount = 1; // One user jack_verbose = verbose; @@ -64,7 +61,6 @@ JackServer::~JackServer() delete fGraphManager; delete fAudioDriver; delete fFreewheelDriver; - delete fLoopbackDriver; delete fEngine; delete fEngineControl; } @@ -94,35 +90,17 @@ int JackServer::Open(jack_driver_desc_t* driver_desc, JSList* driver_params) goto fail_close4; } - if (fLoopbackDriver->Open(fEngineControl->fBufferSize, fEngineControl->fSampleRate, 1, 1, fLoopback, fLoopback, false, "loopback", "loopback", 0, 0) != 0) { - jack_error("Cannot open driver"); - goto fail_close5; - } - if (fAudioDriver->Attach() != 0) { jack_error("Cannot attach audio driver"); - goto fail_close6; - } - - if (fLoopback > 0 && fLoopbackDriver->Attach() != 0) { - jack_error("Cannot attach loopback driver"); - goto fail_close7; + goto fail_close5; } fFreewheelDriver->SetMaster(false); fAudioDriver->SetMaster(true); - if (fLoopback > 0) - fAudioDriver->AddSlave(fLoopbackDriver); fAudioDriver->AddSlave(fFreewheelDriver); // After ??? InitTime(); return 0; - -fail_close7: - fAudioDriver->Detach(); -fail_close6: - fLoopbackDriver->Close(); - fail_close5: fFreewheelDriver->Close(); @@ -145,11 +123,8 @@ int JackServer::Close() jack_log("JackServer::Close"); fChannel.Close(); fAudioDriver->Detach(); - if (fLoopback > 0) - fLoopbackDriver->Detach(); fAudioDriver->Close(); fFreewheelDriver->Close(); - fLoopbackDriver->Close(); fEngine->Close(); // TODO: move that in reworked JackServerGlobals::Destroy() JackMessageBuffer::Destroy(); @@ -305,6 +280,32 @@ void JackServer::ClientKill(int refnum) } } +//---------------------- +// Backend management +//---------------------- + +JackDriverInfo* JackServer::AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params) +{ + JackDriverInfo* info = new JackDriverInfo(); + JackDriverClientInterface* backend = info->Open(driver_desc, fEngine, GetSynchroTable(), driver_params); + if (backend == NULL) { + delete info; + return NULL; + } else { + backend->Attach(); + fAudioDriver->AddSlave(backend); + return info; + } +} + +void JackServer::RemoveSlave(JackDriverInfo* info) +{ + JackDriverClientInterface* backend = info->GetBackend(); + fAudioDriver->RemoveSlave(info->GetBackend()); + backend->Detach(); + backend->Close(); +} + //---------------------- // Transport management //---------------------- diff --git a/common/JackServer.h b/common/JackServer.h index d802965a..fb88101a 100644 --- a/common/JackServer.h +++ b/common/JackServer.h @@ -50,7 +50,6 @@ class SERVER_EXPORT JackServer JackDriverInfo fDriverInfo; JackDriverClientInterface* fAudioDriver; JackDriverClientInterface* fFreewheelDriver; - JackDriverClientInterface* fLoopbackDriver; JackLockedEngine* fEngine; JackEngineControl* fEngineControl; JackGraphManager* fGraphManager; @@ -58,7 +57,6 @@ class SERVER_EXPORT JackServer JackConnectionManager fConnectionState; JackSynchro fSynchroTable[CLIENT_NUM]; bool fFreewheel; - long fLoopback; int InternalClientLoadAux(JackLoadableInternalClient* client, const char* so_name, const char* client_name, int options, int* int_ref, int* status); @@ -86,6 +84,10 @@ class SERVER_EXPORT JackServer // Transport management int ReleaseTimebase(int refnum); int SetTimebaseCallback(int refnum, int conditional); + + // Backend management + JackDriverInfo* AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params); + void RemoveSlave(JackDriverInfo* info); // Object access JackLockedEngine* GetEngine(); diff --git a/common/JackThreadedDriver.h b/common/JackThreadedDriver.h index abd9f0d4..8705e2c4 100644 --- a/common/JackThreadedDriver.h +++ b/common/JackThreadedDriver.h @@ -45,6 +45,19 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi virtual ~JackThreadedDriver(); virtual int Open(); + + virtual int Open (bool capturing, + bool playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) + { + return -1; + } virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, diff --git a/common/Jackdmp.cpp b/common/Jackdmp.cpp index 227cf402..8662a278 100644 --- a/common/Jackdmp.cpp +++ b/common/Jackdmp.cpp @@ -33,7 +33,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackDriverLoader.h" /* -This is a simple port of the old jackdmp.cpp file to use the new Jack 2.0 control API. Available options for the server +This is a simple port of the old jackdmp.cpp file to use the new Jack 2.0 control API. Available options for the server are "hard-coded" in the source. A much better approach would be to use the control API to: - dynamically retrieve available server parameters and then prepare to parse them - get available drivers and their possible parameters, then prepare to parse them. @@ -58,7 +58,7 @@ static void notify_server_start(const char* server_name) static void notify_server_stop(const char* server_name) { // Send notification to be used in the JackRouter plugin - CFStringRef ref1 = CFStringCreateWithCString(NULL, server_name, kCFStringEncodingMacRoman); + CFStringRef ref1 = CFStringCreateWithCString(NULL, server_name, kCFStringEncodingMacRoman); CFNotificationCenterPostNotificationWithOptions(CFNotificationCenterGetDistributedCenter(), CFSTR("com.grame.jackserver.stop"), ref1, @@ -95,14 +95,14 @@ static void usage(FILE* file) "usage: jackdmp [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n" " [ --name OR -n server-name ]\n" " [ --timeout OR -t client-timeout-in-msecs ]\n" - " [ --loopback OR -L loopback-port-number ]\n" + " [ --midi OR -X midi-driver ]\n" " [ --verbose OR -v ]\n" " [ --replace-registry OR -r ]\n" " [ --silent OR -s ]\n" " [ --sync OR -S ]\n" " [ --temporary OR -T ]\n" " [ --version OR -V ]\n" - " -d driver [ ... driver args ... ]\n" + " -d audio-driver [ ... driver args ... ]\n" " where driver can be `alsa', `coreaudio', 'portaudio' or `dummy'\n" " jackdmp -d driver --help\n" " to display options for each driver\n\n"); @@ -154,10 +154,12 @@ int main(int argc, char* argv[]) jackctl_server_t * server_ctl; const JSList * server_parameters; const char* server_name = "default"; - jackctl_driver_t * driver_ctl; - const char *options = "-ad:P:uvrshVRL:STFl:t:mn:p:"; + jackctl_driver_t * audio_driver_ctl; + jackctl_driver_t * midi_driver_ctl; + const char *options = "-ad:X:P:uvrshVRL:STFl:t:mn:p:"; struct option long_options[] = { - { "driver", 1, 0, 'd' }, + { "audio-driver", 1, 0, 'd' }, + { "midi-driver", 1, 0, 'X' }, { "verbose", 0, 0, 'v' }, { "help", 0, 0, 'h' }, { "port-max", 1, 0, 'p' }, @@ -177,10 +179,14 @@ int main(int argc, char* argv[]) }; int i,opt = 0; int option_index = 0; - bool seen_driver = false; - char *driver_name = NULL; - char **driver_args = NULL; - int driver_nargs = 1; + bool seen_audio_driver = false; + bool seen_midi_driver = false; + char *audio_driver_name = NULL; + char **audio_driver_args = NULL; + int audio_driver_nargs = 1; + char *midi_driver_name = NULL; + char **midi_driver_args = NULL; + int midi_driver_nargs = 1; int port_max = 512; int do_mlock = 1; int do_unlock = 0; @@ -190,34 +196,39 @@ int main(int argc, char* argv[]) union jackctl_parameter_value value; copyright(stdout); - + server_ctl = jackctl_server_create(); if (server_ctl == NULL) { fprintf(stderr, "Failed to create server object\n"); return -1; } - + server_parameters = jackctl_server_get_parameters(server_ctl); opterr = 0; - while (!seen_driver && + while (!seen_audio_driver && (opt = getopt_long(argc, argv, options, long_options, &option_index)) != EOF) { switch (opt) { case 'd': - seen_driver = true; - driver_name = optarg; + seen_audio_driver = true; + audio_driver_name = optarg; break; - + + case 'X': + seen_midi_driver = true; + midi_driver_name = optarg; + break; + case 'p': port_max = (unsigned int)atol(optarg); break; - + case 'm': do_mlock = 0; break; - + case 'u': do_unlock = 1; break; @@ -306,6 +317,7 @@ int main(int argc, char* argv[]) default: fprintf(stderr, "unknown option character %c\n", optopt); /*fallthru*/ + case 'h': usage(stdout); goto fail_free; @@ -313,61 +325,77 @@ int main(int argc, char* argv[]) } if (show_version) { - printf("jackdmp version" VERSION - "\n"); + printf( "jackdmp version " VERSION + " tmpdir " jack_server_dir + " protocol %d" + "\n", JACK_PROTOCOL_VERSION); return -1; } - - if (!seen_driver) { + + if (!seen_audio_driver) { usage(stderr); goto fail_free; } - driver_ctl = jackctl_server_get_driver(server_ctl, driver_name); - if (driver_ctl == NULL) { - fprintf(stderr, "Unkown driver \"%s\"\n", driver_name); + // Audio driver + audio_driver_ctl = jackctl_server_get_driver(server_ctl, audio_driver_name); + if (audio_driver_ctl == NULL) { + fprintf(stderr, "Unkown driver \"%s\"\n", audio_driver_name); goto fail_free; } if (optind < argc) { - driver_nargs = 1 + argc - optind; + audio_driver_nargs = 1 + argc - optind; } else { - driver_nargs = 1; + audio_driver_nargs = 1; } - if (driver_nargs == 0) { + if (audio_driver_nargs == 0) { fprintf(stderr, "No driver specified ... hmm. JACK won't do" " anything when run like this.\n"); goto fail_free; } - driver_args = (char **) malloc(sizeof(char *) * driver_nargs); - driver_args[0] = driver_name; + audio_driver_args = (char **) malloc(sizeof(char *) * audio_driver_nargs); + audio_driver_args[0] = audio_driver_name; - for (i = 1; i < driver_nargs; i++) { - driver_args[i] = argv[optind++]; + for (i = 1; i < audio_driver_nargs; i++) { + audio_driver_args[i] = argv[optind++]; } - if (jackctl_parse_driver_params(driver_ctl, driver_nargs, driver_args)) { + if (jackctl_parse_driver_params(audio_driver_ctl, audio_driver_nargs, audio_driver_args)) { goto fail_free; } - - if (!jackctl_server_start(server_ctl, driver_ctl)) { + + // Start server + if (!jackctl_server_start(server_ctl, audio_driver_ctl)) { fprintf(stderr, "Failed to start server\n"); goto fail_free; } - + + // MIDI driver + if (seen_midi_driver) { + + midi_driver_ctl = jackctl_server_get_driver(server_ctl, midi_driver_name); + if (midi_driver_ctl == NULL) { + fprintf(stderr, "Unkown driver \"%s\"\n", midi_driver_name); + goto fail_free; + } + + jackctl_server_load_slave(server_ctl, midi_driver_ctl); + } + notify_server_start(server_name); // Waits for signal signals = jackctl_setup_signals(0); jackctl_wait_signals(signals); - + if (!jackctl_server_stop(server_ctl)) fprintf(stderr, "Cannot stop server...\n"); - + fail_free: - + jackctl_server_destroy(server_ctl); notify_server_stop(server_name); return 1; diff --git a/common/jack/control.h b/common/jack/control.h index 17436f07..87d62e72 100644 --- a/common/jack/control.h +++ b/common/jack/control.h @@ -510,6 +510,14 @@ jack_log( /* @} */ +bool +jackctl_server_load_slave(jackctl_server_t * server, + jackctl_driver_t * driver); + +bool +jackctl_server_unload_slave(jackctl_server_t * server, + jackctl_driver_t * driver); + #if 0 { /* Adjust editor indent */ #endif diff --git a/common/wscript b/common/wscript index 9847c0de..78641f48 100644 --- a/common/wscript +++ b/common/wscript @@ -113,6 +113,7 @@ def build(bld): serverlib.source = [] + common_libsources serverlib.source += [ 'JackAudioDriver.cpp', + 'JackMidiDriver.cpp', 'JackDriver.cpp', 'JackEngine.cpp', 'JackExternalClient.cpp', @@ -170,7 +171,7 @@ def build(bld): serverlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") serverlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc") #serverlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module -arch i386 -arch ppc") - serverlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module") + serverlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework CoreFoundation -framework vecLib -single_module") serverlib.env.append_value("LINKFLAGS", "-compatibility_version 1 -current_version 1") if bld.env['IS_SUN']: diff --git a/example-clients/midisine.c b/example-clients/midisine.c index 38299138..89ff7f75 100644 --- a/example-clients/midisine.c +++ b/example-clients/midisine.c @@ -71,17 +71,21 @@ static int process(jack_nframes_t nframes, void *arg) /* printf("1st byte of 1st event addr is %p\n", in_events[0].buffer);*/ } jack_midi_event_get(&in_event, port_buf, 0); - for(i=0; i +#include +#include +#include +#include + +namespace Jack +{ + +static MIDITimeStamp MIDIGetCurrentHostTime() +{ + return mach_absolute_time(); +} + +void JackCoreMidiDriver::ReadProcAux(const MIDIPacketList *pktlist, jack_ringbuffer_t* ringbuffer) +{ + // Write the number of packets + size_t size = jack_ringbuffer_write(ringbuffer, (char*)&pktlist->numPackets, sizeof(UInt32)); + if (size != sizeof(UInt32)) { + jack_error("ReadProc : ring buffer is full, skip events..."); + return; + } + + for (unsigned int i = 0; i < pktlist->numPackets; ++i) { + + MIDIPacket *packet = (MIDIPacket *)pktlist->packet; + + // TODO : use timestamp + + // Write length of each packet first + size = jack_ringbuffer_write(ringbuffer, (char*)&packet->length, sizeof(UInt16)); + if (size != sizeof(UInt16)) { + jack_error("ReadProc : ring buffer is full, skip events..."); + return; + } + // Write event actual data + size = jack_ringbuffer_write(ringbuffer, (char*)packet->data, packet->length); + if (size != packet->length) { + jack_error("ReadProc : ring buffer is full, skip events..."); + return; + } + + packet = MIDIPacketNext(packet); + } +} + +void JackCoreMidiDriver::ReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) +{ + jack_ringbuffer_t* ringbuffer = (jack_ringbuffer_t*)connRefCon; + ReadProcAux(pktlist, ringbuffer); +} + +void JackCoreMidiDriver::ReadVirtualProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) +{ + jack_ringbuffer_t* ringbuffer = (jack_ringbuffer_t*)refCon; + ReadProcAux(pktlist, ringbuffer); +} + +void JackCoreMidiDriver::NotifyProc(const MIDINotification *message, void *refCon) +{ + jack_info("NotifyProc %d", message->messageID); +} + +JackCoreMidiDriver::JackCoreMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) + : JackMidiDriver(name, alias, engine, table), fMidiClient(NULL), fInputPort(NULL), fOutputPort(NULL), fRealCaptureChannels(0), fRealPlaybackChannels(0) +{} + +JackCoreMidiDriver::~JackCoreMidiDriver() +{} + +int JackCoreMidiDriver::Open(bool capturing, + bool playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) + { + OSStatus err; + CFStringRef coutputStr; + std::string str; + + // Get real input/output number + fRealCaptureChannels = MIDIGetNumberOfSources(); + fRealPlaybackChannels = MIDIGetNumberOfDestinations(); + + // Generic JackMidiDriver Open + if (JackMidiDriver::Open(capturing, playing, inchannels + fRealCaptureChannels, outchannels + fRealPlaybackChannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency) != 0) + return -1; + + coutputStr = CFStringCreateWithCString(0, "JackMidi", CFStringGetSystemEncoding()); + err = MIDIClientCreate(coutputStr, NotifyProc, this, &fMidiClient); + CFRelease(coutputStr); + if (!fMidiClient) { + jack_error("Cannot create CoreMidi client"); + goto error; + } + + err = MIDIInputPortCreate(fMidiClient, CFSTR("Input port"), ReadProc, this, &fInputPort); + if (!fInputPort) { + jack_error("Cannot open CoreMidi in port\n"); + goto error; + } + + err = MIDIOutputPortCreate(fMidiClient, CFSTR("Output port"), &fOutputPort); + if (!fOutputPort) { + jack_error("Cannot open CoreMidi out port\n"); + goto error; + } + + fMidiDestination = new MIDIEndpointRef[inchannels + fRealCaptureChannels]; + assert(fMidiDestination); + + // Virtual input + for (int i = 0; i < inchannels; i++) { + std::stringstream num; + num << i; + str = "JackMidi" + num.str(); + coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding()); + err = MIDIDestinationCreate(fMidiClient, coutputStr, ReadVirtualProc, fRingBuffer[i], &fMidiDestination[i]); + CFRelease(coutputStr); + if (!fMidiDestination[i]) { + jack_error("Cannot create CoreMidi destination"); + goto error; + } + } + + // Real input + for (int i = 0; i < fRealCaptureChannels; i++) { + fMidiDestination[i + inchannels] = MIDIGetSource(i); + MIDIPortConnectSource(fInputPort, fMidiDestination[i + inchannels], fRingBuffer[i + inchannels]); + } + + fMidiSource = new MIDIEndpointRef[outchannels + fRealPlaybackChannels]; + assert(fMidiSource); + + // Virtual output + for (int i = 0; i < outchannels; i++) { + std::stringstream num; + num << i; + str = "JackMidi" + num.str(); + coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding()); + err = MIDISourceCreate(fMidiClient, coutputStr, &fMidiSource[i]); + CFRelease(coutputStr); + if (!fMidiSource[i]) { + jack_error("Cannot create CoreMidi source"); + goto error; + } + } + + // Real output + for (int i = 0; i < fRealPlaybackChannels; i++) { + fMidiSource[i + outchannels] = MIDIGetDestination(i); + } + + return 0; + +error: + Close(); + return -1; +} + +int JackCoreMidiDriver::Close() +{ + if (fInputPort) + MIDIPortDispose(fInputPort); + + if (fOutputPort) + MIDIPortDispose(fOutputPort); + + // Only dispose "virtual" endpoints + for (int i = 0; i < fCaptureChannels - fRealCaptureChannels; i++) { + if (fMidiDestination) + MIDIEndpointDispose(fMidiDestination[i]); + } + delete[] fMidiDestination; + + // Only dispose "virtual" endpoints + for (int i = 0; i < fPlaybackChannels - fRealPlaybackChannels; i++) { + if (fMidiSource[i]) + MIDIEndpointDispose(fMidiSource[i]); + } + delete[] fMidiSource; + + if (fMidiClient) + MIDIClientDispose(fMidiClient); + + return 0; +} + +int JackCoreMidiDriver::Attach() +{ + OSStatus err; + JackPort* port; + CFStringRef pname; + jack_port_id_t port_index; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char endpoint_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + unsigned long port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + int i; + + jack_log("JackCoreMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); + + for (i = 0; i < fCaptureChannels; i++) { + + err = MIDIObjectGetStringProperty(fMidiDestination[i], kMIDIPropertyName, &pname); + if (err == noErr) { + CFStringGetCString(pname, endpoint_name, sizeof(endpoint_name), 0); + CFRelease(pname); + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, endpoint_name, i + 1); + } else { + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); + } + + snprintf(name, sizeof(name) - 1, "%s:capture_%d", fClientControl.fName, i + 1); + if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { + jack_error("driver: cannot register port for %s", name); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + fCapturePortList[i] = port_index; + jack_log("JackCoreMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); + } + + port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; + + for (i = 0; i < fPlaybackChannels; i++) { + + err = MIDIObjectGetStringProperty(fMidiSource[i], kMIDIPropertyName, &pname); + if (err == noErr) { + CFStringGetCString(pname, endpoint_name, sizeof(endpoint_name), 0); + CFRelease(pname); + snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, endpoint_name, i + 1); + } else { + snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); + } + + snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); + if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { + jack_error("driver: cannot register port for %s", name); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + fPlaybackPortList[i] = port_index; + jack_log("JackCoreMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); + } + + return 0; +} +int JackCoreMidiDriver::Read() +{ + for (int chan = 0; chan < fCaptureChannels; chan++) { + + if (fGraphManager->GetConnectionsNum(fCapturePortList[chan]) > 0) { + + // Get JACK port + JackMidiBuffer* midi_buffer = GetInputBuffer(chan); + + if (jack_ringbuffer_read_space(fRingBuffer[chan]) == 0) { + // Reset buffer + midi_buffer->Reset(midi_buffer->nframes); + } else { + + while (jack_ringbuffer_read_space(fRingBuffer[chan]) > 0) { + + // Read event number + int ev_count = 0; + jack_ringbuffer_read(fRingBuffer[chan], (char*)&ev_count, sizeof(int)); + + for (int j = 0; j < ev_count; j++) { + // Read event length + UInt16 event_len; + jack_ringbuffer_read(fRingBuffer[chan], (char*)&event_len, sizeof(UInt16)); + // Read event actual data + jack_midi_data_t* dest = midi_buffer->ReserveEvent(0, event_len); + jack_ringbuffer_read(fRingBuffer[chan], (char*)dest, event_len); + } + } + } + + } else { + // Consume ring buffer + jack_ringbuffer_read_advance(fRingBuffer[chan], jack_ringbuffer_read_space(fRingBuffer[chan])); + } + } + return 0; +} + +int JackCoreMidiDriver::Write() +{ + MIDIPacketList* pktlist = (MIDIPacketList*)fMIDIBuffer; + + for (int chan = 0; chan < fPlaybackChannels; chan++) { + + if (fGraphManager->GetConnectionsNum(fPlaybackPortList[chan]) > 0) { + + MIDIPacket* packet = MIDIPacketListInit(pktlist); + JackMidiBuffer* midi_buffer = GetOutputBuffer(chan); + + // TODO : use timestamp + + for (unsigned int j = 0; j < midi_buffer->event_count; j++) { + JackMidiEvent* ev = &midi_buffer->events[j]; + packet = MIDIPacketListAdd(pktlist, sizeof(fMIDIBuffer), packet, MIDIGetCurrentHostTime(), ev->size, ev->GetData(midi_buffer)); + } + + if (packet) { + if (chan < fPlaybackChannels - fRealPlaybackChannels) { + OSStatus err = MIDIReceived(fMidiSource[chan], pktlist); + if (err != noErr) + jack_error("MIDIReceived error"); + } else { + OSStatus err = MIDISend(fOutputPort, fMidiSource[chan], pktlist); + if (err != noErr) + jack_error("MIDISend error"); + } + } + } + } + + return 0; +} + +} // end of namespace + +#ifdef __cplusplus +extern "C" +{ +#endif + + SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() + { + jack_driver_desc_t * desc; + unsigned int i; + + desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); + strcpy(desc->name, "coremidi"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 + strcpy(desc->desc, "Apple CoreMIDI API based MIDI backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 + + desc->nparams = 2; + desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); + + i = 0; + strcpy(desc->params[i].name, "inchannels"); + desc->params[i].character = 'i'; + desc->params[i].type = JackDriverParamInt; + desc->params[i].value.ui = 0; + strcpy(desc->params[i].short_desc, "CoreMIDI virtual bus"); + strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy(desc->params[i].name, "outchannels"); + desc->params[i].character = 'o'; + desc->params[i].type = JackDriverParamInt; + desc->params[i].value.ui = 0; + strcpy(desc->params[i].short_desc, "CoreMIDI virtual bus"); + strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + + return desc; + } + + SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) + { + const JSList * node; + const jack_driver_param_t * param; + int virtual_in = 0; + int virtual_out = 0; + + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { + + case 'i': + virtual_in = param->value.ui; + break; + + case 'o': + virtual_out = param->value.ui; + break; + } + } + + Jack::JackDriverClientInterface* driver = new Jack::JackCoreMidiDriver("system_midi", "coremidi", engine, table); + if (driver->Open(1, 1, virtual_in, virtual_out, false, "in", "out", 0, 0) == 0) { + return driver; + } else { + delete driver; + return NULL; + } + } + +#ifdef __cplusplus +} +#endif + diff --git a/macosx/coremidi/JackCoreMidiDriver.h b/macosx/coremidi/JackCoreMidiDriver.h new file mode 100644 index 00000000..190e9cd2 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiDriver.h @@ -0,0 +1,80 @@ +/* +Copyright (C) 2009 Grame + +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; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiDriver__ +#define __JackCoreMidiDriver__ + +#include +#include "JackMidiDriver.h" +#include "JackTime.h" + +namespace Jack +{ + +/*! +\brief The CoreMidi driver. +*/ + +class JackCoreMidiDriver : public JackMidiDriver +{ + + private: + + MIDIClientRef fMidiClient; + MIDIPortRef fInputPort; + MIDIPortRef fOutputPort; + MIDIEndpointRef* fMidiDestination; + MIDIEndpointRef* fMidiSource; + + char fMIDIBuffer[BUFFER_SIZE_MAX * sizeof(float)]; + + int fRealCaptureChannels; + int fRealPlaybackChannels; + + static void ReadProcAux(const MIDIPacketList *pktlist, jack_ringbuffer_t* ringbuffer); + static void ReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon); + static void ReadVirtualProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon); + static void NotifyProc(const MIDINotification *message, void *refCon); + + public: + + JackCoreMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); + virtual ~JackCoreMidiDriver(); + + int Open( bool capturing, + bool playing, + int chan_in, + int chan_out, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + int Close(); + + int Attach(); + + int Read(); + int Write(); + +}; + +} // end of namespace + +#endif diff --git a/macosx/wscript b/macosx/wscript index a67869c0..198edef6 100644 --- a/macosx/wscript +++ b/macosx/wscript @@ -30,6 +30,21 @@ def create_jack_audio_driver_obj(bld, target, sources, uselib = None): driver.uselib = uselib return driver +def create_jack_midi_driver_obj(bld, target, sources, uselib = None): + driver = bld.new_task_gen('cxx', 'shlib') + driver.features.append('cc') + driver.env['shlib_PATTERN'] = 'jack_%s.so' + driver.defines = 'HAVE_CONFIG_H' + driver.includes = ['.', '../macosx', '../posix', '../common', '../common/jack'] + driver.target = target + driver.source = sources + driver.install_path = '${ADDON_DIR}/' + driver.uselib_local = 'serverlib' + driver.env.append_value("LINKFLAGS", "-framework CoreMIDI -framework CoreServices -framework AudioUnit") + if uselib: + driver.uselib = uselib + return driver + def build(bld): jackd = bld.new_task_gen('cxx', 'program') jackd.includes = ['.', '../macosx', '../posix', '../common', '../common/jack'] @@ -43,6 +58,8 @@ def build(bld): create_jack_driver_obj(bld, 'dummy', '../common/JackDummyDriver.cpp') create_jack_audio_driver_obj(bld, 'coreaudio', 'coreaudio/JackCoreAudioDriver.cpp') + + create_jack_midi_driver_obj(bld, 'coremidi', 'coremidi/JackCoreMidiDriver.cpp') portaudio_src = [ '../windows/JackPortAudioDriver.cpp', diff --git a/windows/jack_winmme.cbp b/windows/jack_winmme.cbp new file mode 100644 index 00000000..2c6d8bbe --- /dev/null +++ b/windows/jack_winmme.cbp @@ -0,0 +1,121 @@ + + + + + + diff --git a/windows/jackd.workspace b/windows/jackd.workspace index e92ff780..615bae3f 100644 --- a/windows/jackd.workspace +++ b/windows/jackd.workspace @@ -1,7 +1,7 @@ - + @@ -46,5 +46,6 @@ + diff --git a/windows/jackwinmme.rc b/windows/jackwinmme.rc new file mode 100644 index 00000000..57187d6c --- /dev/null +++ b/windows/jackwinmme.rc @@ -0,0 +1,41 @@ +// Generated by ResEdit 1.4.3 +// Copyright (C) 2006-2008 +// http://www.resedit.net + +#include "resource.h" +#include "afxres.h" + + +// +// Version Information resources +// +LANGUAGE LANG_FRENCH, SUBLANG_DEFAULT +1 VERSIONINFO + FILEVERSION 1,9,3,0 + PRODUCTVERSION 1,9,3,0 + FILEOS VOS_UNKNOWN + FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040c04b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "Grame\0" + VALUE "FileDescription", "Jackmp WinMMEo Driver for Windows\0" + VALUE "FileVersion", "1, 9, 3, 0\0" + VALUE "InternalName", "jack_portaudio\0" + VALUE "LegalCopyright", "Copyright Grame © 2006-2009\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "jack_winmme.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "jack_winmme\0" + VALUE "ProductVersion", "1, 9, 3, 0\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 1036, 1200 + END +END diff --git a/windows/libjackserver.cbp b/windows/libjackserver.cbp index ef0bc173..8a82c34b 100644 --- a/windows/libjackserver.cbp +++ b/windows/libjackserver.cbp @@ -1,199 +1,200 @@ - - - - - - + + + + + + diff --git a/windows/winmme/JackWinMMEDriver.cpp b/windows/winmme/JackWinMMEDriver.cpp new file mode 100644 index 00000000..d803f707 --- /dev/null +++ b/windows/winmme/JackWinMMEDriver.cpp @@ -0,0 +1,501 @@ +/* +Copyright (C) 2009 Grame + +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; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackWinMMEDriver.h" +#include "JackGraphManager.h" +#include "JackEngineControl.h" +#include "JackDriverLoader.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace Jack +{ + +static bool InitHeaders(MidiSlot* slot) +{ + slot->fHeader = (LPMIDIHDR)GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT, sizeof(MIDIHDR) + kBuffSize); + if (!slot->fHeader) + return false; + + slot->fHeader->lpData = (LPSTR)((LPBYTE)slot->fHeader + sizeof(MIDIHDR)); + slot->fHeader->dwBufferLength = kBuffSize; + slot->fHeader->dwFlags = 0; + slot->fHeader->dwUser = 0; + slot->fHeader->lpNext = 0; + slot->fHeader->dwBytesRecorded = 0; + return true; +} + +void CALLBACK JackWinMMEDriver::MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD userData, DWORD dwParam1, DWORD dwParam2) +{ + jack_ringbuffer_t* ringbuffer = (jack_ringbuffer_t*)userData; + //jack_info("JackWinMMEDriver::MidiInProc 0\n"); + + switch (wMsg) { + case MIM_OPEN: + break; + + case MIM_ERROR: + case MIM_DATA: { + + //jack_info("JackWinMMEDriver::MidiInProc"); + + // One event + unsigned int num_packet = 1; + jack_ringbuffer_write(ringbuffer, (char*)&num_packet, sizeof(unsigned int)); + + // Write event actual data + jack_ringbuffer_write(ringbuffer, (char*)&dwParam1, 3); + break; + } + + case MIM_LONGERROR: + case MIM_LONGDATA: + /* + Nothing for now + */ + break; + } +} + +JackWinMMEDriver::JackWinMMEDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) + : JackMidiDriver(name, alias, engine, table), + fRealCaptureChannels(0), + fRealPlaybackChannels(0), + fMidiSource(NULL), + fMidiDestination(NULL) +{} + +JackWinMMEDriver::~JackWinMMEDriver() +{} + +int JackWinMMEDriver::Open(bool capturing, + bool playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + + jack_log("JackWinMMEDriver::Open"); + + fRealCaptureChannels = midiInGetNumDevs(); + fRealPlaybackChannels = midiOutGetNumDevs (); + + // Generic JackMidiDriver Open + if (JackMidiDriver::Open(capturing, playing, inchannels + fRealCaptureChannels, outchannels + fRealPlaybackChannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency) != 0) + return -1; + + fMidiDestination = new MidiSlot[fRealCaptureChannels]; + assert(fMidiDestination); + + // Real input + for (int i = 0; i < fRealCaptureChannels; i++) { + + HMIDIIN handle; + fMidiDestination[i].fIndex = i; + MMRESULT ret = midiInOpen(&handle, fMidiDestination[i].fIndex, (DWORD)MidiInProc, (DWORD)fRingBuffer[i], CALLBACK_FUNCTION); + + if (ret == MMSYSERR_NOERROR) { + fMidiDestination[i].fHandle = handle; + if (!InitHeaders(&fMidiDestination[i])) { + jack_error("memory allocation failed"); + midiInClose(handle); + goto error; + //continue; + } + ret = midiInPrepareHeader(handle, fMidiDestination[i].fHeader, sizeof(MIDIHDR)); + + if (ret == MMSYSERR_NOERROR) { + fMidiDestination[i].fHeader->dwUser = 1; + ret = midiInAddBuffer(handle, fMidiDestination[i].fHeader, sizeof(MIDIHDR)); + if (ret == MMSYSERR_NOERROR) { + ret = midiInStart(handle); + if (ret != MMSYSERR_NOERROR) { + jack_error("midiInStart error"); + CloseInput(&fMidiDestination[i]); + goto error; + } + } else { + jack_error ("midiInAddBuffer error"); + CloseInput(&fMidiDestination[i]); + goto error; + } + } else { + jack_error("midiInPrepareHeader error"); + midiInClose(handle); + goto error; + } + } else { + jack_error ("midiInOpen error"); + goto error; + } + } + + fMidiSource = new MidiSlot[fRealPlaybackChannels]; + assert(fMidiSource); + + // Real output + for (int i = 0; i < fRealPlaybackChannels; i++) { + MMRESULT res; + HMIDIOUT handle; + fMidiSource[i].fIndex = i; + UINT ret = midiOutOpen(&handle, fMidiSource[i].fIndex, 0L, 0L, CALLBACK_NULL); + if (ret == MMSYSERR_NOERROR) { + fMidiSource[i].fHandle = handle; + if (!InitHeaders(&fMidiSource[i])) { + jack_error("memory allocation failed"); + midiOutClose(handle); + //continue; + goto error; + } + res = midiOutPrepareHeader(handle, fMidiSource[i].fHeader, sizeof(MIDIHDR)); + if (res != MMSYSERR_NOERROR) { + jack_error("midiOutPrepareHeader error %d %d %d", i, handle, res); + //continue; + goto error; + } else { + fMidiSource[i].fHeader->dwUser = 1; + } + } else { + jack_error("midiOutOpen error"); + goto error; + } + } + + return 0; + +error: + Close(); + return -1; +} + +void JackWinMMEDriver::CloseInput(MidiSlot* slot) +{ + MMRESULT res; + int retry = 0; + + if (slot->fHandle == 0) + return; + + HMIDIIN handle = (HMIDIIN)slot->fHandle; + slot->fHeader->dwUser = 0; + res = midiInStop(handle); + if (res != MMSYSERR_NOERROR) { + jack_error("midiInStop error"); + } + res = midiInReset(handle); + if (res != MMSYSERR_NOERROR) { + jack_error("midiInReset error"); + } + res = midiInUnprepareHeader(handle, slot->fHeader, sizeof(MIDIHDR)); + if (res != MMSYSERR_NOERROR) { + jack_error("midiInUnprepareHeader error"); + } + do { + res = midiInClose(handle); + if (res != MMSYSERR_NOERROR) { + jack_error("midiInClose error"); + } + if (res == MIDIERR_STILLPLAYING) + midiInReset(handle); + Sleep (10); + retry++; + } while ((res == MIDIERR_STILLPLAYING) && (retry < 10)); + + if (slot->fHeader) { + GlobalFreePtr(slot->fHeader); + } +} + +void JackWinMMEDriver::CloseOutput(MidiSlot* slot) +{ + MMRESULT res; + int retry = 0; + + if (slot->fHandle == 0) + return; + + HMIDIOUT handle = (HMIDIOUT)slot->fHandle; + res = midiOutReset(handle); + if (res != MMSYSERR_NOERROR) + jack_error("midiOutReset error"); + midiOutUnprepareHeader(handle, slot->fHeader, sizeof(MIDIHDR)); + do { + res = midiOutClose(handle); + if (res != MMSYSERR_NOERROR) + jack_error("midiOutClose error"); + Sleep(10); + retry++; + } while ((res == MIDIERR_STILLPLAYING) && (retry < 10)); + + if (slot->fHeader) { + GlobalFreePtr(slot->fHeader); + } +} + +int JackWinMMEDriver::Close() +{ + jack_log("JackWinMMEDriver::Close"); + + // Close input + if (fMidiDestination) { + for (int i = 0; i < fRealCaptureChannels; i++) { + CloseInput(&fMidiDestination[i]); + } + delete[] fMidiDestination; + } + + // Close output + if (fMidiSource) { + for (int i = 0; i < fRealPlaybackChannels; i++) { + CloseOutput(&fMidiSource[i]); + } + delete[] fMidiSource; + } + + return 0; +} + +int JackWinMMEDriver::Attach() +{ + JackPort* port; + jack_port_id_t port_index; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + unsigned long port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + MMRESULT res; + int i; + + jack_log("JackMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); + + for (i = 0; i < fCaptureChannels; i++) { + MIDIINCAPS caps; + res = midiInGetDevCaps(i, &caps, sizeof(caps)); + if (res == MMSYSERR_NOERROR) { + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, caps.szPname, i + 1); + } else { + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); + } + snprintf(name, sizeof(name) - 1, "%s:capture_%d", fClientControl.fName, i + 1); + + if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { + jack_error("driver: cannot register port for %s", name); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + fCapturePortList[i] = port_index; + jack_log("JackMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); + } + + port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; + + for (i = 0; i < fPlaybackChannels; i++) { + MIDIOUTCAPS caps; + res = midiOutGetDevCaps(i, &caps, sizeof(caps)); + if (res == MMSYSERR_NOERROR) { + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, caps.szPname, i + 1); + } else { + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fPlaybackDriverName, i + 1); + } + snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); + + if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { + jack_error("driver: cannot register port for %s", name); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + fPlaybackPortList[i] = port_index; + jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); + } + + return 0; +} + +int JackWinMMEDriver::Read() +{ + size_t size; + + for (int chan = 0; chan < fCaptureChannels; chan++) { + + if (fGraphManager->GetConnectionsNum(fCapturePortList[chan]) > 0) { + + JackMidiBuffer* midi_buffer = GetInputBuffer(chan); + + if (jack_ringbuffer_read_space (fRingBuffer[chan]) == 0) { + // Reset buffer + midi_buffer->Reset(midi_buffer->nframes); + } else { + + while ((size = jack_ringbuffer_read_space (fRingBuffer[chan])) > 0) { + + //jack_info("jack_ringbuffer_read_space %d", size); + int ev_count = 0; + jack_ringbuffer_read(fRingBuffer[chan], (char*)&ev_count, sizeof(int)); + + if (ev_count > 0) { + for (int j = 0; j < ev_count; j++) { + unsigned int event_len = 3; + // Read event actual data + jack_midi_data_t* dest = midi_buffer->ReserveEvent(0, event_len); + jack_ringbuffer_read(fRingBuffer[chan], (char*)dest, event_len); + } + } + } + } + } else { + //jack_info("Consume ring buffer"); + jack_ringbuffer_read_advance(fRingBuffer[chan], jack_ringbuffer_read_space(fRingBuffer[chan])); + } + } + return 0; +} + +int JackWinMMEDriver::Write() +{ + for (int chan = 0; chan < fPlaybackChannels; chan++) { + + if (fGraphManager->GetConnectionsNum(fPlaybackPortList[chan]) > 0) { + + JackMidiBuffer* midi_buffer = GetOutputBuffer(chan); + + // TODO : use timestamp + + for (unsigned int j = 0; j < midi_buffer->event_count; j++) { + JackMidiEvent* ev = &midi_buffer->events[j]; + if (ev->size <= 3) { + MMRESULT res = midiOutShortMsg((HMIDIOUT)fMidiSource[chan].fHandle, *((DWORD*)ev->GetData(midi_buffer))); + if (res != MMSYSERR_NOERROR) + jack_error ("midiOutShortMsg error res %d", res); + } else { + + } + } + } + } + + return 0; +} + +} // end of namespace + +#ifdef __cplusplus +extern "C" +{ +#endif + + SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() + { + jack_driver_desc_t * desc; + unsigned int i; + + desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); + strcpy(desc->name, "winmme"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 + strcpy(desc->desc, "WinMME API based MIDI backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 + + desc->nparams = 0; + desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); + + return desc; + } + + SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) + { + /* + unsigned int capture_ports = 2; + unsigned int playback_ports = 2; + unsigned long wait_time = 0; + const JSList * node; + const jack_driver_param_t * param; + bool monitor = false; + + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { + + case 'C': + capture_ports = param->value.ui; + break; + + case 'P': + playback_ports = param->value.ui; + break; + + case 'r': + sample_rate = param->value.ui; + break; + + case 'p': + period_size = param->value.ui; + break; + + case 'w': + wait_time = param->value.ui; + break; + + case 'm': + monitor = param->value.i; + break; + } + } + */ + + Jack::JackDriverClientInterface* driver = new Jack::JackWinMMEDriver("system_midi", "winmme", engine, table); + if (driver->Open(1, 1, 0, 0, false, "in", "out", 0, 0) == 0) { + return driver; + } else { + delete driver; + return NULL; + } + } + +#ifdef __cplusplus +} +#endif + + +/* +jack_connect system:midi_capture_1 system_midi:playback_1 +jack_connect system:midi_capture_1 system_midi:playback_2 + +jack_connect system:midi_capture_1 system_midi:playback_1 + +jack_connect system:midi_capture_1 system_midi:playback_1 + +jack_connect system:midi_capture_1 system_midi:playback_1 + +jack_connect system_midi:capture_1 system:midi_playback_1 +jack_connect system_midi:capture_2 system:midi_playback_1 + +jack_connect system_midi:capture_1 system_midi:playback_1 + +*/ diff --git a/windows/winmme/JackWinMMEDriver.h b/windows/winmme/JackWinMMEDriver.h new file mode 100644 index 00000000..6c28a0ef --- /dev/null +++ b/windows/winmme/JackWinMMEDriver.h @@ -0,0 +1,87 @@ +/* +Copyright (C) 2009 Grame + +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; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackWinMMEDriver__ +#define __JackWinMMEDriver__ + +#include "JackMidiDriver.h" +#include "JackTime.h" + +namespace Jack +{ + +/*! +\brief The WinMME driver. +*/ + +#define kBuffSize 512 + +struct MidiSlot { + + LPVOID fHandle; // MMSystem handler + short fIndex; // MMSystem dev index + LPMIDIHDR fHeader; // for long msg output + + MidiSlot():fHandle(0),fIndex(0) + {} + +}; + +class JackWinMMEDriver : public JackMidiDriver +{ + + private: + + int fRealCaptureChannels; + int fRealPlaybackChannels; + + MidiSlot* fMidiSource; + MidiSlot* fMidiDestination; + + void CloseInput(MidiSlot* slot); + void CloseOutput(MidiSlot* slot); + + static void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); + + public: + + JackWinMMEDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); + virtual ~JackWinMMEDriver(); + + int Open(bool capturing, + bool playing, + int chan_in, + int chan_out, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + int Close(); + + int Attach(); + + int Read(); + int Write(); + +}; + +} // end of namespace + +#endif