From 3940e87eaee6fb59800cffba475a294e3fa5c72c Mon Sep 17 00:00:00 2001 From: "crogers@google.com" Date: Sat, 22 Jun 2013 00:55:52 +0000 Subject: 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 --- content/browser/DEPS | 1 + content/browser/browser_main_loop.cc | 6 +- content/browser/browser_main_loop.h | 3 + content/browser/renderer_host/media/midi_host.cc | 99 ++++++++++ content/browser/renderer_host/media/midi_host.h | 62 +++++++ .../renderer_host/render_process_host_impl.cc | 3 + content/common/content_message_generator.h | 1 + content/common/media/midi_messages.h | 46 +++++ content/content_browser.gypi | 2 + content/content_common.gypi | 1 + content/content_renderer.gypi | 4 + content/renderer/media/midi_message_filter.cc | 199 ++++++++++++++++++++ content/renderer/media/midi_message_filter.h | 116 ++++++++++++ .../media/renderer_webmidiaccessor_impl.cc | 43 +++++ .../renderer/media/renderer_webmidiaccessor_impl.h | 41 +++++ content/renderer/render_thread_impl.cc | 4 + content/renderer/render_thread_impl.h | 5 + .../renderer_webkitplatformsupport_impl.cc | 10 + .../renderer/renderer_webkitplatformsupport_impl.h | 2 + ipc/ipc_message_start.h | 1 + media/media.gyp | 8 + media/midi/midi_manager.cc | 67 +++++++ media/midi/midi_manager.h | 109 +++++++++++ media/midi/midi_manager_mac.cc | 205 +++++++++++++++++++++ media/midi/midi_manager_mac.h | 69 +++++++ media/midi/midi_port_info.cc | 32 ++++ media/midi/midi_port_info.h | 36 ++++ 27 files changed, 1174 insertions(+), 1 deletion(-) create mode 100644 content/browser/renderer_host/media/midi_host.cc create mode 100644 content/browser/renderer_host/media/midi_host.h create mode 100644 content/common/media/midi_messages.h create mode 100644 content/renderer/media/midi_message_filter.cc create mode 100644 content/renderer/media/midi_message_filter.h create mode 100644 content/renderer/media/renderer_webmidiaccessor_impl.cc create mode 100644 content/renderer/media/renderer_webmidiaccessor_impl.h create mode 100644 media/midi/midi_manager.cc create mode 100644 media/midi/midi_manager.h create mode 100644 media/midi/midi_manager_mac.cc create mode 100644 media/midi/midi_manager_mac.h create mode 100644 media/midi/midi_port_info.cc create mode 100644 media/midi/midi_port_info.h diff --git a/content/browser/DEPS b/content/browser/DEPS index c447bea..67ca2ef 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS @@ -5,6 +5,7 @@ include_rules = [ "+content/child/webkitplatformsupport_impl.h", # For in-process webkit "+media/audio", # For audio input for speech input feature. "+media/base", # For Android JNI registration. + "+media/midi", # For Web MIDI API "+sql", "+ui/webui", "+win8/util", diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index 4e1411d..9599d85 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc @@ -47,6 +47,7 @@ #include "crypto/nss_util.h" #include "media/audio/audio_manager.h" #include "media/base/media.h" +#include "media/midi/midi_manager.h" #include "net/base/network_change_notifier.h" #include "net/socket/client_socket_factory.h" #include "net/ssl/ssl_config_service.h" @@ -400,7 +401,10 @@ void BrowserMainLoop::MainMessageLoopStart() { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:AudioMan") audio_manager_.reset(media::AudioManager::Create()); } - + { + TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MIDIManager") + midi_manager_.reset(media::MIDIManager::Create()); + } #if !defined(OS_IOS) { diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h index 0317e03..fc412cc 100644 --- a/content/browser/browser_main_loop.h +++ b/content/browser/browser_main_loop.h @@ -20,6 +20,7 @@ class SystemMonitor; namespace media { class AudioManager; +class MIDIManager; } namespace net { @@ -81,6 +82,7 @@ class BrowserMainLoop { MediaStreamManager* media_stream_manager() const { return media_stream_manager_.get(); } + media::MIDIManager* midi_manager() const { return midi_manager_.get(); } private: class MemoryObserver; @@ -106,6 +108,7 @@ class BrowserMainLoop { scoped_ptr hi_res_timer_manager_; scoped_ptr network_change_notifier_; scoped_ptr audio_manager_; + scoped_ptr midi_manager_; scoped_ptr audio_mirroring_manager_; scoped_ptr media_stream_manager_; // Per-process listener for online state changes. diff --git a/content/browser/renderer_host/media/midi_host.cc b/content/browser/renderer_host/media/midi_host.cc new file mode 100644 index 0000000..56f4099 --- /dev/null +++ b/content/browser/renderer_host/media/midi_host.cc @@ -0,0 +1,99 @@ +// 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 "content/browser/renderer_host/media/midi_host.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/debug/trace_event.h" +#include "base/process.h" +#include "content/browser/browser_main_loop.h" +#include "content/browser/media/media_internals.h" +#include "content/common/media/midi_messages.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/media_observer.h" +#include "media/midi/midi_manager.h" + +using media::MIDIManager; +using media::MIDIPortInfoList; + +namespace content { + +MIDIHost::MIDIHost(media::MIDIManager* midi_manager) + : midi_manager_(midi_manager) { +} + +MIDIHost::~MIDIHost() { + if (midi_manager_) + midi_manager_->ReleaseAccess(this); +} + +void MIDIHost::OnChannelClosing() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + BrowserMessageFilter::OnChannelClosing(); +} + +void MIDIHost::OnDestruct() const { + BrowserThread::DeleteOnIOThread::Destruct(this); +} + +/////////////////////////////////////////////////////////////////////////////// +// IPC Messages handler +bool MIDIHost::OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(MIDIHost, message, *message_was_ok) + IPC_MESSAGE_HANDLER(MIDIHostMsg_RequestAccess, OnRequestAccess) + IPC_MESSAGE_HANDLER(MIDIHostMsg_SendData, OnSendData) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP_EX() + + return handled; +} + +void MIDIHost::OnRequestAccess(int client_id, int access) { + MIDIPortInfoList input_ports; + MIDIPortInfoList output_ports; + + // Ask permission and register to receive MIDI data. + bool approved = false; + if (midi_manager_) { + approved = midi_manager_->RequestAccess(this, access); + if (approved) { + input_ports = midi_manager_->input_ports(); + output_ports = midi_manager_->output_ports(); + } + } + + Send(new MIDIMsg_AccessApproved( + client_id, + access, + approved, + input_ports, + output_ports)); +} + +void MIDIHost::OnSendData(int port, + const std::vector& data, + double timestamp) { + // TODO(crogers): we need to post this to a dedicated thread for + // sending MIDI. For now, we will not implement the sending of MIDI data. + NOTIMPLEMENTED(); + // if (midi_manager_) + // midi_manager_->SendMIDIData(port, data.data(), data.size(), timestamp); +} + +void MIDIHost::ReceiveMIDIData( + int port_index, + const uint8* data, + size_t length, + double timestamp) { + TRACE_EVENT0("midi", "MIDIHost::ReceiveMIDIData"); + // Send to the renderer. + std::vector v(data, data + length); + Send(new MIDIMsg_DataReceived(port_index, v, timestamp)); +} + +} // namespace content diff --git a/content/browser/renderer_host/media/midi_host.h b/content/browser/renderer_host/media/midi_host.h new file mode 100644 index 0000000..e46ed53 --- /dev/null +++ b/content/browser/renderer_host/media/midi_host.h @@ -0,0 +1,62 @@ +// 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 CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "content/public/browser/browser_message_filter.h" +#include "content/public/browser/browser_thread.h" +#include "media/midi/midi_manager.h" + +namespace media { +class MIDIManager; +} + +namespace content { + +class CONTENT_EXPORT MIDIHost + : public BrowserMessageFilter, + public media::MIDIManagerClient { + public: + // Called from UI thread from the owner of this object. + MIDIHost(media::MIDIManager* midi_manager); + + // BrowserMessageFilter implementation. + virtual void OnChannelClosing() OVERRIDE; + virtual void OnDestruct() const OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) OVERRIDE; + + // MIDIManagerClient implementation. + virtual void ReceiveMIDIData( + int port_index, + const uint8* data, + size_t length, + double timestamp) OVERRIDE; + + // Request access to MIDI hardware. + void OnRequestAccess(int client_id, int access); + + // Data to be sent to a MIDI output port. + void OnSendData(int port, + const std::vector& data, + double timestamp); + + private: + friend class base::DeleteHelper; + friend class BrowserThread; + + virtual ~MIDIHost(); + + media::MIDIManager* const midi_manager_; + + DISALLOW_COPY_AND_ASSIGN(MIDIHost); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_ diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 65268c1..20b7b99 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -73,6 +73,7 @@ #include "content/browser/renderer_host/media/audio_mirroring_manager.h" #include "content/browser/renderer_host/media/audio_renderer_host.h" #include "content/browser/renderer_host/media/media_stream_dispatcher_host.h" +#include "content/browser/renderer_host/media/midi_host.h" #include "content/browser/renderer_host/media/peer_connection_tracker_host.h" #include "content/browser/renderer_host/media/video_capture_host.h" #include "content/browser/renderer_host/memory_benchmark_message_filter.h" @@ -604,6 +605,8 @@ void RenderProcessHostImpl::CreateMessageFilters() { GetID(), audio_manager, BrowserMainLoop::GetInstance()->audio_mirroring_manager(), media_internals, media_stream_manager)); + channel_->AddFilter( + new MIDIHost(BrowserMainLoop::GetInstance()->midi_manager())); channel_->AddFilter(new VideoCaptureHost(media_stream_manager)); channel_->AddFilter(new AppCacheDispatcherHost( storage_partition_impl_->GetAppCacheService(), diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h index 2a54f56..35927dd 100644 --- a/content/common/content_message_generator.h +++ b/content/common/content_message_generator.h @@ -31,6 +31,7 @@ #include "content/common/input_messages.h" #include "content/common/java_bridge_messages.h" #include "content/common/media/audio_messages.h" +#include "content/common/media/midi_messages.h" #if defined(OS_ANDROID) #include "content/common/media/media_player_messages_android.h" #endif diff --git a/content/common/media/midi_messages.h b/content/common/media/midi_messages.h new file mode 100644 index 0000000..de3cca6 --- /dev/null +++ b/content/common/media/midi_messages.h @@ -0,0 +1,46 @@ +// 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. + +// IPC messages for access to MIDI hardware. +// Multiply-included message file, hence no include guard. + +#include "base/basictypes.h" +#include "content/common/content_export.h" +#include "ipc/ipc_message_macros.h" +#include "media/midi/midi_port_info.h" + +#undef IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_EXPORT CONTENT_EXPORT +#define IPC_MESSAGE_START MIDIMsgStart + +IPC_STRUCT_TRAITS_BEGIN(media::MIDIPortInfo) + IPC_STRUCT_TRAITS_MEMBER(id) + IPC_STRUCT_TRAITS_MEMBER(manufacturer) + IPC_STRUCT_TRAITS_MEMBER(name) + IPC_STRUCT_TRAITS_MEMBER(version) +IPC_STRUCT_TRAITS_END() + +// Renderer request to browser for access to MIDI services. +IPC_MESSAGE_CONTROL2(MIDIHostMsg_RequestAccess, + int /* client id */, + int /* access */) + +IPC_MESSAGE_CONTROL3(MIDIHostMsg_SendData, + int /* port */, + std::vector /* data */, + double /* timestamp */) + +// Messages sent from the browser to the renderer. + +IPC_MESSAGE_CONTROL5(MIDIMsg_AccessApproved, + int /* client id */, + int /* access */, + bool /* success */, + media::MIDIPortInfoList /* input ports */, + media::MIDIPortInfoList /* output ports */) + +IPC_MESSAGE_CONTROL3(MIDIMsg_DataReceived, + int /* port */, + std::vector /* data */, + double /* timestamp */) diff --git a/content/content_browser.gypi b/content/content_browser.gypi index c7425fb..1984709 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -791,6 +791,8 @@ 'browser/renderer_host/media/media_stream_requester.h', 'browser/renderer_host/media/media_stream_ui_proxy.cc', 'browser/renderer_host/media/media_stream_ui_proxy.h', + 'browser/renderer_host/media/midi_host.cc', + 'browser/renderer_host/media/midi_host.h', 'browser/renderer_host/media/video_capture_buffer_pool.cc', 'browser/renderer_host/media/video_capture_buffer_pool.h', 'browser/renderer_host/media/video_capture_controller.cc', diff --git a/content/content_common.gypi b/content/content_common.gypi index 29b6e07..1ed029b 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -265,6 +265,7 @@ 'common/media/media_stream_messages.h', 'common/media/media_stream_options.cc', 'common/media/media_stream_options.h', + 'common/media/midi_messages.h', 'common/media/video_capture.h', 'common/media/video_capture_messages.h', 'common/memory_benchmark_messages.h', diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index c8e52fd..24cd543b 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -156,6 +156,8 @@ 'renderer/media/media_stream_dispatcher.h', 'renderer/media/media_stream_dispatcher_eventhandler.h', 'renderer/media/media_stream_impl.h', + 'renderer/media/midi_message_filter.cc', + 'renderer/media/midi_message_filter.h', 'renderer/media/pepper_platform_video_decoder_impl.cc', 'renderer/media/pepper_platform_video_decoder_impl.h', 'renderer/media/render_media_log.cc', @@ -164,6 +166,8 @@ 'renderer/media/renderer_gpu_video_decoder_factories.h', 'renderer/media/renderer_webaudiodevice_impl.cc', 'renderer/media/renderer_webaudiodevice_impl.h', + 'renderer/media/renderer_webmidiaccessor_impl.cc', + 'renderer/media/renderer_webmidiaccessor_impl.h', 'renderer/media/rtc_video_renderer.cc', 'renderer/media/rtc_video_renderer.h', 'renderer/media/stream_texture_factory_impl_android.cc', diff --git a/content/renderer/media/midi_message_filter.cc b/content/renderer/media/midi_message_filter.cc new file mode 100644 index 0000000..1abb537 --- /dev/null +++ b/content/renderer/media/midi_message_filter.cc @@ -0,0 +1,199 @@ +// 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 "content/renderer/media/midi_message_filter.h" + +#include "base/bind.h" +#include "base/debug/trace_event.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/strings/utf_string_conversions.h" +#include "content/common/media/midi_messages.h" +#include "content/renderer/render_thread_impl.h" +#include "ipc/ipc_logging.h" + +using media::MIDIPortInfoList; +using base::AutoLock; + +namespace content { + +MIDIMessageFilter::MIDIMessageFilter( + const scoped_refptr& io_message_loop) + : channel_(NULL), + io_message_loop_(io_message_loop), + next_available_id_(0) { +} + +MIDIMessageFilter::~MIDIMessageFilter() {} + +void MIDIMessageFilter::Send(IPC::Message* message) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + if (!channel_) { + delete message; + } else { + channel_->Send(message); + } +} + +bool MIDIMessageFilter::OnMessageReceived(const IPC::Message& message) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(MIDIMessageFilter, message) + IPC_MESSAGE_HANDLER(MIDIMsg_AccessApproved, OnAccessApproved) + IPC_MESSAGE_HANDLER(MIDIMsg_DataReceived, OnDataReceived) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void MIDIMessageFilter::OnFilterAdded(IPC::Channel* channel) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + channel_ = channel; +} + +void MIDIMessageFilter::OnFilterRemoved() { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + + // Once removed, a filter will not be used again. At this time all + // delegates must be notified so they release their reference. + OnChannelClosing(); +} + +void MIDIMessageFilter::OnChannelClosing() { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + channel_ = NULL; +} + +void MIDIMessageFilter::RequestAccess( + WebKit::WebMIDIAccessorClient* client, int access) { + // Generate and keep track of a "client id" which is sent to the browser + // to ask permission to talk to MIDI hardware. + // This id is handed back when we receive the answer in OnAccessApproved(). + if (clients_.find(client) == clients_.end()) { + int client_id = next_available_id_++; + clients_[client] = client_id; + + io_message_loop_->PostTask(FROM_HERE, + base::Bind(&MIDIMessageFilter::RequestAccessOnIOThread, this, + client_id, access)); + } +} + +void MIDIMessageFilter::RequestAccessOnIOThread(int client_id, int access) { + Send(new MIDIHostMsg_RequestAccess(client_id, access)); +} + +void MIDIMessageFilter::RemoveClient(WebKit::WebMIDIAccessorClient* client) { + ClientsMap::iterator i = clients_.find(client); + if (i != clients_.end()) + clients_.erase(i); +} + +// Received from browser. + +void MIDIMessageFilter::OnAccessApproved( + int client_id, + int access, + bool success, + MIDIPortInfoList inputs, + MIDIPortInfoList outputs) { + // Handle on the main JS thread. + ChildThread::current()->message_loop()->PostTask(FROM_HERE, + base::Bind(&MIDIMessageFilter::HandleAccessApproved, this, + client_id, access, success, inputs, outputs)); +} + +void MIDIMessageFilter::HandleAccessApproved( + int client_id, + int access, + bool success, + MIDIPortInfoList inputs, + MIDIPortInfoList outputs) { + WebKit::WebMIDIAccessorClient* client = GetClientFromId(client_id); + if (!client) + return; + + if (success) { + // Add the client's input and output ports. + for (size_t i = 0; i < inputs.size(); ++i) { + client->didAddInputPort( + UTF8ToUTF16(inputs[i].id), + UTF8ToUTF16(inputs[i].manufacturer), + UTF8ToUTF16(inputs[i].name), + UTF8ToUTF16(inputs[i].version)); + } + + for (size_t i = 0; i < outputs.size(); ++i) { + client->didAddOutputPort( + UTF8ToUTF16(outputs[i].id), + UTF8ToUTF16(outputs[i].manufacturer), + UTF8ToUTF16(outputs[i].name), + UTF8ToUTF16(outputs[i].version)); + } + } + + if (success) + client->didAllowAccess(); + else + client->didBlockAccess(); +} + +WebKit::WebMIDIAccessorClient* +MIDIMessageFilter::GetClientFromId(int client_id) { + // Iterating like this seems inefficient, but in practice there generally + // will be very few clients (usually one). Additionally, this lookup + // usually happens one time during page load. So the performance hit is + // negligible. + for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) { + if ((*i).second == client_id) + return (*i).first; + } + return NULL; +} + +void MIDIMessageFilter::OnDataReceived(int port, + const std::vector& data, + double timestamp) { + TRACE_EVENT0("midi", "MIDIMessageFilter::OnDataReceived"); + + ChildThread::current()->message_loop()->PostTask(FROM_HERE, + base::Bind(&MIDIMessageFilter::HandleDataReceived, this, + port, data, timestamp)); +} + +void MIDIMessageFilter::HandleDataReceived(int port, + const std::vector& data, + double timestamp) { + TRACE_EVENT0("midi", "MIDIMessageFilter::HandleDataReceived"); + +#if defined(OS_ANDROID) + // TODO(crogers): figure out why data() method does not compile on Android. + NOTIMPLEMENTED(); +#else + for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) + (*i).first->didReceiveMIDIData(port, data.data(), data.size(), timestamp); +#endif +} + +void MIDIMessageFilter::SendMIDIData(int port, + const uint8* data, + size_t length, + double timestamp) { + // TODO(crogers): we need more work to check the amount of data sent, + // throttle if necessary, and filter out the SYSEX messages if not + // approved. For now, we will not implement the sending of MIDI data. + NOTIMPLEMENTED(); + // std::vector v(data, data + length); + // io_message_loop_->PostTask(FROM_HERE, + // base::Bind(&MIDIMessageFilter::SendMIDIDataOnIOThread, this, + // port, v, timestamp)); +} + +void MIDIMessageFilter::SendMIDIDataOnIOThread(int port, + const std::vector& data, + double timestamp) { + // Send to the browser. + Send(new MIDIHostMsg_SendData(port, data, timestamp)); +} + +} // namespace content diff --git a/content/renderer/media/midi_message_filter.h b/content/renderer/media/midi_message_filter.h new file mode 100644 index 0000000..236ae47 --- /dev/null +++ b/content/renderer/media/midi_message_filter.h @@ -0,0 +1,116 @@ +// 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 CONTENT_RENDERER_MEDIA_MIDI_MESSAGE_FILTER_H_ +#define CONTENT_RENDERER_MEDIA_MIDI_MESSAGE_FILTER_H_ + +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "ipc/ipc_channel_proxy.h" +#include "media/midi/midi_port_info.h" +#include "third_party/WebKit/public/platform/WebMIDIAccessor.h" +#include "third_party/WebKit/public/platform/WebMIDIAccessorClient.h" + +namespace base { +class MessageLoopProxy; +} + +namespace content { + +// MessageFilter that handles MIDI messages. +class CONTENT_EXPORT MIDIMessageFilter + : public IPC::ChannelProxy::MessageFilter { + public: + explicit MIDIMessageFilter( + const scoped_refptr& io_message_loop); + + // Each client registers for MIDI access here. + // If permission is granted, then the client's + // addInputPort() and addOutputPort() methods will be called, + // giving the client access to receive and send data. + void RequestAccess(WebKit::WebMIDIAccessorClient* client, int access); + void RemoveClient(WebKit::WebMIDIAccessorClient* client); + + // A client will only be able to call this method if it has a suitable + // output port (from addOutputPort()). + void SendMIDIData(int port, + const uint8* data, + size_t length, + double timestamp); + + // IO message loop associated with this message filter. + scoped_refptr io_message_loop() const { + return io_message_loop_; + } + + protected: + virtual ~MIDIMessageFilter(); + + private: + // Sends an IPC message using |channel_|. + void Send(IPC::Message* message); + + // IPC::ChannelProxy::MessageFilter override. Called on |io_message_loop|. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE; + virtual void OnFilterRemoved() OVERRIDE; + virtual void OnChannelClosing() OVERRIDE; + + // Called when the browser process has approved (or denied) access to + // MIDI hardware. + void OnAccessApproved(int client_id, + int access, + bool success, + media::MIDIPortInfoList inputs, + media::MIDIPortInfoList outputs); + + // Called when the browser process has sent MIDI data containing one or + // more messages. + void OnDataReceived(int port, + const std::vector& data, + double timestamp); + + void HandleAccessApproved(int client_id, + int access, + bool success, + media::MIDIPortInfoList inputs, + media::MIDIPortInfoList outputs); + + void HandleDataReceived(int port, + const std::vector& data, + double timestamp); + + void RequestAccessOnIOThread(int client_id, int access); + + void SendMIDIDataOnIOThread(int port, + const std::vector& data, + double timestamp); + + WebKit::WebMIDIAccessorClient* GetClientFromId(int client_id); + + // IPC channel for Send(); must only be accessed on |io_message_loop_|. + IPC::Channel* channel_; + + // Message loop on which IPC calls are driven. + const scoped_refptr io_message_loop_; + + // Keeps track of all MIDI clients. + // We map client to "client id" used to track permission. + // When access has been approved, we add the input and output ports to + // the client, allowing it to actually receive and send MIDI data. + typedef std::map ClientsMap; + ClientsMap clients_; + + // Dishes out client ids. + int next_available_id_; + + DISALLOW_COPY_AND_ASSIGN(MIDIMessageFilter); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_MEDIA_MIDI_MESSAGE_FILTER_H_ diff --git a/content/renderer/media/renderer_webmidiaccessor_impl.cc b/content/renderer/media/renderer_webmidiaccessor_impl.cc new file mode 100644 index 0000000..0a8b68f --- /dev/null +++ b/content/renderer/media/renderer_webmidiaccessor_impl.cc @@ -0,0 +1,43 @@ +// 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 "content/renderer/media/renderer_webmidiaccessor_impl.h" + +#include "base/logging.h" +#include "content/renderer/media/midi_message_filter.h" +#include "content/renderer/render_thread_impl.h" + +namespace content { + +RendererWebMIDIAccessorImpl::RendererWebMIDIAccessorImpl( + WebKit::WebMIDIAccessorClient* client) + : client_(client) { + DCHECK(client_); +} + +RendererWebMIDIAccessorImpl::~RendererWebMIDIAccessorImpl() { + midi_message_filter()->RemoveClient(client_); +} + +void RendererWebMIDIAccessorImpl::requestAccess(bool access) { + midi_message_filter()->RequestAccess(client_, access ? 1 : 0); +} + +void RendererWebMIDIAccessorImpl::sendMIDIData( + unsigned port_index, + const unsigned char* data, + size_t length, + double timestamp) { + midi_message_filter()->SendMIDIData( + port_index, + data, + length, + timestamp); +} + +MIDIMessageFilter* RendererWebMIDIAccessorImpl::midi_message_filter() { + return RenderThreadImpl::current()->midi_message_filter(); +} + +} // namespace content diff --git a/content/renderer/media/renderer_webmidiaccessor_impl.h b/content/renderer/media/renderer_webmidiaccessor_impl.h new file mode 100644 index 0000000..a6a07c9 --- /dev/null +++ b/content/renderer/media/renderer_webmidiaccessor_impl.h @@ -0,0 +1,41 @@ +// 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 CONTENT_RENDERER_MEDIA_RENDERER_WEBMIDIACCESSOR_IMPL_H_ +#define CONTENT_RENDERER_MEDIA_RENDERER_WEBMIDIACCESSOR_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "third_party/WebKit/public/platform/WebMIDIAccessor.h" +#include "third_party/WebKit/public/platform/WebMIDIAccessorClient.h" + +namespace content { + +class MIDIMessageFilter; + +class RendererWebMIDIAccessorImpl + : public WebKit::WebMIDIAccessor { + public: + explicit RendererWebMIDIAccessorImpl( + WebKit::WebMIDIAccessorClient* client); + virtual ~RendererWebMIDIAccessorImpl(); + + // WebKit::WebMIDIAccessor implementation. + virtual void requestAccess(bool access) OVERRIDE; + virtual void sendMIDIData(unsigned port_index, + const unsigned char* data, + size_t length, + double timestamp) OVERRIDE; + + private: + WebKit::WebMIDIAccessorClient* client_; + + MIDIMessageFilter* midi_message_filter(); + + DISALLOW_COPY_AND_ASSIGN(RendererWebMIDIAccessorImpl); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_MEDIA_RENDERER_WEBMIDIACCESSOR_IMPL_H_ diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 795a92a..72e7fb5 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -65,6 +65,7 @@ #include "content/renderer/media/audio_renderer_mixer_manager.h" #include "content/renderer/media/media_stream_center.h" #include "content/renderer/media/media_stream_dependency_factory.h" +#include "content/renderer/media/midi_message_filter.h" #include "content/renderer/media/peer_connection_tracker.h" #include "content/renderer/media/video_capture_impl_manager.h" #include "content/renderer/media/video_capture_message_filter.h" @@ -368,6 +369,9 @@ void RenderThreadImpl::Init() { audio_message_filter_ = new AudioMessageFilter(GetIOMessageLoopProxy()); AddFilter(audio_message_filter_.get()); + midi_message_filter_ = new MIDIMessageFilter(GetIOMessageLoopProxy()); + AddFilter(midi_message_filter_.get()); + AddFilter(new IndexedDBMessageFilter); GetContentClient()->renderer()->RenderThreadStarted(); diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index 63cc80c..9ef7002 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h @@ -81,6 +81,7 @@ class InputEventFilter; class InputHandlerManager; class MediaStreamCenter; class MediaStreamDependencyFactory; +class MIDIMessageFilter; class P2PSocketDispatcher; class PeerConnectionTracker; class RendererWebKitPlatformSupportImpl; @@ -227,6 +228,9 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, return audio_message_filter_.get(); } + MIDIMessageFilter* midi_message_filter() { + return midi_message_filter_.get(); + } // Creates the embedder implementation of WebMediaStreamCenter. // The resulting object is owned by WebKit and deleted by WebKit at tear-down. @@ -379,6 +383,7 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, scoped_refptr db_message_filter_; scoped_refptr audio_input_message_filter_; scoped_refptr audio_message_filter_; + scoped_refptr midi_message_filter_; scoped_refptr devtools_agent_message_filter_; scoped_ptr media_stream_factory_; diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc index 5e74dc4..0ec76a1 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.cc +++ b/content/renderer/renderer_webkitplatformsupport_impl.cc @@ -31,6 +31,7 @@ #include "content/renderer/hyphenator/hyphenator.h" #include "content/renderer/media/media_stream_dependency_factory.h" #include "content/renderer/media/renderer_webaudiodevice_impl.h" +#include "content/renderer/media/renderer_webmidiaccessor_impl.h" #include "content/renderer/media/webcontentdecryptionmodule_impl.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/renderer_clipboard_client.h" @@ -91,6 +92,7 @@ using WebKit::WebFileSystem; using WebKit::WebFrame; using WebKit::WebGamepads; using WebKit::WebIDBFactory; +using WebKit::WebMIDIAccessor; using WebKit::Platform; using WebKit::WebMediaStreamCenter; using WebKit::WebMediaStreamCenterClient; @@ -771,6 +773,14 @@ RendererWebKitPlatformSupportImpl::createContentDecryptionModule( //------------------------------------------------------------------------------ +WebKit::WebMIDIAccessor* +RendererWebKitPlatformSupportImpl::createMIDIAccessor( + WebKit::WebMIDIAccessorClient* client) { + return new RendererWebMIDIAccessorImpl(client); +} + +//------------------------------------------------------------------------------ + WebKit::WebString RendererWebKitPlatformSupportImpl::signedPublicKeyAndChallengeString( unsigned key_size_index, diff --git a/content/renderer/renderer_webkitplatformsupport_impl.h b/content/renderer/renderer_webkitplatformsupport_impl.h index 104cd24..83990c4 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.h +++ b/content/renderer/renderer_webkitplatformsupport_impl.h @@ -107,6 +107,8 @@ class CONTENT_EXPORT RendererWebKitPlatformSupportImpl virtual WebKit::WebContentDecryptionModule* createContentDecryptionModule( const WebKit::WebString& key_system); + virtual WebKit::WebMIDIAccessor* + createMIDIAccessor(WebKit::WebMIDIAccessorClient* client); virtual WebKit::WebBlobRegistry* blobRegistry(); virtual void sampleGamepads(WebKit::WebGamepads&); diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h index 8226bc1..3e1e59c 100644 --- a/ipc/ipc_message_start.h +++ b/ipc/ipc_message_start.h @@ -47,6 +47,7 @@ enum IPCMessageStart { DesktopNotificationMsgStart, GeolocationMsgStart, AudioMsgStart, + MIDIMsgStart, ChromeMsgStart, DragMsgStart, PrintMsgStart, diff --git a/media/media.gyp b/media/media.gyp index 486d112..d017913 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -373,6 +373,12 @@ 'filters/video_renderer_base.h', 'filters/vpx_video_decoder.cc', 'filters/vpx_video_decoder.h', + 'midi/midi_manager.h', + 'midi/midi_manager.cc', + 'midi/midi_manager_mac.h', + 'midi/midi_manager_mac.cc', + 'midi/midi_port_info.h', + 'midi/midi_port_info.cc', 'video/capture/android/video_capture_device_android.cc', 'video/capture/android/video_capture_device_android.h', 'video/capture/fake_video_capture_device.cc', @@ -535,6 +541,7 @@ '$(SDKROOT)/System/Library/Frameworks/AudioToolbox.framework', '$(SDKROOT)/System/Library/Frameworks/AVFoundation.framework', '$(SDKROOT)/System/Library/Frameworks/CoreAudio.framework', + '$(SDKROOT)/System/Library/Frameworks/CoreMIDI.framework', ], }, }], @@ -747,6 +754,7 @@ '$(SDKROOT)/System/Library/Frameworks/AudioToolbox.framework', '$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework', '$(SDKROOT)/System/Library/Frameworks/CoreAudio.framework', + '$(SDKROOT)/System/Library/Frameworks/CoreMIDI.framework', '$(SDKROOT)/System/Library/Frameworks/CoreVideo.framework', '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', '$(SDKROOT)/System/Library/Frameworks/QTKit.framework', 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 + +#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 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 +#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; + +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(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); + MIDIEndpointRef source = static_cast(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(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(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 +#include +#include + +#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 SourceMap; + + // Keeps track of the index (0-based) for each of our sources. + SourceMap source_map_; + + // Keeps track of all destinations. + std::vector 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 + +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 +#include + +#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 MIDIPortInfoList; + +} // namespace media + +#endif // MEDIA_MIDI_MIDI_PORT_INFO_H_ -- cgit v1.1