Last active
December 26, 2015 20:49
-
-
Save atsushieno/7211044 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc | |
index 600ad7b..7550de6 100644 | |
--- a/chrome/browser/about_flags.cc | |
+++ b/chrome/browser/about_flags.cc | |
@@ -1666,7 +1666,7 @@ const Experiment kExperiments[] = { | |
"enable-web-midi", | |
IDS_FLAGS_ENABLE_WEB_MIDI_NAME, | |
IDS_FLAGS_ENABLE_WEB_MIDI_DESCRIPTION, | |
- kOsMac, | |
+ kOsMac | kOsLinux, | |
SINGLE_VALUE_TYPE(switches::kEnableWebMIDI) | |
}, | |
{ | |
diff --git a/media/media.gyp b/media/media.gyp | |
index ee42d7c..e91db53 100644 | |
--- a/media/media.gyp | |
+++ b/media/media.gyp | |
@@ -553,6 +553,30 @@ | |
'-lasound', | |
], | |
}, | |
+ 'defines': [ | |
+ 'PMALSA' | |
+ ], | |
+ 'include_dirs': [ | |
+ '../third_party/portmidi/pm_common', | |
+ '../third_party/portmidi/porttime', | |
+ ], | |
+ 'sources': [ | |
+ 'midi/midi_manager_linux.cc', | |
+ 'midi/midi_manager_linux.h', | |
+ '../third_party/portmidi/pm_common/pminternal.h', | |
+ '../third_party/portmidi/pm_common/pmutil.c', | |
+ '../third_party/portmidi/pm_common/pmutil.h', | |
+ '../third_party/portmidi/pm_common/portmidi.c', | |
+ '../third_party/portmidi/pm_common/portmidi.h', | |
+ '../third_party/portmidi/pm_linux/finddefault.c', | |
+ '../third_party/portmidi/pm_linux/pmlinux.c', | |
+ '../third_party/portmidi/pm_linux/pmlinux.h', | |
+ '../third_party/portmidi/pm_linux/pmlinuxalsa.c', | |
+ '../third_party/portmidi/pm_linux/pmlinuxalsa.h', | |
+ '../third_party/portmidi/porttime/porttime.c', | |
+ '../third_party/portmidi/porttime/porttime.h', | |
+ '../third_party/portmidi/porttime/ptlinux.c', | |
+ ], | |
}, { # use_alsa==0 | |
'sources/': [ ['exclude', '/alsa_'], | |
['exclude', '/audio_manager_linux'] ], | |
diff --git a/media/midi/midi_manager.cc b/media/midi/midi_manager.cc | |
index b3262e4..a64571f 100644 | |
--- a/media/midi/midi_manager.cc | |
+++ b/media/midi/midi_manager.cc | |
@@ -11,7 +11,7 @@ | |
namespace media { | |
-#if !defined(OS_MACOSX) | |
+#if !defined(OS_MACOSX) && !defined(OS_LINUX) | |
// TODO(crogers): implement MIDIManager for other platforms. | |
MIDIManager* MIDIManager::Create() { | |
return NULL; | |
diff --git a/media/midi/midi_manager_linux.cc b/media/midi/midi_manager_linux.cc | |
new file mode 100644 | |
index 0000000..2077ee7 | |
--- /dev/null | |
+++ b/media/midi/midi_manager_linux.cc | |
@@ -0,0 +1,287 @@ | |
+// 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_linux.h" | |
+ | |
+#include <string> | |
+ | |
+#include "base/debug/trace_event.h" | |
+#include "base/strings/string_number_conversions.h" | |
+#include "base/strings/sys_string_conversions.h" | |
+ | |
+// NB: System MIDI types are pointer types in 32-bit and integer types in | |
+// 64-bit. Therefore, the initialization is the simplest one that satisfies both | |
+// (if possible). | |
+ | |
+namespace media { | |
+ | |
+PortMidiDeviceMap::PortMidiDeviceMap(PmDeviceID pmIndex, int mmIndex, bool isOutput) | |
+ : portmidi_index (pmIndex), | |
+ midi_manager_index (mmIndex), | |
+ is_midi_manager_index_output (isOutput) | |
+{ | |
+} | |
+ | |
+MIDIManager* MIDIManager::Create() { | |
+ return new MIDIManagerLinux(); | |
+} | |
+ | |
+MIDIManagerLinux::MIDIManagerLinux() | |
+ : cached_out_port_index(-1) | |
+{ | |
+} | |
+ | |
+bool MIDIManagerLinux::Initialize() { | |
+ TRACE_EVENT0("midi", "MIDIManagerLinux::Initialize"); | |
+ | |
+ Pm_Initialize(); | |
+ | |
+ SetupPollingThread(); | |
+ | |
+ in_streams = new std::map<int,PortMidiStream*>(); | |
+ out_streams = new std::map<int,PortMidiStream*>(); | |
+ | |
+ int nDevices = Pm_CountDevices(); | |
+ device_map = new std::vector<PortMidiDeviceMap*>(); | |
+ int nInputs = 0, nOutputs = 0; | |
+ for (int i = 0; i < nDevices; i++) { | |
+ const PmDeviceInfo *deviceInfo = Pm_GetDeviceInfo (i); // device info memory is managed by portmidi, so do not release. | |
+ if (deviceInfo->input) { | |
+ PortMidiStream *stream; | |
+ PmError e = Pm_OpenInput (&stream, (PmDeviceID) i, NULL, portmidi_input_buffer_size, NULL, NULL); | |
+ if (e != pmNoError) | |
+ continue; // failed to open this device/port, skip it. | |
+ in_streams->insert(std::pair<int,PortMidiStream*> (i, stream)); | |
+ AddInputPort(MIDIPortInfo("input", "-", deviceInfo->name, "-")); | |
+ device_map->push_back(new PortMidiDeviceMap((PmDeviceID) i, nInputs++, false)); | |
+ } | |
+ if (deviceInfo->output) { | |
+ AddOutputPort(MIDIPortInfo("output", "-", deviceInfo->name, "-")); | |
+ device_map->push_back(new PortMidiDeviceMap((PmDeviceID) i, nOutputs++, true)); | |
+ } | |
+ } | |
+ | |
+ return true; | |
+} | |
+ | |
+MIDIManagerLinux::~MIDIManagerLinux() { | |
+ | |
+ midi_in_read_working = false; | |
+ | |
+ for (std::map<int,PortMidiStream*>::iterator iter = in_streams->begin(); | |
+ iter != in_streams->end(); iter++) | |
+ if (iter->second) | |
+ Pm_Close(iter->second); | |
+ for (std::map<int,PortMidiStream*>::iterator iter = out_streams->begin(); | |
+ iter != out_streams->end(); iter++) | |
+ if (iter->second) | |
+ Pm_Close(iter->second); | |
+ delete in_streams; | |
+ delete out_streams; | |
+ | |
+ int size = device_map->size(); | |
+ for (int i = 0; i < size; i++) | |
+ delete device_map->at(i); | |
+ delete device_map; | |
+ | |
+ Pm_Terminate(); | |
+} | |
+ | |
+ | |
+void MIDIManagerLinux::SendMIDIData(MIDIManagerClient* client, | |
+ uint32 port_index, | |
+ const std::vector<uint8>& data, | |
+ double timestamp) { | |
+ DCHECK(CurrentlyOnMIDISendThread()); | |
+ | |
+ PmDeviceID pmDevice = GetPortmidiDeviceIndex(true, port_index); | |
+ if (pmDevice == pmNoDevice) | |
+ return; // invalid device ID | |
+ | |
+ PortMidiStream *out; | |
+ | |
+ if (port_index != cached_out_port_index) { | |
+ std::map<int,PortMidiStream*>::iterator iter = out_streams->find(port_index); | |
+ if (iter == out_streams->end()) { | |
+ PmError e = Pm_OpenOutput(&out, pmDevice, NULL, output_buffer_size, NULL, NULL, output_latency); | |
+ if (e != pmNoError) | |
+ return; // cannot open specified device | |
+ out_streams->insert(std::pair<int,PortMidiStream*> (port_index, out)); | |
+ } | |
+ else | |
+ out = iter->second; | |
+ cached_out_port_index = port_index; | |
+ cached_out = out; | |
+ } | |
+ else | |
+ out = cached_out; | |
+ | |
+ // FIXME: | |
+ // it is not very efficient. Add custom function that accepts char*. | |
+ // | |
+ // PortMIDI API is also looking *terrible*, by that it only either | |
+ // accepts 3-bytes-aligned messages or explicit SysEx sequence. | |
+ // It actually has some internal sysex state check (and hopefully not | |
+ // buggy!) so optimistically we can assume that it handles messages | |
+ // as expected in this form. | |
+ // | |
+ // IF a message is incompletely sent from the caller, there is | |
+ // (currently) no way to handle remaining bytes accordingly. | |
+ // I hope (and assume) that wouldn't happen by any sane MIDI output | |
+ // messaging by host. | |
+ int size = data.size(); | |
+ PmTimestamp pts = (PmTimestamp) (timestamp / 1000.0); | |
+ for (int pos = 0; pos + 2 < size; pos += 3) { | |
+ long msg = Pm_Message(data[pos], data[pos + 1], data[pos + 2]); | |
+ Pm_WriteShort(out, pts, msg); | |
+ } | |
+} | |
+ | |
+PmDeviceID MIDIManagerLinux::GetPortmidiDeviceIndex(bool isOutput, int midiManagerIndex) | |
+{ | |
+ int size = device_map->size(); | |
+ for (int i = 0; i < size; i++) { | |
+ PortMidiDeviceMap *map = device_map->at(i); | |
+ if (map->is_midi_manager_index_output == isOutput && map->midi_manager_index == midiManagerIndex) | |
+ return map->portmidi_index; | |
+ } | |
+ return pmNoDevice; | |
+} | |
+ | |
+// This code is mostly custom port of porttime (from portmidi) | |
+void MIDIManagerLinux::MidiInReadCallback() | |
+{ | |
+ for (std::map<PmDeviceID,PortMidiStream*>::iterator iter = in_streams->begin(); | |
+ iter != in_streams->end(); iter++) { | |
+ while (Pm_Poll(iter->second) == TRUE) { | |
+ int nEvents = Pm_Read(iter->second, portmidi_in_buffer, midi_in_event_buffer_size); | |
+ PmTimestamp timestamp_cache = (PmTimestamp) 0; | |
+ if (nEvents == pmBufferOverflow) | |
+ // event buffer overflow, process as much message as possible (seealso portmidi.h) | |
+ nEvents = midi_in_event_buffer_size; | |
+ else if (nEvents < 0) | |
+ continue; // read error, skip this port | |
+ | |
+ int pos = 0; | |
+ for (int i = 0; i < nEvents && pos < portmidi_input_buffer_size; i++) { | |
+ PmEvent e = portmidi_in_buffer[i]; | |
+ if (e.timestamp != timestamp_cache) { | |
+ if (pos > 0) | |
+ ProcessReceivedMidiMessage(iter->first, pos, timestamp_cache); | |
+ pos = 0; | |
+ timestamp_cache = e.timestamp; | |
+ } | |
+ if (Pm_MessageStatus(e.message) != 0xFF) { | |
+ if (pos + 3 >= portmidi_input_buffer_size) { | |
+ ProcessReceivedMidiMessage(iter->first, pos, timestamp_cache); | |
+ pos = 0; | |
+ } | |
+ manager_midi_in_buffer[pos++] = Pm_MessageStatus(e.message); | |
+ manager_midi_in_buffer[pos++] = Pm_MessageData1(e.message); | |
+ manager_midi_in_buffer[pos++] = Pm_MessageData2(e.message); | |
+ } else { | |
+ // EOX might appear at any part of PmMessage... | |
+ int eoxPos = -1; | |
+ uint8 c; | |
+ for (int j = 0; j < nEvents; j++) { | |
+ if (pos == portmidi_input_buffer_size) { | |
+ eoxPos = -2; | |
+ break; | |
+ } | |
+ c = Pm_MessageStatus(portmidi_in_buffer[j].message); | |
+ if (c == 0xF7) | |
+ eoxPos = 0; | |
+ else { | |
+ manager_midi_in_buffer[pos++] = c; | |
+ if (pos == portmidi_input_buffer_size) { | |
+ eoxPos = -2; | |
+ break; | |
+ } | |
+ c = Pm_MessageData1(portmidi_in_buffer[j].message); | |
+ if (c == 0xF7) | |
+ eoxPos = 1; | |
+ else { | |
+ manager_midi_in_buffer[pos++] = c; | |
+ if (pos == portmidi_input_buffer_size) { | |
+ eoxPos = -2; | |
+ break; | |
+ } | |
+ c = Pm_MessageData2(portmidi_in_buffer[j].message); | |
+ if (c == 0xF7) | |
+ eoxPos = 2; | |
+ else | |
+ manager_midi_in_buffer [pos++] = c; | |
+ } | |
+ } | |
+ if (eoxPos == -2) { | |
+ ProcessReceivedMidiMessage(iter->first, pos, timestamp_cache); | |
+ pos = 0; | |
+ } | |
+ } | |
+ if (eoxPos >= 0) | |
+ manager_midi_in_buffer [pos++] = 0xF7; | |
+ } | |
+ } | |
+ if (pos > 0) | |
+ ProcessReceivedMidiMessage(iter->first, pos, timestamp_cache); | |
+ } | |
+ } | |
+} | |
+ | |
+void MIDIManagerLinux::ProcessReceivedMidiMessage (int midiInIndex, int length, long timestamp) | |
+{ | |
+ // FIXME: make use of timestamps, if applicable. | |
+ ReceiveMIDIData(midiInIndex, manager_midi_in_buffer, length, 0.0); | |
+} | |
+ | |
+void *ReadPollLoopGlobal(void *ptr) | |
+{ | |
+ ((MIDIManagerLinux*) ptr)->ReadPollLoop(); | |
+ return NULL; | |
+} | |
+ | |
+void MIDIManagerLinux::ReadPollLoop() | |
+{ | |
+ midi_in_read_working = true; | |
+ int mytime = 1; | |
+ | |
+ ftime(&read_timer_since); | |
+ | |
+ while (midi_in_read_working) { | |
+ struct timeval timeout; | |
+ int delay = mytime++ * midi_in_timer_resolution - GetTimestamp(read_timer_since); | |
+ if (delay < 0) delay = 0; | |
+ timeout.tv_sec = 0; | |
+ timeout.tv_usec = delay * 1000; | |
+ select(0, NULL, NULL, NULL, &timeout); | |
+ if (midi_in_read_working) | |
+ MidiInReadCallback(); | |
+ } | |
+ | |
+ pthread_exit(NULL); | |
+} | |
+ | |
+long MIDIManagerLinux::GetTimestamp(struct timeb comparedTime) | |
+{ | |
+ long seconds, milliseconds; | |
+ struct timeb now; | |
+ ftime(&now); | |
+ seconds = now.time - comparedTime.time; | |
+ milliseconds = now.millitm - comparedTime.millitm; | |
+ return seconds * 1000 + milliseconds; | |
+} | |
+ | |
+ | |
+int MIDIManagerLinux::SetupPollingThread() | |
+{ | |
+ int ret = pthread_create(&read_poll_thread, NULL, ReadPollLoopGlobal, (void*) this); | |
+ if (ret) // error on creating polling thread. | |
+ return ret; | |
+ | |
+ | |
+ | |
+ return ret; | |
+} | |
+ | |
+} // namespace media | |
diff --git a/media/midi/midi_manager_linux.h b/media/midi/midi_manager_linux.h | |
new file mode 100644 | |
index 0000000..62b9b21 | |
--- /dev/null | |
+++ b/media/midi/midi_manager_linux.h | |
@@ -0,0 +1,78 @@ | |
+// 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_LINUX_H_ | |
+#define MEDIA_MIDI_MIDI_MANAGER_LINUX_H_ | |
+ | |
+#include <map> | |
+#include <string> | |
+#include <vector> | |
+ | |
+#include "base/basictypes.h" | |
+#include "base/compiler_specific.h" | |
+#include "media/midi/midi_manager.h" | |
+#include "media/midi/midi_port_info.h" | |
+ | |
+#include "sys/timeb.h" | |
+#include "pthread.h" | |
+ | |
+#include "media/midi/portmidi.h" | |
+#include "media/midi/porttime.h" | |
+ | |
+namespace media { | |
+ | |
+class PortMidiDeviceMap | |
+{ | |
+ public: | |
+ PortMidiDeviceMap(PmDeviceID pmIndex, int mmIndex, bool isOutput); | |
+ PmDeviceID portmidi_index; | |
+ int midi_manager_index; | |
+ bool is_midi_manager_index_output; | |
+}; | |
+ | |
+class MEDIA_EXPORT MIDIManagerLinux : public MIDIManager { | |
+ public: | |
+ MIDIManagerLinux(); | |
+ virtual ~MIDIManagerLinux(); | |
+ | |
+ // MIDIManager implementation. | |
+ virtual bool Initialize() OVERRIDE; | |
+ virtual void SendMIDIData(MIDIManagerClient* client, | |
+ uint32 port_index, | |
+ const std::vector<uint8>& data, | |
+ double timestamp) OVERRIDE; | |
+ | |
+ void ReadPollLoop(); | |
+ | |
+ private: | |
+ DISALLOW_COPY_AND_ASSIGN(MIDIManagerLinux); | |
+ | |
+ static const int output_buffer_size = 0x1000; | |
+ static const int output_latency = 1000; | |
+ | |
+ std::vector<PortMidiDeviceMap*> *device_map; | |
+ std::map<int,PortMidiStream*> *in_streams; | |
+ std::map<int,PortMidiStream*> *out_streams; | |
+ uint32 cached_out_port_index; | |
+ PortMidiStream* cached_out; | |
+ PmDeviceID GetPortmidiDeviceIndex(bool isOutput, int midiManagerIndex); | |
+ | |
+ void MidiInReadCallback(); | |
+ static const int portmidi_input_buffer_size = 0x1024; | |
+ uint8 manager_midi_in_buffer[portmidi_input_buffer_size]; | |
+ static const int midi_in_event_buffer_size = 0x400; | |
+ PmEvent portmidi_in_buffer[midi_in_event_buffer_size]; | |
+ bool midi_in_read_working; | |
+ pthread_t read_poll_thread; | |
+ static const int midi_in_timer_resolution = 50; | |
+ struct timeb read_timer_since; | |
+ int SetupPollingThread(); | |
+ long GetTimestamp(struct timeb comparedTime); | |
+ void ProcessReceivedMidiMessage (int midiInIndex, int length, long timestamp); | |
+ | |
+}; | |
+ | |
+} // namespace media | |
+ | |
+#endif // MEDIA_MIDI_MIDI_MANAGER_LINUX_H_ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Patch::
chromium/src$ cd third_party && git clone https://github.com:atsushieno/portmidi.git
chromium/src$ cd patch -p1 this.patch
Build::
chromium$ cat buildrc
export PATH=
pwd
/depot_tools:$PATHchromium$ cat chromium.gyp_env
{
'GYP_DEFINES': 'component=shared_library disable_nacl=1 disable_pnacl=1',
'GYP_GENERATORS': 'ninja',
}
chromium$ . buildrc
chromium/src$ git runhooks
Run::
chromium/src$ out/Debug/chrome --disable-setuid-sandbox --enable-webmidi