diff options
author | crogers@google.com <crogers@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-22 00:55:52 +0000 |
---|---|---|
committer | crogers@google.com <crogers@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-22 00:55:52 +0000 |
commit | 3940e87eaee6fb59800cffba475a294e3fa5c72c (patch) | |
tree | 615fe36029f356c946c1374ba604967741bd4912 /media/midi | |
parent | 8fce311668977b966f21d61220dcdb2091375a16 (diff) | |
download | chromium_src-3940e87eaee6fb59800cffba475a294e3fa5c72c.zip chromium_src-3940e87eaee6fb59800cffba475a294e3fa5c72c.tar.gz chromium_src-3940e87eaee6fb59800cffba475a294e3fa5c72c.tar.bz2 |
Implement Web MIDI API back-end
This involves browser-side support and IPC for sending and receiving
MIDI messages. Initially support for OSX is included.
BUG=163795
R=palmer@chromium.org, piman@chromium.org, scherkus@chromium.org
Review URL: https://codereview.chromium.org/16025005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207983 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/midi')
-rw-r--r-- | media/midi/midi_manager.cc | 67 | ||||
-rw-r--r-- | media/midi/midi_manager.h | 109 | ||||
-rw-r--r-- | media/midi/midi_manager_mac.cc | 205 | ||||
-rw-r--r-- | media/midi/midi_manager_mac.h | 69 | ||||
-rw-r--r-- | media/midi/midi_port_info.cc | 32 | ||||
-rw-r--r-- | media/midi/midi_port_info.h | 36 |
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_ |