Index: media/midi/midi_manager_mac.cc |
diff --git a/media/midi/midi_manager_mac.cc b/media/midi/midi_manager_mac.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0d33fe23a18fe28dc70181558a52084823768bfb |
--- /dev/null |
+++ b/media/midi/midi_manager_mac.cc |
@@ -0,0 +1,203 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "media/midi/midi_manager_mac.h" |
+ |
+#include <iostream> |
+#include <string> |
+ |
+#include "base/debug/trace_event.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include <CoreAudio/HostTime.h> |
+ |
+using base::IntToString; |
+using base::SysCFStringRefToUTF8; |
+using std::string; |
+ |
+namespace media { |
+ |
+MIDIManager* MIDIManager::Create() { |
+ return new MIDIManagerMac(); |
+} |
+ |
+MIDIManagerMac::MIDIManagerMac() |
+ : midi_client_(NULL), |
+ coremidi_input_(NULL), |
+ coremidi_output_(NULL), |
+ packet_list_(NULL), |
+ midi_packet_(NULL) { |
+} |
+ |
+bool MIDIManagerMac::Initialize() { |
+ TRACE_EVENT0("midi", "MIDIManagerMac::Initialize"); |
+ |
+ // CoreMIDI registration. |
+ midi_client_ = NULL; |
+ OSStatus result = MIDIClientCreate( |
+ CFSTR("Google Chrome"), |
+ NULL, |
+ NULL, |
+ &midi_client_); |
+ |
+ if (result != noErr) |
+ return false; |
+ |
+ coremidi_input_ = NULL; |
+ |
+ // Create input and output port. |
+ result = MIDIInputPortCreate( |
+ midi_client_, |
+ CFSTR("MIDI Input"), |
+ ReadMidiDispatch, |
+ this, |
+ &coremidi_input_); |
+ if (result != noErr) |
+ return false; |
+ |
+ result = MIDIOutputPortCreate( |
+ midi_client_, |
+ CFSTR("MIDI Output"), |
+ &coremidi_output_); |
+ if (result != noErr) |
+ return false; |
+ |
+ int destination_count = MIDIGetNumberOfDestinations(); |
+ destinations_.reserve(destination_count); |
+ |
+ for (int i = 0; i < destination_count ; i++) { |
+ MIDIEndpointRef destination = MIDIGetDestination(i); |
+ |
+ // Keep track of all destinations (known as outputs by the Web MIDI API). |
+ // Cache to avoid any possible overhead in calling MIDIGetDestination(). |
+ destinations_[i] = destination; |
+ |
+ MIDIPortInfo info = GetPortInfoFromEndpoint(destination); |
+ AddOutputPort(info); |
+ } |
+ |
+ // Open connections from all sources. |
+ int source_count = MIDIGetNumberOfSources(); |
+ |
+ for (int i = 0; i < source_count; ++i) { |
+ // Receive from all sources. |
+ MIDIEndpointRef src = MIDIGetSource(i); |
+ MIDIPortConnectSource(coremidi_input_, src, src); |
+ |
+ // Keep track of all sources (known as inputs in Web MIDI API terminology). |
+ source_map_[src] = i; |
+ |
+ MIDIPortInfo info = GetPortInfoFromEndpoint(src); |
+ AddInputPort(info); |
+ } |
+ |
+ // TODO(crogers): Fix the memory management here! |
+ packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_); |
+ midi_packet_ = MIDIPacketListInit(packet_list_); |
+ |
+ return true; |
+} |
+ |
+MIDIManagerMac::~MIDIManagerMac() { |
+ if (coremidi_input_) |
+ MIDIPortDispose(coremidi_input_); |
+ if (coremidi_output_) |
+ MIDIPortDispose(coremidi_output_); |
+} |
+ |
+void MIDIManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list, |
+ void* read_proc_refcon, |
+ void* src_conn_refcon) { |
+ MIDIManagerMac* manager = static_cast<MIDIManagerMac*>(read_proc_refcon); |
+ MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon); |
+ |
+ // Dispatch to class method. |
+ manager->ReadMidi(source, packet_list); |
+} |
+ |
+void MIDIManagerMac::ReadMidi(MIDIEndpointRef source, |
+ const MIDIPacketList* packet_list) { |
+ // Lookup the port index based on the source. |
+ SourceMap::iterator j = source_map_.find(source); |
+ if (j == source_map_.end()) |
+ return; |
+ int port_index = source_map_[source]; |
+ |
+ // Go through each packet and process separately. |
+ for(size_t i = 0; i < packet_list->numPackets; i++) { |
+ // Each packet contains MIDI data for one or more messages (like note-on). |
+ const MIDIPacket &packet = packet_list->packet[i]; |
+ double timestamp_seconds = MIDITimeStampToSeconds(packet.timeStamp); |
+ |
+ ReceiveMIDIData( |
+ port_index, |
+ packet.data, |
+ packet.length, |
+ timestamp_seconds); |
+ } |
+} |
+ |
+void MIDIManagerMac::SendMIDIData(int port_index, |
+ const uint8* data, |
+ size_t length, |
+ double timestamp) { |
+ // TODO(crogers): Filter out sysex. |
+ |
+ MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp); |
+ |
+ midi_packet_ = MIDIPacketListAdd( |
+ packet_list_, |
+ kMaxPacketListSize, |
+ midi_packet_, |
+ coremidi_timestamp, |
+ length, |
+ data); |
+ |
+ // Lookup the destination based on the port index. |
+ if (port_index < 0 || |
palmer
2013/06/19 21:39:15
Funny, I saw the same pattern in my previous (unre
Chris Rogers
2013/06/20 17:41:33
Agreed that unsigned is better. For now adding TO
|
+ static_cast<size_t>(port_index) >= destinations_.size()) |
+ return; |
+ |
+ MIDIEndpointRef destination = destinations_[port_index]; |
+ |
+ MIDISend(coremidi_output_, destination, packet_list_); |
+ |
+ // Re-initialize for next time. |
+ midi_packet_ = MIDIPacketListInit(packet_list_); |
+} |
+ |
+MIDIPortInfo MIDIManagerMac::GetPortInfoFromEndpoint( |
+ MIDIEndpointRef endpoint) { |
+ SInt32 id_number = 0; |
+ MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number); |
+ string id = IntToString(id_number); |
+ |
+ CFStringRef manufacturer_ref = NULL; |
+ MIDIObjectGetStringProperty( |
+ endpoint, kMIDIPropertyManufacturer, &manufacturer_ref); |
+ string manufacturer = SysCFStringRefToUTF8(manufacturer_ref); |
+ |
+ CFStringRef name_ref = NULL; |
+ MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref); |
+ string name = SysCFStringRefToUTF8(name_ref); |
+ |
+ SInt32 version_number = 0; |
+ MIDIObjectGetIntegerProperty( |
+ endpoint, kMIDIPropertyDriverVersion, &version_number); |
+ string version = IntToString(version_number); |
+ |
+ return MIDIPortInfo(id, manufacturer, name, version); |
+} |
+ |
+double MIDIManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) { |
+ UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp); |
+ return static_cast<double>(nanoseconds) / 1.0e9; |
+} |
+ |
+MIDITimeStamp MIDIManagerMac::SecondsToMIDITimeStamp(double seconds) { |
+ UInt64 nanos = UInt64(seconds * 1.0e9); |
+ return AudioConvertNanosToHostTime(nanos); |
+} |
+ |
+} // namespace media |