summaryrefslogtreecommitdiffstats
path: root/arts/midi/rawmidiport_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'arts/midi/rawmidiport_impl.cpp')
-rw-r--r--arts/midi/rawmidiport_impl.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/arts/midi/rawmidiport_impl.cpp b/arts/midi/rawmidiport_impl.cpp
new file mode 100644
index 00000000..f8311bd6
--- /dev/null
+++ b/arts/midi/rawmidiport_impl.cpp
@@ -0,0 +1,306 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ */
+
+#include "artsmidi.h"
+#include <iomanager.h>
+#include <debug.h>
+#include <fcntl.h>
+
+using namespace std;
+
+namespace Arts {
+
+class RawMidiPort_impl : virtual public RawMidiPort_skel,
+ virtual public IONotify
+{
+protected:
+ int fd;
+
+ string _device;
+ bool _input, _output;
+ bool _running;
+ mcopbyte laststatus;
+ queue<mcopbyte> inq;
+ MidiClient clientRecord, clientPlay;
+ MidiPort outputPort;
+ MidiManager manager;
+
+ RawMidiPort self() {
+ return RawMidiPort::_from_base(_copy());
+ }
+ SystemMidiTimer timer;
+
+public:
+ RawMidiPort_impl()
+ :_device("/dev/midi"), _input(true), _output(true), _running(false),
+ clientRecord(MidiClient::null()), clientPlay(MidiClient::null()),
+ outputPort(MidiPort::null()),
+ manager(Reference("global:Arts_MidiManager"))
+ {
+ }
+ Arts::TimeStamp time()
+ {
+ return timer.time();
+ }
+ Arts::TimeStamp playTime()
+ {
+ return timer.time();
+ }
+
+ // attribute string running;
+ bool running() { return _running; }
+ void running(bool newrunning) {
+ if(_running == newrunning) return;
+
+ if(newrunning)
+ open();
+ else
+ close();
+
+ running_changed(_running);
+ }
+
+ // attribute string device;
+ void device(const string& newdevice)
+ {
+ if(newdevice == _device) return;
+
+ if(_running)
+ {
+ close();
+ _device = newdevice;
+ open();
+ }
+ else _device = newdevice;
+
+ device_changed(newdevice);
+ }
+ string device() { return _device; }
+
+ // attribute boolean input;
+ void input(bool newinput)
+ {
+ if(newinput == _input) return;
+
+ if(_running)
+ {
+ close();
+ _input = newinput;
+ open();
+ }
+ else _input = newinput;
+
+ input_changed(newinput);
+ }
+ bool input() { return _input; }
+
+ // attribute boolean output;
+ void output(bool newoutput)
+ {
+ if(newoutput == _output) return;
+
+ if(_running)
+ {
+ close();
+ _output = newoutput;
+ open();
+ }
+ else _output = newoutput;
+
+ output_changed(newoutput);
+ }
+ bool output() { return _output; }
+
+ bool open() {
+ arts_return_val_if_fail(_running == false, true);
+ arts_return_val_if_fail(_output || _input, false);
+ arts_return_val_if_fail(manager.isNull() == false, false);
+ laststatus = 0;
+
+ int mode = O_NDELAY;
+ if(_input)
+ {
+ if(_output)
+ mode |= O_RDWR;
+ else
+ mode |= O_RDONLY;
+ }
+ else mode |= O_WRONLY;
+
+ fd = ::open(_device.c_str(),mode);
+ if(fd != -1)
+ {
+ IOManager *iom = Dispatcher::the()->ioManager();
+ if(_output)
+ iom->watchFD(fd,IOType::read,this);
+
+ string name = "OSS Midi Port ("+_device+")";
+ if(_input)
+ {
+ clientRecord =
+ manager.addClient(mcdRecord,mctDestination,name,name);
+ clientRecord.addInputPort(self());
+ }
+ if(_output)
+ {
+ clientPlay =
+ manager.addClient(mcdPlay,mctDestination,name,name);
+ outputPort = clientPlay.addOutputPort();
+ }
+
+ _running = true;
+ running_changed(true);
+ }
+ return _running;
+ }
+
+ void close()
+ {
+ arts_return_if_fail(_running == true);
+
+ if(_input)
+ {
+ clientRecord.removePort(self());
+ clientRecord = MidiClient::null();
+ }
+ if(_output)
+ {
+ clientPlay.removePort(outputPort);
+ clientPlay = MidiClient::null();
+ }
+
+ Dispatcher::the()->ioManager()->remove(this,IOType::all);
+ ::close(fd);
+ }
+
+ int midiMsgLen(mcopbyte status)
+ {
+ switch(status & mcsCommandMask)
+ {
+ case mcsNoteOn:
+ case mcsNoteOff:
+ case mcsKeyPressure:
+ case mcsParameter:
+ case mcsPitchWheel:
+ return 3;
+ break;
+ case mcsProgram:
+ case mcsChannelPressure:
+ return 2;
+ break;
+ }
+ return 0;
+ }
+ void notifyIO(int fd, int type)
+ {
+ arts_return_if_fail(_running);
+ assert(fd == this->fd);
+
+ // convert iomanager notification types to audiosubsys notification
+ if(type & IOType::read)
+ {
+ mcopbyte buffer[1024];
+ int count = read(fd,buffer,1024);
+ for(int i=0; i<count; i++)
+ {
+ /*
+ * for now ignore all realtime-messages (0xF8 .. 0xFF),
+ * which may get * embedded *inside* every other message
+ */
+ if(buffer[i] < 0xf8)
+ inq.push(buffer[i]);
+ }
+ }
+ processMidi();
+ }
+
+ void processMidi()
+ {
+ for(;;)
+ {
+ // if we get a status byte, this is our new status
+ if(!inq.empty())
+ {
+ if(inq.front() & 0x80)
+ {
+ laststatus = inq.front();
+ inq.pop();
+ }
+ }
+
+ // try to read a midi message with our current status
+ // (this supports running status as well as normal messages)
+ int len = midiMsgLen(laststatus);
+ if(len)
+ {
+ if(len == 2)
+ {
+ if(inq.empty()) return; // need more input
+
+ MidiCommand command;
+ command.status = laststatus;
+ command.data1 = inq.front(); inq.pop();
+ command.data2 = 0;
+ outputPort.processCommand(command);
+ }
+ else if(len == 3)
+ {
+ if(inq.size() < 2) return; // need more input
+
+ MidiCommand command;
+ command.status = laststatus;
+ command.data1 = inq.front(); inq.pop();
+ command.data2 = inq.front(); inq.pop();
+ outputPort.processCommand(command);
+ }
+ else
+ {
+ arts_assert(false);
+ }
+ }
+ else
+ {
+ if(inq.empty()) return; // need more input
+
+ /* we are somewhat out of sync it seems -> read something
+ * away and hope we'll find a status byte */
+ inq.pop();
+ }
+ }
+ }
+ void processCommand(const MidiCommand& command)
+ {
+ unsigned char message[3] = { command.status, command.data1, command.data2 };
+
+ int len = midiMsgLen(command.status);
+ if(midiMsgLen(command.status))
+ write(fd, message, len);
+ }
+ void processEvent(const MidiEvent& event)
+ {
+ timer.queueEvent(self(), event);
+ }
+};
+
+REGISTER_IMPLEMENTATION(RawMidiPort_impl);
+}
+