Chromium Code Reviews| 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 |