You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdemultimedia/arts/midi/rawmidiport_impl.cc

307 lines
6.3 KiB

/*
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)
{
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);
}