summaryrefslogtreecommitdiffstats
path: root/media/midi
diff options
context:
space:
mode:
Diffstat (limited to 'media/midi')
-rw-r--r--media/midi/midi_manager.cc67
-rw-r--r--media/midi/midi_manager.h109
-rw-r--r--media/midi/midi_manager_mac.cc205
-rw-r--r--media/midi/midi_manager_mac.h69
-rw-r--r--media/midi/midi_port_info.cc32
-rw-r--r--media/midi/midi_port_info.h36
6 files changed, 518 insertions, 0 deletions
diff --git a/media/midi/midi_manager.cc b/media/midi/midi_manager.cc
new file mode 100644
index 0000000..f991865
--- /dev/null
+++ b/media/midi/midi_manager.cc
@@ -0,0 +1,67 @@
+// 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.h"
+
+namespace media {
+
+#if !defined(OS_MACOSX)
+// TODO(crogers): implement MIDIManager for other platforms.
+MIDIManager* MIDIManager::Create() {
+ return NULL;
+}
+#endif
+
+MIDIManager::MIDIManager()
+ : initialized_(false) {
+}
+
+MIDIManager::~MIDIManager() {}
+
+bool MIDIManager::RequestAccess(MIDIManagerClient* client, int access) {
+ // TODO(crogers): determine if user prompt is necessary here.
+ // For now, simply don't allow sysex.
+ if (access != kNoSystemExclusive)
+ return false;
+
+ // Lazily initialize the MIDI back-end.
+ if (!initialized_)
+ initialized_ = Initialize();
+
+ if (initialized_) {
+ base::AutoLock auto_lock(clients_lock_);
+ clients_.insert(client);
+ }
+
+ return initialized_;
+}
+
+void MIDIManager::ReleaseAccess(MIDIManagerClient* client) {
+ base::AutoLock auto_lock(clients_lock_);
+ ClientList::iterator i = clients_.find(client);
+ if (i != clients_.end())
+ clients_.erase(i);
+}
+
+void MIDIManager::AddInputPort(const MIDIPortInfo& info) {
+ input_ports_.push_back(info);
+}
+
+void MIDIManager::AddOutputPort(const MIDIPortInfo& info) {
+ output_ports_.push_back(info);
+}
+
+void MIDIManager::ReceiveMIDIData(
+ int port_index,
+ const uint8* data,
+ size_t length,
+ double timestamp) {
+ base::AutoLock auto_lock(clients_lock_);
+
+ // TODO(crogers): Filter out sysex.
+ for (ClientList::iterator i = clients_.begin(); i != clients_.end(); ++i)
+ (*i)->ReceiveMIDIData(port_index, data, length, timestamp);
+};
+
+} // namespace media
diff --git a/media/midi/midi_manager.h b/media/midi/midi_manager.h
new file mode 100644
index 0000000..1df444f
--- /dev/null
+++ b/media/midi/midi_manager.h
@@ -0,0 +1,109 @@
+// 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.
+
+#ifndef MEDIA_MIDI_MIDI_MANAGER_H_
+#define MEDIA_MIDI_MIDI_MANAGER_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/synchronization/lock.h"
+#include "media/base/media_export.h"
+#include "media/midi/midi_port_info.h"
+
+namespace media {
+
+// A MIDIManagerClient registers with the MIDIManager to receive MIDI data.
+// See MIDIManager::RequestAccess() and MIDIManager::ReleaseAccess()
+// for details.
+class MEDIA_EXPORT MIDIManagerClient {
+ public:
+ virtual ~MIDIManagerClient() {}
+
+ // ReceiveMIDIData() is called when MIDI data has been received from the
+ // MIDI system.
+ // |port_index| represents the specific input port from input_ports().
+ // |data| represents a series of bytes encoding one or more MIDI messages.
+ // |length| is the number of bytes in |data|.
+ // |timestamp| is the time the data was received, in seconds.
+ virtual void ReceiveMIDIData(int port_index,
+ const uint8* data,
+ size_t length,
+ double timestamp) = 0;
+};
+
+// Manages access to all MIDI hardware.
+class MEDIA_EXPORT MIDIManager {
+ public:
+ enum AccessType {
+ kNoSystemExclusive,
+ kSystemExclusive
+ };
+
+ static MIDIManager* Create();
+
+ MIDIManager();
+ virtual ~MIDIManager();
+
+ // A client calls RequestAccess() to receive and send MIDI data.
+ // If access is approved, the MIDI system is lazily initialized
+ // and the client is registered to receive MIDI data.
+ // Returns |true| if access is approved.
+ bool RequestAccess(MIDIManagerClient* client, int access);
+
+ // A client calls ReleaseAccess() to stop receiving MIDI data.
+ void ReleaseAccess(MIDIManagerClient* client);
+
+ // SendMIDIData() sends one or more messages at the given time.
+ // |port_index| represents the specific output port from output_ports().
+ // |data| represents a series of bytes encoding one or more MIDI messages.
+ // |length| is the number of bytes in |data|.
+ // |timestamp| is the time to send the data, in seconds.
+ virtual void SendMIDIData(int port_index,
+ const uint8* data,
+ size_t length,
+ double timestamp) = 0;
+
+ // input_ports() is a list of MIDI ports for receiving MIDI data.
+ // Each individual port in this list can be identified by its
+ // integer index into this list.
+ const MIDIPortInfoList& input_ports() { return input_ports_; }
+
+ // output_ports() is a list of MIDI ports for sending MIDI data.
+ // Each individual port in this list can be identified by its
+ // integer index into this list.
+ const MIDIPortInfoList& output_ports() { return output_ports_; }
+
+ protected:
+ // Initializes the MIDI system, returning |true| on success.
+ virtual bool Initialize() = 0;
+
+ void AddInputPort(const MIDIPortInfo& info);
+ void AddOutputPort(const MIDIPortInfo& info);
+
+ // Dispatches to all clients.
+ void ReceiveMIDIData(
+ int port_index,
+ const uint8* data,
+ size_t length,
+ double timestamp);
+
+ bool initialized_;
+
+ // Keeps track of all clients who wish to receive MIDI data.
+ typedef std::set<MIDIManagerClient*> ClientList;
+ ClientList clients_;
+
+ // Protects access to our clients.
+ base::Lock clients_lock_;
+
+ MIDIPortInfoList input_ports_;
+ MIDIPortInfoList output_ports_;
+
+ DISALLOW_COPY_AND_ASSIGN(MIDIManager);
+};
+
+} // namespace media
+
+#endif // MEDIA_MIDI_MIDI_MANAGER_H_
diff --git a/media/midi/midi_manager_mac.cc b/media/midi/midi_manager_mac.cc
new file mode 100644
index 0000000..73df1fe
--- /dev/null
+++ b/media/midi/midi_manager_mac.cc
@@ -0,0 +1,205 @@
+// 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.
+ // TODO(crogers): re-factor |port_index| to use unsigned
+ // to avoid the need for this check.
+ if (port_index < 0 ||
+ 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
diff --git a/media/midi/midi_manager_mac.h b/media/midi/midi_manager_mac.h
new file mode 100644
index 0000000..f513e11
--- /dev/null
+++ b/media/midi/midi_manager_mac.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef MEDIA_MIDI_MIDI_MANAGER_MAC_H_
+#define MEDIA_MIDI_MIDI_MANAGER_MAC_H_
+
+#include <CoreMIDI/MIDIServices.h>
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "media/midi/midi_manager.h"
+#include "media/midi/midi_port_info.h"
+
+namespace media {
+
+class MEDIA_EXPORT MIDIManagerMac : public MIDIManager {
+ public:
+ MIDIManagerMac();
+ virtual ~MIDIManagerMac();
+
+ // MIDIManager implementation.
+ virtual bool Initialize() OVERRIDE;
+ virtual void SendMIDIData(int port_index,
+ const uint8* data,
+ size_t length,
+ double timestamp) OVERRIDE;
+
+ private:
+ // CoreMIDI callback for MIDI data.
+ // Each callback can contain multiple packets, each of which can contain
+ // multiple MIDI messages.
+ static void ReadMidiDispatch(
+ const MIDIPacketList *pktlist,
+ void *read_proc_refcon,
+ void *src_conn_refcon);
+ virtual void ReadMidi(MIDIEndpointRef source, const MIDIPacketList *pktlist);
+
+ // Helper
+ static media::MIDIPortInfo GetPortInfoFromEndpoint(MIDIEndpointRef endpoint);
+ static double MIDITimeStampToSeconds(MIDITimeStamp timestamp);
+ static MIDITimeStamp SecondsToMIDITimeStamp(double seconds);
+
+ // CoreMIDI
+ MIDIClientRef midi_client_;
+ MIDIPortRef coremidi_input_;
+ MIDIPortRef coremidi_output_;
+
+ enum{ kMaxPacketListSize = 512 };
+ char midi_buffer_[kMaxPacketListSize];
+ MIDIPacketList* packet_list_;
+ MIDIPacket* midi_packet_;
+
+ typedef std::map<MIDIEndpointRef, int> SourceMap;
+
+ // Keeps track of the index (0-based) for each of our sources.
+ SourceMap source_map_;
+
+ // Keeps track of all destinations.
+ std::vector<MIDIEndpointRef> destinations_;
+
+ DISALLOW_COPY_AND_ASSIGN(MIDIManagerMac);
+};
+
+} // namespace media
+
+#endif // MEDIA_MIDI_MIDI_MANAGER_MAC_H_
diff --git a/media/midi/midi_port_info.cc b/media/midi/midi_port_info.cc
new file mode 100644
index 0000000..37d077d
--- /dev/null
+++ b/media/midi/midi_port_info.cc
@@ -0,0 +1,32 @@
+// 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_port_info.h"
+
+#include <iostream>
+
+using std::cout;
+
+namespace media {
+
+MIDIPortInfo::MIDIPortInfo() {}
+
+MIDIPortInfo::MIDIPortInfo(const std::string& in_id,
+ const std::string& in_manufacturer,
+ const std::string& in_name,
+ const std::string& in_version)
+ : id(in_id),
+ manufacturer(in_manufacturer),
+ name(in_name),
+ version(in_version) {}
+
+MIDIPortInfo::~MIDIPortInfo() {}
+
+MIDIPortInfo::MIDIPortInfo(const MIDIPortInfo& info)
+ : id(info.id),
+ manufacturer(info.manufacturer),
+ name(info.name),
+ version(info.version) {}
+
+} // namespace media
diff --git a/media/midi/midi_port_info.h b/media/midi/midi_port_info.h
new file mode 100644
index 0000000..f4afb49
--- /dev/null
+++ b/media/midi/midi_port_info.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef MEDIA_MIDI_MIDI_PORT_INFO_H_
+#define MEDIA_MIDI_MIDI_PORT_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+struct MEDIA_EXPORT MIDIPortInfo {
+ MIDIPortInfo();
+ MIDIPortInfo(const std::string& in_id,
+ const std::string& in_manufacturer,
+ const std::string& in_name,
+ const std::string& in_version);
+
+ MIDIPortInfo(const MIDIPortInfo& info);
+ ~MIDIPortInfo();
+
+ std::string id;
+ std::string manufacturer;
+ std::string name;
+ std::string version;
+};
+
+typedef std::vector<MIDIPortInfo> MIDIPortInfoList;
+
+} // namespace media
+
+#endif // MEDIA_MIDI_MIDI_PORT_INFO_H_