summaryrefslogtreecommitdiffstats
path: root/src/engine/AlsaMidiDriver.cpp
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2006-06-10 01:52:02 +0000
committerDavid Robillard <d@drobilla.net>2006-06-10 01:52:02 +0000
commit98fe0e7056e6697396249531785d3899f94d79be (patch)
tree233319008d4bfb6c8bdc546bdf4a81b87ecf7f3a /src/engine/AlsaMidiDriver.cpp
parent6c8eaee73b0ea66216744f49b452e22e26fe83e1 (diff)
downloadingen-98fe0e7056e6697396249531785d3899f94d79be.tar.gz
ingen-98fe0e7056e6697396249531785d3899f94d79be.tar.bz2
ingen-98fe0e7056e6697396249531785d3899f94d79be.zip
More juggling
git-svn-id: http://svn.drobilla.net/lad/grauph@15 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/engine/AlsaMidiDriver.cpp')
-rw-r--r--src/engine/AlsaMidiDriver.cpp373
1 files changed, 0 insertions, 373 deletions
diff --git a/src/engine/AlsaMidiDriver.cpp b/src/engine/AlsaMidiDriver.cpp
deleted file mode 100644
index decd2471..00000000
--- a/src/engine/AlsaMidiDriver.cpp
+++ /dev/null
@@ -1,373 +0,0 @@
-/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
- *
- * Om is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at your option) any later
- * version.
- *
- * Om is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "AlsaMidiDriver.h"
-#include <iostream>
-#include <cstdlib>
-#include <pthread.h>
-#include "Om.h"
-#include "OmApp.h"
-#include "util/types.h"
-#include "OmApp.h"
-#include "Maid.h"
-#include "AudioDriver.h"
-#include "PortInfo.h"
-#include "MidiMessage.h"
-#include "PortBase.h"
-#ifdef HAVE_LASH
-#include "LashDriver.h"
-#endif
-using std::cout; using std::cerr; using std::endl;
-
-namespace Om {
-
-
-//// AlsaMidiPort ////
-
-AlsaMidiPort::AlsaMidiPort(AlsaMidiDriver* driver, PortBase<MidiMessage>* port)
-: DriverPort(),
- ListNode<AlsaMidiPort*>(this),
- m_driver(driver),
- m_patch_port(port),
- m_port_id(0),
- m_midi_pool(new unsigned char*[port->buffer_size()]),
- m_events(1024)
-{
- assert(port->parent() != NULL);
- assert(port->poly() == 1);
-
- if (port->port_info()->is_input()) {
- if ((m_port_id = snd_seq_create_simple_port(driver->seq_handle(), port->path().c_str(),
- SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
- SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
- {
- cerr << "[AlsaMidiPort] Error creating sequencer port." << endl;
- exit(EXIT_FAILURE);
- }
- } else {
- if ((m_port_id = snd_seq_create_simple_port(driver->seq_handle(), port->path().c_str(),
- SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
- SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
- {
- cerr << "[AlsaMidiPort] Error creating sequencer port." << endl;
- exit(EXIT_FAILURE);
- }
- }
-
- /* Allocate event pool. This pool is used when preparing a block from the queue
- * of Alsa events. The buffer member of the MidiMessage's in the patch port's
- * buffer will be set directly to an element in this pool, then next cycle they
- * will be overwritten (eliminating the need for any allocation/freeing). */
- for (size_t i=0; i < port->buffer_size(); ++i)
- m_midi_pool[i] = new unsigned char[MAX_MIDI_EVENT_SIZE];
-
- port->buffer(0)->clear();
- port->fixed_buffers(true);
-}
-
-
-AlsaMidiPort::~AlsaMidiPort()
-{
- snd_seq_delete_simple_port(m_driver->seq_handle(), m_port_id);
-
- // Free event pool
- for (size_t i=0; i < m_patch_port->buffer_size(); ++i)
- delete[] m_midi_pool[i];
-
- delete[] m_midi_pool;
-}
-
-
-void
-AlsaMidiPort::add_to_driver()
-{
- m_driver->add_port(this);
-}
-
-
-void
-AlsaMidiPort::remove_from_driver()
-{
- m_driver->remove_port(this);
-}
-
-
-void
-AlsaMidiPort::set_name(const string& name)
-{
- snd_seq_port_info_t* info = NULL;
- snd_seq_port_info_malloc(&info);
- snd_seq_get_port_info(m_driver->seq_handle(), m_port_id, info);
- snd_seq_port_info_set_name(info, name.c_str());
- snd_seq_set_port_info(m_driver->seq_handle(), m_port_id, info);
- snd_seq_port_info_free(info);
-}
-
-
-void
-AlsaMidiPort::event(snd_seq_event_t* const ev)
-{
- // Abuse the tick field to hold the timestamp
- ev->time.tick = om->audio_driver()->time_stamp();
-
- // Fix noteons with velocity 0 (required for DSSI spec)
- if (ev->type == SND_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0)
- ev->type = SND_SEQ_EVENT_NOTEOFF;
-
- m_events.push(*ev);
-}
-
-
-/** Generates a flat array of MIDI events for patching.
- *
- * Prepares all events that occurred during the time interval passed
- * (which ideally are the events from the previous cycle with an exact
- * 1 cycle delay) and creates a flat port buffer for this cycle.
- */
-void
-AlsaMidiPort::prepare_block(const samplecount block_start, const samplecount block_end)
-{
- assert(block_end >= block_start);
-
- snd_seq_event_t* ev = NULL;
- MidiMessage* message = NULL;
- size_t num_events = 0;
- size_t event_size = 0; // decoded length of Alsa event in bytes
- int timestamp = 0;
-
- while (!m_events.is_empty() && m_events.front().time.tick < block_end) {
- assert(num_events < m_patch_port->buffer_size());
- ev = &m_events.front();
- message = &m_patch_port->buffer(0)->data()[num_events];
-
- timestamp = ev->time.tick - block_start;
- if (timestamp < 0) {
- // FIXME: remove this (obviously not realtime safe)
- cerr << "[AlsaMidiPort] Missed event by " << -timestamp << " samples!" << endl;
- timestamp = 0;
- }
- assert(timestamp < (int)(block_end - block_start));
-
- // Reset decoder so we don't get running status
- snd_midi_event_reset_decode(m_driver->event_coder());
-
- // FIXME: is this realtime safe?
- if ((event_size = snd_midi_event_decode(m_driver->event_coder(),
- m_midi_pool[num_events], MAX_MIDI_EVENT_SIZE, ev)) > 0) {
- message->size = event_size;
- message->time = timestamp;
- message->buffer = m_midi_pool[num_events];
- ++num_events;
- } else {
- cerr << "[AlsaMidiPort] Unable to decode MIDI event" << endl;
- }
-
- m_events.pop();
- }
-
- m_patch_port->buffer(0)->filled_size(num_events);
- m_patch_port->tied_port()->buffer(0)->filled_size(num_events);
-}
-
-
-
-//// AlsaMidiDriver ////
-
-
-bool AlsaMidiDriver::m_midi_thread_exit_flag = true;
-
-
-AlsaMidiDriver::AlsaMidiDriver()
-: m_seq_handle(NULL),
- m_event_coder(NULL),
- m_is_activated(false)
-{
- if (snd_seq_open(&m_seq_handle, "hw", SND_SEQ_OPEN_INPUT, 0) < 0) {
- cerr << "[AlsaMidiDriver] Error opening ALSA sequencer." << endl;
- exit(EXIT_FAILURE);
- } else {
- cout << "[AlsaMidiDriver] Successfully opened ALSA sequencer." << endl;
- }
-
- if (snd_midi_event_new(3, &m_event_coder)) {
- cerr << "[AlsaMidiDriver] Failed to initialize ALSA MIDI event coder!";
- exit(EXIT_FAILURE);
- } else {
- snd_midi_event_reset_encode(m_event_coder);
- snd_midi_event_reset_decode(m_event_coder);
- }
-
- snd_seq_set_client_name(m_seq_handle, "Om");
-}
-
-
-AlsaMidiDriver::~AlsaMidiDriver()
-{
- deactivate();
- snd_midi_event_free(m_event_coder);
- snd_seq_close(m_seq_handle);
-}
-
-
-/** Launch and start the MIDI thread.
- */
-void
-AlsaMidiDriver::activate()
-{
- // Just exit if already running
- if (m_midi_thread_exit_flag == false)
- return;
-
- bool success = false;
- m_midi_thread_exit_flag = false;
-
- //if (om->audio_driver()->is_realtime()) {
- pthread_attr_t attr;
- pthread_attr_init(&attr);
-
- if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
- cerr << "[AlsaMidiDriver] Unable to set realtime scheduling for MIDI thread." << endl;
- }
-
- sched_param param;
- param.sched_priority = 10;
-
- pthread_attr_setstacksize(&attr, 1500000);
-
- if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)
- || pthread_attr_setschedparam(&attr, &param))
- cout << "[AlsaMidiDriver] Unable to set SCHED_FIFO priority "
- << param.sched_priority << endl;
-
- if (!pthread_create(&m_process_thread, &attr, process_midi_in, this)) {
- cout << "[AlsaMidiDriver] Started realtime MIDI thread (SCHED_FIFO, priority "
- << param.sched_priority << ")" << endl;
- success = true;
- } else {
- cerr << "[AlsaMidiDriver] Unable to start realtime MIDI thread." << endl;
- }
- pthread_attr_destroy(&attr);
- //}
-
- if (!success) {
- // FIXME: check for success
- pthread_create(&m_process_thread, NULL, process_midi_in, this);
- cout << "[AlsaMidiDriver] Started non-realtime MIDI thread." << endl;
- }
-
-#ifdef HAVE_LASH
- lash_driver->set_alsa_client_id(snd_seq_client_id(m_seq_handle));
-#endif
-
- m_is_activated = true;
-}
-
-
-/** Terminate the MIDI thread.
- */
-void
-AlsaMidiDriver::deactivate()
-{
- if (m_is_activated) {
- m_midi_thread_exit_flag = true;
- pthread_cancel(m_process_thread);
- pthread_join(m_process_thread, NULL);
- m_is_activated = false;
- }
-}
-
-
-/** Build flat arrays of events for DSSI plugins for each Port.
- */
-void
-AlsaMidiDriver::prepare_block(const samplecount block_start, const samplecount block_end)
-{
- for (List<AlsaMidiPort*>::iterator i = m_in_ports.begin(); i != m_in_ports.end(); ++i)
- (*i)->prepare_block(block_start, block_end);
-}
-
-
-/** Add an Alsa MIDI port.
- *
- * Realtime safe, this is to be called at the beginning of a process cycle to
- * insert (and actually begin using) a new port.
- *
- * See create_port() and remove_port().
- */
-void
-AlsaMidiDriver::add_port(AlsaMidiPort* port)
-{
- if (port->patch_port()->port_info()->is_input())
- m_in_ports.push_back(port);
- else
- m_out_ports.push_back(port);
-}
-
-
-/** Remove an Alsa MIDI port.
- *
- * Realtime safe. This is to be called at the beginning of a process cycle to
- * remove the port from the lists read by the audio thread, so the port
- * will no longer be used and can be removed afterwards.
- *
- * It is the callers responsibility to delete the returned port.
- */
-AlsaMidiPort*
-AlsaMidiDriver::remove_port(AlsaMidiPort* port)
-{
- if (port->patch_port()->port_info()->is_input()) {
- for (List<AlsaMidiPort*>::iterator i = m_in_ports.begin(); i != m_in_ports.end(); ++i)
- if ((*i) == (AlsaMidiPort*)port)
- return m_in_ports.remove(i)->elem();
- } else {
- for (List<AlsaMidiPort*>::iterator i = m_out_ports.begin(); i != m_out_ports.end(); ++i)
- if ((*i) == port)
- return m_out_ports.remove(i)->elem();
- }
-
- cerr << "[AlsaMidiDriver::remove_input] WARNING: Failed to find Jack port to remove!" << endl;
- return NULL;
-}
-
-
-/** MIDI thread.
- */
-void*
-AlsaMidiDriver::process_midi_in(void* alsa_driver)
-{
- AlsaMidiDriver* ad = (AlsaMidiDriver*)alsa_driver;
-
- snd_seq_event_t* ev;
-
- int npfd = snd_seq_poll_descriptors_count(ad->m_seq_handle, POLLIN);
- struct pollfd pfd;
- snd_seq_poll_descriptors(ad->m_seq_handle, &pfd, npfd, POLLIN);
-
- while ( ! m_midi_thread_exit_flag)
- if (poll(&pfd, npfd, 100000) > 0)
- while (snd_seq_event_input(ad->m_seq_handle, &ev) > 0)
- for (List<AlsaMidiPort*>::iterator i = ad->m_in_ports.begin(); i != ad->m_in_ports.end(); ++i)
- if ((*i)->port_id() == ev->dest.port)
- (*i)->event(ev);
-
- cout << "[AlsaMidiDriver] Exiting MIDI thread." << endl;
-
- return NULL;
-}
-
-
-} // namespace Om
-