# HG changeset patch # User Chris Cannam # Date 1235389288 0 # Node ID 32d156c75df7ee7751add7dde7160f2fcfe5080e # Parent 1d7ebc05157e44ce1922bd867a47bdd473147585 * midi stubs (this revision will not compile) diff -r 1d7ebc05157e -r 32d156c75df7 data/fileio/MIDIEvent.h --- a/data/fileio/MIDIEvent.h Thu Feb 12 15:26:43 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,228 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - 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. See the file - COPYING included with this distribution for more information. -*/ - - -/* - This is a modified version of a source file from the - Rosegarden MIDI and audio sequencer and notation editor. - This file copyright 2000-2006 Richard Bown and Chris Cannam. -*/ - -#ifndef _MIDI_EVENT_H_ -#define _MIDI_EVENT_H_ - -#include -#include -#include - -typedef unsigned char MIDIByte; - -namespace MIDIConstants -{ - static const char *const MIDI_FILE_HEADER = "MThd"; - static const char *const MIDI_TRACK_HEADER = "MTrk"; - - static const MIDIByte MIDI_STATUS_BYTE_MASK = 0x80; - static const MIDIByte MIDI_MESSAGE_TYPE_MASK = 0xF0; - static const MIDIByte MIDI_CHANNEL_NUM_MASK = 0x0F; - - static const MIDIByte MIDI_NOTE_OFF = 0x80; - static const MIDIByte MIDI_NOTE_ON = 0x90; - static const MIDIByte MIDI_POLY_AFTERTOUCH = 0xA0; - static const MIDIByte MIDI_CTRL_CHANGE = 0xB0; - static const MIDIByte MIDI_PROG_CHANGE = 0xC0; - static const MIDIByte MIDI_CHNL_AFTERTOUCH = 0xD0; - static const MIDIByte MIDI_PITCH_BEND = 0xE0; - static const MIDIByte MIDI_SELECT_CHNL_MODE = 0xB0; - static const MIDIByte MIDI_SYSTEM_EXCLUSIVE = 0xF0; - static const MIDIByte MIDI_TC_QUARTER_FRAME = 0xF1; - static const MIDIByte MIDI_SONG_POSITION_PTR = 0xF2; - static const MIDIByte MIDI_SONG_SELECT = 0xF3; - static const MIDIByte MIDI_TUNE_REQUEST = 0xF6; - static const MIDIByte MIDI_END_OF_EXCLUSIVE = 0xF7; - static const MIDIByte MIDI_TIMING_CLOCK = 0xF8; - static const MIDIByte MIDI_START = 0xFA; - static const MIDIByte MIDI_CONTINUE = 0xFB; - static const MIDIByte MIDI_STOP = 0xFC; - static const MIDIByte MIDI_ACTIVE_SENSING = 0xFE; - static const MIDIByte MIDI_SYSTEM_RESET = 0xFF; - static const MIDIByte MIDI_SYSEX_NONCOMMERCIAL = 0x7D; - static const MIDIByte MIDI_SYSEX_NON_RT = 0x7E; - static const MIDIByte MIDI_SYSEX_RT = 0x7F; - static const MIDIByte MIDI_SYSEX_RT_COMMAND = 0x06; - static const MIDIByte MIDI_SYSEX_RT_RESPONSE = 0x07; - static const MIDIByte MIDI_MMC_STOP = 0x01; - static const MIDIByte MIDI_MMC_PLAY = 0x02; - static const MIDIByte MIDI_MMC_DEFERRED_PLAY = 0x03; - static const MIDIByte MIDI_MMC_FAST_FORWARD = 0x04; - static const MIDIByte MIDI_MMC_REWIND = 0x05; - static const MIDIByte MIDI_MMC_RECORD_STROBE = 0x06; - static const MIDIByte MIDI_MMC_RECORD_EXIT = 0x07; - static const MIDIByte MIDI_MMC_RECORD_PAUSE = 0x08; - static const MIDIByte MIDI_MMC_PAUSE = 0x08; - static const MIDIByte MIDI_MMC_EJECT = 0x0A; - static const MIDIByte MIDI_MMC_LOCATE = 0x44; - static const MIDIByte MIDI_FILE_META_EVENT = 0xFF; - static const MIDIByte MIDI_SEQUENCE_NUMBER = 0x00; - static const MIDIByte MIDI_TEXT_EVENT = 0x01; - static const MIDIByte MIDI_COPYRIGHT_NOTICE = 0x02; - static const MIDIByte MIDI_TRACK_NAME = 0x03; - static const MIDIByte MIDI_INSTRUMENT_NAME = 0x04; - static const MIDIByte MIDI_LYRIC = 0x05; - static const MIDIByte MIDI_TEXT_MARKER = 0x06; - static const MIDIByte MIDI_CUE_POINT = 0x07; - static const MIDIByte MIDI_CHANNEL_PREFIX = 0x20; - static const MIDIByte MIDI_CHANNEL_PREFIX_OR_PORT = 0x21; - static const MIDIByte MIDI_END_OF_TRACK = 0x2F; - static const MIDIByte MIDI_SET_TEMPO = 0x51; - static const MIDIByte MIDI_SMPTE_OFFSET = 0x54; - static const MIDIByte MIDI_TIME_SIGNATURE = 0x58; - static const MIDIByte MIDI_KEY_SIGNATURE = 0x59; - static const MIDIByte MIDI_SEQUENCER_SPECIFIC = 0x7F; - static const MIDIByte MIDI_CONTROLLER_BANK_MSB = 0x00; - static const MIDIByte MIDI_CONTROLLER_VOLUME = 0x07; - static const MIDIByte MIDI_CONTROLLER_BANK_LSB = 0x20; - static const MIDIByte MIDI_CONTROLLER_MODULATION = 0x01; - static const MIDIByte MIDI_CONTROLLER_PAN = 0x0A; - static const MIDIByte MIDI_CONTROLLER_SUSTAIN = 0x40; - static const MIDIByte MIDI_CONTROLLER_RESONANCE = 0x47; - static const MIDIByte MIDI_CONTROLLER_RELEASE = 0x48; - static const MIDIByte MIDI_CONTROLLER_ATTACK = 0x49; - static const MIDIByte MIDI_CONTROLLER_FILTER = 0x4A; - static const MIDIByte MIDI_CONTROLLER_REVERB = 0x5B; - static const MIDIByte MIDI_CONTROLLER_CHORUS = 0x5D; - static const MIDIByte MIDI_CONTROLLER_NRPN_1 = 0x62; - static const MIDIByte MIDI_CONTROLLER_NRPN_2 = 0x63; - static const MIDIByte MIDI_CONTROLLER_RPN_1 = 0x64; - static const MIDIByte MIDI_CONTROLLER_RPN_2 = 0x65; - static const MIDIByte MIDI_CONTROLLER_SOUNDS_OFF = 0x78; - static const MIDIByte MIDI_CONTROLLER_RESET = 0x79; - static const MIDIByte MIDI_CONTROLLER_LOCAL = 0x7A; - static const MIDIByte MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7B; - static const MIDIByte MIDI_PERCUSSION_CHANNEL = 9; -} - -class MIDIEvent -{ -public: - MIDIEvent(unsigned long deltaTime, - MIDIByte eventCode, - MIDIByte data1 = 0, - MIDIByte data2 = 0) : - m_deltaTime(deltaTime), - m_duration(0), - m_eventCode(eventCode), - m_data1(data1), - m_data2(data2), - m_metaEventCode(0) - { } - - MIDIEvent(unsigned long deltaTime, - MIDIByte eventCode, - MIDIByte metaEventCode, - const std::string &metaMessage) : - m_deltaTime(deltaTime), - m_duration(0), - m_eventCode(eventCode), - m_data1(0), - m_data2(0), - m_metaEventCode(metaEventCode), - m_metaMessage(metaMessage) - { } - - MIDIEvent(unsigned long deltaTime, - MIDIByte eventCode, - const std::string &sysEx) : - m_deltaTime(deltaTime), - m_duration(0), - m_eventCode(eventCode), - m_data1(0), - m_data2(0), - m_metaEventCode(0), - m_metaMessage(sysEx) - { } - - ~MIDIEvent() { } - - void setTime(const unsigned long &time) { m_deltaTime = time; } - void setDuration(const unsigned long& duration) { m_duration = duration;} - unsigned long addTime(const unsigned long &time) { - m_deltaTime += time; - return m_deltaTime; - } - - MIDIByte getMessageType() const - { return (m_eventCode & MIDIConstants::MIDI_MESSAGE_TYPE_MASK); } - - MIDIByte getChannelNumber() const - { return (m_eventCode & MIDIConstants::MIDI_CHANNEL_NUM_MASK); } - - unsigned long getTime() const { return m_deltaTime; } - unsigned long getDuration() const { return m_duration; } - - MIDIByte getPitch() const { return m_data1; } - MIDIByte getVelocity() const { return m_data2; } - MIDIByte getData1() const { return m_data1; } - MIDIByte getData2() const { return m_data2; } - MIDIByte getEventCode() const { return m_eventCode; } - - bool isMeta() const { return (m_eventCode == MIDIConstants::MIDI_FILE_META_EVENT); } - - MIDIByte getMetaEventCode() const { return m_metaEventCode; } - std::string getMetaMessage() const { return m_metaMessage; } - void setMetaMessage(const std::string &meta) { m_metaMessage = meta; } - - friend bool operator<(const MIDIEvent &a, const MIDIEvent &b); - -private: - MIDIEvent& operator=(const MIDIEvent); - - unsigned long m_deltaTime; - unsigned long m_duration; - MIDIByte m_eventCode; - MIDIByte m_data1; // or Note - MIDIByte m_data2; // or Velocity - MIDIByte m_metaEventCode; - std::string m_metaMessage; -}; - -// Comparator for sorting -// -struct MIDIEventCmp -{ - bool operator()(const MIDIEvent &mE1, const MIDIEvent &mE2) const - { return mE1.getTime() < mE2.getTime(); } - - bool operator()(const MIDIEvent *mE1, const MIDIEvent *mE2) const - { return mE1->getTime() < mE2->getTime(); } -}; - -class MIDIException : virtual public std::exception -{ -public: - MIDIException(QString message) throw() : m_message(message) { - std::cerr << "WARNING: MIDI exception: " - << message.toLocal8Bit().data() << std::endl; - } - virtual ~MIDIException() throw() { } - - virtual const char *what() const throw() { - return m_message.toLocal8Bit().data(); - } - -protected: - QString m_message; -}; - -#endif diff -r 1d7ebc05157e -r 32d156c75df7 data/midi/MIDIEvent.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/midi/MIDIEvent.h Mon Feb 23 11:41:28 2009 +0000 @@ -0,0 +1,228 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. See the file + COPYING included with this distribution for more information. +*/ + + +/* + This is a modified version of a source file from the + Rosegarden MIDI and audio sequencer and notation editor. + This file copyright 2000-2006 Richard Bown and Chris Cannam. +*/ + +#ifndef _MIDI_EVENT_H_ +#define _MIDI_EVENT_H_ + +#include +#include +#include + +typedef unsigned char MIDIByte; + +namespace MIDIConstants +{ + static const char *const MIDI_FILE_HEADER = "MThd"; + static const char *const MIDI_TRACK_HEADER = "MTrk"; + + static const MIDIByte MIDI_STATUS_BYTE_MASK = 0x80; + static const MIDIByte MIDI_MESSAGE_TYPE_MASK = 0xF0; + static const MIDIByte MIDI_CHANNEL_NUM_MASK = 0x0F; + + static const MIDIByte MIDI_NOTE_OFF = 0x80; + static const MIDIByte MIDI_NOTE_ON = 0x90; + static const MIDIByte MIDI_POLY_AFTERTOUCH = 0xA0; + static const MIDIByte MIDI_CTRL_CHANGE = 0xB0; + static const MIDIByte MIDI_PROG_CHANGE = 0xC0; + static const MIDIByte MIDI_CHNL_AFTERTOUCH = 0xD0; + static const MIDIByte MIDI_PITCH_BEND = 0xE0; + static const MIDIByte MIDI_SELECT_CHNL_MODE = 0xB0; + static const MIDIByte MIDI_SYSTEM_EXCLUSIVE = 0xF0; + static const MIDIByte MIDI_TC_QUARTER_FRAME = 0xF1; + static const MIDIByte MIDI_SONG_POSITION_PTR = 0xF2; + static const MIDIByte MIDI_SONG_SELECT = 0xF3; + static const MIDIByte MIDI_TUNE_REQUEST = 0xF6; + static const MIDIByte MIDI_END_OF_EXCLUSIVE = 0xF7; + static const MIDIByte MIDI_TIMING_CLOCK = 0xF8; + static const MIDIByte MIDI_START = 0xFA; + static const MIDIByte MIDI_CONTINUE = 0xFB; + static const MIDIByte MIDI_STOP = 0xFC; + static const MIDIByte MIDI_ACTIVE_SENSING = 0xFE; + static const MIDIByte MIDI_SYSTEM_RESET = 0xFF; + static const MIDIByte MIDI_SYSEX_NONCOMMERCIAL = 0x7D; + static const MIDIByte MIDI_SYSEX_NON_RT = 0x7E; + static const MIDIByte MIDI_SYSEX_RT = 0x7F; + static const MIDIByte MIDI_SYSEX_RT_COMMAND = 0x06; + static const MIDIByte MIDI_SYSEX_RT_RESPONSE = 0x07; + static const MIDIByte MIDI_MMC_STOP = 0x01; + static const MIDIByte MIDI_MMC_PLAY = 0x02; + static const MIDIByte MIDI_MMC_DEFERRED_PLAY = 0x03; + static const MIDIByte MIDI_MMC_FAST_FORWARD = 0x04; + static const MIDIByte MIDI_MMC_REWIND = 0x05; + static const MIDIByte MIDI_MMC_RECORD_STROBE = 0x06; + static const MIDIByte MIDI_MMC_RECORD_EXIT = 0x07; + static const MIDIByte MIDI_MMC_RECORD_PAUSE = 0x08; + static const MIDIByte MIDI_MMC_PAUSE = 0x08; + static const MIDIByte MIDI_MMC_EJECT = 0x0A; + static const MIDIByte MIDI_MMC_LOCATE = 0x44; + static const MIDIByte MIDI_FILE_META_EVENT = 0xFF; + static const MIDIByte MIDI_SEQUENCE_NUMBER = 0x00; + static const MIDIByte MIDI_TEXT_EVENT = 0x01; + static const MIDIByte MIDI_COPYRIGHT_NOTICE = 0x02; + static const MIDIByte MIDI_TRACK_NAME = 0x03; + static const MIDIByte MIDI_INSTRUMENT_NAME = 0x04; + static const MIDIByte MIDI_LYRIC = 0x05; + static const MIDIByte MIDI_TEXT_MARKER = 0x06; + static const MIDIByte MIDI_CUE_POINT = 0x07; + static const MIDIByte MIDI_CHANNEL_PREFIX = 0x20; + static const MIDIByte MIDI_CHANNEL_PREFIX_OR_PORT = 0x21; + static const MIDIByte MIDI_END_OF_TRACK = 0x2F; + static const MIDIByte MIDI_SET_TEMPO = 0x51; + static const MIDIByte MIDI_SMPTE_OFFSET = 0x54; + static const MIDIByte MIDI_TIME_SIGNATURE = 0x58; + static const MIDIByte MIDI_KEY_SIGNATURE = 0x59; + static const MIDIByte MIDI_SEQUENCER_SPECIFIC = 0x7F; + static const MIDIByte MIDI_CONTROLLER_BANK_MSB = 0x00; + static const MIDIByte MIDI_CONTROLLER_VOLUME = 0x07; + static const MIDIByte MIDI_CONTROLLER_BANK_LSB = 0x20; + static const MIDIByte MIDI_CONTROLLER_MODULATION = 0x01; + static const MIDIByte MIDI_CONTROLLER_PAN = 0x0A; + static const MIDIByte MIDI_CONTROLLER_SUSTAIN = 0x40; + static const MIDIByte MIDI_CONTROLLER_RESONANCE = 0x47; + static const MIDIByte MIDI_CONTROLLER_RELEASE = 0x48; + static const MIDIByte MIDI_CONTROLLER_ATTACK = 0x49; + static const MIDIByte MIDI_CONTROLLER_FILTER = 0x4A; + static const MIDIByte MIDI_CONTROLLER_REVERB = 0x5B; + static const MIDIByte MIDI_CONTROLLER_CHORUS = 0x5D; + static const MIDIByte MIDI_CONTROLLER_NRPN_1 = 0x62; + static const MIDIByte MIDI_CONTROLLER_NRPN_2 = 0x63; + static const MIDIByte MIDI_CONTROLLER_RPN_1 = 0x64; + static const MIDIByte MIDI_CONTROLLER_RPN_2 = 0x65; + static const MIDIByte MIDI_CONTROLLER_SOUNDS_OFF = 0x78; + static const MIDIByte MIDI_CONTROLLER_RESET = 0x79; + static const MIDIByte MIDI_CONTROLLER_LOCAL = 0x7A; + static const MIDIByte MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7B; + static const MIDIByte MIDI_PERCUSSION_CHANNEL = 9; +} + +class MIDIEvent +{ +public: + MIDIEvent(unsigned long deltaTime, + MIDIByte eventCode, + MIDIByte data1 = 0, + MIDIByte data2 = 0) : + m_deltaTime(deltaTime), + m_duration(0), + m_eventCode(eventCode), + m_data1(data1), + m_data2(data2), + m_metaEventCode(0) + { } + + MIDIEvent(unsigned long deltaTime, + MIDIByte eventCode, + MIDIByte metaEventCode, + const std::string &metaMessage) : + m_deltaTime(deltaTime), + m_duration(0), + m_eventCode(eventCode), + m_data1(0), + m_data2(0), + m_metaEventCode(metaEventCode), + m_metaMessage(metaMessage) + { } + + MIDIEvent(unsigned long deltaTime, + MIDIByte eventCode, + const std::string &sysEx) : + m_deltaTime(deltaTime), + m_duration(0), + m_eventCode(eventCode), + m_data1(0), + m_data2(0), + m_metaEventCode(0), + m_metaMessage(sysEx) + { } + + ~MIDIEvent() { } + + void setTime(const unsigned long &time) { m_deltaTime = time; } + void setDuration(const unsigned long& duration) { m_duration = duration;} + unsigned long addTime(const unsigned long &time) { + m_deltaTime += time; + return m_deltaTime; + } + + MIDIByte getMessageType() const + { return (m_eventCode & MIDIConstants::MIDI_MESSAGE_TYPE_MASK); } + + MIDIByte getChannelNumber() const + { return (m_eventCode & MIDIConstants::MIDI_CHANNEL_NUM_MASK); } + + unsigned long getTime() const { return m_deltaTime; } + unsigned long getDuration() const { return m_duration; } + + MIDIByte getPitch() const { return m_data1; } + MIDIByte getVelocity() const { return m_data2; } + MIDIByte getData1() const { return m_data1; } + MIDIByte getData2() const { return m_data2; } + MIDIByte getEventCode() const { return m_eventCode; } + + bool isMeta() const { return (m_eventCode == MIDIConstants::MIDI_FILE_META_EVENT); } + + MIDIByte getMetaEventCode() const { return m_metaEventCode; } + std::string getMetaMessage() const { return m_metaMessage; } + void setMetaMessage(const std::string &meta) { m_metaMessage = meta; } + + friend bool operator<(const MIDIEvent &a, const MIDIEvent &b); + +private: + MIDIEvent& operator=(const MIDIEvent); + + unsigned long m_deltaTime; + unsigned long m_duration; + MIDIByte m_eventCode; + MIDIByte m_data1; // or Note + MIDIByte m_data2; // or Velocity + MIDIByte m_metaEventCode; + std::string m_metaMessage; +}; + +// Comparator for sorting +// +struct MIDIEventCmp +{ + bool operator()(const MIDIEvent &mE1, const MIDIEvent &mE2) const + { return mE1.getTime() < mE2.getTime(); } + + bool operator()(const MIDIEvent *mE1, const MIDIEvent *mE2) const + { return mE1->getTime() < mE2->getTime(); } +}; + +class MIDIException : virtual public std::exception +{ +public: + MIDIException(QString message) throw() : m_message(message) { + std::cerr << "WARNING: MIDI exception: " + << message.toLocal8Bit().data() << std::endl; + } + virtual ~MIDIException() throw() { } + + virtual const char *what() const throw() { + return m_message.toLocal8Bit().data(); + } + +protected: + QString m_message; +}; + +#endif diff -r 1d7ebc05157e -r 32d156c75df7 data/midi/MIDIInput.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/midi/MIDIInput.h Mon Feb 23 11:41:28 2009 +0000 @@ -0,0 +1,52 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2009 Chris Cannam and QMUL. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _MIDI_INPUT_H_ +#define _MIDI_INPUT_H_ + +#include +#include "MIDIEvent.h" + +class RtMidi; + +class MIDIInput : public QObject +{ + Q_OBJECT + +public: + MIDIInput(); + virtual ~MIDIInput(); + + bool isOK() const; + + bool isEmpty() const { return getEventsAvailable() == 0; } + size_t getEventsAvailable() const; + MIDIEvent readEvent(); + +signals: + void eventsAvailable(); + +protected: + RtMidi *m_rtmidi; + + static void callback(double, std::vector *, void *); + void callback(double, std::vector *); + + void postEvent(MIDIEvent); + RingBuffer m_buffer; +}; + +#endif + diff -r 1d7ebc05157e -r 32d156c75df7 data/midi/rtmidi/RtError.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/midi/rtmidi/RtError.h Mon Feb 23 11:41:28 2009 +0000 @@ -0,0 +1,60 @@ +/************************************************************************/ +/*! \class RtError + \brief Exception handling class for RtAudio & RtMidi. + + The RtError class is quite simple but it does allow errors to be + "caught" by RtError::Type. See the RtAudio and RtMidi + documentation to know which methods can throw an RtError. + +*/ +/************************************************************************/ + +#ifndef RTERROR_H +#define RTERROR_H + +#include +#include + +class RtError +{ +public: + //! Defined RtError types. + enum Type { + WARNING, /*!< A non-critical error. */ + DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ + UNSPECIFIED, /*!< The default, unspecified error type. */ + NO_DEVICES_FOUND, /*!< No devices found on system. */ + INVALID_DEVICE, /*!< An invalid device ID was specified. */ + INVALID_STREAM, /*!< An invalid stream ID was specified. */ + MEMORY_ERROR, /*!< An error occured during memory allocation. */ + INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ + DRIVER_ERROR, /*!< A system driver error occured. */ + SYSTEM_ERROR, /*!< A system error occured. */ + THREAD_ERROR /*!< A thread error occured. */ + }; + +protected: + std::string message_; + Type type_; + +public: + //! The constructor. + RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type) {} + + //! The destructor. + virtual ~RtError(void) {}; + + //! Prints thrown error message to stderr. + virtual void printMessage(void) { std::cerr << '\n' << message_ << "\n\n"; } + + //! Returns the thrown error message type. + virtual const Type& getType(void) { return type_; } + + //! Returns the thrown error message string. + virtual const std::string& getMessage(void) { return message_; } + + //! Returns the thrown error message as a C string. + virtual const char *getMessageString(void) { return message_.c_str(); } +}; + +#endif diff -r 1d7ebc05157e -r 32d156c75df7 data/midi/rtmidi/RtMidi.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/midi/rtmidi/RtMidi.cpp Mon Feb 23 11:41:28 2009 +0000 @@ -0,0 +1,2124 @@ +/**********************************************************************/ +/*! \class RtMidi + \brief An abstract base class for realtime MIDI input/output. + + This class implements some common functionality for the realtime + MIDI input/output subclasses RtMidiIn and RtMidiOut. + + RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ + + RtMidi: realtime MIDI i/o C++ classes + Copyright (c) 2003-2007 Gary P. Scavone + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/**********************************************************************/ + +// RtMidi: Version 1.0.7 + +#include "RtMidi.h" +#include + +//*********************************************************************// +// Common RtMidi Definitions +//*********************************************************************// + +RtMidi :: RtMidi() + : apiData_( 0 ), connected_( false ) +{ +} + +void RtMidi :: error( RtError::Type type ) +{ + if (type == RtError::WARNING) { + std::cerr << '\n' << errorString_ << "\n\n"; + } + else if (type == RtError::DEBUG_WARNING) { +#if defined(__RTMIDI_DEBUG__) + std::cerr << '\n' << errorString_ << "\n\n"; +#endif + } + else { + std::cerr << '\n' << errorString_ << "\n\n"; + throw RtError( errorString_, type ); + } +} + +//*********************************************************************// +// Common RtMidiIn Definitions +//*********************************************************************// + +RtMidiIn :: RtMidiIn() : RtMidi() +{ + this->initialize(); +} + +void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) +{ + if ( inputData_.usingCallback ) { + errorString_ = "RtMidiIn::setCallback: a callback function is already set!"; + error( RtError::WARNING ); + return; + } + + if ( !callback ) { + errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; + error( RtError::WARNING ); + return; + } + + inputData_.userCallback = (void *) callback; + inputData_.userData = userData; + inputData_.usingCallback = true; +} + +void RtMidiIn :: cancelCallback() +{ + if ( !inputData_.usingCallback ) { + errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; + error( RtError::WARNING ); + return; + } + + inputData_.userCallback = 0; + inputData_.userData = 0; + inputData_.usingCallback = false; +} + +void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize ) +{ + inputData_.queueLimit = queueSize; +} + +void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) +{ + inputData_.ignoreFlags = 0; + if ( midiSysex ) inputData_.ignoreFlags = 0x01; + if ( midiTime ) inputData_.ignoreFlags |= 0x02; + if ( midiSense ) inputData_.ignoreFlags |= 0x04; +} + +double RtMidiIn :: getMessage( std::vector *message ) +{ + message->clear(); + + if ( inputData_.usingCallback ) { + errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; + error( RtError::WARNING ); + return 0.0; + } + + if ( inputData_.queue.size() == 0 ) return 0.0; + + // Copy queued message to the vector pointer argument and then "pop" it. + std::vector *bytes = &(inputData_.queue.front().bytes); + message->assign( bytes->begin(), bytes->end() ); + double deltaTime = inputData_.queue.front().timeStamp; + inputData_.queue.pop(); + + return deltaTime; +} + +//*********************************************************************// +// Common RtMidiOut Definitions +//*********************************************************************// + +RtMidiOut :: RtMidiOut() : RtMidi() +{ + this->initialize(); +} + + +//*********************************************************************// +// API: Macintosh OS-X +//*********************************************************************// + +// API information found at: +// - http://developer. apple .com/audio/pdf/coreaudio.pdf + +#if defined(__MACOSX_CORE__) + +// The CoreMIDI API is based on the use of a callback function for +// MIDI input. We convert the system specific time stamps to delta +// time values. + +// OS-X CoreMIDI header files. +#include +#include + +// A structure to hold variables related to the CoreMIDI API +// implementation. +struct CoreMidiData { + MIDIClientRef client; + MIDIPortRef port; + MIDIEndpointRef endpoint; + MIDIEndpointRef destinationId; + unsigned long long lastTime; +}; + +//*********************************************************************// +// API: OS-X +// Class Definitions: RtMidiIn +//*********************************************************************// + +void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) +{ + RtMidiIn::RtMidiInData *data = static_cast (procRef); + CoreMidiData *apiData = static_cast (data->apiData); + + bool continueSysex = false; + unsigned char status; + unsigned short nBytes, iByte, size; + unsigned long long time; + RtMidiIn::MidiMessage message; + const MIDIPacket *packet = &list->packet[0]; + for ( unsigned int i=0; inumPackets; ++i ) { + + // My interpretation of the CoreMIDI documentation: all message + // types, except sysex, are complete within a packet and there may + // be several of them in a single packet. Sysex messages can be + // broken across multiple packets but are bundled alone within a + // packet. I'm assuming that sysex messages, if segmented, must + // be complete within the same MIDIPacketList. + + nBytes = packet->length; + if ( nBytes == 0 ) continue; + + // Calculate time stamp. + message.timeStamp = 0.0; + if ( data->firstMessage ) + data->firstMessage = false; + else { + time = packet->timeStamp; + time -= apiData->lastTime; + time = AudioConvertHostTimeToNanos( time ); + message.timeStamp = time * 0.000000001; + } + apiData->lastTime = packet->timeStamp; + + iByte = 0; + if ( continueSysex ) { + // We have a continuing, segmented sysex message. + if ( !(data->ignoreFlags & 0x01) ) { + // If we're not ignoring sysex messages, copy the entire packet. + for ( unsigned int j=0; jdata[j] ); + } + if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; + if ( !continueSysex ) { + // If not a continuing sysex message, invoke the user callback function or queue the message. + if ( data->usingCallback && message.bytes.size() > 0 ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + } + else { + while ( iByte < nBytes ) { + size = 0; + // We are expecting that the next byte in the packet is a status byte. + status = packet->data[iByte]; + if ( !(status & 0x80) ) break; + // Determine the number of bytes in the MIDI message. + if ( status < 0xC0 ) size = 3; + else if ( status < 0xE0 ) size = 2; + else if ( status < 0xF0 ) size = 3; + else if ( status == 0xF0 ) { + // A MIDI sysex + if ( data->ignoreFlags & 0x01 ) { + size = 0; + iByte = nBytes; + } + else size = nBytes - iByte; + if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; + } + else if ( status < 0xF3 ) { + if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) { + // A MIDI time code message and we're ignoring it. + size = 0; + iByte += 3; + } + else size = 3; + } + else if ( status == 0xF3 ) size = 2; + else if ( status == 0xF8 ) { + size = 1; + if ( data->ignoreFlags & 0x02 ) { + // A MIDI timing tick message and we're ignoring it. + size = 0; + iByte += 3; + } + } + else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { + // A MIDI active sensing message and we're ignoring it. + size = 0; + iByte += 1; + } + else size = 1; + + // Copy the MIDI data to our vector. + if ( size ) { + message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); + if ( !continueSysex ) { + // If not a continuing sysex message, invoke the user callback function or queue the message. + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + iByte += size; + } + } + } + packet = MIDIPacketNext(packet); + } +} + +void RtMidiIn :: initialize( void ) +{ + // Set up our client. + MIDIClientRef client; + OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client ); + if ( result != noErr ) { + errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + CoreMidiData *data = (CoreMidiData *) new CoreMidiData; + data->client = client; + data->endpoint = 0; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; +} + +void RtMidiIn :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nSrc = MIDIGetNumberOfSources(); + if (nSrc < 1) { + errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nSrc ) { + ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + MIDIPortRef port; + CoreMidiData *data = static_cast (apiData_); + OSStatus result = MIDIInputPortCreate( data->client, CFSTR("RtMidi MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port ); + if ( result != noErr ) { + MIDIClientDispose( data->client ); + errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port."; + error( RtError::DRIVER_ERROR ); + } + + // Get the desired input source identifier. + MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); + if ( endpoint == NULL ) { + MIDIPortDispose( port ); + MIDIClientDispose( data->client ); + errorString_ = "RtMidiIn::openPort: error getting MIDI input source reference."; + error( RtError::DRIVER_ERROR ); + } + + // Make the connection. + result = MIDIPortConnectSource( port, endpoint, NULL ); + if ( result != noErr ) { + MIDIPortDispose( port ); + MIDIClientDispose( data->client ); + errorString_ = "RtMidiIn::openPort: error connecting OS-X MIDI input port."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific port information. + data->port = port; + + connected_ = true; +} + +void RtMidiIn :: openVirtualPort( const std::string portName ) +{ + CoreMidiData *data = static_cast (apiData_); + + // Create a virtual MIDI input destination. + MIDIEndpointRef endpoint; + OSStatus result = MIDIDestinationCreate( data->client, + CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + midiInputCallback, (void *)&inputData_, &endpoint ); + if ( result != noErr ) { + errorString_ = "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + data->endpoint = endpoint; +} + +void RtMidiIn :: closePort( void ) +{ + if ( connected_ ) { + CoreMidiData *data = static_cast (apiData_); + MIDIPortDispose( data->port ); + connected_ = false; + } +} + +RtMidiIn :: ~RtMidiIn() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + MIDIClientDispose( data->client ); + if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); + delete data; +} + +unsigned int RtMidiIn :: getPortCount() +{ + return MIDIGetNumberOfSources(); +} + +std::string RtMidiIn :: getPortName( unsigned int portNumber ) +{ + CFStringRef nameRef; + MIDIEndpointRef portRef; + std::ostringstream ost; + char name[128]; + + if ( portNumber >= MIDIGetNumberOfSources() ) { + ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + portRef = MIDIGetSource( portNumber ); + + MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); + CFStringGetCString( nameRef, name, sizeof(name), 0); + CFRelease( nameRef ); + std::string stringName = name; + return stringName; +} + +//*********************************************************************// +// API: OS-X +// Class Definitions: RtMidiOut +//*********************************************************************// + +unsigned int RtMidiOut :: getPortCount() +{ + return MIDIGetNumberOfDestinations(); +} + +std::string RtMidiOut :: getPortName( unsigned int portNumber ) +{ + CFStringRef nameRef; + MIDIEndpointRef portRef; + std::ostringstream ost; + char name[128]; + + if ( portNumber >= MIDIGetNumberOfDestinations() ) { + ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + portRef = MIDIGetDestination( portNumber ); + + MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); + CFStringGetCString( nameRef, name, sizeof(name), 0); + CFRelease( nameRef ); + std::string stringName = name; + return stringName; +} + +void RtMidiOut :: initialize( void ) +{ + // Set up our client. + MIDIClientRef client; + OSStatus result = MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL, NULL, &client ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + CoreMidiData *data = (CoreMidiData *) new CoreMidiData; + data->client = client; + data->endpoint = 0; + apiData_ = (void *) data; +} + +void RtMidiOut :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nDest = MIDIGetNumberOfDestinations(); + if (nDest < 1) { + errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nDest ) { + ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + MIDIPortRef port; + CoreMidiData *data = static_cast (apiData_); + OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("RtMidi Virtual MIDI Output Port"), &port ); + if ( result != noErr ) { + MIDIClientDispose( data->client ); + errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; + error( RtError::DRIVER_ERROR ); + } + + // Get the desired output port identifier. + MIDIEndpointRef destination = MIDIGetDestination( portNumber ); + if ( destination == NULL ) { + MIDIPortDispose( port ); + MIDIClientDispose( data->client ); + errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + data->port = port; + data->destinationId = destination; + connected_ = true; +} + +void RtMidiOut :: closePort( void ) +{ + if ( connected_ ) { + CoreMidiData *data = static_cast (apiData_); + MIDIPortDispose( data->port ); + connected_ = false; + } +} + +void RtMidiOut :: openVirtualPort( std::string portName ) +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!"; + error( RtError::WARNING ); + return; + } + + // Create a virtual MIDI output source. + MIDIEndpointRef endpoint; + OSStatus result = MIDISourceCreate( data->client, + CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + &endpoint ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + data->endpoint = endpoint; +} + +RtMidiOut :: ~RtMidiOut() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + MIDIClientDispose( data->client ); + if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); + delete data; +} + +void RtMidiOut :: sendMessage( std::vector *message ) +{ + unsigned int nBytes = message->size(); + // Pad the buffer for extra (unknown) structure data. + Byte buffer[nBytes+32]; + MIDIPacketList *pktlist = (MIDIPacketList *) buffer; + MIDIPacket *curPacket = MIDIPacketListInit( pktlist ); + + MIDITimeStamp timeStamp = 0; + curPacket = MIDIPacketListAdd( pktlist, sizeof(buffer), curPacket, timeStamp, nBytes, &message->at(0) ); + + CoreMidiData *data = static_cast (apiData_); + + // Send to any destinations that may have connected to us. + OSStatus result; + if ( data->endpoint ) { + result = MIDIReceived( data->endpoint, pktlist ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations."; + error( RtError::WARNING ); + } + } + + // And send to an explicit destination port if we're connected. + if ( connected_ ) { + result = MIDISend( data->port, data->destinationId, pktlist ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; + error( RtError::WARNING ); + } + } +} + +#endif // __MACOSX_CORE__ + + +//*********************************************************************// +// API: LINUX ALSA SEQUENCER +//*********************************************************************// + +// API information found at: +// - http://www.alsa-project.org/documentation.php#Library + +#if defined(__LINUX_ALSASEQ__) + +// The ALSA Sequencer API is based on the use of a callback function for +// MIDI input. +// +// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer +// time stamps and other assorted fixes!!! + +#include +#include + +// ALSA header file. +#include + +// A structure to hold variables related to the ALSA API +// implementation. +struct AlsaMidiData { + snd_seq_t *seq; + int vport; + snd_seq_port_subscribe_t *subscription; + snd_midi_event_t *coder; + unsigned int bufferSize; + unsigned char *buffer; + pthread_t thread; + unsigned long long lastTime; + int queue_id; // an input queue is needed to get timestamped events +}; + +#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) + +//*********************************************************************// +// API: LINUX ALSA +// Class Definitions: RtMidiIn +//*********************************************************************// + +extern "C" void *alsaMidiHandler( void *ptr ) +{ + RtMidiIn::RtMidiInData *data = static_cast (ptr); + AlsaMidiData *apiData = static_cast (data->apiData); + + long nBytes; + unsigned long long time, lastTime; + bool continueSysex = false; + RtMidiIn::MidiMessage message; + + snd_seq_event_t *ev; + int result; + apiData->bufferSize = 32; + result = snd_midi_event_new( 0, &apiData->coder ); + if ( result < 0 ) { + data->doInput = false; + std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n"; + return 0; + } + unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); + if ( buffer == NULL ) { + data->doInput = false; + std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n"; + return 0; + } + snd_midi_event_init( apiData->coder ); + snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages + + while ( data->doInput ) { + + if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { + // No data pending ... sleep a bit. + usleep( 1000 ); + continue; + } + + // If here, there should be data. + result = snd_seq_event_input( apiData->seq, &ev ); + if ( result == -ENOSPC ) { + std::cerr << "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n"; + continue; + } + else if ( result <= 0 ) { + std::cerr << "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n"; + continue; + } + + // This is a bit weird, but we now have to decode an ALSA MIDI + // event (back) into MIDI bytes. We'll ignore non-MIDI types. + message.bytes.clear(); + switch ( ev->type ) { + + case SND_SEQ_EVENT_PORT_SUBSCRIBED: +#if defined(__RTMIDI_DEBUG__) + std::cout << "RtMidiIn::alsaMidiHandler: port connection made!\n"; +#endif + break; + + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: + std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; + data->doInput = false; + break; + + case SND_SEQ_EVENT_QFRAME: // MIDI time code + if ( data->ignoreFlags & 0x02 ) break; + + case SND_SEQ_EVENT_TICK: // MIDI timing tick + if ( data->ignoreFlags & 0x02 ) break; + + case SND_SEQ_EVENT_SENSING: // Active sensing + if ( data->ignoreFlags & 0x04 ) break; + + case SND_SEQ_EVENT_SYSEX: + if ( (data->ignoreFlags & 0x01) ) break; + if ( ev->data.ext.len > apiData->bufferSize ) { + apiData->bufferSize = ev->data.ext.len; + free( buffer ); + buffer = (unsigned char *) malloc( apiData->bufferSize ); + if ( buffer == NULL ) { + data->doInput = false; + std::cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n"; + break; + } + } + + default: + nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); + if ( nBytes <= 0 ) { +#if defined(__RTMIDI_DEBUG__) + std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; +#endif + break; + } + + // The ALSA sequencer has a maximum buffer size for MIDI sysex + // events of 256 bytes. If a device sends sysex messages larger + // than this, they are segmented into 256 byte chunks. So, + // we'll watch for this and concatenate sysex chunks into a + // single sysex message if necessary. + if ( !continueSysex ) + message.bytes.assign( buffer, &buffer[nBytes] ); + else + message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); + + continueSysex = ( ev->type == SND_SEQ_EVENT_SYSEX && message.bytes.back() != 0xF7 ); + if ( continueSysex ) + break; + + // Calculate the time stamp: + message.timeStamp = 0.0; + + // Method 1: Use the system time. + //(void)gettimeofday(&tv, (struct timezone *)NULL); + //time = (tv.tv_sec * 1000000) + tv.tv_usec; + + // Method 2: Use the ALSA sequencer event time data. + // (thanks to Pedro Lopez-Cabanillas!). + time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 ); + lastTime = time; + time -= apiData->lastTime; + apiData->lastTime = lastTime; + if ( data->firstMessage == true ) + data->firstMessage = false; + else + message.timeStamp = time * 0.000001; + } + + snd_seq_free_event(ev); + if ( message.bytes.size() == 0 ) continue; + + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + } + + if ( buffer ) free( buffer ); + snd_midi_event_free( apiData->coder ); + apiData->coder = 0; + return 0; +} + +void RtMidiIn :: initialize( void ) +{ + // Set up the ALSA sequencer client. + snd_seq_t *seq; + int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); + if ( result < 0 ) { + errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object."; + error( RtError::DRIVER_ERROR ); + } + + // Set client name. + snd_seq_set_client_name(seq, "RtMidi Input Client"); + + // Save our api-specific connection information. + AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; + data->seq = seq; + data->vport = -1; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; + + // Create the input queue + data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue"); + // Set arbitrary tempo (mm=100) and resolution (240) + snd_seq_queue_tempo_t *qtempo; + snd_seq_queue_tempo_alloca(&qtempo); + snd_seq_queue_tempo_set_tempo(qtempo, 600000); + snd_seq_queue_tempo_set_ppq(qtempo, 240); + snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); + snd_seq_drain_output(data->seq); +} + +// This function is used to count or get the pinfo structure for a given port number. +unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) +{ + snd_seq_client_info_t *cinfo; + int client; + int count = 0; + snd_seq_client_info_alloca( &cinfo ); + + snd_seq_client_info_set_client( cinfo, -1 ); + while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { + client = snd_seq_client_info_get_client( cinfo ); + if ( client == 0 ) continue; + // Reset query info + snd_seq_port_info_set_client( pinfo, client ); + snd_seq_port_info_set_port( pinfo, -1 ); + while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { + if ( !PORT_TYPE( pinfo, type ) ) continue; + if ( count == portNumber ) return 1; + count++; + } + } + + // If a negative portNumber was used, return the port count. + if ( portNumber < 0 ) return count; + return 0; +} + +void RtMidiIn :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nSrc = this->getPortCount(); + if (nSrc < 1) { + errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + std::ostringstream ost; + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { + ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + + snd_seq_addr_t sender, receiver; + sender.client = snd_seq_port_info_get_client( pinfo ); + sender.port = snd_seq_port_info_get_port( pinfo ); + receiver.client = snd_seq_client_id( data->seq ); + if ( data->vport < 0 ) { + snd_seq_port_info_set_client( pinfo, 0 ); + snd_seq_port_info_set_port( pinfo, 0 ); + snd_seq_port_info_set_capability( pinfo, + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE ); + snd_seq_port_info_set_type( pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION ); + snd_seq_port_info_set_midi_channels(pinfo, 16); + snd_seq_port_info_set_timestamping(pinfo, 1); + snd_seq_port_info_set_timestamp_real(pinfo, 1); + snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); + snd_seq_port_info_set_name(pinfo, "RtMidi Input"); + data->vport = snd_seq_create_port(data->seq, pinfo); + + if ( data->vport < 0 ) { + errorString_ = "RtMidiIn::openPort: ALSA error creating input port."; + error( RtError::DRIVER_ERROR ); + } + } + + receiver.port = data->vport; + + // Make subscription + snd_seq_port_subscribe_malloc( &data->subscription ); + snd_seq_port_subscribe_set_sender(data->subscription, &sender); + snd_seq_port_subscribe_set_dest(data->subscription, &receiver); + if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { + errorString_ = "RtMidiIn::openPort: ALSA error making port connection."; + error( RtError::DRIVER_ERROR ); + } + + if ( inputData_.doInput == false ) { + // Start the input queue + snd_seq_start_queue( data->seq, data->queue_id, NULL ); + snd_seq_drain_output( data->seq ); + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + + inputData_.doInput = true; + int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); + pthread_attr_destroy(&attr); + if (err) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + inputData_.doInput = false; + errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; + error( RtError::THREAD_ERROR ); + } + } + + connected_ = true; +} + +void RtMidiIn :: openVirtualPort( std::string portName ) +{ + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport < 0 ) { + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + snd_seq_port_info_set_capability( pinfo, + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE ); + snd_seq_port_info_set_type( pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION ); + snd_seq_port_info_set_midi_channels(pinfo, 16); + snd_seq_port_info_set_timestamping(pinfo, 1); + snd_seq_port_info_set_timestamp_real(pinfo, 1); + snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); + snd_seq_port_info_set_name(pinfo, portName.c_str()); + data->vport = snd_seq_create_port(data->seq, pinfo); + + if ( data->vport < 0 ) { + errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port."; + error( RtError::DRIVER_ERROR ); + } + } + + if ( inputData_.doInput == false ) { + // Start the input queue + snd_seq_start_queue( data->seq, data->queue_id, NULL ); + snd_seq_drain_output( data->seq ); + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + + inputData_.doInput = true; + int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); + pthread_attr_destroy(&attr); + if (err) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + inputData_.doInput = false; + errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; + error( RtError::THREAD_ERROR ); + } + } +} + +void RtMidiIn :: closePort( void ) +{ + if ( connected_ ) { + AlsaMidiData *data = static_cast (apiData_); + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + // Stop the input queue + snd_seq_stop_queue( data->seq, data->queue_id, NULL ); + snd_seq_drain_output( data->seq ); + connected_ = false; + } +} + +RtMidiIn :: ~RtMidiIn() +{ + // Close a connection if it exists. + closePort(); + + // Shutdown the input thread. + AlsaMidiData *data = static_cast (apiData_); + if ( inputData_.doInput ) { + inputData_.doInput = false; + pthread_join( data->thread, NULL ); + } + + // Cleanup. + if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); + snd_seq_free_queue( data->seq, data->queue_id ); + snd_seq_close( data->seq ); + delete data; +} + +unsigned int RtMidiIn :: getPortCount() +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); +} + +std::string RtMidiIn :: getPortName( unsigned int portNumber ) +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { + std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); + return stringName; + } + + // If we get here, we didn't find a match. + errorString_ = "RtMidiIn::getPortName: error looking for port name!"; + error( RtError::INVALID_PARAMETER ); + return 0; +} + +//*********************************************************************// +// API: LINUX ALSA +// Class Definitions: RtMidiOut +//*********************************************************************// + +unsigned int RtMidiOut :: getPortCount() +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); +} + +std::string RtMidiOut :: getPortName( unsigned int portNumber ) +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { + std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); + return stringName; + } + + // If we get here, we didn't find a match. + errorString_ = "RtMidiOut::getPortName: error looking for port name!"; + error( RtError::INVALID_PARAMETER ); + return 0; +} + +void RtMidiOut :: initialize( void ) +{ + // Set up the ALSA sequencer client. + snd_seq_t *seq; + int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0); + if ( result < 0 ) { + errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object."; + error( RtError::DRIVER_ERROR ); + } + + // Set client name. + snd_seq_set_client_name(seq, "RtMidi Output Client"); + + // Save our api-specific connection information. + AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; + data->seq = seq; + data->vport = -1; + data->bufferSize = 32; + data->coder = 0; + data->buffer = 0; + result = snd_midi_event_new( data->bufferSize, &data->coder ); + if ( result < 0 ) { + delete data; + errorString_ = "RtMidiOut::initialize: error initializing MIDI event parser!\n\n"; + error( RtError::DRIVER_ERROR ); + } + data->buffer = (unsigned char *) malloc( data->bufferSize ); + if ( data->buffer == NULL ) { + delete data; + errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; + error( RtError::MEMORY_ERROR ); + } + snd_midi_event_init( data->coder ); + apiData_ = (void *) data; +} + +void RtMidiOut :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nSrc = this->getPortCount(); + if (nSrc < 1) { + errorString_ = "RtMidiOut::openPort: no MIDI output sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + std::ostringstream ost; + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { + ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + snd_seq_addr_t sender, receiver; + receiver.client = snd_seq_port_info_get_client( pinfo ); + receiver.port = snd_seq_port_info_get_port( pinfo ); + sender.client = snd_seq_client_id( data->seq ); + + if ( data->vport < 0 ) { + data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC ); + if ( data->vport < 0 ) { + errorString_ = "RtMidiOut::openPort: ALSA error creating output port."; + error( RtError::DRIVER_ERROR ); + } + } + + sender.port = data->vport; + + // Make subscription + snd_seq_port_subscribe_malloc( &data->subscription ); + snd_seq_port_subscribe_set_sender(data->subscription, &sender); + snd_seq_port_subscribe_set_dest(data->subscription, &receiver); + snd_seq_port_subscribe_set_time_update(data->subscription, 1); + snd_seq_port_subscribe_set_time_real(data->subscription, 1); + if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { + errorString_ = "RtMidiOut::openPort: ALSA error making port connection."; + error( RtError::DRIVER_ERROR ); + } + + connected_ = true; +} + +void RtMidiOut :: closePort( void ) +{ + if ( connected_ ) { + AlsaMidiData *data = static_cast (apiData_); + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + connected_ = false; + } +} + +void RtMidiOut :: openVirtualPort( std::string portName ) +{ + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport < 0 ) { + data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC ); + + if ( data->vport < 0 ) { + errorString_ = "RtMidiOut::openVirtualPort: ALSA error creating virtual port."; + error( RtError::DRIVER_ERROR ); + } + } +} + +RtMidiOut :: ~RtMidiOut() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); + if ( data->coder ) snd_midi_event_free( data->coder ); + if ( data->buffer ) free( data->buffer ); + snd_seq_close( data->seq ); + delete data; +} + +void RtMidiOut :: sendMessage( std::vector *message ) +{ + int result; + AlsaMidiData *data = static_cast (apiData_); + unsigned int nBytes = message->size(); + if ( nBytes > data->bufferSize ) { + data->bufferSize = nBytes; + result = snd_midi_event_resize_buffer ( data->coder, nBytes); + if ( result != 0 ) { + errorString_ = "RtMidiOut::sendMessage: ALSA error resizing MIDI event buffer."; + error( RtError::DRIVER_ERROR ); + } + free (data->buffer); + data->buffer = (unsigned char *) malloc( data->bufferSize ); + if ( data->buffer == NULL ) { + errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; + error( RtError::MEMORY_ERROR ); + } + } + + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, data->vport); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_direct(&ev); + for ( unsigned int i=0; ibuffer[i] = message->at(i); + result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); + if ( result < (int)nBytes ) { + errorString_ = "RtMidiOut::sendMessage: event parsing error!"; + error( RtError::WARNING ); + return; + } + + // Send the event. + result = snd_seq_event_output(data->seq, &ev); + if ( result < 0 ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; + error( RtError::WARNING ); + } + snd_seq_drain_output(data->seq); +} + +#endif // __LINUX_ALSA__ + + +//*********************************************************************// +// API: IRIX MD +//*********************************************************************// + +// API information gleamed from: +// http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?cmd=getdoc&coll=0650&db=man&fname=3%20mdIntro + +// If the Makefile doesn't work, try the following: +// CC -o midiinfo -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiinfo.cpp -lpthread -lmd +// CC -o midiout -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiout.cpp -lpthread -lmd +// CC -o qmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp qmidiin.cpp -lpthread -lmd +// CC -o cmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp cmidiin.cpp -lpthread -lmd + +#if defined(__IRIX_MD__) + +#include +#include +#include + +// Irix MIDI header file. +#include + +// A structure to hold variables related to the IRIX API +// implementation. +struct IrixMidiData { + MDport port; + pthread_t thread; +}; + +//*********************************************************************// +// API: IRIX +// Class Definitions: RtMidiIn +//*********************************************************************// + +extern "C" void *irixMidiHandler( void *ptr ) +{ + RtMidiIn::RtMidiInData *data = static_cast (ptr); + IrixMidiData *apiData = static_cast (data->apiData); + + bool continueSysex = false; + unsigned char status; + unsigned short size; + MDevent event; + int fd = mdGetFd( apiData->port ); + if ( fd < 0 ) { + data->doInput = false; + std::cerr << "\nRtMidiIn::irixMidiHandler: error getting port descriptor!\n\n"; + return 0; + } + + fd_set mask, rmask; + FD_ZERO( &mask ); + FD_SET( fd, &mask ); + struct timeval timeout = {0, 0}; + RtMidiIn::MidiMessage message; + int result; + + while ( data->doInput ) { + + rmask = mask; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if ( select( fd+1, &rmask, NULL, NULL, &timeout ) <= 0 ) { + // No data pending ... sleep a bit. + usleep( 1000 ); + continue; + } + + // If here, there should be data. + result = mdReceive( apiData->port, &event, 1); + if ( result <= 0 ) { + std::cerr << "\nRtMidiIn::irixMidiHandler: MIDI input read error!\n\n"; + continue; + } + + message.timeStamp = event.stamp * 0.000000001; + + size = 0; + status = event.msg[0]; + if ( !(status & 0x80) ) continue; + if ( status == 0xF0 ) { + // Sysex message ... can be segmented across multiple messages. + if ( !(data->ignoreFlags & 0x01) ) { + if ( continueSysex ) { + // We have a continuing, segmented sysex message. Append + // the new bytes to our existing message. + for ( int i=0; iusingCallback && message.bytes.size() > 0 ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + } + } + mdFree( NULL ); + continue; + } + else if ( status < 0xC0 ) size = 3; + else if ( status < 0xE0 ) size = 2; + else if ( status < 0xF0 ) size = 3; + else if ( status < 0xF3 ) { + if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) { + // A MIDI time code message and we're not ignoring it. + size = 3; + } + } + else if ( status == 0xF3 ) size = 2; + else if ( status == 0xF8 ) { + if ( !(data->ignoreFlags & 0x02) ) { + // A MIDI timing tick message and we're not ignoring it. + size = 1; + } + } + else if ( status == 0xFE ) { // MIDI active sensing + if ( !(data->ignoreFlags & 0x04) ) + size = 1; + } + else size = 1; + + // Copy the MIDI data to our vector. + if ( size ) { + message.bytes.assign( &event.msg[0], &event.msg[size] ); + // Invoke the user callback function or queue the message. + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + } + + return 0; +} + +void RtMidiIn :: initialize( void ) +{ + // Initialize the Irix MIDI system. At the moment, we will not + // worry about a return value of zero (ports) because there is a + // chance the user could plug something in after instantiation. + int nPorts = mdInit(); + + // Create our api-specific connection information. + IrixMidiData *data = (IrixMidiData *) new IrixMidiData; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; +} + +void RtMidiIn :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + int nPorts = mdInit(); + if (nPorts < 1) { + errorString_ = "RtMidiIn::openPort: no Irix MIDI input sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nPorts ) { + ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + IrixMidiData *data = static_cast (apiData_); + data->port = mdOpenInPort( mdGetName(portNumber) ); + if ( data->port == NULL ) { + ost << "RtMidiIn::openPort: Irix error opening the port (" << portNumber << ")."; + errorString_ = ost.str(); + error( RtError::DRIVER_ERROR ); + } + mdSetStampMode(data->port, MD_DELTASTAMP); + + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + + inputData_.doInput = true; + int err = pthread_create(&data->thread, &attr, irixMidiHandler, &inputData_); + pthread_attr_destroy(&attr); + if (err) { + mdClosePort( data->port ); + inputData_.doInput = false; + errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; + error( RtError::THREAD_ERROR ); + } + + connected_ = true; +} + +void RtMidiIn :: openVirtualPort( std::string portName ) +{ + // This function cannot be implemented for the Irix MIDI API. + errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!"; + error( RtError::WARNING ); +} + +void RtMidiIn :: closePort( void ) +{ + if ( connected_ ) { + IrixMidiData *data = static_cast (apiData_); + mdClosePort( data->port ); + connected_ = false; + + // Shutdown the input thread. + inputData_.doInput = false; + pthread_join( data->thread, NULL ); + } +} + +RtMidiIn :: ~RtMidiIn() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + IrixMidiData *data = static_cast (apiData_); + delete data; +} + +unsigned int RtMidiIn :: getPortCount() +{ + int nPorts = mdInit(); + if ( nPorts >= 0 ) return nPorts; + else return 0; +} + +std::string RtMidiIn :: getPortName( unsigned int portNumber ) +{ + int nPorts = mdInit(); + + std::ostringstream ost; + if ( portNumber >= nPorts ) { + ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + std::string stringName = std::string( mdGetName( portNumber ) ); + return stringName; +} + +//*********************************************************************// +// API: IRIX MD +// Class Definitions: RtMidiOut +//*********************************************************************// + +unsigned int RtMidiOut :: getPortCount() +{ + int nPorts = mdInit(); + if ( nPorts >= 0 ) return nPorts; + else return 0; +} + +std::string RtMidiOut :: getPortName( unsigned int portNumber ) +{ + int nPorts = mdInit(); + + std::ostringstream ost; + if ( portNumber >= nPorts ) { + ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + std::string stringName = std::string( mdGetName( portNumber ) ); + return stringName; +} + +void RtMidiOut :: initialize( void ) +{ + // Initialize the Irix MIDI system. At the moment, we will not + // worry about a return value of zero (ports) because there is a + // chance the user could plug something in after instantiation. + int nPorts = mdInit(); + + // Create our api-specific connection information. + IrixMidiData *data = (IrixMidiData *) new IrixMidiData; + apiData_ = (void *) data; +} + +void RtMidiOut :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + int nPorts = mdInit(); + if (nPorts < 1) { + errorString_ = "RtMidiOut::openPort: no Irix MIDI output sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nPorts ) { + ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + IrixMidiData *data = static_cast (apiData_); + data->port = mdOpenOutPort( mdGetName(portNumber) ); + if ( data->port == NULL ) { + ost << "RtMidiOut::openPort: Irix error opening the port (" << portNumber << ")."; + errorString_ = ost.str(); + error( RtError::DRIVER_ERROR ); + } + mdSetStampMode(data->port, MD_NOSTAMP); + + connected_ = true; +} + +void RtMidiOut :: closePort( void ) +{ + if ( connected_ ) { + IrixMidiData *data = static_cast (apiData_); + mdClosePort( data->port ); + connected_ = false; + } +} + +void RtMidiOut :: openVirtualPort( std::string portName ) +{ + // This function cannot be implemented for the Irix MIDI API. + errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!"; + error( RtError::WARNING ); +} + +RtMidiOut :: ~RtMidiOut() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + IrixMidiData *data = static_cast (apiData_); + delete data; +} + +void RtMidiOut :: sendMessage( std::vector *message ) +{ + int result; + MDevent event; + IrixMidiData *data = static_cast (apiData_); + char *buffer = 0; + + unsigned int nBytes = message->size(); + if ( nBytes == 0 ) return; + event.stamp = 0; + if ( message->at(0) == 0xF0 ) { + if ( nBytes < 3 ) return; // check for bogus sysex + event.msg[0] = 0xF0; + event.msglen = nBytes; + buffer = (char *) malloc( nBytes ); + for ( int i=0; iat(i); + event.sysexmsg = buffer; + } + else { + for ( int i=0; iat(i); + } + + // Send the event. + result = mdSend( data->port, &event, 1 ); + if ( buffer ) free( buffer ); + if ( result < 1 ) { + errorString_ = "RtMidiOut::sendMessage: IRIX error sending MIDI message!"; + error( RtError::WARNING ); + return; + } +} + +#endif // __IRIX_MD__ + +//*********************************************************************// +// API: Windows Multimedia Library (MM) +//*********************************************************************// + +// API information deciphered from: +// - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp + +// Thanks to Jean-Baptiste Berruchon for the sysex code. + +#if defined(__WINDOWS_MM__) + +// The Windows MM API is based on the use of a callback function for +// MIDI input. We convert the system specific time stamps to delta +// time values. + +// Windows MM MIDI header files. +#include +#include + +// A structure to hold variables related to the CoreMIDI API +// implementation. +struct WinMidiData { + HMIDIIN inHandle; // Handle to Midi Input Device + HMIDIOUT outHandle; // Handle to Midi Output Device + DWORD lastTime; + RtMidiIn::MidiMessage message; + LPMIDIHDR sysexBuffer; +}; + +#define RT_SYSEX_BUFFER_SIZE 1024 + +//*********************************************************************// +// API: Windows MM +// Class Definitions: RtMidiIn +//*********************************************************************// + +static void CALLBACK midiInputCallback( HMIDIOUT hmin, + UINT inputStatus, + DWORD instancePtr, + DWORD midiMessage, + DWORD timestamp ) +{ + if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA ) return; + + //RtMidiIn::RtMidiInData *data = static_cast (instancePtr); + RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr; + WinMidiData *apiData = static_cast (data->apiData); + + // Calculate time stamp. + apiData->message.timeStamp = 0.0; + if ( data->firstMessage == true ) data->firstMessage = false; + else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; + apiData->lastTime = timestamp; + + if ( inputStatus == MIM_DATA ) { // Channel or system message + + // Make sure the first byte is a status byte. + unsigned char status = (unsigned char) (midiMessage & 0x000000FF); + if ( !(status & 0x80) ) return; + + // Determine the number of bytes in the MIDI message. + unsigned short nBytes = 1; + if ( status < 0xC0 ) nBytes = 3; + else if ( status < 0xE0 ) nBytes = 2; + else if ( status < 0xF0 ) nBytes = 3; + else if ( status < 0xF3 ) { + // A MIDI time code message and we're ignoring it. + if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) return; + nBytes = 3; + } + else if ( status == 0xF3 ) nBytes = 2; + else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) { + // A MIDI timing tick message and we're ignoring it. + return; + } + else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { + // A MIDI active sensing message and we're ignoring it. + return; + } + + // Copy bytes to our MIDI message. + unsigned char *ptr = (unsigned char *) &midiMessage; + for ( int i=0; imessage.bytes.push_back( *ptr++ ); + } + else if ( !(data->ignoreFlags & 0x01) ) { + // Sysex message and we're not ignoring it + MIDIHDR *sysex = ( MIDIHDR *) midiMessage; + for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) + apiData->message.bytes.push_back( sysex->lpData[i] ); + + // When the callback has to be unaffected (application closes), + // it seems WinMM calls it with an empty sysex to de-queue the buffer + // If the buffer is requeued afer that message, the PC suddenly reboots + // after one or two minutes (JB). + if ( apiData->sysexBuffer->dwBytesRecorded > 0 ) { + MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) + std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; + } + } + + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( apiData->message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + + // Clear the vector for the next input message. Note that doing + // this here allows our code to work for sysex messages which are + // segmented across multiple buffers. + apiData->message.bytes.clear(); +} + +void RtMidiIn :: initialize( void ) +{ + // We'll issue a warning here if no devices are available but not + // throw an error since the user can plugin something later. + unsigned int nDevices = midiInGetNumDevs(); + if ( nDevices == 0 ) { + errorString_ = "RtMidiIn::initialize: no MIDI input devices currently available."; + error( RtError::WARNING ); + } + + // Save our api-specific connection information. + WinMidiData *data = (WinMidiData *) new WinMidiData; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; + data->message.bytes.clear(); // needs to be empty for first input message +} + +void RtMidiIn :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nDevices = midiInGetNumDevs(); + if (nDevices == 0) { + errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nDevices ) { + ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + WinMidiData *data = static_cast (apiData_); + MMRESULT result = midiInOpen( &data->inHandle, + portNumber, + (DWORD)&midiInputCallback, + (DWORD)&inputData_, + CALLBACK_FUNCTION ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port."; + error( RtError::DRIVER_ERROR ); + } + + // Allocate and init the sysex buffer. + data->sysexBuffer = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; + data->sysexBuffer->lpData = new char[1024]; + data->sysexBuffer->dwBufferLength = 1024; + data->sysexBuffer->dwFlags = 0; + + result = midiInPrepareHeader( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; + error( RtError::DRIVER_ERROR ); + } + + // Register the buffer. + result = midiInAddBuffer( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (AddBuffer)."; + error( RtError::DRIVER_ERROR ); + } + + result = midiInStart( data->inHandle ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port."; + error( RtError::DRIVER_ERROR ); + } + + connected_ = true; +} + +void RtMidiIn :: openVirtualPort( std::string portName ) +{ + // This function cannot be implemented for the Windows MM MIDI API. + errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; + error( RtError::WARNING ); +} + +void RtMidiIn :: closePort( void ) +{ + if ( connected_ ) { + WinMidiData *data = static_cast (apiData_); + midiInReset( data->inHandle ); + midiInStop( data->inHandle ); + + int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer, sizeof(MIDIHDR)); + delete [] data->sysexBuffer->lpData; + delete [] data->sysexBuffer; + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + errorString_ = "RtMidiIn::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; + error( RtError::DRIVER_ERROR ); + } + + midiInClose( data->inHandle ); + connected_ = false; + } +} + +RtMidiIn :: ~RtMidiIn() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + WinMidiData *data = static_cast (apiData_); + delete data; +} + +unsigned int RtMidiIn :: getPortCount() +{ + return midiInGetNumDevs(); +} + +std::string RtMidiIn :: getPortName( unsigned int portNumber ) +{ + unsigned int nDevices = midiInGetNumDevs(); + if ( portNumber >= nDevices ) { + std::ostringstream ost; + ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + MIDIINCAPS deviceCaps; + midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); + + // For some reason, we need to copy character by character with + // UNICODE (thanks to Eduardo Coutinho!). + //std::string stringName = std::string( deviceCaps.szPname ); + char nameString[MAXPNAMELEN]; + for( int i=0; i= nDevices ) { + std::ostringstream ost; + ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + MIDIOUTCAPS deviceCaps; + midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); + + // For some reason, we need to copy character by character with + // UNICODE (thanks to Eduardo Coutinho!). + //std::string stringName = std::string( deviceCaps.szPname ); + char nameString[MAXPNAMELEN]; + for( int i=0; i= nDevices ) { + ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + WinMidiData *data = static_cast (apiData_); + MMRESULT result = midiOutOpen( &data->outHandle, + portNumber, + (DWORD)NULL, + (DWORD)NULL, + CALLBACK_NULL ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "RtMidiOut::openPort: error creating Windows MM MIDI output port."; + error( RtError::DRIVER_ERROR ); + } + + connected_ = true; +} + +void RtMidiOut :: closePort( void ) +{ + if ( connected_ ) { + WinMidiData *data = static_cast (apiData_); + midiOutReset( data->outHandle ); + midiOutClose( data->outHandle ); + connected_ = false; + } +} + +void RtMidiOut :: openVirtualPort( std::string portName ) +{ + // This function cannot be implemented for the Windows MM MIDI API. + errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; + error( RtError::WARNING ); +} + +RtMidiOut :: ~RtMidiOut() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + WinMidiData *data = static_cast (apiData_); + delete data; +} + +void RtMidiOut :: sendMessage( std::vector *message ) +{ + unsigned int nBytes = message->size(); + if ( nBytes == 0 ) { + errorString_ = "RtMidiOut::sendMessage: message argument is empty!"; + error( RtError::WARNING ); + return; + } + + MMRESULT result; + WinMidiData *data = static_cast (apiData_); + if ( message->at(0) == 0xF0 ) { // Sysex message + + // Allocate buffer for sysex data. + char *buffer = (char *) malloc( nBytes ); + if ( buffer == NULL ) { + errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!"; + error( RtError::MEMORY_ERROR ); + } + + // Copy data to buffer. + for ( unsigned int i=0; iat(i); + + // Create and prepare MIDIHDR structure. + MIDIHDR sysex; + sysex.lpData = (LPSTR) buffer; + sysex.dwBufferLength = nBytes; + sysex.dwFlags = 0; + result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + free( buffer ); + errorString_ = "RtMidiOut::sendMessage: error preparing sysex header."; + error( RtError::DRIVER_ERROR ); + } + + // Send the message. + result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + free( buffer ); + errorString_ = "RtMidiOut::sendMessage: error sending sysex message."; + error( RtError::DRIVER_ERROR ); + } + + // Unprepare the buffer and MIDIHDR. + while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); + free( buffer ); + + } + else { // Channel or system message. + + // Make sure the message size isn't too big. + if ( nBytes > 3 ) { + errorString_ = "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!"; + error( RtError::WARNING ); + return; + } + + // Pack MIDI bytes into double word. + DWORD packet; + unsigned char *ptr = (unsigned char *) &packet; + for ( unsigned int i=0; iat(i); + ptr++; + } + + // Send the message immediately. + result = midiOutShortMsg( data->outHandle, packet ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI message."; + error( RtError::DRIVER_ERROR ); + } + } +} + +#endif // __WINDOWS_MM__ diff -r 1d7ebc05157e -r 32d156c75df7 data/midi/rtmidi/RtMidi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/midi/rtmidi/RtMidi.h Mon Feb 23 11:41:28 2009 +0000 @@ -0,0 +1,296 @@ +/**********************************************************************/ +/*! \class RtMidi + \brief An abstract base class for realtime MIDI input/output. + + This class implements some common functionality for the realtime + MIDI input/output subclasses RtMidiIn and RtMidiOut. + + RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ + + RtMidi: realtime MIDI i/o C++ classes + Copyright (c) 2003-2007 Gary P. Scavone + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/**********************************************************************/ + +// RtMidi: Version 1.0.7 + +#ifndef RTMIDI_H +#define RTMIDI_H + +#include "RtError.h" +#include + +class RtMidi +{ + public: + + //! Pure virtual openPort() function. + virtual void openPort( unsigned int portNumber = 0 ) = 0; + + //! Pure virtual openVirtualPort() function. + virtual void openVirtualPort( const std::string portName = std::string( "RtMidi" ) ) = 0; + + //! Pure virtual getPortCount() function. + virtual unsigned int getPortCount() = 0; + + //! Pure virtual getPortName() function. + virtual std::string getPortName( unsigned int portNumber = 0 ) = 0; + + //! Pure virtual closePort() function. + virtual void closePort( void ) = 0; + + protected: + + RtMidi(); + virtual ~RtMidi() {}; + + // A basic error reporting function for internal use in the RtMidi + // subclasses. The behavior of this function can be modified to + // suit specific needs. + void error( RtError::Type type ); + + void *apiData_; + bool connected_; + std::string errorString_; +}; + +/**********************************************************************/ +/*! \class RtMidiIn + \brief A realtime MIDI input class. + + This class provides a common, platform-independent API for + realtime MIDI input. It allows access to a single MIDI input + port. Incoming MIDI messages are either saved to a queue for + retrieval using the getMessage() function or immediately passed to + a user-specified callback function. Create multiple instances of + this class to connect to more than one MIDI device at the same + time. With the OS-X and Linux ALSA MIDI APIs, it is also possible + to open a virtual input port to which other MIDI software clients + can connect. + + by Gary P. Scavone, 2003-2004. +*/ +/**********************************************************************/ + +#include +#include + +class RtMidiIn : public RtMidi +{ + public: + + //! User callback function type definition. + typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData); + + //! Default constructor. + /*! + An exception will be thrown if a MIDI system initialization error occurs. + */ + RtMidiIn(); + + //! If a MIDI connection is still open, it will be closed by the destructor. + ~RtMidiIn(); + + //! Open a MIDI input connection. + /*! + An optional port number greater than 0 can be specified. + Otherwise, the default or first port found is opened. + */ + void openPort( unsigned int portNumber = 0 ); + + //! Create a virtual input port, with optional name, to allow software connections (OS X and ALSA only). + /*! + This function creates a virtual MIDI input port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X and Linux ALSA + APIs (the function does nothing for the other APIs). + */ + void openVirtualPort( const std::string portName = std::string( "RtMidi Input" ) ); + + //! Set a callback function to be invoked for incoming MIDI messages. + /*! + The callback function will be called whenever an incoming MIDI + message is received. While not absolutely necessary, it is best + to set the callback function before opening a MIDI port to avoid + leaving some messages in the queue. + */ + void setCallback( RtMidiCallback callback, void *userData = 0 ); + + //! Cancel use of the current callback function (if one exists). + /*! + Subsequent incoming MIDI messages will be written to the queue + and can be retrieved with the \e getMessage function. + */ + void cancelCallback(); + + //! Close an open MIDI connection (if one exists). + void closePort( void ); + + //! Return the number of available MIDI input ports. + unsigned int getPortCount(); + + //! Return a string identifier for the specified MIDI input port number. + /*! + An exception is thrown if an invalid port specifier is provided. + */ + std::string getPortName( unsigned int portNumber = 0 ); + + //! Set the maximum number of MIDI messages to be saved in the queue. + /*! + If the queue size limit is reached, incoming messages will be + ignored. The default limit is 1024. + */ + void setQueueSizeLimit( unsigned int queueSize ); + + //! Specify whether certain MIDI message types should be queued or ignored during input. + /*! + By default, MIDI timing and active sensing messages are ignored + during message input because of their relative high data rates. + MIDI sysex messages are ignored by default as well. Variable + values of "true" imply that the respective message type will be + ignored. + */ + void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); + + //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. + /*! + This function returns immediately whether a new message is + available or not. A valid message is indicated by a non-zero + vector size. An exception is thrown if an error occurs during + message retrieval or an input connection was not previously + established. + */ + double getMessage( std::vector *message ); + + // A MIDI structure used internally by the class to store incoming + // messages. Each message represents one and only one MIDI message. + struct MidiMessage { + std::vector bytes; + double timeStamp; + + // Default constructor. + MidiMessage() + :bytes(3), timeStamp(0.0) {} + }; + + // The RtMidiInData structure is used to pass private class data to + // the MIDI input handling function or thread. + struct RtMidiInData { + std::queue queue; + unsigned int queueLimit; + unsigned char ignoreFlags; + bool doInput; + bool firstMessage; + void *apiData; + bool usingCallback; + void *userCallback; + void *userData; + + // Default constructor. + RtMidiInData() + : queueLimit(1024), ignoreFlags(7), doInput(false), firstMessage(true), + apiData(0), usingCallback(false), userCallback(0), userData(0) {} + }; + + private: + + void initialize( void ); + RtMidiInData inputData_; + +}; + +/**********************************************************************/ +/*! \class RtMidiOut + \brief A realtime MIDI output class. + + This class provides a common, platform-independent API for MIDI + output. It allows one to probe available MIDI output ports, to + connect to one such port, and to send MIDI bytes immediately over + the connection. Create multiple instances of this class to + connect to more than one MIDI device at the same time. + + by Gary P. Scavone, 2003-2004. +*/ +/**********************************************************************/ + +class RtMidiOut : public RtMidi +{ + public: + + //! Default constructor. + /*! + An exception will be thrown if a MIDI system initialization error occurs. + */ + RtMidiOut(); + + //! The destructor closes any open MIDI connections. + ~RtMidiOut(); + + //! Open a MIDI output connection. + /*! + An optional port number greater than 0 can be specified. + Otherwise, the default or first port found is opened. An + exception is thrown if an error occurs while attempting to make + the port connection. + */ + void openPort( unsigned int portNumber = 0 ); + + //! Close an open MIDI connection (if one exists). + void closePort(); + + //! Create a virtual output port, with optional name, to allow software connections (OS X and ALSA only). + /*! + This function creates a virtual MIDI output port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X and Linux ALSA + APIs (the function does nothing with the other APIs). An + exception is thrown if an error occurs while attempting to create + the virtual port. + */ + void openVirtualPort( const std::string portName = std::string( "RtMidi Output" ) ); + + //! Return the number of available MIDI output ports. + unsigned int getPortCount(); + + //! Return a string identifier for the specified MIDI port type and number. + /*! + An exception is thrown if an invalid port specifier is provided. + */ + std::string getPortName( unsigned int portNumber = 0 ); + + //! Immediately send a single message out an open MIDI output port. + /*! + An exception is thrown if an error occurs during output or an + output connection was not previously established. + */ + void sendMessage( std::vector *message ); + + private: + + void initialize( void ); +}; + +#endif