|  | /*
  ==============================================================================
   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 <alsa/asoundlib.h>
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
 |