|
- /*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-7 by Raw Material Software ltd.
-
- ------------------------------------------------------------------------------
-
- JUCE can be redistributed and/or modified under the terms of the
- GNU General Public License, as published by the Free Software Foundation;
- either version 2 of the License, or (at your option) any later version.
-
- JUCE is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with JUCE; if not, visit www.gnu.org/licenses or write to the
- Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- Boston, MA 02111-1307 USA
-
- ------------------------------------------------------------------------------
-
- If you'd like to release a closed-source product which uses JUCE, commercial
- licenses are also available: visit www.rawmaterialsoftware.com/juce for
- more information.
-
- ==============================================================================
- */
-
- #include "win32_headers.h"
- #include "../../../src/juce_core/basics/juce_StandardHeader.h"
-
- BEGIN_JUCE_NAMESPACE
-
- #include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h"
- #include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h"
- #include "../../../src/juce_core/basics/juce_Time.h"
- #include "../../../src/juce_core/threads/juce_Thread.h"
- #include "../../../src/juce_core/containers/juce_MemoryBlock.h"
-
- #if JUCE_MSVC
- #pragma warning (disable: 4312)
- #endif
-
- using ::free;
-
- //==============================================================================
- static const int midiBufferSize = 1024 * 10;
- static const int numInHeaders = 32;
- static const int inBufferSize = 256;
- static Array <void*, CriticalSection> activeMidiThreads;
-
- class MidiInThread : public Thread
- {
- public:
- //==============================================================================
- MidiInThread (MidiInput* const input_,
- MidiInputCallback* const callback_)
- : Thread ("Juce Midi"),
- hIn (0),
- input (input_),
- callback (callback_),
- isStarted (false),
- startTime (0),
- pendingLength(0)
- {
- for (int i = numInHeaders; --i >= 0;)
- {
- zeromem (&hdr[i], sizeof (MIDIHDR));
- hdr[i].lpData = inData[i];
- hdr[i].dwBufferLength = inBufferSize;
- }
- };
-
- ~MidiInThread()
- {
- stop();
-
- if (hIn != 0)
- {
- int count = 5;
- while (--count >= 0)
- {
- if (midiInClose (hIn) == MMSYSERR_NOERROR)
- break;
-
- Sleep (20);
- }
- }
- }
-
- //==============================================================================
- void handle (const uint32 message, const uint32 timeStamp) throw()
- {
- const int byte = message & 0xff;
- if (byte < 0x80)
- return;
-
- const int numBytes = MidiMessage::getMessageLengthFromFirstByte ((uint8) byte);
-
- const double time = timeStampToTime (timeStamp);
-
- lock.enter();
- if (pendingLength < midiBufferSize - 12)
- {
- char* const p = pending + pendingLength;
- *(double*) p = time;
- *(uint32*) (p + 8) = numBytes;
- *(uint32*) (p + 12) = message;
- pendingLength += 12 + numBytes;
- }
- else
- {
- jassertfalse // midi buffer overflow! You might need to increase the size..
- }
-
- lock.exit();
- notify();
- }
-
- void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp) throw()
- {
- const int num = hdr->dwBytesRecorded;
-
- if (num > 0)
- {
- const double time = timeStampToTime (timeStamp);
-
- lock.enter();
-
- if (pendingLength < midiBufferSize - (8 + num))
- {
- char* const p = pending + pendingLength;
- *(double*) p = time;
- *(uint32*) (p + 8) = num;
- memcpy (p + 12, hdr->lpData, num);
- pendingLength += 12 + num;
- }
- else
- {
- jassertfalse // midi buffer overflow! You might need to increase the size..
- }
-
- lock.exit();
- notify();
- }
- }
-
- void writeBlock (const int i) throw()
- {
- hdr[i].dwBytesRecorded = 0;
- MMRESULT res = midiInPrepareHeader (hIn, &hdr[i], sizeof (MIDIHDR));
- jassert (res == MMSYSERR_NOERROR);
- res = midiInAddBuffer (hIn, &hdr[i], sizeof (MIDIHDR));
- jassert (res == MMSYSERR_NOERROR);
- }
-
- void run()
- {
- MemoryBlock pendingCopy (64);
-
- while (! threadShouldExit())
- {
- for (int i = 0; i < numInHeaders; ++i)
- {
- if ((hdr[i].dwFlags & WHDR_DONE) != 0)
- {
- MMRESULT res = midiInUnprepareHeader (hIn, &hdr[i], sizeof (MIDIHDR));
- (void) res;
- jassert (res == MMSYSERR_NOERROR);
- writeBlock (i);
- }
- }
-
- lock.enter();
-
- int len = pendingLength;
-
- if (len > 0)
- {
- pendingCopy.ensureSize (len);
- pendingCopy.copyFrom (pending, 0, len);
- pendingLength = 0;
- }
-
- lock.exit();
-
- //xxx needs to figure out if blocks are broken up or not
-
- if (len == 0)
- {
- wait (500);
- }
- else
- {
- const char* p = (const char*) pendingCopy.getData();
-
- while (len > 0)
- {
- const double time = *(const double*) p;
- const int messageLen = *(const int*) (p + 8);
-
- const MidiMessage message ((const uint8*) (p + 12), messageLen, time);
-
- callback->handleIncomingMidiMessage (input, message);
-
- p += 12 + messageLen;
- len -= 12 + messageLen;
- }
- }
- }
- }
-
- void start() throw()
- {
- jassert (hIn != 0);
- if (hIn != 0 && ! isStarted)
- {
- stop();
-
- activeMidiThreads.addIfNotAlreadyThere (this);
-
- int i;
- for (i = 0; i < numInHeaders; ++i)
- writeBlock (i);
-
- startTime = Time::getMillisecondCounter();
- MMRESULT res = midiInStart (hIn);
-
- jassert (res == MMSYSERR_NOERROR);
-
- if (res == MMSYSERR_NOERROR)
- {
- isStarted = true;
- pendingLength = 0;
- startThread (6);
- }
- }
- }
-
- void stop() throw()
- {
- if (isStarted)
- {
- stopThread (5000);
-
- midiInReset (hIn);
- midiInStop (hIn);
-
- activeMidiThreads.removeValue (this);
-
- lock.enter();
- lock.exit();
-
- for (int i = numInHeaders; --i >= 0;)
- {
- if ((hdr[i].dwFlags & WHDR_DONE) != 0)
- {
- int c = 10;
- while (--c >= 0 && midiInUnprepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)) == MIDIERR_STILLPLAYING)
- Sleep (20);
-
- jassert (c >= 0);
- }
- }
-
- isStarted = false;
- pendingLength = 0;
- }
- }
-
- juce_UseDebuggingNewOperator
-
- HMIDIIN hIn;
-
- private:
- MidiInput* input;
- MidiInputCallback* callback;
- bool isStarted;
- uint32 startTime;
- CriticalSection lock;
-
- MIDIHDR hdr [numInHeaders];
- char inData [numInHeaders] [inBufferSize];
-
- int pendingLength;
- char pending [midiBufferSize];
-
- double timeStampToTime (uint32 timeStamp) throw()
- {
- timeStamp += startTime;
-
- const uint32 now = Time::getMillisecondCounter();
- if (timeStamp > now)
- {
- if (timeStamp > now + 2)
- --startTime;
-
- timeStamp = now;
- }
-
- return 0.001 * timeStamp;
- }
-
- MidiInThread (const MidiInThread&);
- const MidiInThread& operator= (const MidiInThread&);
- };
-
- static void CALLBACK midiInCallback (HMIDIIN,
- UINT uMsg,
- DWORD_PTR dwInstance,
- DWORD_PTR midiMessage,
- DWORD_PTR timeStamp)
- {
- MidiInThread* const thread = (MidiInThread*) dwInstance;
-
- if (thread != 0 && activeMidiThreads.contains (thread))
- {
- if (uMsg == MIM_DATA)
- thread->handle ((uint32) midiMessage, (uint32) timeStamp);
- else if (uMsg == MIM_LONGDATA)
- thread->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp);
- }
- }
-
- //==============================================================================
- const StringArray MidiInput::getDevices()
- {
- StringArray s;
- const int num = midiInGetNumDevs();
-
- for (int i = 0; i < num; ++i)
- {
- MIDIINCAPS mc;
- zerostruct (mc);
-
- if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
- s.add (String (mc.szPname, sizeof (mc.szPname)));
- }
-
- return s;
- }
-
- int MidiInput::getDefaultDeviceIndex()
- {
- return 0;
- }
-
- MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const callback)
- {
- if (callback == 0)
- return 0;
-
- UINT deviceId = MIDI_MAPPER;
- int n = 0;
- String name;
-
- const int num = midiInGetNumDevs();
-
- for (int i = 0; i < num; ++i)
- {
- MIDIINCAPS mc;
- zerostruct (mc);
-
- if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
- {
- if (index == n)
- {
- deviceId = i;
- name = String (mc.szPname, sizeof (mc.szPname));
- break;
- }
-
- ++n;
- }
- }
-
- MidiInput* const in = new MidiInput (name);
- MidiInThread* const thread = new MidiInThread (in, callback);
-
- HMIDIIN h;
- HRESULT err = midiInOpen (&h, deviceId,
- (DWORD_PTR) &midiInCallback,
- (DWORD_PTR) thread,
- CALLBACK_FUNCTION);
-
- if (err == MMSYSERR_NOERROR)
- {
- thread->hIn = h;
- in->internal = (void*) thread;
- return in;
- }
- else
- {
- delete in;
- delete thread;
- return 0;
- }
- }
-
- MidiInput::MidiInput (const String& name_)
- : name (name_),
- internal (0)
- {
- }
-
- MidiInput::~MidiInput()
- {
- if (internal != 0)
- {
- MidiInThread* const thread = (MidiInThread*) internal;
- delete thread;
- }
- }
-
- void MidiInput::start()
- {
- ((MidiInThread*) internal)->start();
- }
-
- void MidiInput::stop()
- {
- ((MidiInThread*) internal)->stop();
- }
-
-
- //==============================================================================
- struct MidiOutHandle
- {
- int refCount;
- UINT deviceId;
- HMIDIOUT handle;
-
- juce_UseDebuggingNewOperator
- };
-
- static VoidArray handles (4);
-
- //==============================================================================
- const StringArray MidiOutput::getDevices()
- {
- StringArray s;
- const int num = midiOutGetNumDevs();
-
- for (int i = 0; i < num; ++i)
- {
- MIDIOUTCAPS mc;
- zerostruct (mc);
-
- if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
- s.add (String (mc.szPname, sizeof (mc.szPname)));
- }
-
- return s;
- }
-
- int MidiOutput::getDefaultDeviceIndex()
- {
- const int num = midiOutGetNumDevs();
- int n = 0;
-
- for (int i = 0; i < num; ++i)
- {
- MIDIOUTCAPS mc;
- zerostruct (mc);
-
- if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
- {
- if ((mc.wTechnology & MOD_MAPPER) != 0)
- return n;
-
- ++n;
- }
- }
-
- return 0;
- }
-
- MidiOutput* MidiOutput::openDevice (int index)
- {
- UINT deviceId = MIDI_MAPPER;
- const int num = midiOutGetNumDevs();
- int i, n = 0;
-
- for (i = 0; i < num; ++i)
- {
- MIDIOUTCAPS mc;
- zerostruct (mc);
-
- if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
- {
- // use the microsoft sw synth as a default - best not to allow deviceId
- // to be MIDI_MAPPER, or else device sharing breaks
- if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase (T("microsoft")))
- deviceId = i;
-
- if (index == n)
- {
- deviceId = i;
- break;
- }
-
- ++n;
- }
- }
-
- for (i = handles.size(); --i >= 0;)
- {
- MidiOutHandle* const han = (MidiOutHandle*) handles.getUnchecked(i);
-
- if (han != 0 && han->deviceId == deviceId)
- {
- han->refCount++;
-
- MidiOutput* const out = new MidiOutput();
- out->internal = (void*) han;
- return out;
- }
- }
-
- for (i = 4; --i >= 0;)
- {
- HMIDIOUT h = 0;
- MMRESULT res = midiOutOpen (&h, deviceId, 0, 0, CALLBACK_NULL);
-
- if (res == MMSYSERR_NOERROR)
- {
- MidiOutHandle* const han = new MidiOutHandle();
- han->deviceId = deviceId;
- han->refCount = 1;
- han->handle = h;
- handles.add (han);
-
- MidiOutput* const out = new MidiOutput();
- out->internal = (void*) han;
- return out;
- }
- else if (res == MMSYSERR_ALLOCATED)
- {
- Sleep (100);
- }
- else
- {
- break;
- }
- }
-
- return 0;
- }
-
- MidiOutput::~MidiOutput()
- {
- MidiOutHandle* const h = (MidiOutHandle*) internal;
-
- if (handles.contains ((void*) h) && --(h->refCount) == 0)
- {
- midiOutClose (h->handle);
- handles.removeValue ((void*) h);
- delete h;
- }
- }
-
- void MidiOutput::reset()
- {
- const MidiOutHandle* const h = (MidiOutHandle*) internal;
- midiOutReset (h->handle);
- }
-
- bool MidiOutput::getVolume (float& leftVol,
- float& rightVol)
- {
- const MidiOutHandle* const handle = (const MidiOutHandle*) internal;
-
- DWORD n;
- if (midiOutGetVolume (handle->handle, &n) == MMSYSERR_NOERROR)
- {
- const unsigned short* const nn = (const unsigned short*) &n;
- rightVol = nn[0] / (float) 0xffff;
- leftVol = nn[1] / (float) 0xffff;
- return true;
- }
- else
- {
- rightVol = leftVol = 1.0f;
- return false;
- }
- }
-
- void MidiOutput::setVolume (float leftVol,
- float rightVol)
- {
- const MidiOutHandle* const handle = (MidiOutHandle*) internal;
-
- DWORD n;
- unsigned short* const nn = (unsigned short*) &n;
- nn[0] = (unsigned short) jlimit (0, 0xffff, (int)(rightVol * 0xffff));
- nn[1] = (unsigned short) jlimit (0, 0xffff, (int)(leftVol * 0xffff));
- midiOutSetVolume (handle->handle, n);
- }
-
- void MidiOutput::sendMessageNow (const MidiMessage& message)
- {
- const MidiOutHandle* const handle = (const MidiOutHandle*) internal;
-
- if (message.getRawDataSize() > 3
- || message.isSysEx())
- {
- MIDIHDR h;
- zerostruct (h);
-
- h.lpData = (char*) message.getRawData();
- h.dwBufferLength = message.getRawDataSize();
- h.dwBytesRecorded = message.getRawDataSize();
-
- if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR)
- {
- MMRESULT res = midiOutLongMsg (handle->handle, &h, sizeof (MIDIHDR));
-
- if (res == MMSYSERR_NOERROR)
- {
- while ((h.dwFlags & MHDR_DONE) == 0)
- Sleep (1);
-
- int count = 500; // 1 sec timeout
-
- while (--count >= 0)
- {
- res = midiOutUnprepareHeader (handle->handle, &h, sizeof (MIDIHDR));
-
- if (res == MIDIERR_STILLPLAYING)
- Sleep (2);
- else
- break;
- }
- }
- }
- }
- else
- {
- midiOutShortMsg (handle->handle,
- *(unsigned int*) message.getRawData());
- }
- }
-
- END_JUCE_NAMESPACE
|