midi_manager_mac.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "media/midi/midi_manager_mac.h" 6 7#include <iostream> 8#include <string> 9 10#include "base/debug/trace_event.h" 11#include "base/strings/string_number_conversions.h" 12#include "base/strings/sys_string_conversions.h" 13#include <CoreAudio/HostTime.h> 14 15using base::IntToString; 16using base::SysCFStringRefToUTF8; 17using std::string; 18 19// NB: System MIDI types are pointer types in 32-bit and integer types in 20// 64-bit. Therefore, the initialization is the simplest one that satisfies both 21// (if possible). 22 23namespace media { 24 25MIDIManager* MIDIManager::Create() { 26 return new MIDIManagerMac(); 27} 28 29MIDIManagerMac::MIDIManagerMac() 30 : midi_client_(0), 31 coremidi_input_(0), 32 coremidi_output_(0), 33 packet_list_(NULL), 34 midi_packet_(NULL) { 35} 36 37bool MIDIManagerMac::Initialize() { 38 TRACE_EVENT0("midi", "MIDIManagerMac::Initialize"); 39 40 // CoreMIDI registration. 41 midi_client_ = 0; 42 OSStatus result = MIDIClientCreate( 43 CFSTR("Google Chrome"), 44 NULL, 45 NULL, 46 &midi_client_); 47 48 if (result != noErr) 49 return false; 50 51 coremidi_input_ = 0; 52 53 // Create input and output port. 54 result = MIDIInputPortCreate( 55 midi_client_, 56 CFSTR("MIDI Input"), 57 ReadMIDIDispatch, 58 this, 59 &coremidi_input_); 60 if (result != noErr) 61 return false; 62 63 result = MIDIOutputPortCreate( 64 midi_client_, 65 CFSTR("MIDI Output"), 66 &coremidi_output_); 67 if (result != noErr) 68 return false; 69 70 uint32 destination_count = MIDIGetNumberOfDestinations(); 71 destinations_.resize(destination_count); 72 73 for (uint32 i = 0; i < destination_count ; i++) { 74 MIDIEndpointRef destination = MIDIGetDestination(i); 75 76 // Keep track of all destinations (known as outputs by the Web MIDI API). 77 // Cache to avoid any possible overhead in calling MIDIGetDestination(). 78 destinations_[i] = destination; 79 80 MIDIPortInfo info = GetPortInfoFromEndpoint(destination); 81 AddOutputPort(info); 82 } 83 84 // Open connections from all sources. 85 uint32 source_count = MIDIGetNumberOfSources(); 86 87 for (uint32 i = 0; i < source_count; ++i) { 88 // Receive from all sources. 89 MIDIEndpointRef src = MIDIGetSource(i); 90 MIDIPortConnectSource(coremidi_input_, src, reinterpret_cast<void*>(src)); 91 92 // Keep track of all sources (known as inputs in Web MIDI API terminology). 93 source_map_[src] = i; 94 95 MIDIPortInfo info = GetPortInfoFromEndpoint(src); 96 AddInputPort(info); 97 } 98 99 // TODO(crogers): Fix the memory management here! 100 packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_); 101 midi_packet_ = MIDIPacketListInit(packet_list_); 102 103 return true; 104} 105 106MIDIManagerMac::~MIDIManagerMac() { 107 if (coremidi_input_) 108 MIDIPortDispose(coremidi_input_); 109 if (coremidi_output_) 110 MIDIPortDispose(coremidi_output_); 111} 112 113void MIDIManagerMac::ReadMIDIDispatch(const MIDIPacketList* packet_list, 114 void* read_proc_refcon, 115 void* src_conn_refcon) { 116 MIDIManagerMac* manager = static_cast<MIDIManagerMac*>(read_proc_refcon); 117#if __LP64__ 118 MIDIEndpointRef source = reinterpret_cast<uintptr_t>(src_conn_refcon); 119#else 120 MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon); 121#endif 122 123 // Dispatch to class method. 124 manager->ReadMIDI(source, packet_list); 125} 126 127void MIDIManagerMac::ReadMIDI(MIDIEndpointRef source, 128 const MIDIPacketList* packet_list) { 129 // Lookup the port index based on the source. 130 SourceMap::iterator j = source_map_.find(source); 131 if (j == source_map_.end()) 132 return; 133 uint32 port_index = source_map_[source]; 134 135 // Go through each packet and process separately. 136 for(size_t i = 0; i < packet_list->numPackets; i++) { 137 // Each packet contains MIDI data for one or more messages (like note-on). 138 const MIDIPacket &packet = packet_list->packet[i]; 139 double timestamp_seconds = MIDITimeStampToSeconds(packet.timeStamp); 140 141 ReceiveMIDIData( 142 port_index, 143 packet.data, 144 packet.length, 145 timestamp_seconds); 146 } 147} 148 149void MIDIManagerMac::SendMIDIData(MIDIManagerClient* client, 150 uint32 port_index, 151 const std::vector<uint8>& data, 152 double timestamp) { 153 DCHECK(CurrentlyOnMIDISendThread()); 154 155 // System Exclusive has already been filtered. 156 MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp); 157 158 midi_packet_ = MIDIPacketListAdd( 159 packet_list_, 160 kMaxPacketListSize, 161 midi_packet_, 162 coremidi_timestamp, 163 data.size(), 164 &data[0]); 165 166 // Lookup the destination based on the port index. 167 if (static_cast<size_t>(port_index) >= destinations_.size()) 168 return; 169 170 MIDIEndpointRef destination = destinations_[port_index]; 171 172 MIDISend(coremidi_output_, destination, packet_list_); 173 174 // Re-initialize for next time. 175 midi_packet_ = MIDIPacketListInit(packet_list_); 176 177 client->AccumulateMIDIBytesSent(data.size()); 178} 179 180MIDIPortInfo MIDIManagerMac::GetPortInfoFromEndpoint( 181 MIDIEndpointRef endpoint) { 182 SInt32 id_number = 0; 183 MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number); 184 string id = IntToString(id_number); 185 186 CFStringRef manufacturer_ref = NULL; 187 MIDIObjectGetStringProperty( 188 endpoint, kMIDIPropertyManufacturer, &manufacturer_ref); 189 string manufacturer = SysCFStringRefToUTF8(manufacturer_ref); 190 191 CFStringRef name_ref = NULL; 192 MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref); 193 string name = SysCFStringRefToUTF8(name_ref); 194 195 SInt32 version_number = 0; 196 MIDIObjectGetIntegerProperty( 197 endpoint, kMIDIPropertyDriverVersion, &version_number); 198 string version = IntToString(version_number); 199 200 return MIDIPortInfo(id, manufacturer, name, version); 201} 202 203double MIDIManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) { 204 UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp); 205 return static_cast<double>(nanoseconds) / 1.0e9; 206} 207 208MIDITimeStamp MIDIManagerMac::SecondsToMIDITimeStamp(double seconds) { 209 UInt64 nanos = UInt64(seconds * 1.0e9); 210 return AudioConvertNanosToHostTime(nanos); 211} 212 213} // namespace media 214