summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcrogers@google.com <crogers@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-22 00:55:52 +0000
committercrogers@google.com <crogers@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-22 00:55:52 +0000
commit3940e87eaee6fb59800cffba475a294e3fa5c72c (patch)
tree615fe36029f356c946c1374ba604967741bd4912
parent8fce311668977b966f21d61220dcdb2091375a16 (diff)
downloadchromium_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
-rw-r--r--content/browser/DEPS1
-rw-r--r--content/browser/browser_main_loop.cc6
-rw-r--r--content/browser/browser_main_loop.h3
-rw-r--r--content/browser/renderer_host/media/midi_host.cc99
-rw-r--r--content/browser/renderer_host/media/midi_host.h62
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc3
-rw-r--r--content/common/content_message_generator.h1
-rw-r--r--content/common/media/midi_messages.h46
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/content_common.gypi1
-rw-r--r--content/content_renderer.gypi4
-rw-r--r--content/renderer/media/midi_message_filter.cc199
-rw-r--r--content/renderer/media/midi_message_filter.h116
-rw-r--r--content/renderer/media/renderer_webmidiaccessor_impl.cc43
-rw-r--r--content/renderer/media/renderer_webmidiaccessor_impl.h41
-rw-r--r--content/renderer/render_thread_impl.cc4
-rw-r--r--content/renderer/render_thread_impl.h5
-rw-r--r--content/renderer/renderer_webkitplatformsupport_impl.cc10
-rw-r--r--content/renderer/renderer_webkitplatformsupport_impl.h2
-rw-r--r--ipc/ipc_message_start.h1
-rw-r--r--media/media.gyp8
-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
27 files changed, 1174 insertions, 1 deletions
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<HighResolutionTimerManager> hi_res_timer_manager_;
scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
scoped_ptr<media::AudioManager> audio_manager_;
+ scoped_ptr<media::MIDIManager> midi_manager_;
scoped_ptr<AudioMirroringManager> audio_mirroring_manager_;
scoped_ptr<MediaStreamManager> 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<uint8>& 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<uint8> 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<uint8>& data,
+ double timestamp);
+
+ private:
+ friend class base::DeleteHelper<MIDIHost>;
+ 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<uint8> /* 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<uint8> /* 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<base::MessageLoopProxy>& 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<uint8>& 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<uint8>& 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<uint8> 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<uint8>& 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 <map>
+#include <vector>
+
+#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<base::MessageLoopProxy>& 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<base::MessageLoopProxy> 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<uint8>& 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<uint8>& data,
+ double timestamp);
+
+ void RequestAccessOnIOThread(int client_id, int access);
+
+ void SendMIDIDataOnIOThread(int port,
+ const std::vector<uint8>& 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<base::MessageLoopProxy> 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<WebKit::WebMIDIAccessorClient*, int> 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<DBMessageFilter> db_message_filter_;
scoped_refptr<AudioInputMessageFilter> audio_input_message_filter_;
scoped_refptr<AudioMessageFilter> audio_message_filter_;
+ scoped_refptr<MIDIMessageFilter> midi_message_filter_;
scoped_refptr<DevToolsAgentFilter> devtools_agent_message_filter_;
scoped_ptr<MediaStreamDependencyFactory> 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 <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_