git-svn-id: http://subversion.jackaudio.org/jack/jack2/branches/libjacknet@4324 0c269be4-1314-0410-8aa9-9f06e86f4224tags/1.9.8
| @@ -34,6 +34,10 @@ Valerio Pilo | |||||
| Jackdmp changes log | Jackdmp changes log | ||||
| --------------------------- | --------------------------- | ||||
| 2011-04-18 Stephane Letz <letz@grame.fr> | |||||
| * JackWeakAPI.cpp renamed in JackWeakAPI.c. | |||||
| 2011-04-04 Stephane Letz <letz@grame.fr> | 2011-04-04 Stephane Letz <letz@grame.fr> | ||||
| * Correct driver lifetime management. | * Correct driver lifetime management. | ||||
| @@ -28,12 +28,12 @@ namespace Jack | |||||
| int JackFreewheelDriver::Process() | int JackFreewheelDriver::Process() | ||||
| { | { | ||||
| int res = 0; | |||||
| int res = 0; | |||||
| jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); | |||||
| JackDriver::CycleTakeBeginTime(); | |||||
| jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); | |||||
| JackDriver::CycleTakeBeginTime(); | |||||
| if (fEngine->Process(fBeginDateUst, fEndDateUst)) { | |||||
| if (fEngine->Process(fBeginDateUst, fEndDateUst)) { | |||||
| if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable)) { // Signal all clients | if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable)) { // Signal all clients | ||||
| jack_error("JackFreewheelDriver::Process: ResumeRefNum error"); | jack_error("JackFreewheelDriver::Process: ResumeRefNum error"); | ||||
| @@ -46,10 +46,10 @@ int JackFreewheelDriver::Process() | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| } else { // Graph not finished: do not activate it | |||||
| } else { // Graph not finished: do not activate it | |||||
| jack_error("JackFreewheelDriver::Process: Process error"); | jack_error("JackFreewheelDriver::Process: Process error"); | ||||
| res = -1; | res = -1; | ||||
| } | |||||
| } | |||||
| return res; | return res; | ||||
| } | } | ||||
| @@ -54,23 +54,16 @@ JackMidiAsyncQueue::DequeueEvent() | |||||
| { | { | ||||
| jack_midi_event_t *event = 0; | jack_midi_event_t *event = 0; | ||||
| if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) { | if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) { | ||||
| size_t size; | |||||
| event = &dequeue_event; | event = &dequeue_event; | ||||
| jack_ringbuffer_read(info_ring, (char *) &(event->time), | jack_ringbuffer_read(info_ring, (char *) &(event->time), | ||||
| sizeof(jack_nframes_t)); | sizeof(jack_nframes_t)); | ||||
| size_t size; | |||||
| jack_ringbuffer_read(info_ring, (char *) &size, sizeof(size_t)); | |||||
| jack_ringbuffer_read(info_ring, (char *) &size, | |||||
| sizeof(size_t)); | |||||
| jack_ringbuffer_read(byte_ring, (char *) data_buffer, | |||||
| size * sizeof(jack_midi_data_t)); | |||||
| event->buffer = data_buffer; | event->buffer = data_buffer; | ||||
| event->size = size; | event->size = size; | ||||
| jack_ringbuffer_data_t vector[2]; | |||||
| jack_ringbuffer_get_read_vector(byte_ring, vector); | |||||
| size_t size1 = vector[0].len; | |||||
| memcpy(data_buffer, vector[0].buf, size1 * sizeof(jack_midi_data_t)); | |||||
| if (size1 < size) { | |||||
| memcpy(data_buffer + size1, vector[1].buf, | |||||
| (size - size1) * sizeof(jack_midi_data_t)); | |||||
| } | |||||
| jack_ringbuffer_read_advance(byte_ring, | |||||
| size * sizeof(jack_midi_data_t)); | |||||
| } | } | ||||
| return event; | return event; | ||||
| } | } | ||||
| @@ -28,14 +28,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
| namespace Jack { | namespace Jack { | ||||
| /** | /** | ||||
| * This is a MIDI message queue designed to allow two threads to pass MIDI | |||||
| * messages between two threads (though it can also be used to buffer | |||||
| * events internally). This is especially useful if the MIDI API | |||||
| * you're attempting to interface with doesn't provide the ability to | |||||
| * schedule MIDI events ahead of time and/or has blocking send/receive | |||||
| * calls, as it allows a separate thread to handle input/output while the | |||||
| * JACK process thread copies events from a `JackMidiBufferReadQueue` to | |||||
| * this queue, or from this queue to a `JackMidiBufferWriteQueue`. | |||||
| * This is a MIDI message queue designed to allow one thread to pass MIDI | |||||
| * messages to another thread (though it can also be used to buffer events | |||||
| * internally). This is especially useful if the MIDI API you're | |||||
| * attempting to interface with doesn't provide the ability to schedule | |||||
| * MIDI events ahead of time and/or has blocking send/receive calls, as it | |||||
| * allows a separate thread to handle input/output while the JACK process | |||||
| * thread copies events from a MIDI buffer to this queue, or vice versa. | |||||
| */ | */ | ||||
| class SERVER_EXPORT JackMidiAsyncQueue: | class SERVER_EXPORT JackMidiAsyncQueue: | ||||
| @@ -64,7 +63,8 @@ namespace Jack { | |||||
| JackMidiAsyncQueue(size_t max_bytes=4096, size_t max_messages=1024); | JackMidiAsyncQueue(size_t max_bytes=4096, size_t max_messages=1024); | ||||
| virtual ~JackMidiAsyncQueue(); | |||||
| virtual | |||||
| ~JackMidiAsyncQueue(); | |||||
| /** | /** | ||||
| * Dequeues and returns a MIDI event. Returns '0' if there are no MIDI | * Dequeues and returns a MIDI event. Returns '0' if there are no MIDI | ||||
| @@ -66,6 +66,12 @@ JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||||
| return packet_queue->EnqueueEvent(time, size, buffer); | return packet_queue->EnqueueEvent(time, size, buffer); | ||||
| } | } | ||||
| size_t | |||||
| JackMidiRawInputWriteQueue::GetAvailableSpace() | |||||
| { | |||||
| return packet_queue->GetAvailableSpace(); | |||||
| } | |||||
| void | void | ||||
| JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes, | JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes, | ||||
| size_t total_bytes) | size_t total_bytes) | ||||
| @@ -142,6 +142,13 @@ namespace Jack { | |||||
| EnqueueEvent(jack_nframes_t time, size_t size, | EnqueueEvent(jack_nframes_t time, size_t size, | ||||
| jack_midi_data_t *buffer); | jack_midi_data_t *buffer); | ||||
| /** | |||||
| * Returns the maximum size event that can be enqueued right *now*. | |||||
| */ | |||||
| size_t | |||||
| GetAvailableSpace(); | |||||
| /** | /** | ||||
| * The `Process()` method should be called each time the | * The `Process()` method should be called each time the | ||||
| * `EnqueueEvent()` method returns `OK`. The `Process()` method will | * `EnqueueEvent()` method returns `OK`. The `Process()` method will | ||||
| @@ -50,27 +50,23 @@ JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue() | |||||
| delete rt_queue; | delete rt_queue; | ||||
| } | } | ||||
| bool | |||||
| void | |||||
| JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent() | JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent() | ||||
| { | { | ||||
| non_rt_event = non_rt_queue->DequeueEvent(); | non_rt_event = non_rt_queue->DequeueEvent(); | ||||
| bool result = non_rt_event != 0; | |||||
| if (result) { | |||||
| if (non_rt_event) { | |||||
| non_rt_event_time = non_rt_event->time; | non_rt_event_time = non_rt_event->time; | ||||
| running_status = ApplyRunningStatus(non_rt_event, running_status); | running_status = ApplyRunningStatus(non_rt_event, running_status); | ||||
| } | } | ||||
| return result; | |||||
| } | } | ||||
| bool | |||||
| void | |||||
| JackMidiRawOutputWriteQueue::DequeueRealtimeEvent() | JackMidiRawOutputWriteQueue::DequeueRealtimeEvent() | ||||
| { | { | ||||
| rt_event = rt_queue->DequeueEvent(); | rt_event = rt_queue->DequeueEvent(); | ||||
| bool result = rt_event != 0; | |||||
| if (result) { | |||||
| if (rt_event) { | |||||
| rt_event_time = rt_event->time; | rt_event_time = rt_event->time; | ||||
| } | } | ||||
| return result; | |||||
| } | } | ||||
| Jack::JackMidiWriteQueue::EnqueueResult | Jack::JackMidiWriteQueue::EnqueueResult | ||||
| @@ -79,11 +75,7 @@ JackMidiRawOutputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||||
| { | { | ||||
| JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue : | JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue : | ||||
| non_rt_queue; | non_rt_queue; | ||||
| EnqueueResult result = queue->EnqueueEvent(time, size, buffer); | |||||
| if (result == OK) { | |||||
| last_enqueued_message_time = time; | |||||
| } | |||||
| return result; | |||||
| return queue->EnqueueEvent(time, size, buffer); | |||||
| } | } | ||||
| void | void | ||||
| @@ -99,38 +91,34 @@ JackMidiRawOutputWriteQueue::HandleWriteQueueBug(jack_nframes_t time, | |||||
| jack_nframes_t | jack_nframes_t | ||||
| JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame) | JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame) | ||||
| { | { | ||||
| jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||||
| while (STILL_TIME(current_frame, boundary_frame)) { | |||||
| if (! non_rt_event) { | |||||
| DequeueNonRealtimeEvent(); | |||||
| } | |||||
| if (! rt_event) { | |||||
| DequeueRealtimeEvent(); | |||||
| } | |||||
| if (! (non_rt_event || rt_event)) { | |||||
| return 0; | |||||
| if (! non_rt_event) { | |||||
| DequeueNonRealtimeEvent(); | |||||
| } | |||||
| if (! rt_event) { | |||||
| DequeueRealtimeEvent(); | |||||
| } | |||||
| while (rt_event) { | |||||
| jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||||
| if ((rt_event_time > current_frame) && non_rt_event && | |||||
| (non_rt_event_time < rt_event_time)) { | |||||
| if (! SendNonRTBytes(rt_event_time < boundary_frame ? | |||||
| rt_event_time : boundary_frame)) { | |||||
| return non_rt_event_time; | |||||
| } | |||||
| current_frame = send_queue->GetNextScheduleFrame(); | |||||
| } | } | ||||
| if (! WriteRealtimeEvents(boundary_frame)) { | |||||
| break; | |||||
| if (! STILL_TIME(current_frame, boundary_frame)) { | |||||
| return (! non_rt_event) ? rt_event_time : | |||||
| non_rt_event_time < rt_event_time ? non_rt_event_time : | |||||
| rt_event_time; | |||||
| } | } | ||||
| jack_nframes_t non_rt_boundary = | |||||
| rt_event && STILL_TIME(rt_event_time, boundary_frame) ? | |||||
| rt_event_time : boundary_frame; | |||||
| if (! WriteNonRealtimeEvents(non_rt_boundary)) { | |||||
| break; | |||||
| if (! SendByte(rt_event_time, *(rt_event->buffer))) { | |||||
| return rt_event_time; | |||||
| } | } | ||||
| current_frame = send_queue->GetNextScheduleFrame(); | |||||
| DequeueRealtimeEvent(); | |||||
| } | } | ||||
| // If we get here, that means there is some sort of message available, and | |||||
| // that either we can't currently write to the write queue or we have | |||||
| // reached the boundary frame. Return the earliest time that a message is | |||||
| // scheduled to be sent. | |||||
| return ! non_rt_event ? rt_event_time : | |||||
| non_rt_event->size > 1 ? current_frame : | |||||
| ! rt_event ? non_rt_event_time : | |||||
| non_rt_event_time < rt_event_time ? non_rt_event_time : rt_event_time; | |||||
| SendNonRTBytes(boundary_frame); | |||||
| return non_rt_event ? non_rt_event_time : 0; | |||||
| } | } | ||||
| bool | bool | ||||
| @@ -151,78 +139,20 @@ JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time, | |||||
| } | } | ||||
| bool | bool | ||||
| JackMidiRawOutputWriteQueue:: | |||||
| WriteNonRealtimeEvents(jack_nframes_t boundary_frame) | |||||
| JackMidiRawOutputWriteQueue::SendNonRTBytes(jack_nframes_t boundary_frame) | |||||
| { | { | ||||
| if (! non_rt_event) { | |||||
| if (! DequeueNonRealtimeEvent()) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||||
| do { | |||||
| // Send out as much of the non-realtime buffer as we can, save for one | |||||
| // byte which we will send out when the message is supposed to arrive. | |||||
| for (; non_rt_event->size > 1; | |||||
| while (non_rt_event) { | |||||
| for (; non_rt_event->size; | |||||
| (non_rt_event->size)--, (non_rt_event->buffer)++) { | (non_rt_event->size)--, (non_rt_event->buffer)++) { | ||||
| jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||||
| if (! STILL_TIME(current_frame, boundary_frame)) { | if (! STILL_TIME(current_frame, boundary_frame)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (! SendByte(current_frame, *(non_rt_event->buffer))) { | |||||
| if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) { | |||||
| return false; | return false; | ||||
| } | } | ||||
| current_frame = send_queue->GetNextScheduleFrame(); | |||||
| } | |||||
| if (! (STILL_TIME(current_frame, boundary_frame) && | |||||
| STILL_TIME(non_rt_event_time, boundary_frame))) { | |||||
| return true; | |||||
| } | |||||
| // There's still time. Try to send the byte. | |||||
| if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) { | |||||
| return false; | |||||
| } | |||||
| current_frame = send_queue->GetNextScheduleFrame(); | |||||
| if (! DequeueNonRealtimeEvent()) { | |||||
| break; | |||||
| } | |||||
| } while (STILL_TIME(current_frame, boundary_frame)); | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| JackMidiRawOutputWriteQueue::WriteRealtimeEvents(jack_nframes_t boundary_frame) | |||||
| { | |||||
| jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||||
| if (! rt_event) { | |||||
| if (! DequeueRealtimeEvent()) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| for (;;) { | |||||
| if (! STILL_TIME(current_frame, boundary_frame)) { | |||||
| return false; | |||||
| } | |||||
| // If: | |||||
| // -there's still time before we need to send the realtime event | |||||
| // -there's a non-realtime event available for sending | |||||
| // -non-realtime data can be scheduled before this event | |||||
| if ((rt_event_time > current_frame) && non_rt_event && | |||||
| ((non_rt_event->size > 1) || | |||||
| (non_rt_event_time < rt_event_time))) { | |||||
| return true; | |||||
| } | |||||
| if (! SendByte(rt_event_time, *(rt_event->buffer))) { | |||||
| return false; | |||||
| } | |||||
| current_frame = send_queue->GetNextScheduleFrame(); | |||||
| if (! DequeueRealtimeEvent()) { | |||||
| return true; | |||||
| } | } | ||||
| DequeueNonRealtimeEvent(); | |||||
| } | } | ||||
| return true; | |||||
| } | } | ||||
| @@ -27,7 +27,7 @@ namespace Jack { | |||||
| /** | /** | ||||
| * This queue enqueues valid MIDI events and modifies them for raw output | * This queue enqueues valid MIDI events and modifies them for raw output | ||||
| * to a write queue. It has a number of advantages over straight MIDI | |||||
| * to a write queue. It has a couple of advantages over straight MIDI | |||||
| * event copying: | * event copying: | ||||
| * | * | ||||
| * -Running status: Status bytes can be omitted when the status byte of the | * -Running status: Status bytes can be omitted when the status byte of the | ||||
| @@ -39,10 +39,6 @@ namespace Jack { | |||||
| * non-realtime bytes so that realtime messages can be sent as close as | * non-realtime bytes so that realtime messages can be sent as close as | ||||
| * possible to the time they're scheduled for sending. | * possible to the time they're scheduled for sending. | ||||
| * | * | ||||
| * -Time optimization: Bytes in non-realtime messages are sent out early | |||||
| * when possible, with the last byte of the message being sent out as close | |||||
| * to the specified event time as possible. | |||||
| * | |||||
| * Use this queue if the MIDI API you're interfacing with allows you to | * Use this queue if the MIDI API you're interfacing with allows you to | ||||
| * send raw MIDI bytes. | * send raw MIDI bytes. | ||||
| */ | */ | ||||
| @@ -52,7 +48,6 @@ namespace Jack { | |||||
| private: | private: | ||||
| jack_nframes_t last_enqueued_message_time; | |||||
| jack_midi_event_t *non_rt_event; | jack_midi_event_t *non_rt_event; | ||||
| jack_nframes_t non_rt_event_time; | jack_nframes_t non_rt_event_time; | ||||
| JackMidiAsyncQueue *non_rt_queue; | JackMidiAsyncQueue *non_rt_queue; | ||||
| @@ -62,20 +57,17 @@ namespace Jack { | |||||
| jack_midi_data_t running_status; | jack_midi_data_t running_status; | ||||
| JackMidiSendQueue *send_queue; | JackMidiSendQueue *send_queue; | ||||
| bool | |||||
| void | |||||
| DequeueNonRealtimeEvent(); | DequeueNonRealtimeEvent(); | ||||
| bool | |||||
| void | |||||
| DequeueRealtimeEvent(); | DequeueRealtimeEvent(); | ||||
| bool | bool | ||||
| SendByte(jack_nframes_t time, jack_midi_data_t byte); | SendByte(jack_nframes_t time, jack_midi_data_t byte); | ||||
| bool | bool | ||||
| WriteNonRealtimeEvents(jack_nframes_t boundary_frame); | |||||
| bool | |||||
| WriteRealtimeEvents(jack_nframes_t boundary_frame); | |||||
| SendNonRTBytes(jack_nframes_t boundary_frame); | |||||
| protected: | protected: | ||||
| @@ -55,10 +55,11 @@ Jack::ApplyRunningStatus(jack_midi_event_t *event, | |||||
| jack_nframes_t | jack_nframes_t | ||||
| Jack::GetCurrentFrame() | Jack::GetCurrentFrame() | ||||
| { | { | ||||
| jack_time_t time = GetMicroSeconds(); | |||||
| JackEngineControl *control = GetEngineControl(); | JackEngineControl *control = GetEngineControl(); | ||||
| JackTimer timer; | JackTimer timer; | ||||
| control->ReadFrameTime(&timer); | control->ReadFrameTime(&timer); | ||||
| return timer.Time2Frames(GetMicroSeconds(), control->fBufferSize); | |||||
| return timer.Time2Frames(time, control->fBufferSize); | |||||
| } | } | ||||
| jack_nframes_t | jack_nframes_t | ||||
| @@ -1,10 +1,6 @@ | |||||
| //============================================================================= | //============================================================================= | ||||
| // MuseScore | |||||
| // Linux Music Score Editor | |||||
| // $Id: | |||||
| // | // | ||||
| // jackWeakAPI based on code from Stéphane Letz (Grame) | |||||
| // partly based on Julien Pommier (PianoTeq : http://www.pianoteq.com/) code. | |||||
| // jackWeakAPI partly based on Julien Pommier (PianoTeq : http://www.pianoteq.com/) code. | |||||
| // | // | ||||
| // Copyright (C) 2002-2007 Werner Schweer and others | // Copyright (C) 2002-2007 Werner Schweer and others | ||||
| // Copyright (C) 2009 Grame | // Copyright (C) 2009 Grame | ||||
| @@ -24,6 +20,7 @@ | |||||
| // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
| #include <jack/jack.h> | #include <jack/jack.h> | ||||
| #include <jack/session.h> | |||||
| #include <jack/thread.h> | #include <jack/thread.h> | ||||
| #include <jack/midiport.h> | #include <jack/midiport.h> | ||||
| #include <math.h> | #include <math.h> | ||||
| @@ -31,7 +28,7 @@ | |||||
| #include <dlfcn.h> | #include <dlfcn.h> | ||||
| #endif | #endif | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <iostream> | |||||
| #include <stdio.h> | |||||
| /* dynamically load libjack and forward all registered calls to libjack | /* dynamically load libjack and forward all registered calls to libjack | ||||
| (similar to what relaytool is trying to do, but more portably..) | (similar to what relaytool is trying to do, but more portably..) | ||||
| @@ -40,24 +37,25 @@ | |||||
| typedef void (*print_function)(const char *); | typedef void (*print_function)(const char *); | ||||
| typedef void *(*thread_routine)(void*); | typedef void *(*thread_routine)(void*); | ||||
| using std::cerr; | |||||
| int libjack_is_present = 0; // public symbol, similar to what relaytool does. | |||||
| static int libjack_is_present = 0; // public symbol, similar to what relaytool does. | |||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| HMODULE libjack_handle = 0; | |||||
| static HMODULE libjack_handle = 0; | |||||
| #else | #else | ||||
| static void *libjack_handle = 0; | static void *libjack_handle = 0; | ||||
| #endif | #endif | ||||
| static void __attribute__((constructor)) tryload_libjack() | static void __attribute__((constructor)) tryload_libjack() | ||||
| { | { | ||||
| if (getenv("SKIP_LIBJACK") == 0) { // just in case libjack is causing troubles.. | if (getenv("SKIP_LIBJACK") == 0) { // just in case libjack is causing troubles.. | ||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||
| libjack_handle = dlopen("libjack.0.dylib", RTLD_LAZY); | libjack_handle = dlopen("libjack.0.dylib", RTLD_LAZY); | ||||
| #elif defined(WIN32) | #elif defined(WIN32) | ||||
| libjack_handle = LoadLibrary("libjack.dll"); | |||||
| #ifdef _WIN64 | |||||
| libjack_handle = LoadLibrary("libjack64.dll"); | |||||
| #else | |||||
| libjack_handle = LoadLibrary("libjack.dll"); | |||||
| #endif | |||||
| #else | #else | ||||
| libjack_handle = dlopen("libjack.so.0", RTLD_LAZY); | libjack_handle = dlopen("libjack.so.0", RTLD_LAZY); | ||||
| #endif | #endif | ||||
| @@ -289,8 +287,8 @@ DECL_VOID_FUNCTION(jack_free, (void* ptr), (ptr)); | |||||
| // session | // session | ||||
| DECL_FUNCTION(int, jack_set_session_callback, (jack_client_t* ext_client, JackSessionCallback session_callback, void* arg), (ext_client, session_callback, arg)); | DECL_FUNCTION(int, jack_set_session_callback, (jack_client_t* ext_client, JackSessionCallback session_callback, void* arg), (ext_client, session_callback, arg)); | ||||
| DECL_FUNCTION(jack_session_command_t*, jack_session_notify, (jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path), (ext_client, target, ev_type, path)Ă ); | |||||
| DECL_FUNCTION(int jack_session_reply, (jack_client_t* ext_client, jack_session_event_t *event), (ext_client, event)); | |||||
| DECL_FUNCTION(jack_session_command_t*, jack_session_notify, (jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path), (ext_client, target, ev_type, path)); | |||||
| DECL_FUNCTION(int, jack_session_reply, (jack_client_t* ext_client, jack_session_event_t *event), (ext_client, event)); | |||||
| DECL_VOID_FUNCTION(jack_session_event_free, (jack_session_event_t* ev), (ev)); | DECL_VOID_FUNCTION(jack_session_event_free, (jack_session_event_t* ev), (ev)); | ||||
| DECL_FUNCTION(char*, jack_get_uuid_for_client_name, (jack_client_t* ext_client, const char* client_name),(ext_client, client_name)); | DECL_FUNCTION(char*, jack_get_uuid_for_client_name, (jack_client_t* ext_client, const char* client_name),(ext_client, client_name)); | ||||
| DECL_FUNCTION(char*, jack_get_client_name_by_uuid, (jack_client_t* ext_client, const char* client_uuid),(ext_client, client_uuid)); | DECL_FUNCTION(char*, jack_get_client_name_by_uuid, (jack_client_t* ext_client, const char* client_uuid),(ext_client, client_uuid)); | ||||
| @@ -540,12 +540,15 @@ int main(int argc, char* argv[]) | |||||
| // Slave drivers | // Slave drivers | ||||
| for (it = slaves_list.begin(); it != slaves_list.end(); it++) { | for (it = slaves_list.begin(); it != slaves_list.end(); it++) { | ||||
| jackctl_driver_t * slave_driver_ctl = jackctl_server_get_driver(server_ctl, *it); | jackctl_driver_t * slave_driver_ctl = jackctl_server_get_driver(server_ctl, *it); | ||||
| jackctl_server_remove_slave(server_ctl, slave_driver_ctl); | |||||
| if (slave_driver_ctl) | |||||
| jackctl_server_remove_slave(server_ctl, slave_driver_ctl); | |||||
| } | } | ||||
| // Internal clients | // Internal clients | ||||
| for (it = internals_list.begin(); it != internals_list.end(); it++) { | for (it = internals_list.begin(); it != internals_list.end(); it++) { | ||||
| jackctl_internal_t * internal_driver_ctl = jackctl_server_get_internal(server_ctl, *it); | jackctl_internal_t * internal_driver_ctl = jackctl_server_get_internal(server_ctl, *it); | ||||
| jackctl_server_unload_internal(server_ctl, internal_driver_ctl); | |||||
| if (internal_driver_ctl) | |||||
| jackctl_server_unload_internal(server_ctl, internal_driver_ctl); | |||||
| } | } | ||||
| jackctl_server_close(server_ctl); | jackctl_server_close(server_ctl); | ||||
| destroy_server: | destroy_server: | ||||
| @@ -55,8 +55,6 @@ $Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $ | |||||
| #include <samplerate.h> | #include <samplerate.h> | ||||
| #endif | #endif | ||||
| #include "JackError.h" | |||||
| #include "netjack.h" | #include "netjack.h" | ||||
| #include "netjack_packet.h" | #include "netjack_packet.h" | ||||
| @@ -68,8 +68,6 @@ | |||||
| #include <samplerate.h> | #include <samplerate.h> | ||||
| #endif | #endif | ||||
| #include "JackError.h" | |||||
| #if HAVE_CELT | #if HAVE_CELT | ||||
| #include <celt/celt.h> | #include <celt/celt.h> | ||||
| #endif | #endif | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* -*- Mode: C ; c-basic-offset: 4 -*- */ | /* -*- Mode: C ; c-basic-offset: 4 -*- */ | ||||
| /* | /* | ||||
| Copyright (C) 2007,2008,2010 Nedko Arnaudov | |||||
| Copyright (C) 2007,2008,2010,2011 Nedko Arnaudov | |||||
| Copyright (C) 2007-2008 Juuso Alasuutari | Copyright (C) 2007-2008 Juuso Alasuutari | ||||
| This program is free software; you can redistribute it and/or modify | This program is free software; you can redistribute it and/or modify | ||||
| @@ -42,6 +42,7 @@ struct jack_dbus_interface_descriptor * g_jackcontroller_interfaces[] = | |||||
| NULL | NULL | ||||
| }; | }; | ||||
| static | |||||
| jackctl_driver_t * | jackctl_driver_t * | ||||
| jack_controller_find_driver( | jack_controller_find_driver( | ||||
| jackctl_server_t *server, | jackctl_server_t *server, | ||||
| @@ -64,9 +65,7 @@ jack_controller_find_driver( | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| bool | |||||
| jack_controller_add_slave_drivers( | |||||
| struct jack_controller * controller_ptr) | |||||
| static bool jack_controller_check_slave_driver(struct jack_controller * controller_ptr, const char * name) | |||||
| { | { | ||||
| struct list_head * node_ptr; | struct list_head * node_ptr; | ||||
| struct jack_controller_slave_driver * driver_ptr; | struct jack_controller_slave_driver * driver_ptr; | ||||
| @@ -74,31 +73,40 @@ jack_controller_add_slave_drivers( | |||||
| list_for_each(node_ptr, &controller_ptr->slave_drivers) | list_for_each(node_ptr, &controller_ptr->slave_drivers) | ||||
| { | { | ||||
| driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | ||||
| driver_ptr->handle = jack_controller_find_driver(controller_ptr->server, driver_ptr->name); | |||||
| if (driver_ptr->handle == NULL) | |||||
| if (strcmp(name, driver_ptr->name) == 0) | |||||
| { | { | ||||
| jack_error("Unknown driver \"%s\"", driver_ptr->name); | |||||
| goto fail; | |||||
| return true; | |||||
| } | } | ||||
| } | |||||
| return false; | |||||
| } | |||||
| static bool jack_controller_load_slave_drivers(struct jack_controller * controller_ptr) | |||||
| { | |||||
| struct list_head * node_ptr; | |||||
| struct jack_controller_slave_driver * driver_ptr; | |||||
| list_for_each(node_ptr, &controller_ptr->slave_drivers) | |||||
| { | |||||
| driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | |||||
| assert(driver_ptr->handle != NULL); | |||||
| assert(!driver_ptr->loaded); | |||||
| if (!jackctl_server_add_slave(controller_ptr->server, driver_ptr->handle)) | if (!jackctl_server_add_slave(controller_ptr->server, driver_ptr->handle)) | ||||
| { | { | ||||
| jack_error("Driver \"%s\" cannot be loaded", driver_ptr->name); | jack_error("Driver \"%s\" cannot be loaded", driver_ptr->name); | ||||
| goto fail; | |||||
| return false; | |||||
| } | } | ||||
| driver_ptr->loaded = true; | |||||
| } | } | ||||
| return true; | return true; | ||||
| fail: | |||||
| driver_ptr->handle = NULL; | |||||
| return false; | |||||
| } | } | ||||
| void | |||||
| jack_controller_remove_slave_drivers( | |||||
| struct jack_controller * controller_ptr) | |||||
| static void jack_controller_unload_slave_drivers(struct jack_controller * controller_ptr) | |||||
| { | { | ||||
| struct list_head * node_ptr; | struct list_head * node_ptr; | ||||
| struct jack_controller_slave_driver * driver_ptr; | struct jack_controller_slave_driver * driver_ptr; | ||||
| @@ -106,14 +114,31 @@ jack_controller_remove_slave_drivers( | |||||
| list_for_each(node_ptr, &controller_ptr->slave_drivers) | list_for_each(node_ptr, &controller_ptr->slave_drivers) | ||||
| { | { | ||||
| driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | ||||
| if (driver_ptr->handle != NULL) | |||||
| if (driver_ptr->loaded) | |||||
| { | { | ||||
| jackctl_server_remove_slave(controller_ptr->server, driver_ptr->handle); | jackctl_server_remove_slave(controller_ptr->server, driver_ptr->handle); | ||||
| driver_ptr->handle = NULL; | |||||
| driver_ptr->loaded = false; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void jack_controller_remove_slave_drivers(struct jack_controller * controller_ptr) | |||||
| { | |||||
| struct jack_controller_slave_driver * driver_ptr; | |||||
| while (!list_empty(&controller_ptr->slave_drivers)) | |||||
| { | |||||
| driver_ptr = list_entry(controller_ptr->slave_drivers.next, struct jack_controller_slave_driver, siblings); | |||||
| assert(!driver_ptr->loaded); | |||||
| list_del(&driver_ptr->siblings); | |||||
| free(driver_ptr->name); | |||||
| free(driver_ptr); | |||||
| } | |||||
| controller_ptr->slave_drivers_vparam_value.str[0] = 0; | |||||
| } | |||||
| static | |||||
| jackctl_internal_t * | jackctl_internal_t * | ||||
| jack_controller_find_internal( | jack_controller_find_internal( | ||||
| jackctl_server_t *server, | jackctl_server_t *server, | ||||
| @@ -136,42 +161,18 @@ jack_controller_find_internal( | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| jackctl_parameter_t * | |||||
| jack_controller_find_parameter( | |||||
| const JSList * parameters_list, | |||||
| const char * parameter_name) | |||||
| { | |||||
| while (parameters_list) | |||||
| { | |||||
| if (strcmp(jackctl_parameter_get_name((jackctl_parameter_t *)parameters_list->data), parameter_name) == 0) | |||||
| { | |||||
| return parameters_list->data; | |||||
| } | |||||
| parameters_list = jack_slist_next(parameters_list); | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| bool | bool | ||||
| jack_controller_select_driver( | jack_controller_select_driver( | ||||
| struct jack_controller * controller_ptr, | struct jack_controller * controller_ptr, | ||||
| const char * driver_name) | const char * driver_name) | ||||
| { | { | ||||
| jackctl_driver_t *driver; | |||||
| driver = jack_controller_find_driver(controller_ptr->server, driver_name); | |||||
| if (driver == NULL) | |||||
| if (!jack_params_set_driver(controller_ptr->params, driver_name)) | |||||
| { | { | ||||
| return false; | return false; | ||||
| } | } | ||||
| jack_info("driver \"%s\" selected", driver_name); | jack_info("driver \"%s\" selected", driver_name); | ||||
| controller_ptr->driver = driver; | |||||
| controller_ptr->driver_set = true; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -195,23 +196,17 @@ jack_controller_start_server( | |||||
| assert(!controller_ptr->started); /* should be ensured by caller */ | assert(!controller_ptr->started); /* should be ensured by caller */ | ||||
| if (controller_ptr->driver == NULL) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Select driver first!"); | |||||
| goto fail; | |||||
| } | |||||
| controller_ptr->xruns = 0; | controller_ptr->xruns = 0; | ||||
| if (!jackctl_server_open( | if (!jackctl_server_open( | ||||
| controller_ptr->server, | controller_ptr->server, | ||||
| controller_ptr->driver)) | |||||
| jack_params_get_driver(controller_ptr->params))) | |||||
| { | { | ||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to open server"); | jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to open server"); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| jack_controller_add_slave_drivers(controller_ptr); | |||||
| jack_controller_load_slave_drivers(controller_ptr); | |||||
| if (!jackctl_server_start( | if (!jackctl_server_start( | ||||
| controller_ptr->server)) | controller_ptr->server)) | ||||
| @@ -273,7 +268,7 @@ fail_stop_server: | |||||
| } | } | ||||
| fail_close_server: | fail_close_server: | ||||
| jack_controller_remove_slave_drivers(controller_ptr); | |||||
| jack_controller_unload_slave_drivers(controller_ptr); | |||||
| if (!jackctl_server_close(controller_ptr->server)) | if (!jackctl_server_close(controller_ptr->server)) | ||||
| { | { | ||||
| @@ -317,7 +312,7 @@ jack_controller_stop_server( | |||||
| return FALSE; | return FALSE; | ||||
| } | } | ||||
| jack_controller_remove_slave_drivers(controller_ptr); | |||||
| jack_controller_unload_slave_drivers(controller_ptr); | |||||
| if (!jackctl_server_close(controller_ptr->server)) | if (!jackctl_server_close(controller_ptr->server)) | ||||
| { | { | ||||
| @@ -337,12 +332,13 @@ jack_controller_switch_master( | |||||
| { | { | ||||
| if (!jackctl_server_switch_master( | if (!jackctl_server_switch_master( | ||||
| controller_ptr->server, | controller_ptr->server, | ||||
| controller_ptr->driver)) | |||||
| jack_params_get_driver(controller_ptr->params))) | |||||
| { | { | ||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to switch master"); | jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to switch master"); | ||||
| return FALSE; | return FALSE; | ||||
| } | } | ||||
| return TRUE; | return TRUE; | ||||
| } | } | ||||
| @@ -408,16 +404,108 @@ on_device_release(const char * device_name) | |||||
| g_device_count--; | g_device_count--; | ||||
| } | } | ||||
| #define controller_ptr ((struct jack_controller *)obj) | |||||
| static bool slave_drivers_parameter_is_set(void * obj) | |||||
| { | |||||
| return controller_ptr->slave_drivers_set; | |||||
| } | |||||
| static bool slave_drivers_parameter_reset(void * obj) | |||||
| { | |||||
| if (controller_ptr->started) | |||||
| { | |||||
| jack_error("Cannot modify slave-drivers when server is started"); | |||||
| return false; | |||||
| } | |||||
| jack_controller_remove_slave_drivers(controller_ptr); | |||||
| controller_ptr->slave_drivers_set = false; | |||||
| return true; | |||||
| } | |||||
| static union jackctl_parameter_value slave_drivers_parameter_get_value(void * obj) | |||||
| { | |||||
| return controller_ptr->slave_drivers_vparam_value; | |||||
| } | |||||
| static bool slave_drivers_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) | |||||
| { | |||||
| char * buffer; | |||||
| char * save; | |||||
| const char * token; | |||||
| struct list_head old_list; | |||||
| struct list_head new_list; | |||||
| union jackctl_parameter_value old_value; | |||||
| union jackctl_parameter_value new_value; | |||||
| bool old_set; | |||||
| if (controller_ptr->started) | |||||
| { | |||||
| jack_error("Cannot modify slave-drivers when server is started"); | |||||
| return false; | |||||
| } | |||||
| old_set = controller_ptr->slave_drivers_set; | |||||
| old_value = controller_ptr->slave_drivers_vparam_value; | |||||
| controller_ptr->slave_drivers_vparam_value.str[0] = 0; | |||||
| old_list = controller_ptr->slave_drivers; | |||||
| INIT_LIST_HEAD(&controller_ptr->slave_drivers); | |||||
| buffer = strdup(value_ptr->str); | |||||
| if (buffer == NULL) | |||||
| { | |||||
| jack_error("strdup() failed."); | |||||
| return false; | |||||
| } | |||||
| token = strtok_r(buffer, ",", &save); | |||||
| while (token) | |||||
| { | |||||
| //jack_info("slave driver '%s'", token); | |||||
| if (!jack_controller_add_slave_driver(controller_ptr, token)) | |||||
| { | |||||
| jack_controller_remove_slave_drivers(controller_ptr); | |||||
| controller_ptr->slave_drivers = old_list; | |||||
| controller_ptr->slave_drivers_vparam_value = old_value; | |||||
| controller_ptr->slave_drivers_set = old_set; | |||||
| free(buffer); | |||||
| return false; | |||||
| } | |||||
| token = strtok_r(NULL, ",", &save); | |||||
| } | |||||
| new_value = controller_ptr->slave_drivers_vparam_value; | |||||
| new_list = controller_ptr->slave_drivers; | |||||
| controller_ptr->slave_drivers = old_list; | |||||
| jack_controller_remove_slave_drivers(controller_ptr); | |||||
| controller_ptr->slave_drivers_vparam_value = new_value; | |||||
| controller_ptr->slave_drivers = new_list; | |||||
| controller_ptr->slave_drivers_set = true; | |||||
| free(buffer); | |||||
| return true; | |||||
| } | |||||
| static union jackctl_parameter_value slave_drivers_parameter_get_default_value(void * obj) | |||||
| { | |||||
| union jackctl_parameter_value value; | |||||
| value.str[0] = 0; | |||||
| return value; | |||||
| } | |||||
| #undef controller_ptr | |||||
| void * | void * | ||||
| jack_controller_create( | jack_controller_create( | ||||
| DBusConnection *connection) | DBusConnection *connection) | ||||
| { | { | ||||
| struct jack_controller *controller_ptr; | struct jack_controller *controller_ptr; | ||||
| const JSList * node_ptr; | |||||
| const char ** driver_name_target; | |||||
| const char ** internal_name_target; | |||||
| JSList * drivers; | |||||
| JSList * internals; | |||||
| const char * address[PARAM_ADDRESS_SIZE]; | |||||
| DBusObjectPathVTable vtable = | DBusObjectPathVTable vtable = | ||||
| { | { | ||||
| jack_dbus_message_handler_unregister, | jack_dbus_message_handler_unregister, | ||||
| @@ -439,54 +527,37 @@ jack_controller_create( | |||||
| goto fail_free; | goto fail_free; | ||||
| } | } | ||||
| controller_ptr->client = NULL; | |||||
| controller_ptr->started = false; | |||||
| controller_ptr->driver = NULL; | |||||
| controller_ptr->driver_set = false; | |||||
| INIT_LIST_HEAD(&controller_ptr->slave_drivers); | |||||
| drivers = (JSList *)jackctl_server_get_drivers_list(controller_ptr->server); | |||||
| controller_ptr->drivers_count = jack_slist_length(drivers); | |||||
| controller_ptr->driver_names = malloc(controller_ptr->drivers_count * sizeof(const char *)); | |||||
| if (controller_ptr->driver_names == NULL) | |||||
| controller_ptr->params = jack_params_create(controller_ptr->server); | |||||
| if (controller_ptr->params == NULL) | |||||
| { | { | ||||
| jack_error("Ran out of memory trying to allocate driver names array"); | |||||
| jack_error("Failed to initialize parameter tree"); | |||||
| goto fail_destroy_server; | goto fail_destroy_server; | ||||
| } | } | ||||
| driver_name_target = controller_ptr->driver_names; | |||||
| node_ptr = jackctl_server_get_drivers_list(controller_ptr->server); | |||||
| while (node_ptr != NULL) | |||||
| { | |||||
| *driver_name_target = jackctl_driver_get_name((jackctl_driver_t *)node_ptr->data); | |||||
| controller_ptr->client = NULL; | |||||
| controller_ptr->started = false; | |||||
| /* select default driver */ | |||||
| if (controller_ptr->driver == NULL && strcmp(*driver_name_target, DEFAULT_DRIVER) == 0) | |||||
| { | |||||
| controller_ptr->driver = (jackctl_driver_t *)node_ptr->data; | |||||
| } | |||||
| INIT_LIST_HEAD(&controller_ptr->slave_drivers); | |||||
| controller_ptr->slave_drivers_set = false; | |||||
| controller_ptr->slave_drivers_vparam_value.str[0] = 0; | |||||
| node_ptr = jack_slist_next(node_ptr); | |||||
| driver_name_target++; | |||||
| } | |||||
| internals = (JSList *)jackctl_server_get_internals_list(controller_ptr->server); | |||||
| controller_ptr->internals_count = jack_slist_length(internals); | |||||
| controller_ptr->internal_names = malloc(controller_ptr->internals_count * sizeof(const char *)); | |||||
| if (controller_ptr->internal_names == NULL) | |||||
| { | |||||
| jack_error("Ran out of memory trying to allocate internals names array"); | |||||
| goto fail_free_driver_names_array; | |||||
| } | |||||
| controller_ptr->slave_drivers_vparam.obj = controller_ptr; | |||||
| internal_name_target = controller_ptr->internal_names; | |||||
| node_ptr = jackctl_server_get_internals_list(controller_ptr->server); | |||||
| while (node_ptr != NULL) | |||||
| { | |||||
| *internal_name_target = jackctl_internal_get_name((jackctl_internal_t *)node_ptr->data); | |||||
| node_ptr = jack_slist_next(node_ptr); | |||||
| internal_name_target++; | |||||
| } | |||||
| controller_ptr->slave_drivers_vparam.vtable.is_set = slave_drivers_parameter_is_set; | |||||
| controller_ptr->slave_drivers_vparam.vtable.reset = slave_drivers_parameter_reset; | |||||
| controller_ptr->slave_drivers_vparam.vtable.get_value = slave_drivers_parameter_get_value; | |||||
| controller_ptr->slave_drivers_vparam.vtable.set_value = slave_drivers_parameter_set_value; | |||||
| controller_ptr->slave_drivers_vparam.vtable.get_default_value = slave_drivers_parameter_get_default_value; | |||||
| controller_ptr->slave_drivers_vparam.type = JackParamString; | |||||
| controller_ptr->slave_drivers_vparam.name = "slave-drivers"; | |||||
| controller_ptr->slave_drivers_vparam.short_decr = "Slave drivers to use"; | |||||
| controller_ptr->slave_drivers_vparam.long_descr = "A comma separated list of slave drivers"; | |||||
| controller_ptr->slave_drivers_vparam.constraint_flags = 0; | |||||
| address[0] = PTNODE_ENGINE; | |||||
| address[1] = NULL; | |||||
| jack_params_add_parameter(controller_ptr->params, address, true, &controller_ptr->slave_drivers_vparam); | |||||
| controller_ptr->dbus_descriptor.context = controller_ptr; | controller_ptr->dbus_descriptor.context = controller_ptr; | ||||
| controller_ptr->dbus_descriptor.interfaces = g_jackcontroller_interfaces; | controller_ptr->dbus_descriptor.interfaces = g_jackcontroller_interfaces; | ||||
| @@ -498,18 +569,15 @@ jack_controller_create( | |||||
| &controller_ptr->dbus_descriptor)) | &controller_ptr->dbus_descriptor)) | ||||
| { | { | ||||
| jack_error("Ran out of memory trying to register D-Bus object path"); | jack_error("Ran out of memory trying to register D-Bus object path"); | ||||
| goto fail_free_internal_names_array; | |||||
| goto fail_destroy_params; | |||||
| } | } | ||||
| jack_controller_settings_load(controller_ptr); | jack_controller_settings_load(controller_ptr); | ||||
| return controller_ptr; | return controller_ptr; | ||||
| fail_free_internal_names_array: | |||||
| free(controller_ptr->internal_names); | |||||
| fail_free_driver_names_array: | |||||
| free(controller_ptr->driver_names); | |||||
| fail_destroy_params: | |||||
| jack_params_destroy(controller_ptr->params); | |||||
| fail_destroy_server: | fail_destroy_server: | ||||
| jackctl_server_destroy(controller_ptr->server); | jackctl_server_destroy(controller_ptr->server); | ||||
| @@ -526,7 +594,31 @@ jack_controller_add_slave_driver( | |||||
| struct jack_controller * controller_ptr, | struct jack_controller * controller_ptr, | ||||
| const char * driver_name) | const char * driver_name) | ||||
| { | { | ||||
| jackctl_driver_t * driver; | |||||
| struct jack_controller_slave_driver * driver_ptr; | struct jack_controller_slave_driver * driver_ptr; | ||||
| size_t len_old; | |||||
| size_t len_new; | |||||
| len_old = strlen(controller_ptr->slave_drivers_vparam_value.str); | |||||
| len_new = strlen(driver_name); | |||||
| if (len_old + len_new + 2 > sizeof(controller_ptr->slave_drivers_vparam_value.str)) | |||||
| { | |||||
| jack_error("No more space for slave drivers."); | |||||
| return false; | |||||
| } | |||||
| driver = jack_controller_find_driver(controller_ptr->server, driver_name); | |||||
| if (driver == NULL) | |||||
| { | |||||
| jack_error("Unknown driver \"%s\"", driver_name); | |||||
| return false; | |||||
| } | |||||
| if (jack_controller_check_slave_driver(controller_ptr, driver_name)) | |||||
| { | |||||
| jack_info("Driver \"%s\" is already slave", driver_name); | |||||
| return true; | |||||
| } | |||||
| driver_ptr = malloc(sizeof(struct jack_controller_slave_driver)); | driver_ptr = malloc(sizeof(struct jack_controller_slave_driver)); | ||||
| if (driver_ptr == NULL) | if (driver_ptr == NULL) | ||||
| @@ -543,12 +635,21 @@ jack_controller_add_slave_driver( | |||||
| return false; | return false; | ||||
| } | } | ||||
| driver_ptr->handle = NULL; | |||||
| driver_ptr->handle = driver; | |||||
| driver_ptr->loaded = false; | |||||
| jack_info("slave driver \"%s\" added", driver_name); | |||||
| jack_info("driver \"%s\" set as slave", driver_name); | |||||
| list_add_tail(&driver_ptr->siblings, &controller_ptr->slave_drivers); | list_add_tail(&driver_ptr->siblings, &controller_ptr->slave_drivers); | ||||
| if (len_old != 0) | |||||
| { | |||||
| controller_ptr->slave_drivers_vparam_value.str[len_old++] = ','; | |||||
| } | |||||
| memcpy(controller_ptr->slave_drivers_vparam_value.str + len_old, driver_name, len_new + 1); | |||||
| controller_ptr->slave_drivers_set = true; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -565,10 +666,25 @@ jack_controller_remove_slave_driver( | |||||
| driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | ||||
| if (strcmp(driver_ptr->name, driver_name) == 0) | if (strcmp(driver_ptr->name, driver_name) == 0) | ||||
| { | { | ||||
| jack_info("slave driver \"%s\" removed", driver_name); | |||||
| list_del(&driver_ptr->siblings); | list_del(&driver_ptr->siblings); | ||||
| free(driver_ptr->name); | free(driver_ptr->name); | ||||
| free(driver_ptr); | free(driver_ptr); | ||||
| /* update the slave-drivers param value */ | |||||
| controller_ptr->slave_drivers_vparam_value.str[0] = 0; | |||||
| list_for_each(node_ptr, &controller_ptr->slave_drivers) | |||||
| { | |||||
| driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | |||||
| if (controller_ptr->slave_drivers_vparam_value.str[0] != 0) | |||||
| { | |||||
| strcat(controller_ptr->slave_drivers_vparam_value.str, ","); | |||||
| } | |||||
| strcat(controller_ptr->slave_drivers_vparam_value.str, driver_ptr->name); | |||||
| } | |||||
| jack_info("driver \"%s\" is not slave anymore", driver_name); | |||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| @@ -623,9 +739,8 @@ jack_controller_destroy( | |||||
| jack_controller_stop_server(controller_ptr, NULL); | jack_controller_stop_server(controller_ptr, NULL); | ||||
| } | } | ||||
| free(controller_ptr->driver_names); | |||||
| free(controller_ptr->internal_names); | |||||
| jack_controller_remove_slave_drivers(controller_ptr); | |||||
| jack_params_destroy(controller_ptr->params); | |||||
| jackctl_server_destroy(controller_ptr->server); | jackctl_server_destroy(controller_ptr->server); | ||||
| free(controller_ptr); | free(controller_ptr); | ||||
| @@ -30,6 +30,7 @@ | |||||
| #include "jackdbus.h" | #include "jackdbus.h" | ||||
| #include "controller_internal.h" | #include "controller_internal.h" | ||||
| #include "xml.h" | |||||
| #define JACK_DBUS_IFACE_NAME "org.jackaudio.JackControl" | #define JACK_DBUS_IFACE_NAME "org.jackaudio.JackControl" | ||||
| @@ -127,8 +128,6 @@ jack_control_run_method( | |||||
| assert(call->reply != NULL); | assert(call->reply != NULL); | ||||
| return true; | return true; | ||||
| } | } | ||||
| jack_controller_control_send_signal_server_stopped(); | |||||
| } | } | ||||
| else if (strcmp (call->method_name, "GetLoad") == 0) | else if (strcmp (call->method_name, "GetLoad") == 0) | ||||
| { | { | ||||
| @@ -246,12 +245,17 @@ jack_control_run_method( | |||||
| goto exit; | goto exit; | ||||
| } | } | ||||
| if (!jack_controller_add_slave_driver(controller_ptr, driver_name)) { | |||||
| if (!jack_controller_add_slave_driver(controller_ptr, driver_name)) | |||||
| { | |||||
| jack_dbus_error( | jack_dbus_error( | ||||
| call, | call, | ||||
| JACK_DBUS_ERROR_GENERIC, | JACK_DBUS_ERROR_GENERIC, | ||||
| "jack_controller_add_slave_driver failed for driver (%s)", driver_name); | "jack_controller_add_slave_driver failed for driver (%s)", driver_name); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| jack_controller_settings_save_auto(controller_ptr); | |||||
| } | |||||
| } | } | ||||
| else if (strcmp (call->method_name, "RemoveSlaveDriver") == 0) | else if (strcmp (call->method_name, "RemoveSlaveDriver") == 0) | ||||
| { | { | ||||
| @@ -270,12 +274,17 @@ jack_control_run_method( | |||||
| goto exit; | goto exit; | ||||
| } | } | ||||
| if (!jack_controller_remove_slave_driver(controller_ptr, driver_name)) { | |||||
| if (!jack_controller_remove_slave_driver(controller_ptr, driver_name)) | |||||
| { | |||||
| jack_dbus_error( | jack_dbus_error( | ||||
| call, | call, | ||||
| JACK_DBUS_ERROR_GENERIC, | JACK_DBUS_ERROR_GENERIC, | ||||
| "jack_controller_remove_slave_driver failed for driver (%s)", driver_name); | "jack_controller_remove_slave_driver failed for driver (%s)", driver_name); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| jack_controller_settings_save_auto(controller_ptr); | |||||
| } | |||||
| } | } | ||||
| else if (strcmp (call->method_name, "UnloadInternal") == 0) | else if (strcmp (call->method_name, "UnloadInternal") == 0) | ||||
| { | { | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* -*- Mode: C ; c-basic-offset: 4 -*- */ | /* -*- Mode: C ; c-basic-offset: 4 -*- */ | ||||
| /* | /* | ||||
| Copyright (C) 2007,2008 Nedko Arnaudov | |||||
| Copyright (C) 2007,2008,2011 Nedko Arnaudov | |||||
| Copyright (C) 2007-2008 Juuso Alasuutari | Copyright (C) 2007-2008 Juuso Alasuutari | ||||
| This program is free software; you can redistribute it and/or modify | This program is free software; you can redistribute it and/or modify | ||||
| @@ -27,17 +27,20 @@ | |||||
| #include "jack/jack.h" | #include "jack/jack.h" | ||||
| #include "jackdbus.h" | #include "jackdbus.h" | ||||
| #include "list.h" | #include "list.h" | ||||
| #include "params.h" | |||||
| struct jack_controller_slave_driver | struct jack_controller_slave_driver | ||||
| { | { | ||||
| struct list_head siblings; | struct list_head siblings; | ||||
| char * name; | char * name; | ||||
| jackctl_driver_t * handle; | jackctl_driver_t * handle; | ||||
| bool loaded; | |||||
| }; | }; | ||||
| struct jack_controller | struct jack_controller | ||||
| { | { | ||||
| jackctl_server_t *server; | jackctl_server_t *server; | ||||
| jack_params_handle params; | |||||
| void *patchbay_context; | void *patchbay_context; | ||||
| @@ -45,15 +48,10 @@ struct jack_controller | |||||
| jack_client_t *client; | jack_client_t *client; | ||||
| unsigned int xruns; | unsigned int xruns; | ||||
| const char **driver_names; | |||||
| unsigned int drivers_count; | |||||
| const char **internal_names; | |||||
| unsigned int internals_count; | |||||
| jackctl_driver_t *driver; | |||||
| bool driver_set; /* whether driver is manually set, if false - DEFAULT_DRIVER is auto set */ | |||||
| struct list_head slave_drivers; | struct list_head slave_drivers; | ||||
| bool slave_drivers_set; | |||||
| struct jack_parameter slave_drivers_vparam; | |||||
| union jackctl_parameter_value slave_drivers_vparam_value; | |||||
| struct jack_dbus_object_descriptor dbus_descriptor; | struct jack_dbus_object_descriptor dbus_descriptor; | ||||
| }; | }; | ||||
| @@ -65,21 +63,6 @@ struct jack_controller | |||||
| "You probably don't want to edit this because\n" \ | "You probably don't want to edit this because\n" \ | ||||
| "it will be overwritten next time jackdbus saves.\n" | "it will be overwritten next time jackdbus saves.\n" | ||||
| jackctl_driver_t * | |||||
| jack_controller_find_driver( | |||||
| jackctl_server_t *server, | |||||
| const char *driver_name); | |||||
| jackctl_internal_t * | |||||
| jack_controller_find_internal( | |||||
| jackctl_server_t *server, | |||||
| const char *internal_name); | |||||
| jackctl_parameter_t * | |||||
| jack_controller_find_parameter( | |||||
| const JSList *parameters_list, | |||||
| const char *parameter_name); | |||||
| bool | bool | ||||
| jack_controller_start_server( | jack_controller_start_server( | ||||
| struct jack_controller *controller_ptr, | struct jack_controller *controller_ptr, | ||||
| @@ -121,47 +104,15 @@ jack_controller_unload_internal( | |||||
| const char * internal_name); | const char * internal_name); | ||||
| void | void | ||||
| jack_controller_settings_set_driver_option( | |||||
| jackctl_driver_t *driver, | |||||
| const char *option_name, | |||||
| const char *option_value); | |||||
| void | |||||
| jack_controller_settings_set_internal_option( | |||||
| jackctl_internal_t *internal, | |||||
| const char *option_name, | |||||
| const char *option_value); | |||||
| void | |||||
| jack_controller_settings_set_engine_option( | |||||
| struct jack_controller *controller_ptr, | |||||
| const char *option_name, | |||||
| const char *option_value); | |||||
| bool | |||||
| jack_controller_settings_save_engine_options( | |||||
| void *context, | |||||
| jack_controller_deserialize_parameter_value( | |||||
| struct jack_controller *controller_ptr, | struct jack_controller *controller_ptr, | ||||
| void *dbus_call_context_ptr); | |||||
| bool | |||||
| jack_controller_settings_write_option( | |||||
| void *context, | |||||
| const char *name, | |||||
| const char *content, | |||||
| void *dbus_call_context_ptr); | |||||
| const char * const * address, | |||||
| const char * value); | |||||
| bool | |||||
| jack_controller_settings_save_driver_options( | |||||
| void *context, | |||||
| jackctl_driver_t *driver, | |||||
| void *dbus_call_context_ptr); | |||||
| bool | |||||
| jack_controller_settings_save_internal_options( | |||||
| void *context, | |||||
| jackctl_internal_t *internal, | |||||
| void *dbus_call_context_ptr); | |||||
| void | |||||
| jack_controller_serialize_parameter_value( | |||||
| const struct jack_parameter * param_ptr, | |||||
| char * value_buffer); | |||||
| bool | bool | ||||
| jack_controller_patchbay_init( | jack_controller_patchbay_init( | ||||
| @@ -0,0 +1,726 @@ | |||||
| /* -*- Mode: C ; c-basic-offset: 4 -*- */ | |||||
| /* | |||||
| Copyright (C) 2011 Nedko Arnaudov | |||||
| 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. | |||||
| 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. | |||||
| */ | |||||
| /* | |||||
| * Parameter addresses: | |||||
| * | |||||
| * "engine" | |||||
| * "engine", "driver" | |||||
| * "engine", "realtime" | |||||
| * "engine", ...more engine parameters | |||||
| * | |||||
| * "driver", "device" | |||||
| * "driver", ...more driver parameters | |||||
| * | |||||
| * "drivers", "alsa", "device" | |||||
| * "drivers", "alsa", ...more alsa driver parameters | |||||
| * | |||||
| * "drivers", ...more drivers | |||||
| * | |||||
| * "internals", "netmanager", "multicast_ip" | |||||
| * "internals", "netmanager", ...more netmanager parameters | |||||
| * | |||||
| * "internals", ...more internals | |||||
| * | |||||
| */ | |||||
| #include <stdbool.h> | |||||
| #include <string.h> | |||||
| #include <assert.h> | |||||
| #include <dbus/dbus.h> | |||||
| #include "params.h" | |||||
| #include "controller_internal.h" | |||||
| #define PTNODE_ENGINE "engine" | |||||
| #define PTNODE_DRIVER "driver" | |||||
| #define PTNODE_DRIVERS "drivers" | |||||
| #define PTNODE_INTERNALS "internals" | |||||
| struct jack_parameter_container | |||||
| { | |||||
| struct list_head siblings; | |||||
| char * name; | |||||
| struct jack_parameter_container * symlink; | |||||
| bool leaf; | |||||
| struct list_head children; | |||||
| void * obj; | |||||
| }; | |||||
| struct jack_params | |||||
| { | |||||
| jackctl_server_t * server; | |||||
| struct jack_parameter_container root; | |||||
| struct list_head * drivers_ptr; | |||||
| uint32_t drivers_count; | |||||
| struct jack_parameter_container * driver_ptr; | |||||
| bool driver_set; /* whether driver is manually set, if false - DEFAULT_DRIVER is auto set */ | |||||
| }; | |||||
| static bool controlapi_parameter_is_set(void * obj) | |||||
| { | |||||
| return jackctl_parameter_is_set((jackctl_parameter_t *)obj); | |||||
| } | |||||
| static bool controlapi_parameter_reset(void * obj) | |||||
| { | |||||
| return jackctl_parameter_reset((jackctl_parameter_t *)obj); | |||||
| } | |||||
| union jackctl_parameter_value controlapi_parameter_get_value(void * obj) | |||||
| { | |||||
| return jackctl_parameter_get_value((jackctl_parameter_t *)obj); | |||||
| } | |||||
| bool controlapi_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) | |||||
| { | |||||
| return jackctl_parameter_set_value((jackctl_parameter_t *)obj, value_ptr); | |||||
| } | |||||
| union jackctl_parameter_value controlapi_parameter_get_default_value(void * obj) | |||||
| { | |||||
| return jackctl_parameter_get_default_value((jackctl_parameter_t *)obj); | |||||
| } | |||||
| static struct jack_parameter_container * create_container(struct list_head * parent_list_ptr, const char * name) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| container_ptr = malloc(sizeof(struct jack_parameter_container)); | |||||
| if (container_ptr == NULL) | |||||
| { | |||||
| jack_error("Ran out of memory trying to allocate struct jack_parameter_container"); | |||||
| goto fail; | |||||
| } | |||||
| container_ptr->name = strdup(name); | |||||
| if (container_ptr->name == NULL) | |||||
| { | |||||
| jack_error("Ran out of memory trying to strdup parameter container name"); | |||||
| goto free; | |||||
| } | |||||
| container_ptr->leaf = false; | |||||
| container_ptr->symlink = NULL; | |||||
| container_ptr->obj = NULL; | |||||
| INIT_LIST_HEAD(&container_ptr->children); | |||||
| list_add_tail(&container_ptr->siblings, parent_list_ptr); | |||||
| return container_ptr; | |||||
| free: | |||||
| free(container_ptr); | |||||
| fail: | |||||
| return NULL; | |||||
| } | |||||
| static bool add_controlapi_param(struct list_head * parent_list_ptr, jackctl_parameter_t * param) | |||||
| { | |||||
| struct jack_parameter * param_ptr; | |||||
| uint32_t i; | |||||
| param_ptr = malloc(sizeof(struct jack_parameter)); | |||||
| if (param_ptr == NULL) | |||||
| { | |||||
| jack_error("Ran out of memory trying to allocate struct jack_parameter"); | |||||
| goto fail; | |||||
| } | |||||
| param_ptr->ext = false; | |||||
| param_ptr->obj = param; | |||||
| param_ptr->vtable.is_set = controlapi_parameter_is_set; | |||||
| param_ptr->vtable.reset = controlapi_parameter_reset; | |||||
| param_ptr->vtable.get_value = controlapi_parameter_get_value; | |||||
| param_ptr->vtable.set_value = controlapi_parameter_set_value; | |||||
| param_ptr->vtable.get_default_value = controlapi_parameter_get_default_value; | |||||
| param_ptr->type = jackctl_parameter_get_type(param); | |||||
| param_ptr->name = jackctl_parameter_get_name(param); | |||||
| param_ptr->short_decr = jackctl_parameter_get_short_description(param); | |||||
| param_ptr->long_descr = jackctl_parameter_get_long_description(param); | |||||
| if (jackctl_parameter_has_range_constraint(param)) | |||||
| { | |||||
| param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID; | |||||
| param_ptr->constraint_range = true; | |||||
| jackctl_parameter_get_range_constraint(param, ¶m_ptr->constraint.range.min, ¶m_ptr->constraint.range.max); | |||||
| } | |||||
| else if (jackctl_parameter_has_enum_constraint(param)) | |||||
| { | |||||
| param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID; | |||||
| param_ptr->constraint_range = false; | |||||
| param_ptr->constraint.enumeration.count = jackctl_parameter_get_enum_constraints_count(param); | |||||
| param_ptr->constraint.enumeration.possible_values_array = malloc(sizeof(struct jack_parameter_enum) * param_ptr->constraint.enumeration.count); | |||||
| if (param_ptr->constraint.enumeration.possible_values_array == NULL) | |||||
| { | |||||
| goto free; | |||||
| } | |||||
| for (i = 0; i < param_ptr->constraint.enumeration.count; i++) | |||||
| { | |||||
| param_ptr->constraint.enumeration.possible_values_array[i].value = jackctl_parameter_get_enum_constraint_value(param, i); | |||||
| param_ptr->constraint.enumeration.possible_values_array[i].short_desc = jackctl_parameter_get_enum_constraint_description(param, i); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| param_ptr->constraint_flags = 0; | |||||
| goto add; | |||||
| } | |||||
| if (jackctl_parameter_constraint_is_strict(param)) | |||||
| { | |||||
| param_ptr->constraint_flags |= JACK_CONSTRAINT_FLAG_STRICT; | |||||
| } | |||||
| if (jackctl_parameter_constraint_is_fake_value(param)) | |||||
| { | |||||
| param_ptr->constraint_flags |= JACK_CONSTRAINT_FLAG_FAKE_VALUE; | |||||
| } | |||||
| add: | |||||
| list_add_tail(¶m_ptr->siblings, parent_list_ptr); | |||||
| return true; | |||||
| free: | |||||
| free(param_ptr); | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| static void free_params(struct list_head * parent_list_ptr) | |||||
| { | |||||
| struct jack_parameter * param_ptr; | |||||
| while (!list_empty(parent_list_ptr)) | |||||
| { | |||||
| param_ptr = list_entry(parent_list_ptr->next, struct jack_parameter, siblings); | |||||
| list_del(¶m_ptr->siblings); | |||||
| if (param_ptr->ext) | |||||
| { | |||||
| continue; | |||||
| } | |||||
| if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0 && | |||||
| !param_ptr->constraint_range && | |||||
| param_ptr->constraint.enumeration.possible_values_array != NULL) | |||||
| { | |||||
| free(param_ptr->constraint.enumeration.possible_values_array); | |||||
| } | |||||
| free(param_ptr); | |||||
| } | |||||
| } | |||||
| static void free_containers(struct list_head * parent_list_ptr) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| while (!list_empty(parent_list_ptr)) | |||||
| { | |||||
| container_ptr = list_entry(parent_list_ptr->next, struct jack_parameter_container, siblings); | |||||
| list_del(&container_ptr->siblings); | |||||
| if (container_ptr->leaf) | |||||
| { | |||||
| free_params(&container_ptr->children); | |||||
| } | |||||
| else | |||||
| { | |||||
| free_containers(&container_ptr->children); | |||||
| } | |||||
| free(container_ptr->name); | |||||
| free(container_ptr); | |||||
| } | |||||
| } | |||||
| static struct jack_parameter_container * find_container(struct jack_parameter_container * parent_ptr, const char * const * address, int max_depth) | |||||
| { | |||||
| struct list_head * node_ptr; | |||||
| struct jack_parameter_container * container_ptr; | |||||
| if (max_depth == 0 || *address == NULL) | |||||
| { | |||||
| return parent_ptr; | |||||
| } | |||||
| if (parent_ptr->leaf) | |||||
| { | |||||
| return NULL; | |||||
| } | |||||
| if (max_depth > 0) | |||||
| { | |||||
| max_depth--; | |||||
| } | |||||
| list_for_each(node_ptr, &parent_ptr->children) | |||||
| { | |||||
| container_ptr = list_entry(node_ptr, struct jack_parameter_container, siblings); | |||||
| if (strcmp(container_ptr->name, *address) == 0) | |||||
| { | |||||
| if (container_ptr->symlink != NULL) | |||||
| { | |||||
| container_ptr = container_ptr->symlink; | |||||
| } | |||||
| return find_container(container_ptr, address + 1, max_depth); | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| static bool init_leaf(struct list_head * parent_list_ptr, const char * name, const JSList * params_list, void * obj) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| container_ptr = create_container(parent_list_ptr, name); | |||||
| if (container_ptr == NULL) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| container_ptr->leaf = true; | |||||
| container_ptr->obj = obj; | |||||
| while (params_list) | |||||
| { | |||||
| if (!add_controlapi_param(&container_ptr->children, params_list->data)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| params_list = jack_slist_next(params_list); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static bool init_engine(struct jack_params * params_ptr) | |||||
| { | |||||
| return init_leaf(¶ms_ptr->root.children, PTNODE_ENGINE, jackctl_server_get_parameters(params_ptr->server), NULL); | |||||
| } | |||||
| static bool init_drivers(struct jack_params * params_ptr) | |||||
| { | |||||
| const JSList * list; | |||||
| struct jack_parameter_container * container_ptr; | |||||
| container_ptr = create_container(¶ms_ptr->root.children, PTNODE_DRIVERS); | |||||
| if (container_ptr == NULL) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| params_ptr->drivers_ptr = &container_ptr->children; | |||||
| params_ptr->drivers_count = 0; | |||||
| list = jackctl_server_get_drivers_list(params_ptr->server); | |||||
| while (list) | |||||
| { | |||||
| if (!init_leaf(&container_ptr->children, jackctl_driver_get_name(list->data), jackctl_driver_get_parameters(list->data), list->data)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| params_ptr->drivers_count++; | |||||
| list = jack_slist_next(list); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static bool init_internals(struct jack_params * params_ptr) | |||||
| { | |||||
| const JSList * list; | |||||
| struct jack_parameter_container * container_ptr; | |||||
| container_ptr = create_container(¶ms_ptr->root.children, PTNODE_INTERNALS); | |||||
| if (container_ptr == NULL) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| list = jackctl_server_get_internals_list(params_ptr->server); | |||||
| while (list) | |||||
| { | |||||
| if (!init_leaf(&container_ptr->children, jackctl_internal_get_name(list->data), jackctl_internal_get_parameters(list->data), NULL)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| list = jack_slist_next(list); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static bool init_driver(struct jack_params * params_ptr) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| container_ptr = create_container(¶ms_ptr->root.children, PTNODE_DRIVER); | |||||
| if (container_ptr == NULL) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| params_ptr->driver_ptr = container_ptr; | |||||
| return true; | |||||
| } | |||||
| #define params_ptr ((struct jack_params *)obj) | |||||
| static bool engine_driver_parameter_is_set(void * obj) | |||||
| { | |||||
| return params_ptr->driver_set; | |||||
| } | |||||
| static bool engine_driver_parameter_reset(void * obj) | |||||
| { | |||||
| if (!jack_params_set_driver(obj, DEFAULT_DRIVER)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| params_ptr->driver_set = false; | |||||
| return true; | |||||
| } | |||||
| union jackctl_parameter_value engine_driver_parameter_get_value(void * obj) | |||||
| { | |||||
| union jackctl_parameter_value value; | |||||
| strcpy(value.str, params_ptr->driver_ptr->symlink->name); | |||||
| return value; | |||||
| } | |||||
| bool engine_driver_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) | |||||
| { | |||||
| return jack_params_set_driver(obj, value_ptr->str); | |||||
| } | |||||
| union jackctl_parameter_value engine_driver_parameter_get_default_value(void * obj) | |||||
| { | |||||
| union jackctl_parameter_value value; | |||||
| strcpy(value.str, DEFAULT_DRIVER); | |||||
| return value; | |||||
| } | |||||
| #undef params_ptr | |||||
| static bool add_engine_driver_enum_constraint(void * context, const char * name) | |||||
| { | |||||
| strcpy((*((struct jack_parameter_enum **)context))->value.str, name); | |||||
| (*((struct jack_parameter_enum **)context))->short_desc = name; | |||||
| (*((struct jack_parameter_enum **)context))++; | |||||
| return true; | |||||
| } | |||||
| static bool init_engine_driver_parameter(struct jack_params * params_ptr) | |||||
| { | |||||
| struct jack_parameter * param_ptr; | |||||
| const char * address[PARAM_ADDRESS_SIZE] = {PTNODE_ENGINE, NULL}; | |||||
| struct jack_parameter_container * engine_ptr; | |||||
| struct jack_parameter_enum * possible_value; | |||||
| engine_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); | |||||
| if (engine_ptr == NULL) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| param_ptr = malloc(sizeof(struct jack_parameter)); | |||||
| if (param_ptr == NULL) | |||||
| { | |||||
| jack_error("Ran out of memory trying to allocate struct jack_parameter"); | |||||
| goto fail; | |||||
| } | |||||
| param_ptr->ext = false; | |||||
| param_ptr->obj = params_ptr; | |||||
| param_ptr->vtable.is_set = engine_driver_parameter_is_set; | |||||
| param_ptr->vtable.reset = engine_driver_parameter_reset; | |||||
| param_ptr->vtable.get_value = engine_driver_parameter_get_value; | |||||
| param_ptr->vtable.set_value = engine_driver_parameter_set_value; | |||||
| param_ptr->vtable.get_default_value = engine_driver_parameter_get_default_value; | |||||
| param_ptr->type = JackParamString; | |||||
| param_ptr->name = "driver"; | |||||
| param_ptr->short_decr = "Driver to use"; | |||||
| param_ptr->long_descr = ""; | |||||
| param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID | JACK_CONSTRAINT_FLAG_STRICT | JACK_CONSTRAINT_FLAG_FAKE_VALUE; | |||||
| param_ptr->constraint_range = false; | |||||
| param_ptr->constraint.enumeration.count = params_ptr->drivers_count; | |||||
| param_ptr->constraint.enumeration.possible_values_array = malloc(sizeof(struct jack_parameter_enum) * params_ptr->drivers_count); | |||||
| if (param_ptr->constraint.enumeration.possible_values_array == NULL) | |||||
| { | |||||
| goto free; | |||||
| } | |||||
| address[0] = PTNODE_DRIVERS; | |||||
| possible_value = param_ptr->constraint.enumeration.possible_values_array; | |||||
| jack_params_iterate_container((jack_params_handle)params_ptr, address, add_engine_driver_enum_constraint, &possible_value); | |||||
| list_add(¶m_ptr->siblings, &engine_ptr->children); | |||||
| return true; | |||||
| free: | |||||
| free(param_ptr); | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| jack_params_handle jack_params_create(jackctl_server_t * server) | |||||
| { | |||||
| struct jack_params * params_ptr; | |||||
| params_ptr = malloc(sizeof(struct jack_params)); | |||||
| if (params_ptr == NULL) | |||||
| { | |||||
| jack_error("Ran out of memory trying to allocate struct jack_params"); | |||||
| return NULL; | |||||
| } | |||||
| params_ptr->server = server; | |||||
| INIT_LIST_HEAD(¶ms_ptr->root.children); | |||||
| params_ptr->root.leaf = false; | |||||
| params_ptr->root.name = NULL; | |||||
| if (!init_engine(params_ptr) || | |||||
| !init_drivers(params_ptr) || | |||||
| !init_driver(params_ptr) || | |||||
| !init_engine_driver_parameter(params_ptr) || | |||||
| !jack_params_set_driver((jack_params_handle)params_ptr, DEFAULT_DRIVER) || | |||||
| !init_internals(params_ptr)) | |||||
| { | |||||
| jack_params_destroy((jack_params_handle)params_ptr); | |||||
| return NULL; | |||||
| } | |||||
| params_ptr->driver_set = false; | |||||
| assert(strcmp(params_ptr->driver_ptr->symlink->name, DEFAULT_DRIVER) == 0); | |||||
| return (jack_params_handle)params_ptr; | |||||
| } | |||||
| #define params_ptr ((struct jack_params *)params) | |||||
| void jack_params_destroy(jack_params_handle params) | |||||
| { | |||||
| free_containers(¶ms_ptr->root.children); | |||||
| free(params); | |||||
| } | |||||
| bool jack_params_set_driver(jack_params_handle params, const char * name) | |||||
| { | |||||
| struct list_head * node_ptr; | |||||
| struct jack_parameter_container * container_ptr; | |||||
| list_for_each(node_ptr, params_ptr->drivers_ptr) | |||||
| { | |||||
| container_ptr = list_entry(node_ptr, struct jack_parameter_container, siblings); | |||||
| if (strcmp(container_ptr->name, name) == 0) | |||||
| { | |||||
| params_ptr->driver_ptr->symlink = container_ptr; | |||||
| params_ptr->driver_set = true; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| jackctl_driver_t * jack_params_get_driver(jack_params_handle params) | |||||
| { | |||||
| return params_ptr->driver_ptr->symlink->obj; | |||||
| } | |||||
| bool jack_params_check_address(jack_params_handle params, const char * const * address, bool want_leaf) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); | |||||
| if (container_ptr == NULL) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| if (want_leaf && !container_ptr->leaf) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool jack_params_is_leaf_container(jack_params_handle params, const char * const * address) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); | |||||
| if (container_ptr == NULL) | |||||
| { | |||||
| assert(false); | |||||
| return false; | |||||
| } | |||||
| return container_ptr->leaf; | |||||
| } | |||||
| bool | |||||
| jack_params_iterate_container( | |||||
| jack_params_handle params, | |||||
| const char * const * address, | |||||
| bool (* callback)(void * context, const char * name), | |||||
| void * context) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| struct list_head * node_ptr; | |||||
| const char * name; | |||||
| container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); | |||||
| if (container_ptr == NULL) | |||||
| { | |||||
| assert(false); | |||||
| return true; | |||||
| } | |||||
| list_for_each(node_ptr, &container_ptr->children) | |||||
| { | |||||
| if (container_ptr->leaf) | |||||
| { | |||||
| name = list_entry(node_ptr, struct jack_parameter, siblings)->name; | |||||
| } | |||||
| else | |||||
| { | |||||
| name = list_entry(node_ptr, struct jack_parameter_container, siblings)->name; | |||||
| } | |||||
| if (!callback(context, name)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| jack_params_iterate_params( | |||||
| jack_params_handle params, | |||||
| const char * const * address, | |||||
| bool (* callback)(void * context, const struct jack_parameter * param_ptr), | |||||
| void * context) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| struct list_head * node_ptr; | |||||
| struct jack_parameter * param_ptr; | |||||
| container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); | |||||
| if (container_ptr == NULL || !container_ptr->leaf) | |||||
| { | |||||
| assert(false); | |||||
| return true; | |||||
| } | |||||
| list_for_each(node_ptr, &container_ptr->children) | |||||
| { | |||||
| param_ptr = list_entry(node_ptr, struct jack_parameter, siblings); | |||||
| if (!callback(context, param_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| const struct jack_parameter * jack_params_get_parameter(jack_params_handle params, const char * const * address) | |||||
| { | |||||
| int depth; | |||||
| struct jack_parameter_container * container_ptr; | |||||
| struct list_head * node_ptr; | |||||
| struct jack_parameter * param_ptr; | |||||
| for (depth = 0; depth < PARAM_ADDRESS_SIZE; depth++) | |||||
| { | |||||
| if (address[depth] == NULL) | |||||
| { | |||||
| break; | |||||
| } | |||||
| } | |||||
| depth--; | |||||
| container_ptr = find_container(¶ms_ptr->root, address, depth); | |||||
| if (container_ptr == NULL || !container_ptr->leaf) | |||||
| { | |||||
| return NULL; | |||||
| } | |||||
| list_for_each(node_ptr, &container_ptr->children) | |||||
| { | |||||
| param_ptr = list_entry(node_ptr, struct jack_parameter, siblings); | |||||
| if (strcmp(param_ptr->name, address[depth]) == 0) | |||||
| { | |||||
| return param_ptr; | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| void jack_params_add_parameter(jack_params_handle params, const char * const * address, bool end, struct jack_parameter * param_ptr) | |||||
| { | |||||
| struct jack_parameter_container * container_ptr; | |||||
| container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); | |||||
| if (container_ptr == NULL || !container_ptr->leaf) | |||||
| { | |||||
| assert(false); | |||||
| return; | |||||
| } | |||||
| param_ptr->ext = true; | |||||
| if (end) | |||||
| { | |||||
| list_add_tail(¶m_ptr->siblings, &container_ptr->children); | |||||
| } | |||||
| else | |||||
| { | |||||
| list_add(¶m_ptr->siblings, &container_ptr->children); | |||||
| } | |||||
| return; | |||||
| } | |||||
| #undef params_ptr | |||||
| @@ -0,0 +1,111 @@ | |||||
| /* -*- Mode: C ; c-basic-offset: 4 -*- */ | |||||
| /* | |||||
| Copyright (C) 2011 Nedko Arnaudov | |||||
| 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. | |||||
| 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 PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED | |||||
| #define PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED | |||||
| #include "jack/control.h" | |||||
| #include "list.h" | |||||
| #define PARAM_ADDRESS_SIZE 3 | |||||
| #define PTNODE_ENGINE "engine" | |||||
| #define PTNODE_DRIVER "driver" | |||||
| #define PTNODE_DRIVERS "drivers" | |||||
| #define PTNODE_INTERNALS "internals" | |||||
| struct jack_parameter_vtable | |||||
| { | |||||
| bool (* is_set)(void * obj); | |||||
| bool (* reset)(void * obj); | |||||
| union jackctl_parameter_value (* get_value)(void * obj); | |||||
| bool (* set_value)(void * obj, const union jackctl_parameter_value * value_ptr); | |||||
| union jackctl_parameter_value (* get_default_value)(void * obj); | |||||
| }; | |||||
| #define JACK_CONSTRAINT_FLAG_VALID ((uint32_t)1) /**< if not set, there is no constraint */ | |||||
| #define JACK_CONSTRAINT_FLAG_STRICT ((uint32_t)2) /**< if set, constraint is strict, i.e. supplying non-matching value will not work */ | |||||
| #define JACK_CONSTRAINT_FLAG_FAKE_VALUE ((uint32_t)4) /**< if set, values have no user meaningful meaning */ | |||||
| struct jack_parameter_enum | |||||
| { | |||||
| union jackctl_parameter_value value; | |||||
| const char * short_desc; | |||||
| }; | |||||
| struct jack_parameter | |||||
| { | |||||
| void * obj; | |||||
| struct jack_parameter_vtable vtable; | |||||
| struct list_head siblings; | |||||
| bool ext; | |||||
| jackctl_param_type_t type; | |||||
| const char * name; | |||||
| const char * short_decr; | |||||
| const char * long_descr; | |||||
| uint32_t constraint_flags; /**< JACK_CONSTRAINT_FLAG_XXX */ | |||||
| bool constraint_range; /**< if true, constraint is a range (min-max), otherwise it is an enumeration */ | |||||
| union | |||||
| { | |||||
| struct | |||||
| { | |||||
| union jackctl_parameter_value min; | |||||
| union jackctl_parameter_value max; | |||||
| } range; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is set */ | |||||
| struct | |||||
| { | |||||
| uint32_t count; | |||||
| struct jack_parameter_enum * possible_values_array; | |||||
| } enumeration; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is not set */ | |||||
| } constraint; | |||||
| }; | |||||
| typedef struct _jack_params { int unused; } * jack_params_handle; | |||||
| jack_params_handle jack_params_create(jackctl_server_t * server); | |||||
| void jack_params_destroy(jack_params_handle params); | |||||
| bool jack_params_set_driver(jack_params_handle params, const char * name); | |||||
| jackctl_driver_t * jack_params_get_driver(jack_params_handle params); | |||||
| bool jack_params_check_address(jack_params_handle params, const char * const * address, bool want_leaf); | |||||
| bool jack_params_is_leaf_container(jack_params_handle params, const char * const * address); | |||||
| bool | |||||
| jack_params_iterate_container( | |||||
| jack_params_handle params, | |||||
| const char * const * address, | |||||
| bool (* callback)(void * context, const char * name), | |||||
| void * context); | |||||
| bool | |||||
| jack_params_iterate_params( | |||||
| jack_params_handle params, | |||||
| const char * const * address, | |||||
| bool (* callback)(void * context, const struct jack_parameter * param_ptr), | |||||
| void * context); | |||||
| const struct jack_parameter * jack_params_get_parameter(jack_params_handle params, const char * const * address); | |||||
| void jack_params_add_parameter(jack_params_handle params, const char * const * address, bool end, struct jack_parameter * param_ptr); | |||||
| #endif /* #ifndef PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED */ | |||||
| @@ -52,6 +52,7 @@ def build(bld): | |||||
| obj.source = [ | obj.source = [ | ||||
| 'jackdbus.c', | 'jackdbus.c', | ||||
| 'controller.c', | 'controller.c', | ||||
| 'params.c', | |||||
| 'controller_iface_configure.c', | 'controller_iface_configure.c', | ||||
| 'controller_iface_control.c', | 'controller_iface_control.c', | ||||
| 'controller_iface_introspectable.c', | 'controller_iface_introspectable.c', | ||||
| @@ -59,7 +60,6 @@ def build(bld): | |||||
| 'controller_iface_transport.c', | 'controller_iface_transport.c', | ||||
| 'xml.c', | 'xml.c', | ||||
| 'xml_expat.c', | 'xml_expat.c', | ||||
| #'xml_libxml.c', | |||||
| #'xml_nop.c', | #'xml_nop.c', | ||||
| 'xml_write_raw.c', | 'xml_write_raw.c', | ||||
| 'sigsegv.c', | 'sigsegv.c', | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* -*- Mode: C ; c-basic-offset: 4 -*- */ | /* -*- Mode: C ; c-basic-offset: 4 -*- */ | ||||
| /* | /* | ||||
| Copyright (C) 2007,2008 Nedko Arnaudov | |||||
| Copyright (C) 2007,2008,2011 Nedko Arnaudov | |||||
| This program is free software; you can redistribute it and/or modify | 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 | it under the terms of the GNU General Public License as published by | ||||
| @@ -25,358 +25,116 @@ | |||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <assert.h> | |||||
| #include <dbus/dbus.h> | #include <dbus/dbus.h> | ||||
| #include "controller_internal.h" | #include "controller_internal.h" | ||||
| void | void | ||||
| jack_controller_settings_set_bool_option( | |||||
| const char *value_str, | |||||
| int *value_ptr) | |||||
| { | |||||
| if (strcmp(value_str, "true") == 0) | |||||
| { | |||||
| *value_ptr = true; | |||||
| } | |||||
| else if (strcmp(value_str, "false") == 0) | |||||
| { | |||||
| *value_ptr = false; | |||||
| } | |||||
| else | |||||
| { | |||||
| jack_error("ignoring unknown bool value \"%s\"", value_str); | |||||
| } | |||||
| } | |||||
| void | |||||
| jack_controller_settings_set_sint_option( | |||||
| const char *value_str, | |||||
| int *value_ptr) | |||||
| { | |||||
| *value_ptr = atoi(value_str); | |||||
| } | |||||
| void | |||||
| jack_controller_settings_set_uint_option( | |||||
| const char *value_str, | |||||
| unsigned int *value_ptr) | |||||
| { | |||||
| *value_ptr = strtoul(value_str, NULL, 10); | |||||
| } | |||||
| void | |||||
| jack_controller_settings_set_char_option( | |||||
| const char *value_str, | |||||
| char *value_ptr) | |||||
| { | |||||
| if (value_str[0] == 0 || value_str[1] != 0) | |||||
| { | |||||
| jack_error("invalid char option value \"%s\"", value_str); | |||||
| return; | |||||
| } | |||||
| *value_ptr = *value_str; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_set_string_option( | |||||
| const char *value_str, | |||||
| char *value_ptr, | |||||
| size_t max_size) | |||||
| { | |||||
| size_t size; | |||||
| size = strlen(value_str); | |||||
| if (size >= max_size) | |||||
| { | |||||
| jack_error("string option value \"%s\" is too long, max is %u chars (including terminating zero)", value_str, (unsigned int)max_size); | |||||
| return; | |||||
| } | |||||
| strcpy(value_ptr, value_str); | |||||
| } | |||||
| void | |||||
| jack_controller_settings_set_driver_option( | |||||
| jackctl_driver_t *driver, | |||||
| const char *option_name, | |||||
| const char *option_value) | |||||
| jack_controller_deserialize_parameter_value( | |||||
| struct jack_controller *controller_ptr, | |||||
| const char * const * address, | |||||
| const char * option_value) | |||||
| { | { | ||||
| jackctl_parameter_t *parameter; | |||||
| jackctl_param_type_t type; | |||||
| int value_int = 0; | |||||
| unsigned int value_uint = 0; | |||||
| const struct jack_parameter * param_ptr; | |||||
| union jackctl_parameter_value value; | union jackctl_parameter_value value; | ||||
| size_t size; | |||||
| jack_info("setting driver option \"%s\" to value \"%s\"", option_name, option_value); | |||||
| parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(driver), option_name); | |||||
| if (parameter == NULL) | |||||
| param_ptr = jack_params_get_parameter(controller_ptr->params, address); | |||||
| if (param_ptr == NULL) | |||||
| { | { | ||||
| jack_error( | |||||
| "Unknown parameter \"%s\" of driver \"%s\"", | |||||
| option_name, | |||||
| jackctl_driver_get_name(driver)); | |||||
| return; | |||||
| jack_error("Unknown parameter"); | |||||
| goto ignore; | |||||
| } | } | ||||
| type = jackctl_parameter_get_type(parameter); | |||||
| jack_info("setting parameter '%s':'%s':'%s' to value \"%s\"", address[0], address[1], address[2], option_value); | |||||
| switch (type) | |||||
| switch (param_ptr->type) | |||||
| { | { | ||||
| case JackParamInt: | case JackParamInt: | ||||
| jack_controller_settings_set_sint_option(option_value, &value_int); | |||||
| value.i = value_int; | |||||
| value.i = atoi(option_value); | |||||
| break; | break; | ||||
| case JackParamUInt: | case JackParamUInt: | ||||
| jack_controller_settings_set_uint_option(option_value, &value_uint); | |||||
| value.ui = value_uint; | |||||
| value.ui = strtoul(option_value, NULL, 10); | |||||
| break; | break; | ||||
| case JackParamChar: | case JackParamChar: | ||||
| jack_controller_settings_set_char_option(option_value, &value.c); | |||||
| if (option_value[0] == 0 || option_value[1] != 0) | |||||
| { | |||||
| jack_error("invalid char option value \"%s\"", option_value); | |||||
| goto ignore; | |||||
| } | |||||
| value.c = *option_value; | |||||
| break; | break; | ||||
| case JackParamString: | case JackParamString: | ||||
| jack_controller_settings_set_string_option(option_value, value.str, sizeof(value.str)); | |||||
| size = strlen(option_value); | |||||
| if (size >= sizeof(value.str)) | |||||
| { | |||||
| jack_error("string option value \"%s\" is too long, max is %zu chars (including terminating zero)", option_value, sizeof(value.str)); | |||||
| goto ignore; | |||||
| } | |||||
| strcpy(value.str, option_value); | |||||
| break; | break; | ||||
| case JackParamBool: | case JackParamBool: | ||||
| jack_controller_settings_set_bool_option(option_value, &value_int); | |||||
| value.i = value_int; | |||||
| if (strcmp(option_value, "true") == 0) | |||||
| { | |||||
| value.b = true; | |||||
| } | |||||
| else if (strcmp(option_value, "false") == 0) | |||||
| { | |||||
| value.b = false; | |||||
| } | |||||
| else | |||||
| { | |||||
| jack_error("ignoring unknown bool value \"%s\"", option_value); | |||||
| goto ignore; | |||||
| } | |||||
| break; | break; | ||||
| default: | default: | ||||
| jack_error("Parameter \"%s\" of driver \"%s\" is of unknown type %d", | |||||
| jackctl_parameter_get_name(parameter), | |||||
| jackctl_driver_get_name(driver), | |||||
| type); | |||||
| jack_error("Unknown type %d", (int)param_ptr->type); | |||||
| goto ignore; | |||||
| } | } | ||||
| jackctl_parameter_set_value(parameter, &value); | |||||
| } | |||||
| void | |||||
| jack_controller_settings_set_internal_option( | |||||
| jackctl_internal_t *internal, | |||||
| const char *option_name, | |||||
| const char *option_value) | |||||
| { | |||||
| jackctl_parameter_t *parameter; | |||||
| jackctl_param_type_t type; | |||||
| int value_int = 0; | |||||
| unsigned int value_uint = 0; | |||||
| union jackctl_parameter_value value; | |||||
| jack_info("setting internal option \"%s\" to value \"%s\"", option_name, option_value); | |||||
| parameter = jack_controller_find_parameter(jackctl_internal_get_parameters(internal), option_name); | |||||
| if (parameter == NULL) | |||||
| if (param_ptr->vtable.set_value(param_ptr->obj, &value)) | |||||
| { | { | ||||
| jack_error( | |||||
| "Unknown parameter \"%s\" of internal \"%s\"", | |||||
| option_name, | |||||
| jackctl_internal_get_name(internal)); | |||||
| return; | return; | ||||
| } | } | ||||
| type = jackctl_parameter_get_type(parameter); | |||||
| switch (type) | |||||
| { | |||||
| case JackParamInt: | |||||
| jack_controller_settings_set_sint_option(option_value, &value_int); | |||||
| value.i = value_int; | |||||
| break; | |||||
| case JackParamUInt: | |||||
| jack_controller_settings_set_uint_option(option_value, &value_uint); | |||||
| value.ui = value_uint; | |||||
| break; | |||||
| case JackParamChar: | |||||
| jack_controller_settings_set_char_option(option_value, &value.c); | |||||
| break; | |||||
| case JackParamString: | |||||
| jack_controller_settings_set_string_option(option_value, value.str, sizeof(value.str)); | |||||
| break; | |||||
| case JackParamBool: | |||||
| jack_controller_settings_set_bool_option(option_value, &value_int); | |||||
| value.i = value_int; | |||||
| break; | |||||
| default: | |||||
| jack_error("Parameter \"%s\" of internal \"%s\" is of unknown type %d", | |||||
| jackctl_parameter_get_name(parameter), | |||||
| jackctl_internal_get_name(internal), | |||||
| type); | |||||
| } | |||||
| jack_error("Parameter set failed"); | |||||
| jackctl_parameter_set_value(parameter, &value); | |||||
| ignore: | |||||
| jack_error("Ignoring restore attempt of parameter '%s':'%s':'%s'", address[0], address[1], address[2]); | |||||
| } | } | ||||
| void | void | ||||
| jack_controller_settings_set_engine_option( | |||||
| struct jack_controller *controller_ptr, | |||||
| const char *option_name, | |||||
| const char *option_value) | |||||
| jack_controller_serialize_parameter_value( | |||||
| const struct jack_parameter * param_ptr, | |||||
| char * value_buffer) | |||||
| { | { | ||||
| jackctl_parameter_t *parameter; | |||||
| jackctl_param_type_t type; | |||||
| int value_int = 0; | |||||
| unsigned int value_uint = 0; | |||||
| union jackctl_parameter_value value; | union jackctl_parameter_value value; | ||||
| jack_info("setting engine option \"%s\" to value \"%s\"", option_name, option_value); | |||||
| value = param_ptr->vtable.get_value(param_ptr->obj); | |||||
| if (strcmp(option_name, "driver") == 0) | |||||
| { | |||||
| if (!jack_controller_select_driver(controller_ptr, option_value)) | |||||
| { | |||||
| jack_error("unknown driver '%s'", option_value); | |||||
| } | |||||
| return; | |||||
| } | |||||
| parameter = jack_controller_find_parameter(jackctl_server_get_parameters(controller_ptr->server), option_name); | |||||
| if (parameter == NULL) | |||||
| { | |||||
| jack_error( | |||||
| "Unknown engine parameter \"%s\"", | |||||
| option_name); | |||||
| return; | |||||
| } | |||||
| type = jackctl_parameter_get_type(parameter); | |||||
| switch (type) | |||||
| switch (param_ptr->type) | |||||
| { | { | ||||
| case JackParamInt: | case JackParamInt: | ||||
| jack_controller_settings_set_sint_option(option_value, &value_int); | |||||
| value.i = value_int; | |||||
| break; | |||||
| sprintf(value_buffer, "%d", (int)value.i); | |||||
| return; | |||||
| case JackParamUInt: | case JackParamUInt: | ||||
| jack_controller_settings_set_uint_option(option_value, &value_uint); | |||||
| value.ui = value_uint; | |||||
| break; | |||||
| sprintf(value_buffer, "%u", (unsigned int)value.ui); | |||||
| return; | |||||
| case JackParamChar: | case JackParamChar: | ||||
| jack_controller_settings_set_char_option(option_value, &value.c); | |||||
| break; | |||||
| sprintf(value_buffer, "%c", (char)value.c); | |||||
| return; | |||||
| case JackParamString: | case JackParamString: | ||||
| jack_controller_settings_set_string_option(option_value, value.str, sizeof(value.str)); | |||||
| break; | |||||
| strcpy(value_buffer, value.str); | |||||
| return; | |||||
| case JackParamBool: | case JackParamBool: | ||||
| jack_controller_settings_set_bool_option(option_value, &value_int); | |||||
| value.i = value_int; | |||||
| break; | |||||
| default: | |||||
| jack_error("Engine parameter \"%s\" is of unknown type %d", | |||||
| jackctl_parameter_get_name(parameter), | |||||
| type); | |||||
| } | |||||
| jackctl_parameter_set_value(parameter, &value); | |||||
| } | |||||
| static | |||||
| bool | |||||
| jack_controller_settings_save_options( | |||||
| void *context, | |||||
| const JSList * parameters_list, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| jackctl_parameter_t *parameter; | |||||
| jackctl_param_type_t type; | |||||
| union jackctl_parameter_value value; | |||||
| const char * name; | |||||
| char value_str[50]; | |||||
| while (parameters_list != NULL) | |||||
| { | |||||
| parameter = (jackctl_parameter_t *)parameters_list->data; | |||||
| if (jackctl_parameter_is_set(parameter)) | |||||
| { | |||||
| type = jackctl_parameter_get_type(parameter); | |||||
| value = jackctl_parameter_get_value(parameter); | |||||
| name = jackctl_parameter_get_name(parameter); | |||||
| switch (type) | |||||
| { | |||||
| case JackParamInt: | |||||
| sprintf(value_str, "%d", (int)value.i); | |||||
| if (!jack_controller_settings_write_option(context, name, value_str, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| break; | |||||
| case JackParamUInt: | |||||
| sprintf(value_str, "%u", (unsigned int)value.ui); | |||||
| if (!jack_controller_settings_write_option(context, name, value_str, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| break; | |||||
| case JackParamChar: | |||||
| sprintf(value_str, "%c", (char)value.c); | |||||
| if (!jack_controller_settings_write_option(context, name, value_str, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| break; | |||||
| case JackParamString: | |||||
| if (!jack_controller_settings_write_option(context, name, value.str, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| break; | |||||
| case JackParamBool: | |||||
| if (!jack_controller_settings_write_option(context, name, value.b ? "true" : "false", dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| break; | |||||
| default: | |||||
| jack_error("parameter of unknown type %d", type); | |||||
| } | |||||
| } | |||||
| parameters_list = jack_slist_next(parameters_list); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| jack_controller_settings_save_engine_options( | |||||
| void *context, | |||||
| struct jack_controller *controller_ptr, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| if (controller_ptr->driver != NULL) | |||||
| { | |||||
| if (!jack_controller_settings_write_option( | |||||
| context, | |||||
| "driver", | |||||
| jackctl_driver_get_name(controller_ptr->driver), | |||||
| dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| strcpy(value_buffer, value.b ? "true" : "false"); | |||||
| return; | |||||
| } | } | ||||
| return jack_controller_settings_save_options(context, jackctl_server_get_parameters(controller_ptr->server), dbus_call_context_ptr); | |||||
| } | |||||
| bool | |||||
| jack_controller_settings_save_driver_options( | |||||
| void *context, | |||||
| jackctl_driver_t *driver, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| return jack_controller_settings_save_options(context, jackctl_driver_get_parameters(driver), dbus_call_context_ptr); | |||||
| } | |||||
| bool | |||||
| jack_controller_settings_save_internal_options( | |||||
| void *context, | |||||
| jackctl_internal_t *internal, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| return jack_controller_settings_save_options(context, jackctl_internal_get_parameters(internal), dbus_call_context_ptr); | |||||
| jack_error("parameter of unknown type %d", (int)param_ptr->type); | |||||
| assert(false); | |||||
| *value_buffer = 0; | |||||
| } | } | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* -*- Mode: C ; c-basic-offset: 4 -*- */ | /* -*- Mode: C ; c-basic-offset: 4 -*- */ | ||||
| /* | /* | ||||
| Copyright (C) 2007,2008 Nedko Arnaudov | |||||
| Copyright (C) 2007,2008,2011 Nedko Arnaudov | |||||
| This program is free software; you can redistribute it and/or modify | 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 | it under the terms of the GNU General Public License as published by | ||||
| @@ -29,6 +29,7 @@ | |||||
| #include <fcntl.h> | #include <fcntl.h> | ||||
| #include <errno.h> | #include <errno.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <assert.h> | |||||
| #include <expat.h> | #include <expat.h> | ||||
| #include <dbus/dbus.h> | #include <dbus/dbus.h> | ||||
| @@ -46,27 +47,16 @@ jack_controller_settings_uninit() | |||||
| { | { | ||||
| } | } | ||||
| #define PARSE_CONTEXT_ROOT 0 | |||||
| #define PARSE_CONTEXT_JACK 1 | |||||
| #define PARSE_CONTEXT_ENGINE 1 | |||||
| #define PARSE_CONTEXT_DRIVERS 2 | |||||
| #define PARSE_CONTEXT_DRIVER 3 | |||||
| #define PARSE_CONTEXT_OPTION 4 | |||||
| #define PARSE_CONTEXT_INTERNALS 5 | |||||
| #define PARSE_CONTEXT_INTERNAL 6 | |||||
| #define MAX_STACK_DEPTH 10 | |||||
| struct parse_context | struct parse_context | ||||
| { | { | ||||
| struct jack_controller *controller_ptr; | struct jack_controller *controller_ptr; | ||||
| XML_Bool error; | XML_Bool error; | ||||
| unsigned int element[MAX_STACK_DEPTH]; | |||||
| signed int depth; | |||||
| jackctl_driver_t *driver; | |||||
| jackctl_internal_t *internal; | |||||
| bool option_value_capture; | |||||
| char option[JACK_PARAM_STRING_MAX+1]; | char option[JACK_PARAM_STRING_MAX+1]; | ||||
| int option_used; | int option_used; | ||||
| const char * address[PARAM_ADDRESS_SIZE]; | |||||
| int address_index; | |||||
| char * container; | |||||
| char *name; | char *name; | ||||
| }; | }; | ||||
| @@ -80,7 +70,7 @@ jack_controller_settings_callback_chrdata(void *data, const XML_Char *s, int len | |||||
| return; | return; | ||||
| } | } | ||||
| if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_OPTION) | |||||
| if (context_ptr->option_value_capture) | |||||
| { | { | ||||
| if (context_ptr->option_used + len >= JACK_PARAM_STRING_MAX) | if (context_ptr->option_used + len >= JACK_PARAM_STRING_MAX) | ||||
| { | { | ||||
| @@ -97,107 +87,69 @@ jack_controller_settings_callback_chrdata(void *data, const XML_Char *s, int len | |||||
| void | void | ||||
| jack_controller_settings_callback_elstart(void *data, const char *el, const char **attr) | jack_controller_settings_callback_elstart(void *data, const char *el, const char **attr) | ||||
| { | { | ||||
| jackctl_driver_t *driver; | |||||
| jackctl_internal_t *internal; | |||||
| if (context_ptr->error) | if (context_ptr->error) | ||||
| { | { | ||||
| return; | return; | ||||
| } | } | ||||
| if (context_ptr->depth + 1 >= MAX_STACK_DEPTH) | |||||
| if (context_ptr->address_index >= PARAM_ADDRESS_SIZE) | |||||
| { | { | ||||
| jack_error("xml parse max stack depth reached"); | |||||
| assert(context_ptr->address_index == PARAM_ADDRESS_SIZE); | |||||
| jack_error("xml param address max depth reached"); | |||||
| context_ptr->error = XML_TRUE; | context_ptr->error = XML_TRUE; | ||||
| return; | return; | ||||
| } | } | ||||
| //jack_info("<%s>", el); | |||||
| if (strcmp(el, "jack") == 0) | if (strcmp(el, "jack") == 0) | ||||
| { | { | ||||
| //jack_info("<jack>"); | |||||
| context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_JACK; | |||||
| return; | return; | ||||
| } | } | ||||
| if (strcmp(el, "engine") == 0) | |||||
| if (strcmp(el, PTNODE_ENGINE) == 0) | |||||
| { | { | ||||
| //jack_info("<engine>"); | |||||
| context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_ENGINE; | |||||
| context_ptr->address[context_ptr->address_index++] = PTNODE_ENGINE; | |||||
| return; | return; | ||||
| } | } | ||||
| if (strcmp(el, "drivers") == 0) | |||||
| if (strcmp(el, PTNODE_DRIVERS) == 0) | |||||
| { | { | ||||
| //jack_info("<drivers>"); | |||||
| context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_DRIVERS; | |||||
| context_ptr->address[context_ptr->address_index++] = PTNODE_DRIVERS; | |||||
| return; | return; | ||||
| } | } | ||||
| if (strcmp(el, "internals") == 0) | |||||
| if (strcmp(el, PTNODE_INTERNALS) == 0) | |||||
| { | { | ||||
| //jack_info("<internals>"); | |||||
| context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_INTERNALS; | |||||
| context_ptr->address[context_ptr->address_index++] = PTNODE_INTERNALS; | |||||
| return; | return; | ||||
| } | } | ||||
| if (strcmp(el, "driver") == 0) | |||||
| if (strcmp(el, "driver") == 0 || | |||||
| strcmp(el, "internal") == 0) | |||||
| { | { | ||||
| if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) | if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) | ||||
| { | { | ||||
| jack_error("<driver> XML element must contain exactly one attribute, named \"name\""); | |||||
| jack_error("<%s> XML element must contain exactly one attribute, named \"name\"", el); | |||||
| context_ptr->error = XML_TRUE; | context_ptr->error = XML_TRUE; | ||||
| return; | return; | ||||
| } | } | ||||
| //jack_info("<driver>"); | |||||
| context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_DRIVER; | |||||
| driver = jack_controller_find_driver(context_ptr->controller_ptr->server, attr[1]); | |||||
| if (driver == NULL) | |||||
| { | |||||
| jack_error("ignoring settings for unknown driver \"%s\"", attr[1]); | |||||
| } | |||||
| else | |||||
| { | |||||
| jack_info("setting for driver \"%s\" found", attr[1]); | |||||
| } | |||||
| context_ptr->driver = driver; | |||||
| return; | |||||
| } | |||||
| if (strcmp(el, "internal") == 0) | |||||
| { | |||||
| if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) | |||||
| context_ptr->container = strdup(attr[1]); | |||||
| if (context_ptr->container == NULL) | |||||
| { | { | ||||
| jack_error("<internal> XML element must contain exactly one attribute, named \"name\""); | |||||
| jack_error("strdup() failed"); | |||||
| context_ptr->error = XML_TRUE; | context_ptr->error = XML_TRUE; | ||||
| return; | return; | ||||
| } | } | ||||
| //jack_info("<internal>"); | |||||
| context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_INTERNAL; | |||||
| internal = jack_controller_find_internal(context_ptr->controller_ptr->server, attr[1]); | |||||
| if (internal == NULL) | |||||
| { | |||||
| jack_error("ignoring settings for unknown internal \"%s\"", attr[1]); | |||||
| } | |||||
| else | |||||
| { | |||||
| jack_info("setting for internal \"%s\" found", attr[1]); | |||||
| } | |||||
| context_ptr->internal = internal; | |||||
| context_ptr->address[context_ptr->address_index++] = context_ptr->container; | |||||
| return; | return; | ||||
| } | } | ||||
| if (strcmp(el, "option") == 0) | if (strcmp(el, "option") == 0) | ||||
| { | { | ||||
| //jack_info("<option>"); | |||||
| if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) | if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) | ||||
| { | { | ||||
| jack_error("<option> XML element must contain exactly one attribute, named \"name\""); | jack_error("<option> XML element must contain exactly one attribute, named \"name\""); | ||||
| @@ -213,7 +165,8 @@ jack_controller_settings_callback_elstart(void *data, const char *el, const char | |||||
| return; | return; | ||||
| } | } | ||||
| context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_OPTION; | |||||
| context_ptr->address[context_ptr->address_index++] = context_ptr->name; | |||||
| context_ptr->option_value_capture = true; | |||||
| context_ptr->option_used = 0; | context_ptr->option_used = 0; | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -225,49 +178,48 @@ jack_controller_settings_callback_elstart(void *data, const char *el, const char | |||||
| void | void | ||||
| jack_controller_settings_callback_elend(void *data, const char *el) | jack_controller_settings_callback_elend(void *data, const char *el) | ||||
| { | { | ||||
| int i; | |||||
| if (context_ptr->error) | if (context_ptr->error) | ||||
| { | { | ||||
| return; | return; | ||||
| } | } | ||||
| //jack_info("element end (depth = %d, element = %u)", context_ptr->depth, context_ptr->element[context_ptr->depth]); | |||||
| //jack_info("</%s> (depth = %d)", el, context_ptr->address_index); | |||||
| if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_OPTION) | |||||
| if (strcmp(el, "option") == 0) | |||||
| { | { | ||||
| assert(context_ptr->option_value_capture); | |||||
| context_ptr->option[context_ptr->option_used] = 0; | context_ptr->option[context_ptr->option_used] = 0; | ||||
| if (context_ptr->depth == 2 && | |||||
| context_ptr->element[0] == PARSE_CONTEXT_JACK && | |||||
| context_ptr->element[1] == PARSE_CONTEXT_ENGINE) | |||||
| for (i = context_ptr->address_index; i < PARAM_ADDRESS_SIZE; i++) | |||||
| { | { | ||||
| jack_controller_settings_set_engine_option(context_ptr->controller_ptr, context_ptr->name, context_ptr->option); | |||||
| context_ptr->address[context_ptr->address_index] = NULL; | |||||
| } | } | ||||
| if (context_ptr->depth == 3 && | |||||
| context_ptr->element[0] == PARSE_CONTEXT_JACK && | |||||
| context_ptr->element[1] == PARSE_CONTEXT_DRIVERS && | |||||
| context_ptr->element[2] == PARSE_CONTEXT_DRIVER && | |||||
| context_ptr->driver != NULL) | |||||
| { | |||||
| jack_controller_settings_set_driver_option(context_ptr->driver, context_ptr->name, context_ptr->option); | |||||
| } | |||||
| if (context_ptr->depth == 3 && | |||||
| context_ptr->element[0] == PARSE_CONTEXT_JACK && | |||||
| context_ptr->element[1] == PARSE_CONTEXT_INTERNALS && | |||||
| context_ptr->element[2] == PARSE_CONTEXT_INTERNAL && | |||||
| context_ptr->internal != NULL) | |||||
| { | |||||
| jack_controller_settings_set_internal_option(context_ptr->internal, context_ptr->name, context_ptr->option); | |||||
| } | |||||
| } | |||||
| jack_controller_deserialize_parameter_value(context_ptr->controller_ptr, context_ptr->address, context_ptr->option); | |||||
| context_ptr->depth--; | |||||
| if (context_ptr->name != NULL) | |||||
| { | |||||
| free(context_ptr->name); | free(context_ptr->name); | ||||
| context_ptr->name = NULL; | context_ptr->name = NULL; | ||||
| context_ptr->option_value_capture = false; | |||||
| context_ptr->address_index--; | |||||
| } | |||||
| else if (context_ptr->container != NULL) | |||||
| { | |||||
| //jack_info("'%s'", context_ptr->container); | |||||
| free(context_ptr->container); | |||||
| context_ptr->container = NULL; | |||||
| context_ptr->address_index--; | |||||
| } | |||||
| else if (strcmp(el, PTNODE_ENGINE) == 0 || | |||||
| strcmp(el, PTNODE_DRIVERS) == 0 || | |||||
| strcmp(el, PTNODE_INTERNALS) == 0) | |||||
| { | |||||
| context_ptr->address_index--; | |||||
| } | |||||
| else | |||||
| { | |||||
| //jack_info("no depth decrement"); | |||||
| } | } | ||||
| } | } | ||||
| @@ -341,14 +293,20 @@ jack_controller_settings_load( | |||||
| context.controller_ptr = controller_ptr; | context.controller_ptr = controller_ptr; | ||||
| context.error = XML_FALSE; | context.error = XML_FALSE; | ||||
| context.depth = -1; | |||||
| context.option_value_capture = false; | |||||
| context.address_index = 0; | |||||
| context.name = NULL; | context.name = NULL; | ||||
| context.container = NULL; | |||||
| XML_SetElementHandler(parser, jack_controller_settings_callback_elstart, jack_controller_settings_callback_elend); | XML_SetElementHandler(parser, jack_controller_settings_callback_elstart, jack_controller_settings_callback_elend); | ||||
| XML_SetCharacterDataHandler(parser, jack_controller_settings_callback_chrdata); | XML_SetCharacterDataHandler(parser, jack_controller_settings_callback_chrdata); | ||||
| XML_SetUserData(parser, &context); | XML_SetUserData(parser, &context); | ||||
| xmls = XML_ParseBuffer(parser, bytes_read, XML_TRUE); | xmls = XML_ParseBuffer(parser, bytes_read, XML_TRUE); | ||||
| free(context.name); | |||||
| free(context.container); | |||||
| if (xmls == XML_STATUS_ERROR) | if (xmls == XML_STATUS_ERROR) | ||||
| { | { | ||||
| jack_error("XML_ParseBuffer() failed."); | jack_error("XML_ParseBuffer() failed."); | ||||
| @@ -1,797 +0,0 @@ | |||||
| /* -*- Mode: C ; c-basic-offset: 4 -*- */ | |||||
| /* | |||||
| Copyright (C) 2007,2008 Nedko Arnaudov | |||||
| 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. | |||||
| 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. | |||||
| */ | |||||
| #if defined(HAVE_CONFIG_H) | |||||
| #include "config.h" | |||||
| #endif | |||||
| #include <stdbool.h> | |||||
| #include <string.h> | |||||
| #include <dbus/dbus.h> | |||||
| #include <libxml/xmlwriter.h> | |||||
| #include <libxml/parser.h> | |||||
| #include <libxml/xpath.h> | |||||
| #include <jack/driver.h> | |||||
| #include <jack/engine.h> | |||||
| #include "controller_internal.h" | |||||
| #include "dbus.h" | |||||
| /* XPath expression used for engine options selection */ | |||||
| #define XPATH_ENGINE_OPTIONS_EXPRESSION "/jack/engine/option" | |||||
| /* XPath expression used for drivers selection */ | |||||
| #define XPATH_DRIVERS_EXPRESSION "/jack/drivers/driver" | |||||
| /* XPath expression used for driver options selection */ | |||||
| #define XPATH_DRIVER_OPTIONS_EXPRESSION "/jack/drivers/driver[@name = '%s']/option" | |||||
| bool | |||||
| jack_controller_settings_init() | |||||
| { | |||||
| /* | |||||
| * this initialize the library and check potential ABI mismatches | |||||
| * between the version it was compiled for and the actual shared | |||||
| * library used. | |||||
| */ | |||||
| LIBXML_TEST_VERSION; | |||||
| return true; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_uninit() | |||||
| { | |||||
| } | |||||
| #define writer ((xmlTextWriterPtr)context) | |||||
| bool | |||||
| jack_controller_settings_write_option( | |||||
| void *context, | |||||
| const char *name, | |||||
| const char *content, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| if (xmlTextWriterStartElement(writer, BAD_CAST "option") == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed."); | |||||
| return false; | |||||
| } | |||||
| if (xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST name) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteAttribute() failed."); | |||||
| return false; | |||||
| } | |||||
| if (xmlTextWriterWriteString(writer, BAD_CAST content) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteString() failed."); | |||||
| return false; | |||||
| } | |||||
| if (xmlTextWriterEndElement(writer) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed."); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| #undef writer | |||||
| bool | |||||
| jack_controller_settings_write_engine( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlTextWriterPtr writer, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| /* jack_info("engine settings begin"); */ | |||||
| /* if (xmlTextWriterWriteComment(writer, BAD_CAST "engine parameters") == -1) */ | |||||
| /* { */ | |||||
| /* jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed."); */ | |||||
| /* return false; */ | |||||
| /* } */ | |||||
| if (xmlTextWriterStartElement(writer, BAD_CAST "engine") == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed."); | |||||
| return false; | |||||
| } | |||||
| if (!jack_controller_settings_save_engine_options(writer, controller_ptr, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| if (xmlTextWriterEndElement(writer) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed."); | |||||
| return false; | |||||
| } | |||||
| /* jack_info("engine settings end"); */ | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| jack_controller_settings_write_driver( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlTextWriterPtr writer, | |||||
| jackctl_driver driver, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| /* if (xmlTextWriterWriteComment(writer, BAD_CAST "driver parameters") == -1) */ | |||||
| /* { */ | |||||
| /* jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed."); */ | |||||
| /* return false; */ | |||||
| /* } */ | |||||
| if (xmlTextWriterStartElement(writer, BAD_CAST "driver") == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed."); | |||||
| return false; | |||||
| } | |||||
| if (xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST jackctl_driver_get_name(driver)) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteAttribute() failed."); | |||||
| return false; | |||||
| } | |||||
| if (!jack_controller_settings_save_driver_options(writer, driver, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| if (xmlTextWriterEndElement(writer) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed."); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| jack_controller_settings_write_drivers( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlTextWriterPtr writer, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| const JSList * node_ptr; | |||||
| jackctl_driver driver; | |||||
| if (xmlTextWriterStartElement(writer, BAD_CAST "drivers") == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed."); | |||||
| return false; | |||||
| } | |||||
| node_ptr = jackctl_server_get_drivers_list(controller_ptr->server); | |||||
| while (node_ptr != NULL) | |||||
| { | |||||
| driver = (jackctl_driver)node_ptr->data; | |||||
| if (!jack_controller_settings_write_driver( | |||||
| controller_ptr, | |||||
| writer, | |||||
| driver, | |||||
| dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| node_ptr = jack_slist_next(node_ptr); | |||||
| } | |||||
| if (xmlTextWriterEndElement(writer) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed."); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| jack_controller_settings_write_internal( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlTextWriterPtr writer, | |||||
| jackctl_internal internal, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| /* if (xmlTextWriterWriteComment(writer, BAD_CAST "driver parameters") == -1) */ | |||||
| /* { */ | |||||
| /* jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed."); */ | |||||
| /* return false; */ | |||||
| /* } */ | |||||
| if (xmlTextWriterStartElement(writer, BAD_CAST "internal") == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed."); | |||||
| return false; | |||||
| } | |||||
| if (xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST jackctl_internal_get_name(driver)) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteAttribute() failed."); | |||||
| return false; | |||||
| } | |||||
| if (!jack_controller_settings_save_internal_options(writer, internal, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| if (xmlTextWriterEndElement(writer) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed."); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| jack_controller_settings_write_internals( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlTextWriterPtr writer, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| const JSList * node_ptr; | |||||
| jackctl_driver internal; | |||||
| if (xmlTextWriterStartElement(writer, BAD_CAST "internals") == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed."); | |||||
| return false; | |||||
| } | |||||
| node_ptr = jackctl_server_get_internals_list(controller_ptr->server); | |||||
| while (node_ptr != NULL) | |||||
| { | |||||
| internal = (jackctl_internal)node_ptr->data; | |||||
| if (!jack_controller_settings_write_internal( | |||||
| controller_ptr, | |||||
| writer, | |||||
| internal, | |||||
| dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| node_ptr = jack_slist_next(node_ptr); | |||||
| } | |||||
| if (xmlTextWriterEndElement(writer) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed."); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| jack_controller_settings_save( | |||||
| struct jack_controller * controller_ptr, | |||||
| void *dbus_call_context_ptr) | |||||
| { | |||||
| xmlTextWriterPtr writer; | |||||
| char *filename; | |||||
| size_t conf_len; | |||||
| bool ret; | |||||
| time_t timestamp; | |||||
| char timestamp_str[28]; | |||||
| time(×tamp); | |||||
| timestamp_str[0] = ' '; | |||||
| ctime_r(×tamp, timestamp_str + 1); | |||||
| timestamp_str[25] = ' '; | |||||
| ret = false; | |||||
| conf_len = strlen(JACKDBUS_CONF); | |||||
| filename = malloc(g_jackdbus_dir_len + conf_len + 1); | |||||
| if (filename == NULL) | |||||
| { | |||||
| jack_error("Out of memory."); | |||||
| goto fail; | |||||
| } | |||||
| memcpy(filename, g_jackdbus_dir, g_jackdbus_dir_len); | |||||
| memcpy(filename + g_jackdbus_dir_len, JACKDBUS_CONF, conf_len); | |||||
| filename[g_jackdbus_dir_len + conf_len] = 0; | |||||
| jack_info("saving settings to \"%s\"", filename); | |||||
| writer = xmlNewTextWriterFilename(filename, 0); | |||||
| if (writer == NULL) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Error creating the xml writer."); | |||||
| goto fail_free_filename; | |||||
| } | |||||
| if (xmlTextWriterSetIndent(writer, 1) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterSetIndent() failed."); | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (xmlTextWriterStartDocument(writer, NULL, NULL, NULL) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartDocument() failed."); | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (xmlTextWriterWriteComment(writer, BAD_CAST "\n" JACK_CONF_HEADER_TEXT) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed."); | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (xmlTextWriterWriteComment(writer, BAD_CAST timestamp_str) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed."); | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (xmlTextWriterStartElement(writer, BAD_CAST "jack") == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed."); | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (!jack_controller_settings_write_engine(controller_ptr, writer, dbus_call_context_ptr)) | |||||
| { | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (!jack_controller_settings_write_drivers(controller_ptr, writer, dbus_call_context_ptr)) | |||||
| { | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (!jack_controller_settings_write_internals(controller_ptr, writer, dbus_call_context_ptr)) | |||||
| { | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (xmlTextWriterEndElement(writer) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed."); | |||||
| goto fail_free_writter; | |||||
| } | |||||
| if (xmlTextWriterEndDocument(writer) == -1) | |||||
| { | |||||
| jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndDocument() failed."); | |||||
| goto fail_free_writter; | |||||
| } | |||||
| ret = true; | |||||
| fail_free_writter: | |||||
| xmlFreeTextWriter(writer); | |||||
| fail_free_filename: | |||||
| free(filename); | |||||
| fail: | |||||
| return ret; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_read_engine( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlXPathContextPtr xpath_ctx_ptr) | |||||
| { | |||||
| xmlXPathObjectPtr xpath_obj_ptr; | |||||
| xmlBufferPtr content_buffer_ptr; | |||||
| int i; | |||||
| const char *option_name; | |||||
| const char *option_value; | |||||
| /* Evaluate xpath expression */ | |||||
| xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)XPATH_ENGINE_OPTIONS_EXPRESSION, xpath_ctx_ptr); | |||||
| if (xpath_obj_ptr == NULL) | |||||
| { | |||||
| jack_error("Unable to evaluate XPath expression \"%s\"", XPATH_ENGINE_OPTIONS_EXPRESSION); | |||||
| goto exit; | |||||
| } | |||||
| if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0) | |||||
| { | |||||
| jack_error("XPath \"%s\" evaluation returned no data", XPATH_ENGINE_OPTIONS_EXPRESSION); | |||||
| goto free_xpath_obj; | |||||
| } | |||||
| content_buffer_ptr = xmlBufferCreate(); | |||||
| if (content_buffer_ptr == NULL) | |||||
| { | |||||
| jack_error("xmlBufferCreate() failed."); | |||||
| goto free_xpath_obj; | |||||
| } | |||||
| for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++) | |||||
| { | |||||
| //jack_info("engine option \"%s\" at index %d", xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"), i); | |||||
| if (xmlNodeBufGetContent(content_buffer_ptr, xpath_obj_ptr->nodesetval->nodeTab[i]) == -1) | |||||
| { | |||||
| jack_error("xmlNodeBufGetContent() failed."); | |||||
| goto next_option; | |||||
| } | |||||
| option_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"); | |||||
| option_value = (const char *)xmlBufferContent(content_buffer_ptr); | |||||
| jack_controller_settings_set_engine_option(controller_ptr, option_name, option_value); | |||||
| next_option: | |||||
| xmlBufferEmpty(content_buffer_ptr); | |||||
| } | |||||
| //free_buffer: | |||||
| xmlBufferFree(content_buffer_ptr); | |||||
| free_xpath_obj: | |||||
| xmlXPathFreeObject(xpath_obj_ptr); | |||||
| exit: | |||||
| return; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_read_driver( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlXPathContextPtr xpath_ctx_ptr, | |||||
| jackctl_driver driver) | |||||
| { | |||||
| char *xpath; | |||||
| size_t xpath_len; | |||||
| xmlXPathObjectPtr xpath_obj_ptr; | |||||
| xmlBufferPtr content_buffer_ptr; | |||||
| int i; | |||||
| const char *option_name; | |||||
| const char *option_value; | |||||
| const char *driver_name; | |||||
| driver_name = jackctl_driver_get_name(driver); | |||||
| jack_info("reading options for driver \"%s\"", driver_name); | |||||
| xpath_len = snprintf(NULL, 0, XPATH_DRIVER_OPTIONS_EXPRESSION, driver_name); | |||||
| xpath = malloc(xpath_len); | |||||
| if (xpath == NULL) | |||||
| { | |||||
| jack_error("Out of memory."); | |||||
| goto exit; | |||||
| } | |||||
| snprintf(xpath, xpath_len, XPATH_DRIVER_OPTIONS_EXPRESSION, driver_name); | |||||
| //jack_info("xpath = \"%s\"", xpath); | |||||
| /* Evaluate xpath expression */ | |||||
| xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)xpath, xpath_ctx_ptr); | |||||
| if (xpath_obj_ptr == NULL) | |||||
| { | |||||
| jack_error("Unable to evaluate XPath expression \"%s\"", xpath); | |||||
| goto free_xpath; | |||||
| } | |||||
| if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0) | |||||
| { | |||||
| //jack_info("XPath \"%s\" evaluation returned no data", xpath); | |||||
| goto free_xpath_obj; | |||||
| } | |||||
| content_buffer_ptr = xmlBufferCreate(); | |||||
| if (content_buffer_ptr == NULL) | |||||
| { | |||||
| jack_error("xmlBufferCreate() failed."); | |||||
| goto free_xpath_obj; | |||||
| } | |||||
| for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++) | |||||
| { | |||||
| //jack_info("driver option \"%s\" at index %d", xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"), i); | |||||
| if (xmlNodeBufGetContent(content_buffer_ptr, xpath_obj_ptr->nodesetval->nodeTab[i]) == -1) | |||||
| { | |||||
| jack_error("xmlNodeBufGetContent() failed."); | |||||
| goto next_option; | |||||
| } | |||||
| option_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"); | |||||
| option_value = (const char *)xmlBufferContent(content_buffer_ptr); | |||||
| jack_controller_settings_set_driver_option(driver, option_name, option_value); | |||||
| next_option: | |||||
| xmlBufferEmpty(content_buffer_ptr); | |||||
| } | |||||
| //free_buffer: | |||||
| xmlBufferFree(content_buffer_ptr); | |||||
| free_xpath_obj: | |||||
| xmlXPathFreeObject(xpath_obj_ptr); | |||||
| free_xpath: | |||||
| free(xpath); | |||||
| exit: | |||||
| return; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_read_internal( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlXPathContextPtr xpath_ctx_ptr, | |||||
| jackctl_internal internal) | |||||
| { | |||||
| char *xpath; | |||||
| size_t xpath_len; | |||||
| xmlXPathObjectPtr xpath_obj_ptr; | |||||
| xmlBufferPtr content_buffer_ptr; | |||||
| int i; | |||||
| const char *option_name; | |||||
| const char *option_value; | |||||
| const char *internal_name; | |||||
| internal_name = jackctl_internal_get_name(internal); | |||||
| jack_info("reading options for internal \"%s\"", internal_name); | |||||
| xpath_len = snprintf(NULL, 0, XPATH_DRIVER_OPTIONS_EXPRESSION, internal_name); | |||||
| xpath = malloc(xpath_len); | |||||
| if (xpath == NULL) | |||||
| { | |||||
| jack_error("Out of memory."); | |||||
| goto exit; | |||||
| } | |||||
| snprintf(xpath, xpath_len, XPATH_DRIVER_OPTIONS_EXPRESSION, internal_name); | |||||
| //jack_info("xpath = \"%s\"", xpath); | |||||
| /* Evaluate xpath expression */ | |||||
| xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)xpath, xpath_ctx_ptr); | |||||
| if (xpath_obj_ptr == NULL) | |||||
| { | |||||
| jack_error("Unable to evaluate XPath expression \"%s\"", xpath); | |||||
| goto free_xpath; | |||||
| } | |||||
| if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0) | |||||
| { | |||||
| //jack_info("XPath \"%s\" evaluation returned no data", xpath); | |||||
| goto free_xpath_obj; | |||||
| } | |||||
| content_buffer_ptr = xmlBufferCreate(); | |||||
| if (content_buffer_ptr == NULL) | |||||
| { | |||||
| jack_error("xmlBufferCreate() failed."); | |||||
| goto free_xpath_obj; | |||||
| } | |||||
| for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++) | |||||
| { | |||||
| //jack_info("driver option \"%s\" at index %d", xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"), i); | |||||
| if (xmlNodeBufGetContent(content_buffer_ptr, xpath_obj_ptr->nodesetval->nodeTab[i]) == -1) | |||||
| { | |||||
| jack_error("xmlNodeBufGetContent() failed."); | |||||
| goto next_option; | |||||
| } | |||||
| option_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"); | |||||
| option_value = (const char *)xmlBufferContent(content_buffer_ptr); | |||||
| jack_controller_settings_set_internal_option(internal, option_name, option_value); | |||||
| next_option: | |||||
| xmlBufferEmpty(content_buffer_ptr); | |||||
| } | |||||
| //free_buffer: | |||||
| xmlBufferFree(content_buffer_ptr); | |||||
| free_xpath_obj: | |||||
| xmlXPathFreeObject(xpath_obj_ptr); | |||||
| free_xpath: | |||||
| free(xpath); | |||||
| exit: | |||||
| return; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_read_drivers( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlXPathContextPtr xpath_ctx_ptr) | |||||
| { | |||||
| xmlXPathObjectPtr xpath_obj_ptr; | |||||
| int i; | |||||
| const char *driver_name; | |||||
| jackctl_driver driver; | |||||
| /* Evaluate xpath expression */ | |||||
| xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)XPATH_DRIVERS_EXPRESSION, xpath_ctx_ptr); | |||||
| if (xpath_obj_ptr == NULL) | |||||
| { | |||||
| jack_error("Unable to evaluate XPath expression \"%s\"", XPATH_DRIVERS_EXPRESSION); | |||||
| goto exit; | |||||
| } | |||||
| if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0) | |||||
| { | |||||
| jack_error("XPath \"%s\" evaluation returned no data", XPATH_DRIVERS_EXPRESSION); | |||||
| goto free_xpath_obj; | |||||
| } | |||||
| for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++) | |||||
| { | |||||
| driver_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"); | |||||
| driver = jack_controller_find_driver(controller_ptr->server, driver_name); | |||||
| if (driver == NULL) | |||||
| { | |||||
| jack_error("ignoring settings for unknown driver \"%s\"", driver_name); | |||||
| } | |||||
| else | |||||
| { | |||||
| jack_info("setting for driver \"%s\" found", driver_name); | |||||
| jack_controller_settings_read_driver(controller_ptr, xpath_ctx_ptr, driver); | |||||
| } | |||||
| } | |||||
| free_xpath_obj: | |||||
| xmlXPathFreeObject(xpath_obj_ptr); | |||||
| exit: | |||||
| return; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_read_internals( | |||||
| struct jack_controller * controller_ptr, | |||||
| xmlXPathContextPtr xpath_ctx_ptr) | |||||
| { | |||||
| xmlXPathObjectPtr xpath_obj_ptr; | |||||
| int i; | |||||
| const char *internal_name; | |||||
| jackctl_internal internal; | |||||
| /* Evaluate xpath expression */ | |||||
| xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)XPATH_DRIVERS_EXPRESSION, xpath_ctx_ptr); | |||||
| if (xpath_obj_ptr == NULL) | |||||
| { | |||||
| jack_error("Unable to evaluate XPath expression \"%s\"", XPATH_DRIVERS_EXPRESSION); | |||||
| goto exit; | |||||
| } | |||||
| if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0) | |||||
| { | |||||
| jack_error("XPath \"%s\" evaluation returned no data", XPATH_DRIVERS_EXPRESSION); | |||||
| goto free_xpath_obj; | |||||
| } | |||||
| for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++) | |||||
| { | |||||
| internal_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"); | |||||
| driver = jack_controller_find_internal(controller_ptr->server, driver_name); | |||||
| if (driver == NULL) | |||||
| { | |||||
| jack_error("ignoring settings for unknown internal \"%s\"", internal_name); | |||||
| } | |||||
| else | |||||
| { | |||||
| jack_info("setting for internal \"%s\" found", internal_name); | |||||
| jack_controller_settings_read_internal(controller_ptr, xpath_ctx_ptr, driver); | |||||
| } | |||||
| } | |||||
| free_xpath_obj: | |||||
| xmlXPathFreeObject(xpath_obj_ptr); | |||||
| exit: | |||||
| return; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_load( | |||||
| struct jack_controller * controller_ptr) | |||||
| { | |||||
| char *filename; | |||||
| size_t conf_len; | |||||
| xmlDocPtr doc_ptr; | |||||
| xmlXPathContextPtr xpath_ctx_ptr; | |||||
| conf_len = strlen(JACKDBUS_CONF); | |||||
| filename = malloc(g_jackdbus_dir_len + conf_len + 1); | |||||
| if (filename == NULL) | |||||
| { | |||||
| jack_error("Out of memory."); | |||||
| goto exit; | |||||
| } | |||||
| memcpy(filename, g_jackdbus_dir, g_jackdbus_dir_len); | |||||
| memcpy(filename + g_jackdbus_dir_len, JACKDBUS_CONF, conf_len); | |||||
| filename[g_jackdbus_dir_len + conf_len] = 0; | |||||
| jack_info("loading settings from \"%s\"", filename); | |||||
| doc_ptr = xmlParseFile(filename); | |||||
| if (doc_ptr == NULL) | |||||
| { | |||||
| jack_error("Failed to parse \"%s\"", filename); | |||||
| goto free_filename; | |||||
| } | |||||
| /* Create xpath evaluation context */ | |||||
| xpath_ctx_ptr = xmlXPathNewContext(doc_ptr); | |||||
| if (xpath_ctx_ptr == NULL) | |||||
| { | |||||
| jack_error("Unable to create new XPath context"); | |||||
| goto free_doc; | |||||
| } | |||||
| jack_controller_settings_read_engine(controller_ptr, xpath_ctx_ptr); | |||||
| jack_controller_settings_read_drivers(controller_ptr, xpath_ctx_ptr); | |||||
| jack_controller_settings_read_internals(controller_ptr, xpath_ctx_ptr); | |||||
| xmlXPathFreeContext(xpath_ctx_ptr); | |||||
| free_doc: | |||||
| xmlFreeDoc(doc_ptr); | |||||
| free_filename: | |||||
| free(filename); | |||||
| exit: | |||||
| return; | |||||
| } | |||||
| void | |||||
| jack_controller_settings_save_auto( | |||||
| struct jack_controller * controller_ptr) | |||||
| { | |||||
| jack_controller_settings_save(controller_ptr, NULL); | |||||
| } | |||||
| @@ -53,54 +53,57 @@ jack_controller_settings_write_string(int fd, const char * string, void *dbus_ca | |||||
| struct save_context | struct save_context | ||||
| { | { | ||||
| void * call; | |||||
| int fd; | int fd; | ||||
| const char *indent; | |||||
| const char * indent; | |||||
| jack_params_handle params; | |||||
| const char * address[PARAM_ADDRESS_SIZE]; | |||||
| const char * str; | |||||
| }; | }; | ||||
| #define save_context_ptr ((struct save_context *)context) | |||||
| #define fd (save_context_ptr->fd) | |||||
| #define ctx_ptr ((struct save_context *)context) | |||||
| #define fd (ctx_ptr->fd) | |||||
| bool | |||||
| jack_controller_settings_write_option( | |||||
| void *context, | |||||
| const char *name, | |||||
| const char *content, | |||||
| void *dbus_call_context_ptr) | |||||
| static bool jack_controller_serialize_parameter(void * context, const struct jack_parameter * param_ptr) | |||||
| { | { | ||||
| if (!jack_controller_settings_write_string(fd, save_context_ptr->indent, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| if (!jack_controller_settings_write_string(fd, "<option name=\"", dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| char value[JACK_PARAM_STRING_MAX + 1]; | |||||
| if (!jack_controller_settings_write_string(fd, name, dbus_call_context_ptr)) | |||||
| if (!param_ptr->vtable.is_set(param_ptr->obj)) | |||||
| { | { | ||||
| return false; | |||||
| return true; | |||||
| } | } | ||||
| if (!jack_controller_settings_write_string(fd, "\">", dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| jack_controller_serialize_parameter_value(param_ptr, value); | |||||
| if (!jack_controller_settings_write_string(fd, content, dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| if (!jack_controller_settings_write_string(fd, "</option>\n", dbus_call_context_ptr)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| return | |||||
| jack_controller_settings_write_string(fd, ctx_ptr->indent, ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, "<option name=\"", ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, param_ptr->name, ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, "\">", ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, value, ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, "</option>\n", ctx_ptr->call); | |||||
| } | |||||
| return true; | |||||
| bool serialize_modules(void * context, const char * name) | |||||
| { | |||||
| ctx_ptr->indent = " "; | |||||
| ctx_ptr->address[1] = name; | |||||
| ctx_ptr->address[2] = NULL; | |||||
| return | |||||
| jack_controller_settings_write_string(fd, " <", ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, ctx_ptr->str, ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, " name=\"", ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, name, ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, "\">\n", ctx_ptr->call) && | |||||
| jack_params_iterate_params(ctx_ptr->params, ctx_ptr->address, jack_controller_serialize_parameter, ctx_ptr) && | |||||
| jack_controller_settings_write_string(fd, " </", ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, ctx_ptr->str, ctx_ptr->call) && | |||||
| jack_controller_settings_write_string(fd, ">\n", ctx_ptr->call); | |||||
| } | } | ||||
| #undef fd | #undef fd | ||||
| #undef ctx_ptr | |||||
| bool | bool | ||||
| jack_controller_settings_save( | jack_controller_settings_save( | ||||
| @@ -114,9 +117,9 @@ jack_controller_settings_save( | |||||
| time_t timestamp; | time_t timestamp; | ||||
| char timestamp_str[26]; | char timestamp_str[26]; | ||||
| struct save_context context; | struct save_context context; | ||||
| const JSList * node_ptr; | |||||
| jackctl_driver_t *driver; | |||||
| jackctl_internal_t *internal; | |||||
| const char * modules[] = {"driver", "internal", NULL}; | |||||
| char buffer[100]; | |||||
| unsigned int i; | |||||
| time(×tamp); | time(×tamp); | ||||
| ctime_r(×tamp, timestamp_str); | ctime_r(×tamp, timestamp_str); | ||||
| @@ -147,6 +150,7 @@ jack_controller_settings_save( | |||||
| } | } | ||||
| context.fd = fd; | context.fd = fd; | ||||
| context.call = dbus_call_context_ptr; | |||||
| if (!jack_controller_settings_write_string(fd, "<?xml version=\"1.0\"?>\n", dbus_call_context_ptr)) | if (!jack_controller_settings_write_string(fd, "<?xml version=\"1.0\"?>\n", dbus_call_context_ptr)) | ||||
| { | { | ||||
| @@ -196,7 +200,9 @@ jack_controller_settings_save( | |||||
| } | } | ||||
| context.indent = " "; | context.indent = " "; | ||||
| if (!jack_controller_settings_save_engine_options(&context, controller_ptr, dbus_call_context_ptr)) | |||||
| context.address[0] = PTNODE_ENGINE; | |||||
| context.address[1] = NULL; | |||||
| if (!jack_params_iterate_params(controller_ptr->params, context.address, jack_controller_serialize_parameter, &context)) | |||||
| { | { | ||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| @@ -206,100 +212,50 @@ jack_controller_settings_save( | |||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| /* drivers */ | |||||
| if (!jack_controller_settings_write_string(fd, " <drivers>\n", dbus_call_context_ptr)) | |||||
| for (i = 0; modules[i] != NULL; i++) | |||||
| { | { | ||||
| goto exit_close; | |||||
| } | |||||
| node_ptr = jackctl_server_get_drivers_list(controller_ptr->server); | |||||
| while (node_ptr != NULL) | |||||
| { | |||||
| driver = (jackctl_driver_t *)node_ptr->data; | |||||
| if (!jack_controller_settings_write_string(fd, " <driver name=\"", dbus_call_context_ptr)) | |||||
| { | |||||
| goto exit_close; | |||||
| } | |||||
| if (!jack_controller_settings_write_string(fd, jackctl_driver_get_name(driver), dbus_call_context_ptr)) | |||||
| { | |||||
| goto exit_close; | |||||
| } | |||||
| if (!jack_controller_settings_write_string(fd, "\">\n", dbus_call_context_ptr)) | |||||
| if (!jack_controller_settings_write_string(fd, " <", dbus_call_context_ptr)) | |||||
| { | { | ||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| context.indent = " "; | |||||
| if (!jack_controller_settings_save_driver_options(&context, driver, dbus_call_context_ptr)) | |||||
| if (!jack_controller_settings_write_string(fd, modules[i], dbus_call_context_ptr)) | |||||
| { | { | ||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| if (!jack_controller_settings_write_string(fd, " </driver>\n", dbus_call_context_ptr)) | |||||
| if (!jack_controller_settings_write_string(fd, "s>\n", dbus_call_context_ptr)) | |||||
| { | { | ||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| node_ptr = jack_slist_next(node_ptr); | |||||
| } | |||||
| context.indent = " "; | |||||
| context.params = controller_ptr->params; | |||||
| context.str = modules[i]; | |||||
| strcpy(buffer, modules[i]); | |||||
| strcat(buffer, "s"); | |||||
| context.address[0] = buffer; | |||||
| context.address[1] = NULL; | |||||
| if (!jack_controller_settings_write_string(fd, " </drivers>\n", dbus_call_context_ptr)) | |||||
| { | |||||
| goto exit_close; | |||||
| } | |||||
| /* internals */ | |||||
| if (!jack_controller_settings_write_string(fd, " <internals>\n", dbus_call_context_ptr)) | |||||
| { | |||||
| goto exit_close; | |||||
| } | |||||
| node_ptr = jackctl_server_get_internals_list(controller_ptr->server); | |||||
| while (node_ptr != NULL) | |||||
| { | |||||
| internal = (jackctl_internal_t *)node_ptr->data; | |||||
| if (!jack_controller_settings_write_string(fd, " <internal name=\"", dbus_call_context_ptr)) | |||||
| if (!jack_params_iterate_container(controller_ptr->params, context.address, serialize_modules, &context)) | |||||
| { | { | ||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| if (!jack_controller_settings_write_string(fd, jackctl_internal_get_name(internal), dbus_call_context_ptr)) | |||||
| if (!jack_controller_settings_write_string(fd, " </", dbus_call_context_ptr)) | |||||
| { | { | ||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| if (!jack_controller_settings_write_string(fd, "\">\n", dbus_call_context_ptr)) | |||||
| if (!jack_controller_settings_write_string(fd, modules[i], dbus_call_context_ptr)) | |||||
| { | { | ||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| context.indent = " "; | |||||
| if (!jack_controller_settings_save_internal_options(&context, internal, dbus_call_context_ptr)) | |||||
| if (!jack_controller_settings_write_string(fd, "s>\n", dbus_call_context_ptr)) | |||||
| { | { | ||||
| goto exit_close; | goto exit_close; | ||||
| } | } | ||||
| if (!jack_controller_settings_write_string(fd, " </internal>\n", dbus_call_context_ptr)) | |||||
| { | |||||
| goto exit_close; | |||||
| } | |||||
| node_ptr = jack_slist_next(node_ptr); | |||||
| } | |||||
| if (!jack_controller_settings_write_string(fd, " </internals>\n", dbus_call_context_ptr)) | |||||
| { | |||||
| goto exit_close; | |||||
| } | } | ||||
| if (!jack_controller_settings_write_string(fd, "</jack>\n", dbus_call_context_ptr)) | if (!jack_controller_settings_write_string(fd, "</jack>\n", dbus_call_context_ptr)) | ||||
| @@ -49,6 +49,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
| #include <errno.h> | #include <errno.h> | ||||
| #include <math.h> | #include <math.h> | ||||
| #include <signal.h> | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| @@ -65,8 +66,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
| #include <semaphore.h> | #include <semaphore.h> | ||||
| #endif | #endif | ||||
| #include <signal.h> | |||||
| #define ABS(x) (((x) >= 0) ? (x) : (-(x))) | #define ABS(x) (((x) >= 0) ? (x) : (-(x))) | ||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| @@ -75,9 +74,9 @@ typedef HANDLE semaphore_t; | |||||
| typedef sem_t *semaphore_t; | typedef sem_t *semaphore_t; | ||||
| #endif | #endif | ||||
| const char *ERROR_MSG_TIMEOUT = "timed out while waiting for MIDI message"; | |||||
| const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer"; | const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer"; | ||||
| const char *ERROR_SHUTDOWN = "the JACK server has been shutdown"; | const char *ERROR_SHUTDOWN = "the JACK server has been shutdown"; | ||||
| const char *ERROR_TIMEOUT1 = "timed out while waiting for MIDI message"; | |||||
| const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve"; | const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve"; | ||||
| const char *SOURCE_PROCESS = "handle_process"; | const char *SOURCE_PROCESS = "handle_process"; | ||||
| @@ -107,7 +106,7 @@ size_t message_size; | |||||
| jack_latency_range_t out_latency_range; | jack_latency_range_t out_latency_range; | ||||
| jack_port_t *out_port; | jack_port_t *out_port; | ||||
| semaphore_t process_semaphore; | semaphore_t process_semaphore; | ||||
| int process_state; | |||||
| volatile sig_atomic_t process_state; | |||||
| char *program_name; | char *program_name; | ||||
| jack_port_t *remote_in_port; | jack_port_t *remote_in_port; | ||||
| jack_port_t *remote_out_port; | jack_port_t *remote_out_port; | ||||
| @@ -118,13 +117,6 @@ jack_time_t total_latency_time; | |||||
| size_t unexpected_messages; | size_t unexpected_messages; | ||||
| size_t xrun_count; | size_t xrun_count; | ||||
| static void signal_handler(int sig) | |||||
| { | |||||
| jack_client_close(client); | |||||
| fprintf(stderr, "signal received, exiting ...\n"); | |||||
| exit(0); | |||||
| } | |||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| char semaphore_error_msg[1024]; | char semaphore_error_msg[1024]; | ||||
| #endif | #endif | ||||
| @@ -133,7 +125,7 @@ static void | |||||
| output_error(const char *source, const char *message); | output_error(const char *source, const char *message); | ||||
| static void | static void | ||||
| output_usage(); | |||||
| output_usage(void); | |||||
| static void | static void | ||||
| set_process_error(const char *source, const char *message); | set_process_error(const char *source, const char *message); | ||||
| @@ -150,7 +142,7 @@ create_semaphore(int id) | |||||
| semaphore_t semaphore; | semaphore_t semaphore; | ||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| semaphore = CreateSemaphore(NULL, 0, 1, NULL); | |||||
| semaphore = CreateSemaphore(NULL, 0, 2, NULL); | |||||
| #elif defined (__APPLE__) | #elif defined (__APPLE__) | ||||
| char name[128]; | char name[128]; | ||||
| sprintf(name, "midi_sem_%d", id); | sprintf(name, "midi_sem_%d", id); | ||||
| @@ -201,7 +193,7 @@ die(const char *source, const char *error_message) | |||||
| } | } | ||||
| static const char * | static const char * | ||||
| get_semaphore_error() | |||||
| get_semaphore_error(void) | |||||
| { | { | ||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| @@ -247,7 +239,7 @@ handle_process(jack_nframes_t frames, void *arg) | |||||
| switch (wait_semaphore(init_semaphore, 0)) { | switch (wait_semaphore(init_semaphore, 0)) { | ||||
| case -1: | case -1: | ||||
| set_process_error(SOURCE_WAIT_SEMAPHORE, get_semaphore_error()); | set_process_error(SOURCE_WAIT_SEMAPHORE, get_semaphore_error()); | ||||
| // Fallthrough on purpose | |||||
| /* Fallthrough on purpose */ | |||||
| case 0: | case 0: | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -284,7 +276,7 @@ handle_process(jack_nframes_t frames, void *arg) | |||||
| microseconds = jack_frames_to_time(client, last_frame_time) - | microseconds = jack_frames_to_time(client, last_frame_time) - | ||||
| last_activity_time; | last_activity_time; | ||||
| if ((microseconds / 1000000) >= timeout) { | if ((microseconds / 1000000) >= timeout) { | ||||
| set_process_error(SOURCE_PROCESS, ERROR_TIMEOUT1); | |||||
| set_process_error(SOURCE_PROCESS, ERROR_MSG_TIMEOUT); | |||||
| } | } | ||||
| break; | break; | ||||
| found_message: | found_message: | ||||
| @@ -307,7 +299,7 @@ handle_process(jack_nframes_t frames, void *arg) | |||||
| if (messages_received == samples) { | if (messages_received == samples) { | ||||
| process_state = 2; | process_state = 2; | ||||
| if (! signal_semaphore(process_semaphore)) { | if (! signal_semaphore(process_semaphore)) { | ||||
| // Sigh ... | |||||
| /* Sigh ... */ | |||||
| die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | ||||
| } | } | ||||
| break; | break; | ||||
| @@ -331,11 +323,11 @@ handle_process(jack_nframes_t frames, void *arg) | |||||
| case 2: | case 2: | ||||
| /* State: finished - do nothing */ | /* State: finished - do nothing */ | ||||
| case -1: | case -1: | ||||
| /* State: error - do nothing */ | /* State: error - do nothing */ | ||||
| case -2: | |||||
| /* State: signalled - do nothing */ | |||||
| ; | ; | ||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -346,6 +338,16 @@ handle_shutdown(void *arg) | |||||
| set_process_error(SOURCE_SHUTDOWN, ERROR_SHUTDOWN); | set_process_error(SOURCE_SHUTDOWN, ERROR_SHUTDOWN); | ||||
| } | } | ||||
| static void | |||||
| handle_signal(int sig) | |||||
| { | |||||
| process_state = -2; | |||||
| if (! signal_semaphore(process_semaphore)) { | |||||
| /* Sigh ... */ | |||||
| die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | |||||
| } | |||||
| } | |||||
| static int | static int | ||||
| handle_xrun(void *arg) | handle_xrun(void *arg) | ||||
| { | { | ||||
| @@ -360,7 +362,7 @@ output_error(const char *source, const char *message) | |||||
| } | } | ||||
| static void | static void | ||||
| output_usage() | |||||
| output_usage(void) | |||||
| { | { | ||||
| fprintf(stderr, "Usage: %s [options] out-port-name in-port-name\n\n" | fprintf(stderr, "Usage: %s [options] out-port-name in-port-name\n\n" | ||||
| "\t-h, --help print program usage\n" | "\t-h, --help print program usage\n" | ||||
| @@ -392,6 +394,32 @@ parse_positive_number_arg(char *s, char *name) | |||||
| return result; | return result; | ||||
| } | } | ||||
| static int | |||||
| register_signal_handler(void (*func)(int)) | |||||
| { | |||||
| #ifdef WIN32 | |||||
| if (signal(SIGABRT, func) == SIG_ERR) { | |||||
| return 0; | |||||
| } | |||||
| #else | |||||
| if (signal(SIGQUIT, func) == SIG_ERR) { | |||||
| return 0; | |||||
| } | |||||
| if (signal(SIGHUP, func) == SIG_ERR) { | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| if (signal(SIGINT, func) == SIG_ERR) { | |||||
| return 0; | |||||
| } | |||||
| if (signal(SIGTERM, func) == SIG_ERR) { | |||||
| return 0; | |||||
| } | |||||
| return 1; | |||||
| } | |||||
| static void | static void | ||||
| set_process_error(const char *source, const char *message) | set_process_error(const char *source, const char *message) | ||||
| { | { | ||||
| @@ -399,7 +427,7 @@ set_process_error(const char *source, const char *message) | |||||
| error_message = message; | error_message = message; | ||||
| process_state = -1; | process_state = -1; | ||||
| if (! signal_semaphore(process_semaphore)) { | if (! signal_semaphore(process_semaphore)) { | ||||
| // Sigh | |||||
| /* Sigh ... */ | |||||
| output_error(source, message); | output_error(source, message); | ||||
| die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | ||||
| } | } | ||||
| @@ -565,19 +593,6 @@ main(int argc, char **argv) | |||||
| (message_size - 2) * sizeof(jack_midi_data_t)); | (message_size - 2) * sizeof(jack_midi_data_t)); | ||||
| message_2[message_size - 1] = 0xf7; | message_2[message_size - 1] = 0xf7; | ||||
| } | } | ||||
| /* install a signal handler to properly quits jack client */ | |||||
| #ifdef WIN32 | |||||
| signal(SIGINT, signal_handler); | |||||
| signal(SIGABRT, signal_handler); | |||||
| signal(SIGTERM, signal_handler); | |||||
| #else | |||||
| signal(SIGQUIT, signal_handler); | |||||
| signal(SIGTERM, signal_handler); | |||||
| signal(SIGHUP, signal_handler); | |||||
| signal(SIGINT, signal_handler); | |||||
| #endif | |||||
| client = jack_client_open(program_name, JackNullOption, NULL); | client = jack_client_open(program_name, JackNullOption, NULL); | ||||
| if (client == NULL) { | if (client == NULL) { | ||||
| error_message = "failed to open JACK client"; | error_message = "failed to open JACK client"; | ||||
| @@ -657,11 +672,21 @@ main(int argc, char **argv) | |||||
| error_source = "post_semaphore"; | error_source = "post_semaphore"; | ||||
| goto deactivate_client; | goto deactivate_client; | ||||
| } | } | ||||
| if (! register_signal_handler(handle_signal)) { | |||||
| error_message = strerror(errno); | |||||
| error_source = "register_signal_handler"; | |||||
| goto deactivate_client; | |||||
| } | |||||
| if (wait_semaphore(process_semaphore, 1) == -1) { | if (wait_semaphore(process_semaphore, 1) == -1) { | ||||
| error_message = get_semaphore_error(); | error_message = get_semaphore_error(); | ||||
| error_source = "wait_semaphore"; | error_source = "wait_semaphore"; | ||||
| goto deactivate_client; | goto deactivate_client; | ||||
| } | } | ||||
| if (! register_signal_handler(SIG_DFL)) { | |||||
| error_message = strerror(errno); | |||||
| error_source = "register_signal_handler"; | |||||
| goto deactivate_client; | |||||
| } | |||||
| if (process_state == 2) { | if (process_state == 2) { | ||||
| double average_latency = ((double) total_latency) / samples; | double average_latency = ((double) total_latency) / samples; | ||||
| double average_latency_time = total_latency_time / samples; | double average_latency_time = total_latency_time / samples; | ||||
| @@ -739,17 +764,22 @@ main(int argc, char **argv) | |||||
| latency_plot[100]); | latency_plot[100]); | ||||
| } | } | ||||
| } | } | ||||
| printf("\nMessages sent: %d\n" | |||||
| "Messages received: %d\n", | |||||
| messages_sent, messages_received); | |||||
| deactivate_client: | |||||
| jack_deactivate(client); | |||||
| /* Output this information after deactivation to prevent two threads | |||||
| from accessing data at the same time. */ | |||||
| if (process_state != 2) { | |||||
| printf("\nMessages sent: %d\nMessages received: %d\n", messages_sent, | |||||
| messages_received); | |||||
| } | |||||
| if (unexpected_messages) { | if (unexpected_messages) { | ||||
| printf("Unexpected messages received: %d\n", unexpected_messages); | printf("Unexpected messages received: %d\n", unexpected_messages); | ||||
| } | } | ||||
| if (xrun_count) { | if (xrun_count) { | ||||
| printf("Xruns: %d (messages may have been lost)\n", xrun_count); | |||||
| printf("Xruns: %d\n", xrun_count); | |||||
| } | } | ||||
| deactivate_client: | |||||
| jack_deactivate(client); | |||||
| destroy_process_semaphore: | destroy_process_semaphore: | ||||
| destroy_semaphore(process_semaphore, 1); | destroy_semaphore(process_semaphore, 1); | ||||
| destroy_init_semaphore: | destroy_init_semaphore: | ||||
| @@ -56,7 +56,7 @@ void parse_arguments(int argc, char *argv[]) | |||||
| if (argc==1) { | if (argc==1) { | ||||
| return; | return; | ||||
| } | } | ||||
| fprintf(stderr, "usage: %s [bufsize]\n", package); | |||||
| fprintf(stderr, "usage: %s\n", package); | |||||
| exit(9); | exit(9); | ||||
| } | } | ||||
| @@ -166,6 +166,8 @@ static jack_time_t jack_get_mhz (void) | |||||
| ret = sscanf(buf, "Clocking: %" SCNu64, &mhz); | ret = sscanf(buf, "Clocking: %" SCNu64, &mhz); | ||||
| #elif defined( __s390__ ) | #elif defined( __s390__ ) | ||||
| ret = sscanf(buf, "bogomips per cpu: %" SCNu64, &mhz); | ret = sscanf(buf, "bogomips per cpu: %" SCNu64, &mhz); | ||||
| #elif defined( __sh__ ) | |||||
| ret = sscanf(buf, "bogomips : %" SCNu64, &mhz); | |||||
| #else /* MIPS, ARM, alpha */ | #else /* MIPS, ARM, alpha */ | ||||
| ret = sscanf(buf, "BogoMIPS : %" SCNu64, &mhz); | ret = sscanf(buf, "BogoMIPS : %" SCNu64, &mhz); | ||||
| #endif | #endif | ||||
| @@ -24,6 +24,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| #include <alsa/asoundlib.h> | #include <alsa/asoundlib.h> | ||||
| #include "JackALSARawMidiDriver.h" | #include "JackALSARawMidiDriver.h" | ||||
| #include "JackALSARawMidiUtil.h" | |||||
| #include "JackEngineControl.h" | #include "JackEngineControl.h" | ||||
| #include "JackError.h" | #include "JackError.h" | ||||
| #include "JackMidiUtil.h" | #include "JackMidiUtil.h" | ||||
| @@ -43,6 +44,7 @@ JackALSARawMidiDriver::JackALSARawMidiDriver(const char *name, | |||||
| fPlaybackChannels = 0; | fPlaybackChannels = 0; | ||||
| input_ports = 0; | input_ports = 0; | ||||
| output_ports = 0; | output_ports = 0; | ||||
| output_port_timeouts = 0; | |||||
| poll_fds = 0; | poll_fds = 0; | ||||
| } | } | ||||
| @@ -72,7 +74,7 @@ JackALSARawMidiDriver::Attach() | |||||
| if (index == NO_PORT) { | if (index == NO_PORT) { | ||||
| jack_error("JackALSARawMidiDriver::Attach - cannot register input " | jack_error("JackALSARawMidiDriver::Attach - cannot register input " | ||||
| "port with name '%s'.", name); | "port with name '%s'.", name); | ||||
| // X: Do we need to deallocate ports? | |||||
| // XX: Do we need to deallocate ports? | |||||
| return -1; | return -1; | ||||
| } | } | ||||
| alias = input_port->GetAlias(); | alias = input_port->GetAlias(); | ||||
| @@ -99,7 +101,7 @@ JackALSARawMidiDriver::Attach() | |||||
| if (index == NO_PORT) { | if (index == NO_PORT) { | ||||
| jack_error("JackALSARawMidiDriver::Attach - cannot register " | jack_error("JackALSARawMidiDriver::Attach - cannot register " | ||||
| "output port with name '%s'.", name); | "output port with name '%s'.", name); | ||||
| // X: Do we need to deallocate ports? | |||||
| // XX: Do we need to deallocate ports? | |||||
| return -1; | return -1; | ||||
| } | } | ||||
| alias = output_port->GetAlias(); | alias = output_port->GetAlias(); | ||||
| @@ -143,15 +145,51 @@ JackALSARawMidiDriver::Execute() | |||||
| { | { | ||||
| jack_nframes_t timeout_frame = 0; | jack_nframes_t timeout_frame = 0; | ||||
| for (;;) { | for (;;) { | ||||
| jack_nframes_t process_frame; | |||||
| unsigned short revents; | |||||
| jack_nframes_t *timeout_frame_ptr; | |||||
| struct timespec timeout; | |||||
| struct timespec *timeout_ptr; | |||||
| if (! timeout_frame) { | if (! timeout_frame) { | ||||
| timeout_frame_ptr = 0; | |||||
| timeout_ptr = 0; | |||||
| } else { | } else { | ||||
| timeout_frame_ptr = &timeout_frame; | |||||
| // The timeout value is relative to the time that | |||||
| // 'GetMicroSeconds()' is called, not the time that 'poll()' is | |||||
| // called. This means that the amount of time that passes between | |||||
| // 'GetMicroSeconds()' and 'ppoll()' is time that will be lost | |||||
| // while waiting for 'poll() to timeout. | |||||
| // | |||||
| // I tried to replace the timeout with a 'timerfd' with absolute | |||||
| // times, but, strangely, it actually slowed things down, and made | |||||
| // the code a lot more complicated. | |||||
| // | |||||
| // I wonder about using the 'epoll' interface instead of 'ppoll()'. | |||||
| // The problem with the 'epoll' interface is that the timeout | |||||
| // resolution of 'epoll_wait()' is set in milliseconds. We need | |||||
| // microsecond resolution. Without microsecond resolution, we | |||||
| // impose the same jitter as USB MIDI. | |||||
| // | |||||
| // Another problem is that 'ppoll()' returns later than the wait | |||||
| // time. The problem can be minimized with high precision timers. | |||||
| timeout_ptr = &timeout; | |||||
| jack_time_t next_time = GetTimeFromFrames(timeout_frame); | |||||
| jack_time_t now = GetMicroSeconds(); | |||||
| if (next_time <= now) { | |||||
| timeout.tv_sec = 0; | |||||
| timeout.tv_nsec = 0; | |||||
| } else { | |||||
| jack_time_t wait_time = next_time - now; | |||||
| timeout.tv_sec = wait_time / 1000000; | |||||
| timeout.tv_nsec = (wait_time % 1000000) * 1000; | |||||
| } | |||||
| } | } | ||||
| if (Poll(timeout_frame_ptr) == -1) { | |||||
| int poll_result = ppoll(poll_fds, poll_fd_count, timeout_ptr, 0); | |||||
| // Getting the current frame value here allows us to use it for | |||||
| // incoming MIDI bytes. This makes sense, as the data has already | |||||
| // arrived at this point. | |||||
| jack_nframes_t current_frame = GetCurrentFrame(); | |||||
| if (poll_result == -1) { | |||||
| if (errno == EINTR) { | if (errno == EINTR) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| @@ -159,38 +197,73 @@ JackALSARawMidiDriver::Execute() | |||||
| strerror(errno)); | strerror(errno)); | ||||
| break; | break; | ||||
| } | } | ||||
| revents = poll_fds[0].revents; | |||||
| if (revents & POLLHUP) { | |||||
| // Driver is being stopped. | |||||
| break; | |||||
| jack_nframes_t port_timeout; | |||||
| timeout_frame = 0; | |||||
| if (! poll_result) { | |||||
| // No I/O events occurred. So, only handle timeout events on | |||||
| // output ports. | |||||
| for (int i = 0; i < fPlaybackChannels; i++) { | |||||
| port_timeout = output_port_timeouts[i]; | |||||
| if (port_timeout && (port_timeout <= current_frame)) { | |||||
| if (! output_ports[i]->ProcessPollEvents(false, true, | |||||
| &port_timeout)) { | |||||
| jack_error("JackALSARawMidiDriver::Execute - a fatal " | |||||
| "error occurred while processing ALSA " | |||||
| "output events."); | |||||
| goto cleanup; | |||||
| } | |||||
| output_port_timeouts[i] = port_timeout; | |||||
| } | |||||
| if (port_timeout && ((! timeout_frame) || | |||||
| (port_timeout < timeout_frame))) { | |||||
| timeout_frame = port_timeout; | |||||
| } | |||||
| } | |||||
| continue; | |||||
| } | } | ||||
| if (revents & (~ POLLIN)) { | |||||
| jack_error("JackALSARawMidiDriver::Execute - unexpected poll " | |||||
| "event on pipe file descriptor."); | |||||
| // See if it's time to shutdown. | |||||
| unsigned short revents = poll_fds[0].revents; | |||||
| if (revents) { | |||||
| if (revents & (~ POLLHUP)) { | |||||
| jack_error("JackALSARawMidiDriver::Execute - unexpected poll " | |||||
| "event on pipe file descriptor."); | |||||
| } | |||||
| break; | break; | ||||
| } | } | ||||
| timeout_frame = 0; | |||||
| for (int i = 0; i < fCaptureChannels; i++) { | |||||
| if (! input_ports[i]->ProcessALSA(&process_frame)) { | |||||
| // Handle I/O events *and* timeout events on output ports. | |||||
| for (int i = 0; i < fPlaybackChannels; i++) { | |||||
| port_timeout = output_port_timeouts[i]; | |||||
| bool timeout = port_timeout && (port_timeout <= current_frame); | |||||
| if (! output_ports[i]->ProcessPollEvents(true, timeout, | |||||
| &port_timeout)) { | |||||
| jack_error("JackALSARawMidiDriver::Execute - a fatal error " | jack_error("JackALSARawMidiDriver::Execute - a fatal error " | ||||
| "occurred while processing ALSA input events."); | |||||
| "occurred while processing ALSA output events."); | |||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| if (process_frame && ((! timeout_frame) || | |||||
| (process_frame < timeout_frame))) { | |||||
| timeout_frame = process_frame; | |||||
| output_port_timeouts[i] = port_timeout; | |||||
| if (port_timeout && ((! timeout_frame) || | |||||
| (port_timeout < timeout_frame))) { | |||||
| timeout_frame = port_timeout; | |||||
| } | } | ||||
| } | } | ||||
| for (int i = 0; i < fPlaybackChannels; i++) { | |||||
| if (! output_ports[i]->ProcessALSA(fds[0], &process_frame)) { | |||||
| // Handle I/O events on input ports. We handle these last because we | |||||
| // already computed the arrival time above, and will impose a delay on | |||||
| // the events by 'period-size' frames anyway, which gives us a bit of | |||||
| // borrowed time. | |||||
| for (int i = 0; i < fCaptureChannels; i++) { | |||||
| if (! input_ports[i]->ProcessPollEvents(current_frame)) { | |||||
| jack_error("JackALSARawMidiDriver::Execute - a fatal error " | jack_error("JackALSARawMidiDriver::Execute - a fatal error " | ||||
| "occurred while processing ALSA output events."); | |||||
| "occurred while processing ALSA input events."); | |||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| if (process_frame && ((! timeout_frame) || | |||||
| (process_frame < timeout_frame))) { | |||||
| timeout_frame = process_frame; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| cleanup: | cleanup: | ||||
| @@ -202,6 +275,21 @@ JackALSARawMidiDriver::Execute() | |||||
| return false; | return false; | ||||
| } | } | ||||
| void | |||||
| JackALSARawMidiDriver:: | |||||
| FreeDeviceInfo(std::vector<snd_rawmidi_info_t *> *in_info_list, | |||||
| std::vector<snd_rawmidi_info_t *> *out_info_list) | |||||
| { | |||||
| size_t length = in_info_list->size(); | |||||
| for (size_t i = 0; i < length; i++) { | |||||
| snd_rawmidi_info_free(in_info_list->at(i)); | |||||
| } | |||||
| length = out_info_list->size(); | |||||
| for (size_t i = 0; i < length; i++) { | |||||
| snd_rawmidi_info_free(out_info_list->at(i)); | |||||
| } | |||||
| } | |||||
| void | void | ||||
| JackALSARawMidiDriver:: | JackALSARawMidiDriver:: | ||||
| GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info, | GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info, | ||||
| @@ -315,39 +403,35 @@ JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels, | |||||
| if (! (potential_inputs || potential_outputs)) { | if (! (potential_inputs || potential_outputs)) { | ||||
| jack_error("JackALSARawMidiDriver::Open - no ALSA raw MIDI input or " | jack_error("JackALSARawMidiDriver::Open - no ALSA raw MIDI input or " | ||||
| "output ports found."); | "output ports found."); | ||||
| FreeDeviceInfo(&in_info_list, &out_info_list); | |||||
| return -1; | return -1; | ||||
| } | } | ||||
| // XXX: Can't use auto_ptr here. These are arrays, and require the | |||||
| // delete[] operator. | |||||
| std::auto_ptr<JackALSARawMidiInputPort *> input_ptr; | |||||
| size_t num_inputs = 0; | |||||
| size_t num_outputs = 0; | |||||
| if (potential_inputs) { | if (potential_inputs) { | ||||
| input_ports = new JackALSARawMidiInputPort *[potential_inputs]; | |||||
| input_ptr.reset(input_ports); | |||||
| try { | |||||
| input_ports = new JackALSARawMidiInputPort *[potential_inputs]; | |||||
| } catch (std::exception e) { | |||||
| jack_error("JackALSARawMidiDriver::Open - while creating input " | |||||
| "port array: %s", e.what()); | |||||
| FreeDeviceInfo(&in_info_list, &out_info_list); | |||||
| return -1; | |||||
| } | |||||
| } | } | ||||
| std::auto_ptr<JackALSARawMidiOutputPort *> output_ptr; | |||||
| if (potential_outputs) { | if (potential_outputs) { | ||||
| output_ports = new JackALSARawMidiOutputPort *[potential_outputs]; | |||||
| output_ptr.reset(output_ports); | |||||
| try { | |||||
| output_ports = new JackALSARawMidiOutputPort *[potential_outputs]; | |||||
| } catch (std::exception e) { | |||||
| jack_error("JackALSARawMidiDriver::Open - while creating output " | |||||
| "port array: %s", e.what()); | |||||
| FreeDeviceInfo(&in_info_list, &out_info_list); | |||||
| goto delete_input_ports; | |||||
| } | |||||
| } | } | ||||
| size_t num_inputs = 0; | |||||
| size_t num_outputs = 0; | |||||
| for (size_t i = 0; i < potential_inputs; i++) { | for (size_t i = 0; i < potential_inputs; i++) { | ||||
| snd_rawmidi_info_t *info = in_info_list.at(i); | snd_rawmidi_info_t *info = in_info_list.at(i); | ||||
| try { | try { | ||||
| input_ports[num_inputs] = new JackALSARawMidiInputPort(info, i); | input_ports[num_inputs] = new JackALSARawMidiInputPort(info, i); | ||||
| jack_info("JackALSARawMidiDriver::Open - Input port: card=%d, " | |||||
| "device=%d, subdevice=%d, id=%s, name=%s, subdevice " | |||||
| "name=%s", | |||||
| snd_rawmidi_info_get_card(info), | |||||
| snd_rawmidi_info_get_device(info), | |||||
| snd_rawmidi_info_get_subdevice(info), | |||||
| snd_rawmidi_info_get_id(info), | |||||
| snd_rawmidi_info_get_name(info), | |||||
| snd_rawmidi_info_get_subdevice_name(info)); | |||||
| num_inputs++; | num_inputs++; | ||||
| } catch (std::exception e) { | } catch (std::exception e) { | ||||
| jack_error("JackALSARawMidiDriver::Open - while creating new " | jack_error("JackALSARawMidiDriver::Open - while creating new " | ||||
| @@ -359,17 +443,6 @@ JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels, | |||||
| snd_rawmidi_info_t *info = out_info_list.at(i); | snd_rawmidi_info_t *info = out_info_list.at(i); | ||||
| try { | try { | ||||
| output_ports[num_outputs] = new JackALSARawMidiOutputPort(info, i); | output_ports[num_outputs] = new JackALSARawMidiOutputPort(info, i); | ||||
| jack_info("JackALSARawMidiDriver::Open - Output port: card=%d, " | |||||
| "device=%d, subdevice=%d, id=%s, name=%s, subdevice " | |||||
| "name=%s", | |||||
| snd_rawmidi_info_get_card(info), | |||||
| snd_rawmidi_info_get_device(info), | |||||
| snd_rawmidi_info_get_subdevice(info), | |||||
| snd_rawmidi_info_get_id(info), | |||||
| snd_rawmidi_info_get_name(info), | |||||
| snd_rawmidi_info_get_subdevice_name(info)); | |||||
| num_outputs++; | num_outputs++; | ||||
| } catch (std::exception e) { | } catch (std::exception e) { | ||||
| jack_error("JackALSARawMidiDriver::Open - while creating new " | jack_error("JackALSARawMidiDriver::Open - while creating new " | ||||
| @@ -377,49 +450,33 @@ JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels, | |||||
| } | } | ||||
| snd_rawmidi_info_free(info); | snd_rawmidi_info_free(info); | ||||
| } | } | ||||
| if (num_inputs || num_outputs) { | |||||
| if (! JackMidiDriver::Open(capturing, playing, num_inputs, num_outputs, | |||||
| monitor, capture_driver_name, | |||||
| playback_driver_name, capture_latency, | |||||
| playback_latency)) { | |||||
| if (potential_inputs) { | |||||
| input_ptr.release(); | |||||
| } | |||||
| if (potential_outputs) { | |||||
| output_ptr.release(); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| jack_error("JackALSARawMidiDriver::Open - JackMidiDriver::Open error"); | |||||
| } else { | |||||
| if (! (num_inputs || num_outputs)) { | |||||
| jack_error("JackALSARawMidiDriver::Open - none of the potential " | jack_error("JackALSARawMidiDriver::Open - none of the potential " | ||||
| "inputs or outputs were successfully opened."); | "inputs or outputs were successfully opened."); | ||||
| } | |||||
| Close(); | |||||
| return -1; | |||||
| } | |||||
| int | |||||
| JackALSARawMidiDriver::Poll(const jack_nframes_t *wakeup_frame) | |||||
| { | |||||
| struct timespec timeout; | |||||
| struct timespec *timeout_ptr; | |||||
| if (! wakeup_frame) { | |||||
| timeout_ptr = 0; | |||||
| } else if (JackMidiDriver::Open(capturing, playing, num_inputs, | |||||
| num_outputs, monitor, capture_driver_name, | |||||
| playback_driver_name, capture_latency, | |||||
| playback_latency)) { | |||||
| jack_error("JackALSARawMidiDriver::Open - JackMidiDriver::Open error"); | |||||
| } else { | } else { | ||||
| timeout_ptr = &timeout; | |||||
| jack_time_t next_time = GetTimeFromFrames(*wakeup_frame); | |||||
| jack_time_t now = GetMicroSeconds(); | |||||
| if (next_time <= now) { | |||||
| timeout.tv_sec = 0; | |||||
| timeout.tv_nsec = 0; | |||||
| } else { | |||||
| jack_time_t wait_time = next_time - now; | |||||
| timeout.tv_sec = wait_time / 1000000; | |||||
| timeout.tv_nsec = (wait_time % 1000000) * 1000; | |||||
| return 0; | |||||
| } | |||||
| if (output_ports) { | |||||
| for (size_t i = 0; i < num_outputs; i++) { | |||||
| delete output_ports[i]; | |||||
| } | |||||
| delete[] output_ports; | |||||
| output_ports = 0; | |||||
| } | |||||
| delete_input_ports: | |||||
| if (input_ports) { | |||||
| for (size_t i = 0; i < num_inputs; i++) { | |||||
| delete input_ports[i]; | |||||
| } | } | ||||
| delete[] input_ports; | |||||
| input_ports = 0; | |||||
| } | } | ||||
| return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0); | |||||
| return -1; | |||||
| } | } | ||||
| int | int | ||||
| @@ -450,39 +507,27 @@ JackALSARawMidiDriver::Start() | |||||
| } | } | ||||
| try { | try { | ||||
| poll_fds = new pollfd[poll_fd_count]; | poll_fds = new pollfd[poll_fd_count]; | ||||
| } catch (std::bad_alloc e) { | |||||
| } catch (std::exception e) { | |||||
| jack_error("JackALSARawMidiDriver::Start - creating poll descriptor " | jack_error("JackALSARawMidiDriver::Start - creating poll descriptor " | ||||
| "structures failed: %s", e.what()); | "structures failed: %s", e.what()); | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| int flags; | |||||
| if (fPlaybackChannels) { | |||||
| try { | |||||
| output_port_timeouts = new jack_nframes_t[fPlaybackChannels]; | |||||
| } catch (std::exception e) { | |||||
| jack_error("JackALSARawMidiDriver::Start - creating array for " | |||||
| "output port timeout values failed: %s", e.what()); | |||||
| goto free_poll_descriptors; | |||||
| } | |||||
| } | |||||
| struct pollfd *poll_fd_iter; | struct pollfd *poll_fd_iter; | ||||
| if (pipe(fds) == -1) { | |||||
| try { | |||||
| CreateNonBlockingPipe(fds); | |||||
| } catch (std::exception e) { | |||||
| jack_error("JackALSARawMidiDriver::Start - while creating wake pipe: " | jack_error("JackALSARawMidiDriver::Start - while creating wake pipe: " | ||||
| "%s", strerror(errno)); | |||||
| goto free_poll_descriptors; | |||||
| } | |||||
| flags = fcntl(fds[0], F_GETFL); | |||||
| if (flags == -1) { | |||||
| jack_error("JackALSARawMidiDriver::Start = while getting flags for " | |||||
| "read file descriptor: %s", strerror(errno)); | |||||
| goto close_fds; | |||||
| } | |||||
| if (fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) == -1) { | |||||
| jack_error("JackALSARawMidiDriver::Start - while setting non-blocking " | |||||
| "mode for read file descriptor: %s", strerror(errno)); | |||||
| goto close_fds; | |||||
| } | |||||
| flags = fcntl(fds[1], F_GETFL); | |||||
| if (flags == -1) { | |||||
| jack_error("JackALSARawMidiDriver::Start = while getting flags for " | |||||
| "write file descriptor: %s", strerror(errno)); | |||||
| goto close_fds; | |||||
| } | |||||
| if (fcntl(fds[1], F_SETFL, flags | O_NONBLOCK) == -1) { | |||||
| jack_error("JackALSARawMidiDriver::Start - while setting non-blocking " | |||||
| "mode for write file descriptor: %s", strerror(errno)); | |||||
| goto close_fds; | |||||
| "%s", e.what()); | |||||
| goto free_output_port_timeouts; | |||||
| } | } | ||||
| poll_fds[0].events = POLLERR | POLLIN | POLLNVAL; | poll_fds[0].events = POLLERR | POLLIN | POLLNVAL; | ||||
| poll_fds[0].fd = fds[0]; | poll_fds[0].fd = fds[0]; | ||||
| @@ -496,6 +541,7 @@ JackALSARawMidiDriver::Start() | |||||
| JackALSARawMidiOutputPort *output_port = output_ports[i]; | JackALSARawMidiOutputPort *output_port = output_ports[i]; | ||||
| output_port->PopulatePollDescriptors(poll_fd_iter); | output_port->PopulatePollDescriptors(poll_fd_iter); | ||||
| poll_fd_iter += output_port->GetPollDescriptorCount(); | poll_fd_iter += output_port->GetPollDescriptorCount(); | ||||
| output_port_timeouts[i] = 0; | |||||
| } | } | ||||
| jack_info("JackALSARawMidiDriver::Start - starting ALSA thread ..."); | jack_info("JackALSARawMidiDriver::Start - starting ALSA thread ..."); | ||||
| @@ -508,11 +554,13 @@ JackALSARawMidiDriver::Start() | |||||
| } | } | ||||
| jack_error("JackALSARawMidiDriver::Start - failed to start MIDI " | jack_error("JackALSARawMidiDriver::Start - failed to start MIDI " | ||||
| "processing thread."); | "processing thread."); | ||||
| close_fds: | |||||
| close(fds[1]); | |||||
| DestroyNonBlockingPipe(fds); | |||||
| fds[1] = -1; | fds[1] = -1; | ||||
| close(fds[0]); | |||||
| fds[0] = -1; | fds[0] = -1; | ||||
| free_output_port_timeouts: | |||||
| delete[] output_port_timeouts; | |||||
| output_port_timeouts = 0; | |||||
| free_poll_descriptors: | free_poll_descriptors: | ||||
| delete[] poll_fds; | delete[] poll_fds; | ||||
| poll_fds = 0; | poll_fds = 0; | ||||
| @@ -548,6 +596,10 @@ JackALSARawMidiDriver::Stop() | |||||
| close(fds[0]); | close(fds[0]); | ||||
| fds[0] = -1; | fds[0] = -1; | ||||
| } | } | ||||
| if (output_port_timeouts) { | |||||
| delete[] output_port_timeouts; | |||||
| output_port_timeouts = 0; | |||||
| } | |||||
| if (poll_fds) { | if (poll_fds) { | ||||
| delete[] poll_fds; | delete[] poll_fds; | ||||
| poll_fds = 0; | poll_fds = 0; | ||||
| @@ -563,10 +615,8 @@ int | |||||
| JackALSARawMidiDriver::Write() | JackALSARawMidiDriver::Write() | ||||
| { | { | ||||
| jack_nframes_t buffer_size = fEngineControl->fBufferSize; | jack_nframes_t buffer_size = fEngineControl->fBufferSize; | ||||
| int write_fd = fds[1]; | |||||
| for (int i = 0; i < fPlaybackChannels; i++) { | for (int i = 0; i < fPlaybackChannels; i++) { | ||||
| if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size, | |||||
| write_fd)) { | |||||
| if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size)) { | |||||
| return -1; | return -1; | ||||
| } | } | ||||
| } | } | ||||
| @@ -40,10 +40,15 @@ namespace Jack { | |||||
| int fds[2]; | int fds[2]; | ||||
| JackALSARawMidiInputPort **input_ports; | JackALSARawMidiInputPort **input_ports; | ||||
| JackALSARawMidiOutputPort **output_ports; | JackALSARawMidiOutputPort **output_ports; | ||||
| jack_nframes_t *output_port_timeouts; | |||||
| nfds_t poll_fd_count; | nfds_t poll_fd_count; | ||||
| struct pollfd *poll_fds; | struct pollfd *poll_fds; | ||||
| JackThread *thread; | JackThread *thread; | ||||
| void | |||||
| FreeDeviceInfo(std::vector<snd_rawmidi_info_t *> *in_info_list, | |||||
| std::vector<snd_rawmidi_info_t *> *out_info_list); | |||||
| void | void | ||||
| GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info, | GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info, | ||||
| std::vector<snd_rawmidi_info_t *> *info_list); | std::vector<snd_rawmidi_info_t *> *info_list); | ||||
| @@ -52,9 +57,6 @@ namespace Jack { | |||||
| HandleALSAError(const char *driver_func, const char *alsa_func, | HandleALSAError(const char *driver_func, const char *alsa_func, | ||||
| int code); | int code); | ||||
| int | |||||
| Poll(const jack_nframes_t *wakeup_frame); | |||||
| public: | public: | ||||
| JackALSARawMidiDriver(const char *name, const char *alias, | JackALSARawMidiDriver(const char *name, const char *alias, | ||||
| @@ -17,6 +17,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| */ | */ | ||||
| #include <cassert> | |||||
| #include <memory> | #include <memory> | ||||
| #include "JackALSARawMidiInputPort.h" | #include "JackALSARawMidiInputPort.h" | ||||
| @@ -28,7 +29,7 @@ JackALSARawMidiInputPort::JackALSARawMidiInputPort(snd_rawmidi_info_t *info, | |||||
| size_t index, | size_t index, | ||||
| size_t max_bytes, | size_t max_bytes, | ||||
| size_t max_messages): | size_t max_messages): | ||||
| JackALSARawMidiPort(info, index) | |||||
| JackALSARawMidiPort(info, index, POLLIN) | |||||
| { | { | ||||
| alsa_event = 0; | alsa_event = 0; | ||||
| jack_event = 0; | jack_event = 0; | ||||
| @@ -53,91 +54,69 @@ JackALSARawMidiInputPort::~JackALSARawMidiInputPort() | |||||
| delete write_queue; | delete write_queue; | ||||
| } | } | ||||
| jack_nframes_t | |||||
| JackALSARawMidiInputPort::EnqueueALSAEvent() | |||||
| { | |||||
| switch (raw_queue->EnqueueEvent(alsa_event)) { | |||||
| case JackMidiWriteQueue::BUFFER_FULL: | |||||
| // Processing events early might free up some space in the raw queue. | |||||
| raw_queue->Process(); | |||||
| switch (raw_queue->EnqueueEvent(alsa_event)) { | |||||
| case JackMidiWriteQueue::BUFFER_TOO_SMALL: | |||||
| jack_error("JackALSARawMidiInputPort::Process - **BUG** " | |||||
| "JackMidiRawInputWriteQueue::EnqueueEvent returned " | |||||
| "`BUFFER_FULL` and then returned `BUFFER_TOO_SMALL` " | |||||
| "after a `Process()` call."); | |||||
| // Fallthrough on purpose | |||||
| case JackMidiWriteQueue::OK: | |||||
| return 0; | |||||
| default: | |||||
| ; | |||||
| } | |||||
| break; | |||||
| case JackMidiWriteQueue::BUFFER_TOO_SMALL: | |||||
| jack_error("JackALSARawMidiInputPort::Execute - The thread queue " | |||||
| "couldn't enqueue a %d-byte packet. Dropping event.", | |||||
| alsa_event->size); | |||||
| // Fallthrough on purpose | |||||
| case JackMidiWriteQueue::OK: | |||||
| return 0; | |||||
| default: | |||||
| ; | |||||
| } | |||||
| jack_nframes_t alsa_time = alsa_event->time; | |||||
| jack_nframes_t next_time = raw_queue->Process(); | |||||
| return (next_time < alsa_time) ? next_time : alsa_time; | |||||
| } | |||||
| bool | |||||
| JackALSARawMidiInputPort::ProcessALSA(jack_nframes_t *frame) | |||||
| { | |||||
| unsigned short revents; | |||||
| if (! ProcessPollEvents(&revents)) { | |||||
| return false; | |||||
| } | |||||
| if (alsa_event) { | |||||
| *frame = EnqueueALSAEvent(); | |||||
| if (*frame) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| if (revents & POLLIN) { | |||||
| for (alsa_event = receive_queue->DequeueEvent(); alsa_event; | |||||
| alsa_event = receive_queue->DequeueEvent()) { | |||||
| *frame = EnqueueALSAEvent(); | |||||
| if (*frame) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| *frame = raw_queue->Process(); | |||||
| return true; | |||||
| } | |||||
| bool | bool | ||||
| JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, | JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, | ||||
| jack_nframes_t frames) | jack_nframes_t frames) | ||||
| { | { | ||||
| write_queue->ResetMidiBuffer(port_buffer, frames); | write_queue->ResetMidiBuffer(port_buffer, frames); | ||||
| bool dequeued = false; | |||||
| if (! jack_event) { | if (! jack_event) { | ||||
| jack_event = thread_queue->DequeueEvent(); | |||||
| goto dequeue_event; | |||||
| } | } | ||||
| for (; jack_event; jack_event = thread_queue->DequeueEvent()) { | |||||
| // We add `frames` so that MIDI events align with audio as closely as | |||||
| // possible. | |||||
| for (;;) { | |||||
| switch (write_queue->EnqueueEvent(jack_event, frames)) { | switch (write_queue->EnqueueEvent(jack_event, frames)) { | ||||
| case JackMidiWriteQueue::BUFFER_TOO_SMALL: | case JackMidiWriteQueue::BUFFER_TOO_SMALL: | ||||
| jack_error("JackALSARawMidiInputPort::ProcessJack - The write " | jack_error("JackALSARawMidiInputPort::ProcessJack - The write " | ||||
| "queue couldn't enqueue a %d-byte event. Dropping " | "queue couldn't enqueue a %d-byte event. Dropping " | ||||
| "event.", jack_event->size); | "event.", jack_event->size); | ||||
| // Fallthrough on purpose | |||||
| // Fallthrough on purpose. | |||||
| case JackMidiWriteQueue::OK: | case JackMidiWriteQueue::OK: | ||||
| continue; | |||||
| break; | |||||
| default: | default: | ||||
| ; | |||||
| goto trigger_queue_event; | |||||
| } | |||||
| dequeue_event: | |||||
| jack_event = thread_queue->DequeueEvent(); | |||||
| if (! jack_event) { | |||||
| break; | |||||
| } | |||||
| dequeued = true; | |||||
| } | |||||
| trigger_queue_event: | |||||
| return dequeued ? TriggerQueueEvent() : true; | |||||
| } | |||||
| bool | |||||
| JackALSARawMidiInputPort::ProcessPollEvents(jack_nframes_t current_frame) | |||||
| { | |||||
| if (GetQueuePollEvent() == -1) { | |||||
| return false; | |||||
| } | |||||
| int io_event = GetIOPollEvent(); | |||||
| switch (io_event) { | |||||
| case -1: | |||||
| return false; | |||||
| case 1: | |||||
| alsa_event = receive_queue->DequeueEvent(); | |||||
| } | |||||
| if (alsa_event) { | |||||
| size_t size = alsa_event->size; | |||||
| size_t space = raw_queue->GetAvailableSpace(); | |||||
| bool enough_room = space >= size; | |||||
| if (enough_room) { | |||||
| assert(raw_queue->EnqueueEvent(current_frame, size, | |||||
| alsa_event->buffer) == | |||||
| JackMidiWriteQueue::OK); | |||||
| alsa_event = 0; | |||||
| } else if (space) { | |||||
| assert(raw_queue->EnqueueEvent(current_frame, space, | |||||
| alsa_event->buffer) == | |||||
| JackMidiWriteQueue::OK); | |||||
| alsa_event->buffer += space; | |||||
| alsa_event->size -= space; | |||||
| } | } | ||||
| break; | |||||
| SetIOEventsEnabled(enough_room); | |||||
| } | } | ||||
| raw_queue->Process(); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -39,21 +39,19 @@ namespace Jack { | |||||
| JackMidiAsyncQueue *thread_queue; | JackMidiAsyncQueue *thread_queue; | ||||
| JackMidiBufferWriteQueue *write_queue; | JackMidiBufferWriteQueue *write_queue; | ||||
| jack_nframes_t | |||||
| EnqueueALSAEvent(); | |||||
| public: | public: | ||||
| JackALSARawMidiInputPort(snd_rawmidi_info_t *info, size_t index, | JackALSARawMidiInputPort(snd_rawmidi_info_t *info, size_t index, | ||||
| size_t max_bytes=4096, | size_t max_bytes=4096, | ||||
| size_t max_messages=1024); | size_t max_messages=1024); | ||||
| ~JackALSARawMidiInputPort(); | ~JackALSARawMidiInputPort(); | ||||
| bool | bool | ||||
| ProcessALSA(jack_nframes_t *frame); | |||||
| ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); | |||||
| bool | bool | ||||
| ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); | |||||
| ProcessPollEvents(jack_nframes_t current_frame); | |||||
| }; | }; | ||||
| @@ -17,6 +17,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| */ | */ | ||||
| #include <cassert> | |||||
| #include <memory> | #include <memory> | ||||
| #include "JackALSARawMidiOutputPort.h" | #include "JackALSARawMidiOutputPort.h" | ||||
| @@ -25,15 +26,15 @@ using Jack::JackALSARawMidiOutputPort; | |||||
| JackALSARawMidiOutputPort::JackALSARawMidiOutputPort(snd_rawmidi_info_t *info, | JackALSARawMidiOutputPort::JackALSARawMidiOutputPort(snd_rawmidi_info_t *info, | ||||
| size_t index, | size_t index, | ||||
| size_t max_bytes_per_poll, | |||||
| size_t max_bytes, | size_t max_bytes, | ||||
| size_t max_messages): | size_t max_messages): | ||||
| JackALSARawMidiPort(info, index) | |||||
| JackALSARawMidiPort(info, index, POLLOUT) | |||||
| { | { | ||||
| alsa_event = 0; | alsa_event = 0; | ||||
| blocked = false; | |||||
| read_queue = new JackMidiBufferReadQueue(); | read_queue = new JackMidiBufferReadQueue(); | ||||
| std::auto_ptr<JackMidiBufferReadQueue> read_ptr(read_queue); | std::auto_ptr<JackMidiBufferReadQueue> read_ptr(read_queue); | ||||
| send_queue = new JackALSARawMidiSendQueue(rawmidi); | |||||
| send_queue = new JackALSARawMidiSendQueue(rawmidi, max_bytes_per_poll); | |||||
| std::auto_ptr<JackALSARawMidiSendQueue> send_ptr(send_queue); | std::auto_ptr<JackALSARawMidiSendQueue> send_ptr(send_queue); | ||||
| thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); | thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); | ||||
| std::auto_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue); | std::auto_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue); | ||||
| @@ -52,121 +53,94 @@ JackALSARawMidiOutputPort::~JackALSARawMidiOutputPort() | |||||
| delete thread_queue; | delete thread_queue; | ||||
| } | } | ||||
| jack_midi_event_t * | |||||
| JackALSARawMidiOutputPort::DequeueALSAEvent(int read_fd) | |||||
| bool | |||||
| JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, | |||||
| jack_nframes_t frames) | |||||
| { | { | ||||
| jack_midi_event_t *event = thread_queue->DequeueEvent(); | |||||
| if (event) { | |||||
| char c; | |||||
| ssize_t result = read(read_fd, &c, 1); | |||||
| if (! result) { | |||||
| jack_error("JackALSARawMidiOutputPort::DequeueALSAEvent - **BUG** " | |||||
| "An event was dequeued from the thread queue, but no " | |||||
| "byte was available for reading from the pipe file " | |||||
| "descriptor."); | |||||
| } else if (result < 0) { | |||||
| jack_error("JackALSARawMidiOutputPort::DequeueALSAEvent - error " | |||||
| "reading a byte from the pipe file descriptor: %s", | |||||
| strerror(errno)); | |||||
| read_queue->ResetMidiBuffer(port_buffer); | |||||
| bool enqueued = false; | |||||
| for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; | |||||
| event = read_queue->DequeueEvent()) { | |||||
| switch (thread_queue->EnqueueEvent(event, frames)) { | |||||
| case JackMidiWriteQueue::BUFFER_FULL: | |||||
| jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread " | |||||
| "queue doesn't have enough room to enqueue a %d-byte " | |||||
| "event. Dropping event.", event->size); | |||||
| continue; | |||||
| case JackMidiWriteQueue::BUFFER_TOO_SMALL: | |||||
| jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread " | |||||
| "queue is too small to enqueue a %d-byte event. " | |||||
| "Dropping event.", event->size); | |||||
| continue; | |||||
| default: | |||||
| enqueued = true; | |||||
| } | } | ||||
| } | } | ||||
| return event; | |||||
| return enqueued ? TriggerQueueEvent() : true; | |||||
| } | } | ||||
| bool | bool | ||||
| JackALSARawMidiOutputPort::ProcessALSA(int read_fd, jack_nframes_t *frame) | |||||
| JackALSARawMidiOutputPort::ProcessPollEvents(bool handle_output, bool timeout, | |||||
| jack_nframes_t *frame) | |||||
| { | { | ||||
| unsigned short revents; | |||||
| if (! ProcessPollEvents(&revents)) { | |||||
| int io_event; | |||||
| int queue_event; | |||||
| send_queue->ResetPollByteCount(); | |||||
| if (! handle_output) { | |||||
| assert(timeout); | |||||
| goto process_raw_queue; | |||||
| } | |||||
| io_event = GetIOPollEvent(); | |||||
| if (io_event == -1) { | |||||
| return false; | return false; | ||||
| } | } | ||||
| if (blocked) { | |||||
| if (! (revents & POLLOUT)) { | |||||
| *frame = 0; | |||||
| return true; | |||||
| } | |||||
| blocked = false; | |||||
| queue_event = GetQueuePollEvent(); | |||||
| if (queue_event == -1) { | |||||
| return false; | |||||
| } | |||||
| if (io_event || timeout) { | |||||
| process_raw_queue: | |||||
| // We call the 'Process' event early because there are events waiting | |||||
| // to be processed that either need to be sent now, or before now. | |||||
| raw_queue->Process(); | |||||
| } else if (! queue_event) { | |||||
| return true; | |||||
| } | } | ||||
| if (! alsa_event) { | if (! alsa_event) { | ||||
| alsa_event = DequeueALSAEvent(read_fd); | |||||
| alsa_event = thread_queue->DequeueEvent(); | |||||
| } | } | ||||
| for (; alsa_event; alsa_event = DequeueALSAEvent(read_fd)) { | |||||
| for (; alsa_event; alsa_event = thread_queue->DequeueEvent()) { | |||||
| switch (raw_queue->EnqueueEvent(alsa_event)) { | switch (raw_queue->EnqueueEvent(alsa_event)) { | ||||
| case JackMidiWriteQueue::BUFFER_FULL: | |||||
| // Try to free up some space by processing events early. | |||||
| raw_queue->Process(); | |||||
| switch (raw_queue->EnqueueEvent(alsa_event)) { | |||||
| case JackMidiWriteQueue::BUFFER_TOO_SMALL: | |||||
| jack_error("JackALSARawMidiOutputPort::ProcessALSA - **BUG** " | |||||
| "JackMidiRawOutputWriteQueue::EnqueueEvent " | |||||
| "returned `BUFFER_FULL`, and then returned " | |||||
| "`BUFFER_TOO_SMALL` after a Process() call."); | |||||
| // Fallthrough on purpose | |||||
| case JackMidiWriteQueue::OK: | |||||
| continue; | |||||
| default: | |||||
| ; | |||||
| } | |||||
| goto process_events; | |||||
| case JackMidiWriteQueue::BUFFER_TOO_SMALL: | case JackMidiWriteQueue::BUFFER_TOO_SMALL: | ||||
| jack_error("JackALSARawMidiOutputPort::ProcessALSA - The raw " | |||||
| jack_error("JackALSARawMidiOutputPort::ProcessQueues - The raw " | |||||
| "output queue couldn't enqueue a %d-byte event. " | "output queue couldn't enqueue a %d-byte event. " | ||||
| "Dropping event.", alsa_event->size); | "Dropping event.", alsa_event->size); | ||||
| // Fallthrough on purpose | |||||
| // Fallthrough on purpose. | |||||
| case JackMidiWriteQueue::OK: | case JackMidiWriteQueue::OK: | ||||
| continue; | continue; | ||||
| default: | default: | ||||
| ; | ; | ||||
| } | } | ||||
| break; | |||||
| // Try to free up some space by processing events early. | |||||
| *frame = raw_queue->Process(); | |||||
| switch (raw_queue->EnqueueEvent(alsa_event)) { | |||||
| case JackMidiWriteQueue::BUFFER_FULL: | |||||
| goto set_io_events; | |||||
| case JackMidiWriteQueue::BUFFER_TOO_SMALL: | |||||
| // This shouldn't happen. | |||||
| assert(false); | |||||
| default: | |||||
| ; | |||||
| } | |||||
| } | } | ||||
| process_events: | |||||
| *frame = raw_queue->Process(); | *frame = raw_queue->Process(); | ||||
| blocked = send_queue->IsBlocked(); | |||||
| set_io_events: | |||||
| bool blocked = send_queue->IsBlocked(); | |||||
| SetIOEventsEnabled(blocked); | |||||
| if (blocked) { | if (blocked) { | ||||
| jack_info("JackALSARawMidiOutputPort::ProcessALSA - MIDI port is " | |||||
| "blocked"); | |||||
| SetPollEventMask(POLLERR | POLLNVAL | POLLOUT); | |||||
| *frame = 0; | *frame = 0; | ||||
| } else { | |||||
| SetPollEventMask(POLLERR | POLLNVAL); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, | |||||
| jack_nframes_t frames, int write_fd) | |||||
| { | |||||
| read_queue->ResetMidiBuffer(port_buffer); | |||||
| for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; | |||||
| event = read_queue->DequeueEvent()) { | |||||
| if (event->size > thread_queue->GetAvailableSpace()) { | |||||
| jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread " | |||||
| "queue doesn't have enough room to enqueue a %d-byte " | |||||
| "event. Dropping event.", event->size); | |||||
| continue; | |||||
| } | |||||
| char c = 1; | |||||
| ssize_t result = write(write_fd, &c, 1); | |||||
| assert(result <= 1); | |||||
| if (result < 0) { | |||||
| jack_error("JackALSARawMidiOutputPort::ProcessJack - error " | |||||
| "writing a byte to the pipe file descriptor: %s", | |||||
| strerror(errno)); | |||||
| return false; | |||||
| } | |||||
| if (! result) { | |||||
| // Recoverable. | |||||
| jack_error("JackALSARawMidiOutputPort::ProcessJack - Couldn't " | |||||
| "write a byte to the pipe file descriptor. Dropping " | |||||
| "event."); | |||||
| } else { | |||||
| assert(thread_queue->EnqueueEvent(event, frames) == | |||||
| JackMidiWriteQueue::OK); | |||||
| } | |||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -33,28 +33,26 @@ namespace Jack { | |||||
| private: | private: | ||||
| jack_midi_event_t *alsa_event; | jack_midi_event_t *alsa_event; | ||||
| bool blocked; | |||||
| JackMidiRawOutputWriteQueue *raw_queue; | JackMidiRawOutputWriteQueue *raw_queue; | ||||
| JackMidiBufferReadQueue *read_queue; | JackMidiBufferReadQueue *read_queue; | ||||
| JackALSARawMidiSendQueue *send_queue; | JackALSARawMidiSendQueue *send_queue; | ||||
| JackMidiAsyncQueue *thread_queue; | JackMidiAsyncQueue *thread_queue; | ||||
| jack_midi_event_t * | |||||
| DequeueALSAEvent(int read_fd); | |||||
| public: | public: | ||||
| JackALSARawMidiOutputPort(snd_rawmidi_info_t *info, size_t index, | JackALSARawMidiOutputPort(snd_rawmidi_info_t *info, size_t index, | ||||
| size_t max_bytes_per_poll=3, | |||||
| size_t max_bytes=4096, | size_t max_bytes=4096, | ||||
| size_t max_messages=1024); | size_t max_messages=1024); | ||||
| ~JackALSARawMidiOutputPort(); | ~JackALSARawMidiOutputPort(); | ||||
| bool | bool | ||||
| ProcessALSA(int read_fd, jack_nframes_t *frame); | |||||
| ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); | |||||
| bool | bool | ||||
| ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames, | |||||
| int write_fd); | |||||
| ProcessPollEvents(bool handle_output, bool timeout, | |||||
| jack_nframes_t *frame); | |||||
| }; | }; | ||||
| @@ -17,16 +17,18 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| */ | */ | ||||
| #include <cassert> | |||||
| #include <stdexcept> | #include <stdexcept> | ||||
| #include <string> | #include <string> | ||||
| #include "JackALSARawMidiPort.h" | #include "JackALSARawMidiPort.h" | ||||
| #include "JackALSARawMidiUtil.h" | |||||
| #include "JackError.h" | #include "JackError.h" | ||||
| using Jack::JackALSARawMidiPort; | using Jack::JackALSARawMidiPort; | ||||
| JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info, | JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info, | ||||
| size_t index) | |||||
| size_t index, unsigned short io_mask) | |||||
| { | { | ||||
| int card = snd_rawmidi_info_get_card(info); | int card = snd_rawmidi_info_get_card(info); | ||||
| unsigned int device = snd_rawmidi_info_get_device(info); | unsigned int device = snd_rawmidi_info_get_device(info); | ||||
| @@ -50,7 +52,6 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info, | |||||
| name_suffix = "in"; | name_suffix = "in"; | ||||
| out = 0; | out = 0; | ||||
| } | } | ||||
| const char *device_name; | |||||
| const char *func; | const char *func; | ||||
| int code = snd_rawmidi_open(in, out, device_id, SND_RAWMIDI_NONBLOCK); | int code = snd_rawmidi_open(in, out, device_id, SND_RAWMIDI_NONBLOCK); | ||||
| if (code) { | if (code) { | ||||
| @@ -78,7 +79,7 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info, | |||||
| goto free_params; | goto free_params; | ||||
| } | } | ||||
| // Smallest valid buffer size. | |||||
| // Minimum buffer size allowed by ALSA | |||||
| code = snd_rawmidi_params_set_buffer_size(rawmidi, params, 32); | code = snd_rawmidi_params_set_buffer_size(rawmidi, params, 32); | ||||
| if (code) { | if (code) { | ||||
| error_message = snd_strerror(code); | error_message = snd_strerror(code); | ||||
| @@ -99,15 +100,23 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info, | |||||
| goto free_params; | goto free_params; | ||||
| } | } | ||||
| snd_rawmidi_params_free(params); | snd_rawmidi_params_free(params); | ||||
| num_fds = snd_rawmidi_poll_descriptors_count(rawmidi); | |||||
| if (! num_fds) { | |||||
| alsa_poll_fd_count = snd_rawmidi_poll_descriptors_count(rawmidi); | |||||
| if (! alsa_poll_fd_count) { | |||||
| error_message = "returned '0' count for poll descriptors"; | error_message = "returned '0' count for poll descriptors"; | ||||
| func = "snd_rawmidi_poll_descriptors_count"; | func = "snd_rawmidi_poll_descriptors_count"; | ||||
| goto close; | goto close; | ||||
| } | } | ||||
| try { | |||||
| CreateNonBlockingPipe(fds); | |||||
| } catch (std::exception e) { | |||||
| error_message = e.what(); | |||||
| func = "CreateNonBlockingPipe"; | |||||
| goto close; | |||||
| } | |||||
| snprintf(alias, sizeof(alias), "%s%d", alias_prefix, index + 1); | snprintf(alias, sizeof(alias), "%s%d", alias_prefix, index + 1); | ||||
| snprintf(name, sizeof(name), "system:%d-%d %s %d %s", card + 1, device + 1, | snprintf(name, sizeof(name), "system:%d-%d %s %d %s", card + 1, device + 1, | ||||
| snd_rawmidi_info_get_name(info), subdevice + 1, name_suffix); | snd_rawmidi_info_get_name(info), subdevice + 1, name_suffix); | ||||
| this->io_mask = io_mask; | |||||
| return; | return; | ||||
| free_params: | free_params: | ||||
| snd_rawmidi_params_free(params); | snd_rawmidi_params_free(params); | ||||
| @@ -119,6 +128,7 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info, | |||||
| JackALSARawMidiPort::~JackALSARawMidiPort() | JackALSARawMidiPort::~JackALSARawMidiPort() | ||||
| { | { | ||||
| DestroyNonBlockingPipe(fds); | |||||
| if (rawmidi) { | if (rawmidi) { | ||||
| int code = snd_rawmidi_close(rawmidi); | int code = snd_rawmidi_close(rawmidi); | ||||
| if (code) { | if (code) { | ||||
| @@ -135,6 +145,32 @@ JackALSARawMidiPort::GetAlias() | |||||
| return alias; | return alias; | ||||
| } | } | ||||
| int | |||||
| JackALSARawMidiPort::GetIOPollEvent() | |||||
| { | |||||
| unsigned short events; | |||||
| int code = snd_rawmidi_poll_descriptors_revents(rawmidi, alsa_poll_fds, | |||||
| alsa_poll_fd_count, | |||||
| &events); | |||||
| if (code) { | |||||
| jack_error("JackALSARawMidiPort::GetIOPollEvents - " | |||||
| "snd_rawmidi_poll_descriptors_revents: %s", | |||||
| snd_strerror(code)); | |||||
| return -1; | |||||
| } | |||||
| if (events & POLLNVAL) { | |||||
| jack_error("JackALSARawMidiPort::GetIOPollEvents - the file " | |||||
| "descriptor is invalid."); | |||||
| return -1; | |||||
| } | |||||
| if (events & POLLERR) { | |||||
| jack_error("JackALSARawMidiPort::GetIOPollEvents - an error has " | |||||
| "occurred on the device or stream."); | |||||
| return -1; | |||||
| } | |||||
| return (events & io_mask) ? 1 : 0; | |||||
| } | |||||
| const char * | const char * | ||||
| JackALSARawMidiPort::GetName() | JackALSARawMidiPort::GetName() | ||||
| { | { | ||||
| @@ -144,48 +180,76 @@ JackALSARawMidiPort::GetName() | |||||
| int | int | ||||
| JackALSARawMidiPort::GetPollDescriptorCount() | JackALSARawMidiPort::GetPollDescriptorCount() | ||||
| { | { | ||||
| return num_fds; | |||||
| } | |||||
| bool | |||||
| JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd) | |||||
| { | |||||
| bool result = snd_rawmidi_poll_descriptors(rawmidi, poll_fd, num_fds) == | |||||
| num_fds; | |||||
| if (result) { | |||||
| poll_fds = poll_fd; | |||||
| } | |||||
| return result; | |||||
| return alsa_poll_fd_count + 1; | |||||
| } | } | ||||
| bool | |||||
| JackALSARawMidiPort::ProcessPollEvents(unsigned short *revents) | |||||
| int | |||||
| JackALSARawMidiPort::GetQueuePollEvent() | |||||
| { | { | ||||
| int code = snd_rawmidi_poll_descriptors_revents(rawmidi, poll_fds, num_fds, | |||||
| revents); | |||||
| if (code) { | |||||
| jack_error("JackALSARawMidiPort::ProcessPollEvents - " | |||||
| "snd_rawmidi_poll_descriptors_revents: %s", | |||||
| snd_strerror(code)); | |||||
| return false; | |||||
| } | |||||
| if ((*revents) & POLLNVAL) { | |||||
| jack_error("JackALSARawMidiPort::ProcessPollEvents - the file " | |||||
| unsigned short events = queue_poll_fd->revents; | |||||
| if (events & POLLNVAL) { | |||||
| jack_error("JackALSARawMidiPort::GetQueuePollEvents - the file " | |||||
| "descriptor is invalid."); | "descriptor is invalid."); | ||||
| return false; | |||||
| return -1; | |||||
| } | } | ||||
| if ((*revents) & POLLERR) { | |||||
| jack_error("JackALSARawMidiPort::ProcessPollEvents - an error has " | |||||
| if (events & POLLERR) { | |||||
| jack_error("JackALSARawMidiPort::GetQueuePollEvents - an error has " | |||||
| "occurred on the device or stream."); | "occurred on the device or stream."); | ||||
| return false; | |||||
| return -1; | |||||
| } | } | ||||
| return true; | |||||
| int event = events & POLLIN ? 1 : 0; | |||||
| if (event) { | |||||
| char c; | |||||
| ssize_t result = read(fds[0], &c, 1); | |||||
| assert(result); | |||||
| if (result < 0) { | |||||
| jack_error("JackALSARawMidiPort::GetQueuePollEvents - error " | |||||
| "reading a byte from the pipe file descriptor: %s", | |||||
| strerror(errno)); | |||||
| return -1; | |||||
| } | |||||
| } | |||||
| return event; | |||||
| } | } | ||||
| void | void | ||||
| JackALSARawMidiPort::SetPollEventMask(unsigned short events) | |||||
| JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd) | |||||
| { | |||||
| alsa_poll_fds = poll_fd + 1; | |||||
| assert(snd_rawmidi_poll_descriptors(rawmidi, alsa_poll_fds, | |||||
| alsa_poll_fd_count) == | |||||
| alsa_poll_fd_count); | |||||
| queue_poll_fd = poll_fd; | |||||
| queue_poll_fd->events = POLLERR | POLLIN | POLLNVAL; | |||||
| queue_poll_fd->fd = fds[0]; | |||||
| SetIOEventsEnabled(true); | |||||
| } | |||||
| void | |||||
| JackALSARawMidiPort::SetIOEventsEnabled(bool enabled) | |||||
| { | |||||
| unsigned short mask = POLLNVAL | POLLERR | (enabled ? io_mask : 0); | |||||
| for (int i = 0; i < alsa_poll_fd_count; i++) { | |||||
| (alsa_poll_fds + i)->events = mask; | |||||
| } | |||||
| } | |||||
| bool | |||||
| JackALSARawMidiPort::TriggerQueueEvent() | |||||
| { | { | ||||
| for (int i = 0; i < num_fds; i++) { | |||||
| (poll_fds + i)->events = events; | |||||
| char c; | |||||
| ssize_t result = write(fds[1], &c, 1); | |||||
| assert(result <= 1); | |||||
| switch (result) { | |||||
| case 1: | |||||
| return true; | |||||
| case 0: | |||||
| jack_error("JackALSARawMidiPort::TriggerQueueEvent - error writing a " | |||||
| "byte to the pipe file descriptor: %s", strerror(errno)); | |||||
| break; | |||||
| default: | |||||
| jack_error("JackALSARawMidiPort::TriggerQueueEvent - couldn't write a " | |||||
| "byte to the pipe file descriptor."); | |||||
| } | } | ||||
| return false; | |||||
| } | } | ||||
| @@ -32,23 +32,36 @@ namespace Jack { | |||||
| private: | private: | ||||
| char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | ||||
| struct pollfd *alsa_poll_fds; | |||||
| int alsa_poll_fd_count; | |||||
| int fds[2]; | |||||
| unsigned short io_mask; | |||||
| char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | ||||
| int num_fds; | |||||
| struct pollfd *poll_fds; | |||||
| struct pollfd *queue_poll_fd; | |||||
| protected: | protected: | ||||
| snd_rawmidi_t *rawmidi; | snd_rawmidi_t *rawmidi; | ||||
| bool | |||||
| ProcessPollEvents(unsigned short *revents); | |||||
| int | |||||
| GetIOPollEvent(); | |||||
| int | |||||
| GetQueuePollEvent(); | |||||
| void | |||||
| SetIOEventsEnabled(bool enabled); | |||||
| void | void | ||||
| SetPollEventMask(unsigned short events); | |||||
| SetQueueEventsEnabled(bool enabled); | |||||
| bool | |||||
| TriggerQueueEvent(); | |||||
| public: | public: | ||||
| JackALSARawMidiPort(snd_rawmidi_info_t *info, size_t index); | |||||
| JackALSARawMidiPort(snd_rawmidi_info_t *info, size_t index, | |||||
| unsigned short io_mask); | |||||
| virtual | virtual | ||||
| ~JackALSARawMidiPort(); | ~JackALSARawMidiPort(); | ||||
| @@ -62,7 +75,7 @@ namespace Jack { | |||||
| int | int | ||||
| GetPollDescriptorCount(); | GetPollDescriptorCount(); | ||||
| bool | |||||
| void | |||||
| PopulatePollDescriptors(struct pollfd *poll_fd); | PopulatePollDescriptors(struct pollfd *poll_fd); | ||||
| }; | }; | ||||
| @@ -24,10 +24,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| using Jack::JackALSARawMidiSendQueue; | using Jack::JackALSARawMidiSendQueue; | ||||
| JackALSARawMidiSendQueue::JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi) | |||||
| JackALSARawMidiSendQueue::JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi, | |||||
| size_t bytes_per_poll) | |||||
| { | { | ||||
| assert(bytes_per_poll > 0); | |||||
| this->bytes_per_poll = bytes_per_poll; | |||||
| this->rawmidi = rawmidi; | this->rawmidi = rawmidi; | ||||
| blocked = false; | blocked = false; | ||||
| bytes_available = bytes_per_poll; | |||||
| } | } | ||||
| Jack::JackMidiWriteQueue::EnqueueResult | Jack::JackMidiWriteQueue::EnqueueResult | ||||
| @@ -38,10 +42,14 @@ JackALSARawMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||||
| if (time > GetCurrentFrame()) { | if (time > GetCurrentFrame()) { | ||||
| return EVENT_EARLY; | return EVENT_EARLY; | ||||
| } | } | ||||
| if (! bytes_available) { | |||||
| return BUFFER_FULL; | |||||
| } | |||||
| ssize_t result = snd_rawmidi_write(rawmidi, buffer, 1); | ssize_t result = snd_rawmidi_write(rawmidi, buffer, 1); | ||||
| switch (result) { | switch (result) { | ||||
| case 1: | case 1: | ||||
| blocked = false; | blocked = false; | ||||
| bytes_available--; | |||||
| return OK; | return OK; | ||||
| case -EWOULDBLOCK: | case -EWOULDBLOCK: | ||||
| blocked = true; | blocked = true; | ||||
| @@ -57,3 +65,9 @@ JackALSARawMidiSendQueue::IsBlocked() | |||||
| { | { | ||||
| return blocked; | return blocked; | ||||
| } | } | ||||
| void | |||||
| JackALSARawMidiSendQueue::ResetPollByteCount() | |||||
| { | |||||
| bytes_available = bytes_per_poll; | |||||
| } | |||||
| @@ -31,11 +31,14 @@ namespace Jack { | |||||
| private: | private: | ||||
| bool blocked; | bool blocked; | ||||
| size_t bytes_available; | |||||
| size_t bytes_per_poll; | |||||
| snd_rawmidi_t *rawmidi; | snd_rawmidi_t *rawmidi; | ||||
| public: | public: | ||||
| JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi); | |||||
| JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi, | |||||
| size_t bytes_per_poll=0); | |||||
| JackMidiWriteQueue::EnqueueResult | JackMidiWriteQueue::EnqueueResult | ||||
| EnqueueEvent(jack_nframes_t time, size_t size, | EnqueueEvent(jack_nframes_t time, size_t size, | ||||
| @@ -44,6 +47,9 @@ namespace Jack { | |||||
| bool | bool | ||||
| IsBlocked(); | IsBlocked(); | ||||
| void | |||||
| ResetPollByteCount(); | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -0,0 +1,43 @@ | |||||
| #include <cerrno> | |||||
| #include <cstring> | |||||
| #include <stdexcept> | |||||
| #include <fcntl.h> | |||||
| #include <unistd.h> | |||||
| #include "JackALSARawMidiUtil.h" | |||||
| void | |||||
| Jack::CreateNonBlockingPipe(int *fds) | |||||
| { | |||||
| if (pipe(fds) == -1) { | |||||
| throw std::runtime_error(strerror(errno)); | |||||
| } | |||||
| try { | |||||
| SetNonBlocking(fds[0]); | |||||
| SetNonBlocking(fds[1]); | |||||
| } catch (...) { | |||||
| close(fds[1]); | |||||
| close(fds[0]); | |||||
| throw; | |||||
| } | |||||
| } | |||||
| void | |||||
| Jack::DestroyNonBlockingPipe(int *fds) | |||||
| { | |||||
| close(fds[1]); | |||||
| close(fds[0]); | |||||
| } | |||||
| void | |||||
| Jack::SetNonBlocking(int fd) | |||||
| { | |||||
| int flags = fcntl(fd, F_GETFL); | |||||
| if (flags == -1) { | |||||
| throw std::runtime_error(strerror(errno)); | |||||
| } | |||||
| if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { | |||||
| throw std::runtime_error(strerror(errno)); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,36 @@ | |||||
| /* | |||||
| Copyright (C) 2011 Devin Anderson | |||||
| 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 __JackALSARawMidiUtil__ | |||||
| #define __JackALSARawMidiUtil__ | |||||
| namespace Jack { | |||||
| void | |||||
| CreateNonBlockingPipe(int *fds); | |||||
| void | |||||
| DestroyNonBlockingPipe(int *fds); | |||||
| void | |||||
| SetNonBlocking(int fd); | |||||
| } | |||||
| #endif | |||||
| @@ -13,12 +13,14 @@ def configure(conf): | |||||
| conf.define('HAVE_PPOLL', 1 ) | conf.define('HAVE_PPOLL', 1 ) | ||||
| def create_jack_driver_obj(bld, target, sources, uselib = None): | def create_jack_driver_obj(bld, target, sources, uselib = None): | ||||
| driver = bld.new_task_gen('cxx', 'shlib') | driver = bld.new_task_gen('cxx', 'shlib') | ||||
| driver.features.append('cc') | driver.features.append('cc') | ||||
| driver.env['shlib_PATTERN'] = 'jack_%s.so' | driver.env['shlib_PATTERN'] = 'jack_%s.so' | ||||
| driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL'] | |||||
| #driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL'] | |||||
| driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD'] | |||||
| driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack', '../dbus'] | driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack', '../dbus'] | ||||
| driver.target = target | driver.target = target | ||||
| driver.source = sources | driver.source = sources | ||||
| @@ -62,7 +64,8 @@ def build(bld): | |||||
| 'alsarawmidi/JackALSARawMidiOutputPort.cpp', | 'alsarawmidi/JackALSARawMidiOutputPort.cpp', | ||||
| 'alsarawmidi/JackALSARawMidiPort.cpp', | 'alsarawmidi/JackALSARawMidiPort.cpp', | ||||
| 'alsarawmidi/JackALSARawMidiReceiveQueue.cpp', | 'alsarawmidi/JackALSARawMidiReceiveQueue.cpp', | ||||
| 'alsarawmidi/JackALSARawMidiSendQueue.cpp' | |||||
| 'alsarawmidi/JackALSARawMidiSendQueue.cpp', | |||||
| 'alsarawmidi/JackALSARawMidiUtil.cpp' | |||||
| ] | ] | ||||
| ffado_driver_src = ['firewire/JackFFADODriver.cpp', | ffado_driver_src = ['firewire/JackFFADODriver.cpp', | ||||
| @@ -54,7 +54,7 @@ static const double twoRaisedTo32Reciprocal = 1. / twoRaisedTo32; | |||||
| #include "windows.h" | #include "windows.h" | ||||
| #include "mmsystem.h" | #include "mmsystem.h" | ||||
| #ifdef _WIN64 | #ifdef _WIN64 | ||||
| #define JACK_ROUTER "JackRouter64.dll" | |||||
| #define JACK_ROUTER "JackRouter.dll" | |||||
| #include <psapi.h> | #include <psapi.h> | ||||
| #else | #else | ||||
| #define JACK_ROUTER "JackRouter.dll" | #define JACK_ROUTER "JackRouter.dll" | ||||
| @@ -242,7 +242,7 @@ | |||||
| <SuppressStartupBanner>true</SuppressStartupBanner> | <SuppressStartupBanner>true</SuppressStartupBanner> | ||||
| <LinkDLL>true</LinkDLL> | <LinkDLL>true</LinkDLL> | ||||
| <SubSystem>Windows</SubSystem> | <SubSystem>Windows</SubSystem> | ||||
| <OutputFile>.\Release\JackRouter64.dll</OutputFile> | |||||
| <OutputFile>.\x64\Release\JackRouter.dll</OutputFile> | |||||
| <ImportLibrary>.\Release\JackRouter64.lib</ImportLibrary> | <ImportLibrary>.\Release\JackRouter64.lib</ImportLibrary> | ||||
| <AdditionalDependencies>odbc32.lib;odbccp32.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies> | <AdditionalDependencies>odbc32.lib;odbccp32.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
| <ModuleDefinitionFile>.\JackRouter.def</ModuleDefinitionFile> | <ModuleDefinitionFile>.\JackRouter.def</ModuleDefinitionFile> | ||||
| @@ -53,7 +53,6 @@ | |||||
| <bgfoot> </> | <bgfoot> </> | ||||
| <bgback> </> | <bgback> </> | ||||
| <files listview > | <files listview > | ||||
| <_><src>.\src\vcredist_2010_x86.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>..\Release\bin\libjack.a</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release\bin\libjack.a</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release\bin\libjack.lib</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release\bin\libjack.lib</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release\bin\libjack.def</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release\bin\libjack.def</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | ||||
| @@ -95,8 +94,10 @@ | |||||
| <_><src>..\..\common\jack\systemdeps.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | <_><src>..\..\common\jack\systemdeps.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | ||||
| <_><src>..\..\common\jack\weakjack.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | <_><src>..\..\common\jack\weakjack.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | ||||
| <_><src>..\..\common\jack\weakmacros.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | <_><src>..\..\common\jack\weakmacros.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | ||||
| <_><src>.\JackRouter.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\JackRouter.ini</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\32bits\JackRouter.dll</><dest>inst</><custom>32bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\32bits\JackRouter.ini</><dest>inst</><custom>32bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\32bits\msvcr100.dll</><dest>inst</><custom>32bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\32bits\msvcp100.dll</><dest>inst</><custom>32bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\qjackctl\mingwm10.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>.\qjackctl\mingwm10.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>.\qjackctl\qjackctl.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>.\qjackctl\qjackctl.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>.\qjackctl\QtCore4.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>.\qjackctl\QtCore4.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| @@ -107,7 +108,6 @@ | |||||
| </files> | </files> | ||||
| <runx listview > | <runx listview > | ||||
| <_><type>app</><path>inst</><file>vcredist_2010_x86.exe</><cmdline></><wait>1</><workdir>inst</><custdir></><when>end</></> | |||||
| </runx> | </runx> | ||||
| <registry listview > | <registry listview > | ||||
| @@ -126,7 +126,7 @@ | |||||
| </copy> | </copy> | ||||
| <activex listview > | <activex listview > | ||||
| <_><path>inst</><name>JackRouter.dll</></> | |||||
| <_><path>inst</><name>32bits\JackRouter.dll</></> | |||||
| </activex> | </activex> | ||||
| <font listview > | <font listview > | ||||
| @@ -53,8 +53,6 @@ | |||||
| <bgfoot> </> | <bgfoot> </> | ||||
| <bgback> </> | <bgback> </> | ||||
| <files listview > | <files listview > | ||||
| <_><src>.\src\vcredist_2010_x86.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\vcredist_2010_x64.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>..\Release64\bin\libjack64.a</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\libjack64.a</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release64\bin\libjack64.lib</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\libjack64.lib</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release64\bin\libjack64.def</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\libjack64.def</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | ||||
| @@ -71,6 +69,7 @@ | |||||
| <_><src>..\Release\bin\libjackserver.lib</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release\bin\libjackserver.lib</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release\bin\libjackserver.def</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release\bin\libjackserver.def</><dest>inst</><custom>lib</><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release\bin\libjackserver.dll</><dest>sys</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release\bin\libjackserver.dll</><dest>sys</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release\bin\portaudio_x86.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>..\Release64\bin\jack_connect.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\jack_connect.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release64\bin\jack_disconnect.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\jack_disconnect.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release64\bin\jack_load.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\jack_load.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| @@ -81,7 +80,6 @@ | |||||
| <_><src>..\Release64\bin\jackd.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\jackd.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release64\bin\libsamplerate_x86_64.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\libsamplerate_x86_64.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release64\bin\portaudio_x86_64.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\portaudio_x86_64.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release\bin\portaudio_x86.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>..\Release64\bin\jack\jack_net.dll</><dest>inst</><custom>jack</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\jack\jack_net.dll</><dest>inst</><custom>jack</><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release64\bin\jack\jack_netone.dll</><dest>inst</><custom>jack</><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\jack\jack_netone.dll</><dest>inst</><custom>jack</><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>..\Release64\bin\jack_netsource.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>..\Release64\bin\jack_netsource.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| @@ -105,9 +103,14 @@ | |||||
| <_><src>..\..\common\jack\systemdeps.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | <_><src>..\..\common\jack\systemdeps.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | ||||
| <_><src>..\..\common\jack\weakjack.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | <_><src>..\..\common\jack\weakjack.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | ||||
| <_><src>..\..\common\jack\weakmacros.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | <_><src>..\..\common\jack\weakmacros.h</><dest>inst</><custom>includes\jack</><ifexist>overnewer</><recurs>1</></> | ||||
| <_><src>.\JackRouter.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\JackRouter64.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\JackRouter.ini</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\32bits\JackRouter.dll</><dest>inst</><custom>32bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\32bits\JackRouter.ini</><dest>inst</><custom>32bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\32bits\msvcr100.dll</><dest>inst</><custom>32bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\32bits\msvcp100.dll</><dest>inst</><custom>32bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\64bits\JackRouter.dll</><dest>inst</><custom>64bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\64bits\JackRouter.ini</><dest>inst</><custom>64bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\64bits\msvcr100.dll</><dest>inst</><custom>64bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\src\64bits\msvcp100.dll</><dest>inst</><custom>64bits</><ifexist>overnewer</><recurs>0</></> | |||||
| <_><src>.\qjackctl\mingwm10.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>.\qjackctl\mingwm10.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>.\qjackctl\qjackctl.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>.\qjackctl\qjackctl.exe</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| <_><src>.\qjackctl\QtCore4.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | <_><src>.\qjackctl\QtCore4.dll</><dest>inst</><custom></><ifexist>overnewer</><recurs>0</></> | ||||
| @@ -118,9 +121,7 @@ | |||||
| </files> | </files> | ||||
| <runx listview > | <runx listview > | ||||
| <_><type>app</><path>inst</><file>vcredist_2010_x86.exe</><cmdline></><wait>1</><workdir>inst</><custdir></><when>end</></> | |||||
| <_><type>app</><path>inst</><file>vcredist_2010_x64.exe</><cmdline></><wait>1</><workdir>inst</><custdir></><when>end</></> | |||||
| </runx> | |||||
| </runx> | |||||
| <registry listview > | <registry listview > | ||||
| </registry> | </registry> | ||||
| @@ -138,8 +139,8 @@ | |||||
| </copy> | </copy> | ||||
| <activex listview > | <activex listview > | ||||
| <_><path>inst</><name>JackRouter.dll</></> | |||||
| <_><path>inst</><name>JackRouter64.dll</></> | |||||
| <_><path>inst</><name>32bits\JackRouter.dll</></> | |||||
| <_><path>inst</><name>64bits\JackRouter.dll</></> | |||||
| </activex> | </activex> | ||||
| <font listview > | <font listview > | ||||
| @@ -0,0 +1,7 @@ | |||||
| [IO] | |||||
| input=4 | |||||
| output=4 | |||||
| [AUTO_CONNECT] | |||||
| input=1 | |||||
| output=1 | |||||
| @@ -0,0 +1,7 @@ | |||||
| [IO] | |||||
| input=4 | |||||
| output=4 | |||||
| [AUTO_CONNECT] | |||||
| input=1 | |||||
| output=1 | |||||
| @@ -1,13 +1,13 @@ | |||||
| <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> | <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> | ||||
| <CodeBlocks_workspace_file> | <CodeBlocks_workspace_file> | ||||
| <Workspace title="jack"> | <Workspace title="jack"> | ||||
| <Project filename="libjackserver.cbp" /> | |||||
| <Project filename="libjackserver.cbp" active="1" /> | |||||
| <Project filename="jack_portaudio.cbp" /> | <Project filename="jack_portaudio.cbp" /> | ||||
| <Project filename="jack_netdriver.cbp" /> | <Project filename="jack_netdriver.cbp" /> | ||||
| <Project filename="jack_netonedriver.cbp" /> | <Project filename="jack_netonedriver.cbp" /> | ||||
| <Project filename="jack_dummy.cbp" /> | <Project filename="jack_dummy.cbp" /> | ||||
| <Project filename="jack_netmanager.cbp" /> | <Project filename="jack_netmanager.cbp" /> | ||||
| <Project filename="jack_audioadapter.cbp" active="1" /> | |||||
| <Project filename="jack_audioadapter.cbp" /> | |||||
| <Project filename="libjack.cbp" /> | <Project filename="libjack.cbp" /> | ||||
| <Project filename="jack_load.cbp" /> | <Project filename="jack_load.cbp" /> | ||||
| <Project filename="jack_unload.cbp" /> | <Project filename="jack_unload.cbp" /> | ||||
| @@ -21,7 +21,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| #include <memory> | #include <memory> | ||||
| #include <stdexcept> | #include <stdexcept> | ||||
| #include "JackError.h" | |||||
| #include "JackError.h" | |||||
| #include "JackTime.h" | |||||
| #include "JackMidiUtil.h" | #include "JackMidiUtil.h" | ||||
| #include "JackWinMMEInputPort.h" | #include "JackWinMMEInputPort.h" | ||||
| #include "JackMidiWriteQueue.h" | #include "JackMidiWriteQueue.h" | ||||
| @@ -55,7 +56,8 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, | |||||
| std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue); | std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue); | ||||
| sysex_buffer = new jack_midi_data_t[max_bytes]; | sysex_buffer = new jack_midi_data_t[max_bytes]; | ||||
| char error_message[MAXERRORLENGTH]; | char error_message[MAXERRORLENGTH]; | ||||
| MMRESULT result = midiInOpen(&handle, index, (DWORD_PTR)HandleMidiInputEvent, | |||||
| MMRESULT result = midiInOpen(&handle, index, | |||||
| (DWORD_PTR) HandleMidiInputEvent, | |||||
| (DWORD_PTR)this, | (DWORD_PTR)this, | ||||
| CALLBACK_FUNCTION | MIDI_IO_STATUS); | CALLBACK_FUNCTION | MIDI_IO_STATUS); | ||||
| if (result != MMSYSERR_NOERROR) { | if (result != MMSYSERR_NOERROR) { | ||||
| @@ -63,11 +65,8 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, | |||||
| goto delete_sysex_buffer; | goto delete_sysex_buffer; | ||||
| } | } | ||||
| sysex_header.dwBufferLength = max_bytes; | sysex_header.dwBufferLength = max_bytes; | ||||
| sysex_header.dwBytesRecorded = 0; | |||||
| sysex_header.dwFlags = 0; | sysex_header.dwFlags = 0; | ||||
| sysex_header.dwUser = 0; | |||||
| sysex_header.lpData = (LPSTR)(((LPBYTE) &sysex_header) + sizeof(MIDIHDR)); | sysex_header.lpData = (LPSTR)(((LPBYTE) &sysex_header) + sizeof(MIDIHDR)); | ||||
| sysex_header.lpNext = 0; | |||||
| result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); | result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); | ||||
| if (result != MMSYSERR_NOERROR) { | if (result != MMSYSERR_NOERROR) { | ||||
| GetInErrorString(result, error_message); | GetInErrorString(result, error_message); | ||||
| @@ -103,12 +102,13 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, | |||||
| result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); | result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); | ||||
| if (result != MMSYSERR_NOERROR) { | if (result != MMSYSERR_NOERROR) { | ||||
| WriteInError("JackWinMMEInputPort [constructor]", | WriteInError("JackWinMMEInputPort [constructor]", | ||||
| "midiInUnprepareHeader", result); | |||||
| "midiInUnprepareHeader", result); | |||||
| } | } | ||||
| close_handle: | close_handle: | ||||
| result = midiInClose(handle); | result = midiInClose(handle); | ||||
| if (result != MMSYSERR_NOERROR) { | if (result != MMSYSERR_NOERROR) { | ||||
| WriteInError("JackWinMMEInputPort [constructor]", "midiInClose", result); | |||||
| WriteInError("JackWinMMEInputPort [constructor]", "midiInClose", | |||||
| result); | |||||
| } | } | ||||
| delete_sysex_buffer: | delete_sysex_buffer: | ||||
| delete[] sysex_buffer; | delete[] sysex_buffer; | ||||
| @@ -123,8 +123,8 @@ JackWinMMEInputPort::~JackWinMMEInputPort() | |||||
| } | } | ||||
| result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); | result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); | ||||
| if (result != MMSYSERR_NOERROR) { | if (result != MMSYSERR_NOERROR) { | ||||
| WriteInError("JackWinMMEInputPort [destructor]", "midiInUnprepareHeader", | |||||
| result); | |||||
| WriteInError("JackWinMMEInputPort [destructor]", | |||||
| "midiInUnprepareHeader", result); | |||||
| } | } | ||||
| result = midiInClose(handle); | result = midiInClose(handle); | ||||
| if (result != MMSYSERR_NOERROR) { | if (result != MMSYSERR_NOERROR) { | ||||
| @@ -136,10 +136,11 @@ JackWinMMEInputPort::~JackWinMMEInputPort() | |||||
| } | } | ||||
| void | void | ||||
| JackWinMMEInputPort::EnqueueMessage(jack_nframes_t time, size_t length, | |||||
| JackWinMMEInputPort::EnqueueMessage(DWORD timestamp, size_t length, | |||||
| jack_midi_data_t *data) | jack_midi_data_t *data) | ||||
| { | { | ||||
| switch (thread_queue->EnqueueEvent(time, length, data)) { | |||||
| jack_nframes_t frame = GetFramesFromTime(start_time + (timestamp * 1000)); | |||||
| switch (thread_queue->EnqueueEvent(frame, length, data)) { | |||||
| case JackMidiWriteQueue::BUFFER_FULL: | case JackMidiWriteQueue::BUFFER_FULL: | ||||
| jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue " | jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue " | ||||
| "cannot currently accept a %d-byte event. Dropping event.", | "cannot currently accept a %d-byte event. Dropping event.", | ||||
| @@ -155,6 +156,15 @@ JackWinMMEInputPort::EnqueueMessage(jack_nframes_t time, size_t length, | |||||
| } | } | ||||
| } | } | ||||
| void | |||||
| JackWinMMEInputPort::GetInErrorString(MMRESULT error, LPTSTR text) | |||||
| { | |||||
| MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH); | |||||
| if (result != MMSYSERR_NOERROR) { | |||||
| snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error); | |||||
| } | |||||
| } | |||||
| void | void | ||||
| JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, | JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, | ||||
| jack_nframes_t frames) | jack_nframes_t frames) | ||||
| @@ -164,7 +174,7 @@ JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, | |||||
| jack_event = thread_queue->DequeueEvent(); | jack_event = thread_queue->DequeueEvent(); | ||||
| } | } | ||||
| for (; jack_event; jack_event = thread_queue->DequeueEvent()) { | for (; jack_event; jack_event = thread_queue->DequeueEvent()) { | ||||
| switch (write_queue->EnqueueEvent(jack_event)) { | |||||
| switch (write_queue->EnqueueEvent(jack_event, frames)) { | |||||
| case JackMidiWriteQueue::BUFFER_TOO_SMALL: | case JackMidiWriteQueue::BUFFER_TOO_SMALL: | ||||
| jack_error("JackWinMMEMidiInputPort::Process - The buffer write " | jack_error("JackWinMMEMidiInputPort::Process - The buffer write " | ||||
| "queue couldn't enqueue a %d-byte event. Dropping " | "queue couldn't enqueue a %d-byte event. Dropping " | ||||
| @@ -181,8 +191,6 @@ void | |||||
| JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) | JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) | ||||
| { | { | ||||
| set_threaded_log_function(); | set_threaded_log_function(); | ||||
| jack_nframes_t current_frame = GetCurrentFrame(); | |||||
| switch (message) { | switch (message) { | ||||
| case MIM_CLOSE: | case MIM_CLOSE: | ||||
| jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed."); | jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed."); | ||||
| @@ -218,18 +226,24 @@ JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) | |||||
| "status byte."); | "status byte."); | ||||
| return; | return; | ||||
| } | } | ||||
| EnqueueMessage(current_frame, (size_t) length, message_buffer); | |||||
| break; | |||||
| EnqueueMessage(param2, (size_t) length, message_buffer); | |||||
| break; | |||||
| } | } | ||||
| case MIM_LONGDATA: { | case MIM_LONGDATA: { | ||||
| LPMIDIHDR header = (LPMIDIHDR) param1; | LPMIDIHDR header = (LPMIDIHDR) param1; | ||||
| size_t byte_count = header->dwBytesRecorded; | |||||
| if (! byte_count) { | |||||
| jack_info("JackWinMMEInputPort::ProcessWinMME - WinMME driver has " | |||||
| "returned sysex header to us with no bytes. The JACK " | |||||
| "driver is probably being stopped."); | |||||
| break; | |||||
| } | |||||
| jack_midi_data_t *data = (jack_midi_data_t *) header->lpData; | jack_midi_data_t *data = (jack_midi_data_t *) header->lpData; | ||||
| size_t length1 = header->dwBytesRecorded; | |||||
| if ((data[0] != 0xf0) || (data[length1 - 1] != 0xf7)) { | |||||
| if ((data[0] != 0xf0) || (data[byte_count - 1] != 0xf7)) { | |||||
| jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding " | jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding " | ||||
| "%d-byte sysex chunk.", length1); | |||||
| "%d-byte sysex chunk.", byte_count); | |||||
| } else { | } else { | ||||
| EnqueueMessage(current_frame, length1, data); | |||||
| EnqueueMessage(param2, byte_count, data); | |||||
| } | } | ||||
| // Is this realtime-safe? This function isn't run in the JACK thread, | // Is this realtime-safe? This function isn't run in the JACK thread, | ||||
| // but we still want it to perform as quickly as possible. Even if | // but we still want it to perform as quickly as possible. Even if | ||||
| @@ -237,10 +251,10 @@ JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) | |||||
| MMRESULT result = midiInAddBuffer(handle, &sysex_header, | MMRESULT result = midiInAddBuffer(handle, &sysex_header, | ||||
| sizeof(MIDIHDR)); | sizeof(MIDIHDR)); | ||||
| if (result != MMSYSERR_NOERROR) { | if (result != MMSYSERR_NOERROR) { | ||||
| WriteInError("JackWinMMEInputPort::ProcessWinMME", "midiInAddBuffer", | |||||
| result); | |||||
| WriteInError("JackWinMMEInputPort::ProcessWinMME", | |||||
| "midiInAddBuffer", result); | |||||
| } | } | ||||
| break; | |||||
| break; | |||||
| } | } | ||||
| case MIM_LONGERROR: | case MIM_LONGERROR: | ||||
| jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or " | jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or " | ||||
| @@ -255,6 +269,7 @@ bool | |||||
| JackWinMMEInputPort::Start() | JackWinMMEInputPort::Start() | ||||
| { | { | ||||
| if (! started) { | if (! started) { | ||||
| start_time = GetMicroSeconds(); | |||||
| MMRESULT result = midiInStart(handle); | MMRESULT result = midiInStart(handle); | ||||
| started = result == MMSYSERR_NOERROR; | started = result == MMSYSERR_NOERROR; | ||||
| if (! started) { | if (! started) { | ||||
| @@ -277,15 +292,6 @@ JackWinMMEInputPort::Stop() | |||||
| return ! started; | return ! started; | ||||
| } | } | ||||
| void | |||||
| JackWinMMEInputPort::GetInErrorString(MMRESULT error, LPTSTR text) | |||||
| { | |||||
| MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH); | |||||
| if (result != MMSYSERR_NOERROR) { | |||||
| snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error); | |||||
| } | |||||
| } | |||||
| void | void | ||||
| JackWinMMEInputPort::WriteInError(const char *jack_func, const char *mm_func, | JackWinMMEInputPort::WriteInError(const char *jack_func, const char *mm_func, | ||||
| MMRESULT result) | MMRESULT result) | ||||
| @@ -294,5 +300,3 @@ JackWinMMEInputPort::WriteInError(const char *jack_func, const char *mm_func, | |||||
| GetInErrorString(result, error_message); | GetInErrorString(result, error_message); | ||||
| jack_error("%s - %s: %s", jack_func, mm_func, error_message); | jack_error("%s - %s: %s", jack_func, mm_func, error_message); | ||||
| } | } | ||||
| @@ -37,30 +37,27 @@ namespace Jack { | |||||
| DWORD param1, DWORD param2); | DWORD param1, DWORD param2); | ||||
| void | void | ||||
| EnqueueMessage(jack_nframes_t time, size_t length, | |||||
| jack_midi_data_t *data); | |||||
| EnqueueMessage(DWORD timestamp, size_t length, jack_midi_data_t *data); | |||||
| void | |||||
| GetInErrorString(MMRESULT error, LPTSTR text); | |||||
| void | void | ||||
| ProcessWinMME(UINT message, DWORD param1, DWORD param2); | ProcessWinMME(UINT message, DWORD param1, DWORD param2); | ||||
| void | |||||
| WriteInError(const char *jack_func, const char *mm_func, | |||||
| MMRESULT result); | |||||
| HMIDIIN handle; | HMIDIIN handle; | ||||
| jack_midi_event_t *jack_event; | jack_midi_event_t *jack_event; | ||||
| jack_time_t start_time; | |||||
| bool started; | |||||
| jack_midi_data_t *sysex_buffer; | jack_midi_data_t *sysex_buffer; | ||||
| MIDIHDR sysex_header; | MIDIHDR sysex_header; | ||||
| JackMidiAsyncQueue *thread_queue; | JackMidiAsyncQueue *thread_queue; | ||||
| JackMidiBufferWriteQueue *write_queue; | JackMidiBufferWriteQueue *write_queue; | ||||
| bool started; | |||||
| void | |||||
| WriteInError(const char *jack_func, const char *mm_func, | |||||
| MMRESULT result); | |||||
| void | |||||
| GetInErrorString(MMRESULT error, LPTSTR text); | |||||
| public: | public: | ||||
| JackWinMMEInputPort(const char *alias_name, const char *client_name, | JackWinMMEInputPort(const char *alias_name, const char *client_name, | ||||
| @@ -184,11 +184,8 @@ JackWinMMEOutputPort::Execute() | |||||
| } | } | ||||
| MIDIHDR header; | MIDIHDR header; | ||||
| header.dwBufferLength = size; | header.dwBufferLength = size; | ||||
| header.dwBytesRecorded = size; | |||||
| header.dwFlags = 0; | header.dwFlags = 0; | ||||
| header.dwOffset = 0; | |||||
| header.dwUser = 0; | |||||
| header.lpData = (LPSTR)data; | |||||
| header.lpData = (LPSTR) data; | |||||
| result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR)); | result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR)); | ||||
| if (result != MMSYSERR_NOERROR) { | if (result != MMSYSERR_NOERROR) { | ||||
| WriteOutError("JackWinMMEOutputPort::Execute", | WriteOutError("JackWinMMEOutputPort::Execute", | ||||
| @@ -215,9 +212,7 @@ JackWinMMEOutputPort::Execute() | |||||
| "midiOutUnprepareHeader", result); | "midiOutUnprepareHeader", result); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| stop_execution: | |||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -237,7 +232,7 @@ JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, | |||||
| jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened."); | jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened."); | ||||
| break; | break; | ||||
| case MOM_POSITIONCB: | case MOM_POSITIONCB: | ||||
| LPMIDIHDR header = (LPMIDIHDR) param2; | |||||
| LPMIDIHDR header = (LPMIDIHDR) param1; | |||||
| jack_info("JackWinMMEOutputPort::HandleMessage - %d bytes out of %d " | jack_info("JackWinMMEOutputPort::HandleMessage - %d bytes out of %d " | ||||
| "bytes of the current sysex message have been sent.", | "bytes of the current sysex message have been sent.", | ||||
| header->dwOffset, header->dwBytesRecorded); | header->dwOffset, header->dwBytesRecorded); | ||||
| @@ -262,7 +257,6 @@ JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer, | |||||
| jack_nframes_t frames) | jack_nframes_t frames) | ||||
| { | { | ||||
| read_queue->ResetMidiBuffer(port_buffer); | read_queue->ResetMidiBuffer(port_buffer); | ||||
| for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; | for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; | ||||
| event = read_queue->DequeueEvent()) { | event = read_queue->DequeueEvent()) { | ||||
| @@ -369,4 +363,3 @@ JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func, | |||||
| GetOutErrorString(result, error_message); | GetOutErrorString(result, error_message); | ||||
| jack_error("%s - %s: %s", jack_func, mm_func, error_message); | jack_error("%s - %s: %s", jack_func, mm_func, error_message); | ||||
| } | } | ||||