Home | History | Annotate | Download | only in midi
      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 
     15 using base::IntToString;
     16 using base::SysCFStringRefToUTF8;
     17 using 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 
     23 namespace media {
     24 
     25 MIDIManager* MIDIManager::Create() {
     26   return new MIDIManagerMac();
     27 }
     28 
     29 MIDIManagerMac::MIDIManagerMac()
     30     : midi_client_(0),
     31       coremidi_input_(0),
     32       coremidi_output_(0),
     33       packet_list_(NULL),
     34       midi_packet_(NULL) {
     35 }
     36 
     37 bool 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   int destination_count = MIDIGetNumberOfDestinations();
     71   destinations_.reserve(destination_count);
     72 
     73   for (int 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   int source_count = MIDIGetNumberOfSources();
     86 
     87   for (int 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 
    106 MIDIManagerMac::~MIDIManagerMac() {
    107   if (coremidi_input_)
    108     MIDIPortDispose(coremidi_input_);
    109   if (coremidi_output_)
    110     MIDIPortDispose(coremidi_output_);
    111 }
    112 
    113 void 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 
    127 void 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   int 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 
    149 void MIDIManagerMac::SendMIDIData(MIDIManagerClient* client,
    150                                   int port_index,
    151                                   const uint8* data,
    152                                   size_t length,
    153                                   double timestamp) {
    154   // System Exclusive has already been filtered.
    155   MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp);
    156 
    157   midi_packet_ = MIDIPacketListAdd(
    158       packet_list_,
    159       kMaxPacketListSize,
    160       midi_packet_,
    161       coremidi_timestamp,
    162       length,
    163       data);
    164 
    165   // Lookup the destination based on the port index.
    166   // TODO(crogers): re-factor |port_index| to use unsigned
    167   // to avoid the need for this check.
    168   if (port_index < 0 ||
    169       static_cast<size_t>(port_index) >= destinations_.size())
    170     return;
    171 
    172   MIDIEndpointRef destination = destinations_[port_index];
    173 
    174   MIDISend(coremidi_output_, destination, packet_list_);
    175 
    176   // Re-initialize for next time.
    177   midi_packet_ = MIDIPacketListInit(packet_list_);
    178 
    179   client->AccumulateMIDIBytesSent(length);
    180 }
    181 
    182 MIDIPortInfo MIDIManagerMac::GetPortInfoFromEndpoint(
    183     MIDIEndpointRef endpoint) {
    184   SInt32 id_number = 0;
    185   MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
    186   string id = IntToString(id_number);
    187 
    188   CFStringRef manufacturer_ref = NULL;
    189   MIDIObjectGetStringProperty(
    190       endpoint, kMIDIPropertyManufacturer, &manufacturer_ref);
    191   string manufacturer = SysCFStringRefToUTF8(manufacturer_ref);
    192 
    193   CFStringRef name_ref = NULL;
    194   MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref);
    195   string name = SysCFStringRefToUTF8(name_ref);
    196 
    197   SInt32 version_number = 0;
    198   MIDIObjectGetIntegerProperty(
    199       endpoint, kMIDIPropertyDriverVersion, &version_number);
    200   string version = IntToString(version_number);
    201 
    202   return MIDIPortInfo(id, manufacturer, name, version);
    203 }
    204 
    205 double MIDIManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
    206   UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
    207   return static_cast<double>(nanoseconds) / 1.0e9;
    208 }
    209 
    210 MIDITimeStamp MIDIManagerMac::SecondsToMIDITimeStamp(double seconds) {
    211   UInt64 nanos = UInt64(seconds * 1.0e9);
    212   return AudioConvertNanosToHostTime(nanos);
    213 }
    214 
    215 }  // namespace media
    216