/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-7 by Raw Material Software ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified 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. JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ If you'd like to release a closed-source product which uses JUCE, commercial licenses are also available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #include "../../../juce_Config.h" #if JUCE_BUILD_GUI_CLASSES #if JUCE_ALSA #include "../../../src/juce_core/basics/juce_StandardHeader.h" #include BEGIN_JUCE_NAMESPACE #include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h" #include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h" #include "../../../src/juce_core/threads/juce_Thread.h" #include "../../../src/juce_core/basics/juce_Time.h" //============================================================================== static snd_seq_t* iterateDevices (const bool forInput, StringArray& deviceNamesFound, const int deviceIndexToOpen) { snd_seq_t* returnedHandle = 0; snd_seq_t* seqHandle; if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT : SND_SEQ_OPEN_OUTPUT, 0) == 0) { snd_seq_system_info_t* systemInfo; snd_seq_client_info_t* clientInfo; if (snd_seq_system_info_malloc (&systemInfo) == 0) { if (snd_seq_system_info (seqHandle, systemInfo) == 0 && snd_seq_client_info_malloc (&clientInfo) == 0) { int numClients = snd_seq_system_info_get_cur_clients (systemInfo); while (--numClients >= 0 && returnedHandle == 0) { if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) { snd_seq_port_info_t* portInfo; if (snd_seq_port_info_malloc (&portInfo) == 0) { int numPorts = snd_seq_client_info_get_num_ports (clientInfo); const int client = snd_seq_client_info_get_client (clientInfo); snd_seq_port_info_set_client (portInfo, client); snd_seq_port_info_set_port (portInfo, -1); while (--numPorts >= 0) { if (snd_seq_query_next_port (seqHandle, portInfo) == 0 && (snd_seq_port_info_get_capability (portInfo) & (forInput ? SND_SEQ_PORT_CAP_READ : SND_SEQ_PORT_CAP_WRITE)) != 0) { deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); if (deviceNamesFound.size() == deviceIndexToOpen + 1) { const int sourcePort = snd_seq_port_info_get_port (portInfo); const int sourceClient = snd_seq_client_info_get_client (clientInfo); if (sourcePort != -1) { snd_seq_set_client_name (seqHandle, forInput ? "Juce Midi Input" : "Juce Midi Output"); const int portId = snd_seq_create_simple_port (seqHandle, forInput ? "Juce Midi In Port" : "Juce Midi Out Port", forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), SND_SEQ_PORT_TYPE_MIDI_GENERIC); snd_seq_connect_from (seqHandle, portId, sourceClient, sourcePort); returnedHandle = seqHandle; break; } } } } snd_seq_port_info_free (portInfo); } } } snd_seq_client_info_free (clientInfo); } snd_seq_system_info_free (systemInfo); } if (returnedHandle == 0) snd_seq_close (seqHandle); } deviceNamesFound.appendNumbersToDuplicates (true, true); return returnedHandle; } static snd_seq_t* createDevice (const bool forInput, const String& deviceNameToOpen) { snd_seq_t* seqHandle = 0; if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT : SND_SEQ_OPEN_OUTPUT, 0) == 0) { snd_seq_set_client_name (seqHandle, (const char*) (forInput ? (deviceNameToOpen + T(" Input")) : (deviceNameToOpen + T(" Output")))); const int portId = snd_seq_create_simple_port (seqHandle, forInput ? "in" : "out", forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), forInput ? SND_SEQ_PORT_TYPE_APPLICATION : SND_SEQ_PORT_TYPE_MIDI_GENERIC); if (portId < 0) { snd_seq_close (seqHandle); seqHandle = 0; } } return seqHandle; } //============================================================================== class MidiOutputDevice { public: MidiOutputDevice (MidiOutput* const midiOutput_, snd_seq_t* const seqHandle_) : midiOutput (midiOutput_), seqHandle (seqHandle_), maxEventSize (16 * 1024) { jassert (seqHandle != 0 && midiOutput != 0); snd_midi_event_new (maxEventSize, &midiParser); } ~MidiOutputDevice() { snd_midi_event_free (midiParser); snd_seq_close (seqHandle); } void sendMessageNow (const MidiMessage& message) { if (message.getRawDataSize() > maxEventSize) { maxEventSize = message.getRawDataSize(); snd_midi_event_free (midiParser); snd_midi_event_new (maxEventSize, &midiParser); } snd_seq_event_t event; snd_seq_ev_clear (&event); snd_midi_event_encode (midiParser, message.getRawData(), message.getRawDataSize(), &event); snd_midi_event_reset_encode (midiParser); snd_seq_ev_set_source (&event, 0); snd_seq_ev_set_subs (&event); snd_seq_ev_set_direct (&event); snd_seq_event_output_direct (seqHandle, &event); } juce_UseDebuggingNewOperator private: MidiOutput* const midiOutput; snd_seq_t* const seqHandle; snd_midi_event_t* midiParser; int maxEventSize; }; const StringArray MidiOutput::getDevices() { StringArray devices; iterateDevices (false, devices, -1); return devices; } int MidiOutput::getDefaultDeviceIndex() { return 0; } MidiOutput* MidiOutput::openDevice (int deviceIndex) { MidiOutput* newDevice = 0; StringArray devices; snd_seq_t* const handle = iterateDevices (false, devices, deviceIndex); if (handle != 0) { newDevice = new MidiOutput(); newDevice->internal = new MidiOutputDevice (newDevice, handle); } return newDevice; } MidiOutput* MidiOutput::createNewDevice (const String& deviceName) { MidiOutput* newDevice = 0; snd_seq_t* const handle = createDevice (false, deviceName); if (handle != 0) { newDevice = new MidiOutput(); newDevice->internal = new MidiOutputDevice (newDevice, handle); } return newDevice; } MidiOutput::~MidiOutput() { MidiOutputDevice* const device = (MidiOutputDevice*) internal; delete device; } void MidiOutput::reset() { } bool MidiOutput::getVolume (float& leftVol, float& rightVol) { return false; } void MidiOutput::setVolume (float leftVol, float rightVol) { } void MidiOutput::sendMessageNow (const MidiMessage& message) { ((MidiOutputDevice*) internal)->sendMessageNow (message); } //============================================================================== class MidiInputThread : public Thread { public: MidiInputThread (MidiInput* const midiInput_, snd_seq_t* const seqHandle_, MidiInputCallback* const callback_) : Thread (T("Juce MIDI Input")), midiInput (midiInput_), seqHandle (seqHandle_), callback (callback_) { jassert (seqHandle != 0 && callback != 0 && midiInput != 0); } ~MidiInputThread() { snd_seq_close (seqHandle); } void run() { const int maxEventSize = 16 * 1024; snd_midi_event_t* midiParser; if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) { uint8* const buffer = (uint8*) juce_malloc (maxEventSize); const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); struct pollfd* const pfd = (struct pollfd*) alloca (numPfds * sizeof (struct pollfd)); snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); while (! threadShouldExit()) { if (poll (pfd, numPfds, 500) > 0) { snd_seq_event_t* inputEvent = 0; snd_seq_nonblock (seqHandle, 1); do { if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) { // xxx what about SYSEXes that are too big for the buffer? const int numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent); snd_midi_event_reset_decode (midiParser); if (numBytes > 0) { const MidiMessage message ((const uint8*) buffer, numBytes, Time::getMillisecondCounter() * 0.001); callback->handleIncomingMidiMessage (midiInput, message); } snd_seq_free_event (inputEvent); } } while (snd_seq_event_input_pending (seqHandle, 0) > 0); snd_seq_free_event (inputEvent); } } snd_midi_event_free (midiParser); juce_free (buffer); } }; juce_UseDebuggingNewOperator private: MidiInput* const midiInput; snd_seq_t* const seqHandle; MidiInputCallback* const callback; }; //============================================================================== MidiInput::MidiInput (const String& name_) : name (name_), internal (0) { } MidiInput::~MidiInput() { stop(); MidiInputThread* const thread = (MidiInputThread*) internal; delete thread; } void MidiInput::start() { ((MidiInputThread*) internal)->startThread(); } void MidiInput::stop() { ((MidiInputThread*) internal)->stopThread (3000); } int MidiInput::getDefaultDeviceIndex() { return 0; } const StringArray MidiInput::getDevices() { StringArray devices; iterateDevices (true, devices, -1); return devices; } MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) { MidiInput* newDevice = 0; StringArray devices; snd_seq_t* const handle = iterateDevices (true, devices, deviceIndex); if (handle != 0) { newDevice = new MidiInput (devices [deviceIndex]); newDevice->internal = new MidiInputThread (newDevice, handle, callback); } return newDevice; } MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) { MidiInput* newDevice = 0; snd_seq_t* const handle = createDevice (true, deviceName); if (handle != 0) { newDevice = new MidiInput (deviceName); newDevice->internal = new MidiInputThread (newDevice, handle, callback); } return newDevice; } END_JUCE_NAMESPACE //============================================================================== #else //============================================================================== // (These are just stub functions if ALSA is unavailable...) #include "../../../src/juce_core/basics/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE #include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h" #include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h" //============================================================================== const StringArray MidiOutput::getDevices() { return StringArray(); } int MidiOutput::getDefaultDeviceIndex() { return 0; } MidiOutput* MidiOutput::openDevice (int) { return 0; } MidiOutput* MidiOutput::createNewDevice (const String&) { return 0; } MidiOutput::~MidiOutput() {} void MidiOutput::reset() {} bool MidiOutput::getVolume (float&, float&) { return false; } void MidiOutput::setVolume (float, float) {} void MidiOutput::sendMessageNow (const MidiMessage&) {} MidiInput::MidiInput (const String& name_) : name (name_), internal (0) {} MidiInput::~MidiInput() {} void MidiInput::start() {} void MidiInput::stop() {} int MidiInput::getDefaultDeviceIndex() { return 0; } const StringArray MidiInput::getDevices() { return StringArray(); } MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return 0; } MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return 0; } END_JUCE_NAMESPACE #endif #endif