// 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 #include #include "base/debug/trace_event.h" #include "base/strings/string_number_conversions.h" #include "base/strings/sys_string_conversions.h" #include using base::IntToString; using base::SysCFStringRefToUTF8; using std::string; // NB: System MIDI types are pointer types in 32-bit and integer types in // 64-bit. Therefore, the initialization is the simplest one that satisfies both // (if possible). namespace media { MIDIManager* MIDIManager::Create() { return new MIDIManagerMac(); } MIDIManagerMac::MIDIManagerMac() : midi_client_(0), coremidi_input_(0), coremidi_output_(0), packet_list_(NULL), midi_packet_(NULL) { } bool MIDIManagerMac::Initialize() { TRACE_EVENT0("midi", "MIDIManagerMac::Initialize"); // CoreMIDI registration. midi_client_ = 0; OSStatus result = MIDIClientCreate( CFSTR("Google Chrome"), NULL, NULL, &midi_client_); if (result != noErr) return false; coremidi_input_ = 0; // 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; uint32 destination_count = MIDIGetNumberOfDestinations(); destinations_.resize(destination_count); for (uint32 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. uint32 source_count = MIDIGetNumberOfSources(); for (uint32 i = 0; i < source_count; ++i) { // Receive from all sources. MIDIEndpointRef src = MIDIGetSource(i); MIDIPortConnectSource(coremidi_input_, src, reinterpret_cast(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(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(read_proc_refcon); #if __LP64__ MIDIEndpointRef source = reinterpret_cast(src_conn_refcon); #else MIDIEndpointRef source = static_cast(src_conn_refcon); #endif // 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; uint32 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(MIDIManagerClient* client, uint32 port_index, const std::vector& data, double timestamp) { DCHECK(CurrentlyOnMIDISendThread()); // System Exclusive has already been filtered. MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp); midi_packet_ = MIDIPacketListAdd( packet_list_, kMaxPacketListSize, midi_packet_, coremidi_timestamp, data.size(), &data[0]); // Lookup the destination based on the port index. if (static_cast(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_); client->AccumulateMIDIBytesSent(data.size()); } 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(nanoseconds) / 1.0e9; } MIDITimeStamp MIDIManagerMac::SecondsToMIDITimeStamp(double seconds) { UInt64 nanos = UInt64(seconds * 1.0e9); return AudioConvertNanosToHostTime(nanos); } } // namespace media