|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2015 - ROLI Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- 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.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
- */
-
- class MidiInCollector
- {
- public:
- MidiInCollector (MidiInput* const input_,
- MidiInputCallback& callback_)
- : deviceHandle (0),
- input (input_),
- callback (callback_),
- concatenator (4096),
- isStarted (false),
- startTime (0)
- {
- }
-
- ~MidiInCollector()
- {
- stop();
-
- if (deviceHandle != 0)
- {
- for (int count = 5; --count >= 0;)
- {
- if (midiInClose (deviceHandle) == MMSYSERR_NOERROR)
- break;
-
- Sleep (20);
- }
- }
- }
-
- //==============================================================================
- void handleMessage (const uint8* bytes, const uint32 timeStamp)
- {
- if (bytes[0] >= 0x80 && isStarted)
- {
- concatenator.pushMidiData (bytes, MidiMessage::getMessageLengthFromFirstByte (bytes[0]),
- convertTimeStamp (timeStamp), input, callback);
- writeFinishedBlocks();
- }
- }
-
- void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp)
- {
- if (isStarted && hdr->dwBytesRecorded > 0)
- {
- concatenator.pushMidiData (hdr->lpData, (int) hdr->dwBytesRecorded,
- convertTimeStamp (timeStamp), input, callback);
- writeFinishedBlocks();
- }
- }
-
- void start()
- {
- if (deviceHandle != 0 && ! isStarted)
- {
- activeMidiCollectors.addIfNotAlreadyThere (this);
-
- for (int i = 0; i < (int) numHeaders; ++i)
- {
- headers[i].prepare (deviceHandle);
- headers[i].write (deviceHandle);
- }
-
- startTime = Time::getMillisecondCounterHiRes();
- MMRESULT res = midiInStart (deviceHandle);
-
- if (res == MMSYSERR_NOERROR)
- {
- concatenator.reset();
- isStarted = true;
- }
- else
- {
- unprepareAllHeaders();
- }
- }
- }
-
- void stop()
- {
- if (isStarted)
- {
- isStarted = false;
- midiInReset (deviceHandle);
- midiInStop (deviceHandle);
- activeMidiCollectors.removeFirstMatchingValue (this);
- unprepareAllHeaders();
- concatenator.reset();
- }
- }
-
- static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance,
- DWORD_PTR midiMessage, DWORD_PTR timeStamp)
- {
- MidiInCollector* const collector = reinterpret_cast<MidiInCollector*> (dwInstance);
-
- if (activeMidiCollectors.contains (collector))
- {
- if (uMsg == MIM_DATA)
- collector->handleMessage ((const uint8*) &midiMessage, (uint32) timeStamp);
- else if (uMsg == MIM_LONGDATA)
- collector->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp);
- }
- }
-
- HMIDIIN deviceHandle;
-
- private:
- static Array <MidiInCollector*, CriticalSection> activeMidiCollectors;
-
- MidiInput* input;
- MidiInputCallback& callback;
- MidiDataConcatenator concatenator;
- bool volatile isStarted;
- double startTime;
-
- class MidiHeader
- {
- public:
- MidiHeader() {}
-
- void prepare (HMIDIIN device)
- {
- zerostruct (hdr);
- hdr.lpData = data;
- hdr.dwBufferLength = (DWORD) numElementsInArray (data);
-
- midiInPrepareHeader (device, &hdr, sizeof (hdr));
- }
-
- void unprepare (HMIDIIN device)
- {
- if ((hdr.dwFlags & WHDR_DONE) != 0)
- {
- int c = 10;
- while (--c >= 0 && midiInUnprepareHeader (device, &hdr, sizeof (hdr)) == MIDIERR_STILLPLAYING)
- Thread::sleep (20);
-
- jassert (c >= 0);
- }
- }
-
- void write (HMIDIIN device)
- {
- hdr.dwBytesRecorded = 0;
- midiInAddBuffer (device, &hdr, sizeof (hdr));
- }
-
- void writeIfFinished (HMIDIIN device)
- {
- if ((hdr.dwFlags & WHDR_DONE) != 0)
- write (device);
- }
-
- private:
- MIDIHDR hdr;
- char data [256];
-
- JUCE_DECLARE_NON_COPYABLE (MidiHeader)
- };
-
- enum { numHeaders = 32 };
- MidiHeader headers [numHeaders];
-
- void writeFinishedBlocks()
- {
- for (int i = 0; i < (int) numHeaders; ++i)
- headers[i].writeIfFinished (deviceHandle);
- }
-
- void unprepareAllHeaders()
- {
- for (int i = 0; i < (int) numHeaders; ++i)
- headers[i].unprepare (deviceHandle);
- }
-
- double convertTimeStamp (uint32 timeStamp)
- {
- double t = startTime + timeStamp;
-
- const double now = Time::getMillisecondCounterHiRes();
- if (t > now)
- {
- if (t > now + 2.0)
- startTime -= 1.0;
-
- t = now;
- }
-
- return t * 0.001;
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInCollector)
- };
-
- Array <MidiInCollector*, CriticalSection> MidiInCollector::activeMidiCollectors;
-
-
- //==============================================================================
- StringArray MidiInput::getDevices()
- {
- StringArray s;
- const UINT num = midiInGetNumDevs();
-
- for (UINT i = 0; i < num; ++i)
- {
- MIDIINCAPS mc = { 0 };
-
- 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 == nullptr)
- return nullptr;
-
- UINT deviceId = MIDI_MAPPER;
- int n = 0;
- String name;
-
- const UINT num = midiInGetNumDevs();
-
- for (UINT i = 0; i < num; ++i)
- {
- MIDIINCAPS mc = { 0 };
-
- if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
- {
- if (index == n)
- {
- deviceId = i;
- name = String (mc.szPname, (size_t) numElementsInArray (mc.szPname));
- break;
- }
-
- ++n;
- }
- }
-
- ScopedPointer<MidiInput> in (new MidiInput (name));
- ScopedPointer<MidiInCollector> collector (new MidiInCollector (in, *callback));
-
- HMIDIIN h;
- MMRESULT err = midiInOpen (&h, deviceId,
- (DWORD_PTR) &MidiInCollector::midiInCallback,
- (DWORD_PTR) (MidiInCollector*) collector,
- CALLBACK_FUNCTION);
-
- if (err == MMSYSERR_NOERROR)
- {
- collector->deviceHandle = h;
- in->internal = collector.release();
- return in.release();
- }
-
- return nullptr;
- }
-
- MidiInput::MidiInput (const String& name_)
- : name (name_),
- internal (0)
- {
- }
-
- MidiInput::~MidiInput()
- {
- delete static_cast<MidiInCollector*> (internal);
- }
-
- void MidiInput::start() { static_cast<MidiInCollector*> (internal)->start(); }
- void MidiInput::stop() { static_cast<MidiInCollector*> (internal)->stop(); }
-
-
- //==============================================================================
- struct MidiOutHandle
- {
- int refCount;
- UINT deviceId;
- HMIDIOUT handle;
-
- static Array<MidiOutHandle*> activeHandles;
-
- private:
- JUCE_LEAK_DETECTOR (MidiOutHandle)
- };
-
- Array<MidiOutHandle*> MidiOutHandle::activeHandles;
-
- //==============================================================================
- StringArray MidiOutput::getDevices()
- {
- StringArray s;
- const UINT num = midiOutGetNumDevs();
-
- for (UINT i = 0; i < num; ++i)
- {
- MIDIOUTCAPS mc = { 0 };
-
- if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
- s.add (String (mc.szPname, sizeof (mc.szPname)));
- }
-
- return s;
- }
-
- int MidiOutput::getDefaultDeviceIndex()
- {
- const UINT num = midiOutGetNumDevs();
- int n = 0;
-
- for (UINT i = 0; i < num; ++i)
- {
- MIDIOUTCAPS mc = { 0 };
-
- 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 UINT num = midiOutGetNumDevs();
- int n = 0;
- String deviceName;
-
- for (UINT i = 0; i < num; ++i)
- {
- MIDIOUTCAPS mc = { 0 };
-
- if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
- {
- String name = String (mc.szPname, sizeof (mc.szPname));
-
- // use the microsoft sw synth as a default - best not to allow deviceId
- // to be MIDI_MAPPER, or else device sharing breaks
- if (name.containsIgnoreCase ("microsoft"))
- deviceId = i;
-
- if (index == n)
- {
- deviceName = name;
- deviceId = i;
- break;
- }
-
- ++n;
- }
- }
-
- for (int i = MidiOutHandle::activeHandles.size(); --i >= 0;)
- {
- MidiOutHandle* const han = MidiOutHandle::activeHandles.getUnchecked(i);
-
- if (han->deviceId == deviceId)
- {
- han->refCount++;
-
- MidiOutput* const out = new MidiOutput (deviceName);
- out->internal = han;
- return out;
- }
- }
-
- for (int 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;
- MidiOutHandle::activeHandles.add (han);
-
- MidiOutput* const out = new MidiOutput (deviceName);
- out->internal = han;
- return out;
- }
- else if (res == MMSYSERR_ALLOCATED)
- {
- Sleep (100);
- }
- else
- {
- break;
- }
- }
-
- return nullptr;
- }
-
- MidiOutput::~MidiOutput()
- {
- stopBackgroundThread();
-
- MidiOutHandle* const h = static_cast<MidiOutHandle*> (internal);
-
- if (MidiOutHandle::activeHandles.contains (h) && --(h->refCount) == 0)
- {
- midiOutClose (h->handle);
- MidiOutHandle::activeHandles.removeFirstMatchingValue (h);
- delete h;
- }
- }
-
- void MidiOutput::sendMessageNow (const MidiMessage& message)
- {
- const MidiOutHandle* const handle = static_cast<const MidiOutHandle*> (internal);
-
- if (message.getRawDataSize() > 3 || message.isSysEx())
- {
- MIDIHDR h = { 0 };
-
- h.lpData = (char*) message.getRawData();
- h.dwBytesRecorded = h.dwBufferLength = (DWORD) 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
- {
- for (int i = 0; i < 50; ++i)
- {
- if (midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()) != MIDIERR_NOTREADY)
- break;
-
- Sleep (1);
- }
- }
- }
|