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.
rosegarden/src/sound/MappedEvent.cpp

592 lines
16 KiB

/*
Rosegarden
A sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <bownie@bownie.com>
The moral right of the authors to claim authorship of this work
has been asserted.
This program 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. See the file
COPYING included with this distribution for more information.
*/
#include <tqdir.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <kstddirs.h>
#include "MappedEvent.h"
#include "BaseProperties.h"
#include "Midi.h"
#include "MidiTypes.h"
#define DEBUG_MAPPEDEVENT 1
namespace Rosegarden
{
MappedEvent::MappedEvent(InstrumentId id,
const Event &e,
const RealTime &eventTime,
const RealTime &duration):
m_trackId(0),
m_instrument(id),
m_type(MidiNote),
m_data1(0),
m_data2(0),
m_eventTime(eventTime),
m_duration(duration),
m_audioStartMarker(0, 0),
m_dataBlockId(0),
m_isPersistent(false),
m_runtimeSegmentId( -1),
m_autoFade(false),
m_fadeInTime(RealTime::zeroTime),
m_fadeOutTime(RealTime::zeroTime),
m_recordedChannel(0),
m_recordedDevice(0)
{
try {
// For each event type, we set the properties in a particular
// order: first the type, then whichever of data1 and data2 fits
// less well with its default value. This way if one throws an
// exception for no data, we still have a good event with the
// defaults set.
if (e.isa(Note::EventType)) {
m_type = MidiNoteOneShot;
long v = MidiMaxValue;
e.get<Int>(BaseProperties::VELOCITY, v);
m_data2 = v;
m_data1 = e.get<Int>(BaseProperties::PITCH);
} else if (e.isa(PitchBend::EventType)) {
m_type = MidiPitchBend;
PitchBend pb(e);
m_data1 = pb.getMSB();
m_data2 = pb.getLSB();
} else if (e.isa(Controller::EventType)) {
m_type = MidiController;
Controller c(e);
m_data1 = c.getNumber();
m_data2 = c.getValue();
} else if (e.isa(ProgramChange::EventType)) {
m_type = MidiProgramChange;
ProgramChange pc(e);
m_data1 = pc.getProgram();
} else if (e.isa(KeyPressure::EventType)) {
m_type = MidiKeyPressure;
KeyPressure kp(e);
m_data1 = kp.getPitch();
m_data2 = kp.getPressure();
} else if (e.isa(ChannelPressure::EventType)) {
m_type = MidiChannelPressure;
ChannelPressure cp(e);
m_data1 = cp.getPressure();
} else if (e.isa(SystemExclusive::EventType)) {
m_type = MidiSystemMessage;
m_data1 = MIDI_SYSTEM_EXCLUSIVE;
SystemExclusive s(e);
std::string dataBlock = s.getRawData();
DataBlockRepository::getInstance()->registerDataBlockForEvent(dataBlock, this);
} else {
m_type = InvalidMappedEvent;
}
} catch (MIDIValueOutOfRange r) {
#ifdef DEBUG_MAPPEDEVENT
std::cerr << "MIDI value out of range in MappedEvent ctor"
<< std::endl;
#else
;
#endif
} catch (Event::NoData d) {
#ifdef DEBUG_MAPPEDEVENT
std::cerr << "Caught Event::NoData in MappedEvent ctor, message is:"
<< std::endl << d.getMessage() << std::endl;
#else
;
#endif
} catch (Event::BadType b) {
#ifdef DEBUG_MAPPEDEVENT
std::cerr << "Caught Event::BadType in MappedEvent ctor, message is:"
<< std::endl << b.getMessage() << std::endl;
#else
;
#endif
} catch (SystemExclusive::BadEncoding e) {
#ifdef DEBUG_MAPPEDEVENT
std::cerr << "Caught bad SysEx encoding in MappedEvent ctor"
<< std::endl;
#else
;
#endif
}
}
bool
operator<(const MappedEvent &a, const MappedEvent &b)
{
return a.getEventTime() < b.getEventTime();
}
MappedEvent&
MappedEvent::operator=(const MappedEvent &mE)
{
if (&mE == this)
return * this;
m_trackId = mE.getTrackId();
m_instrument = mE.getInstrument();
m_type = mE.getType();
m_data1 = mE.getData1();
m_data2 = mE.getData2();
m_eventTime = mE.getEventTime();
m_duration = mE.getDuration();
m_audioStartMarker = mE.getAudioStartMarker();
m_dataBlockId = mE.getDataBlockId();
m_runtimeSegmentId = mE.getRuntimeSegmentId();
m_autoFade = mE.isAutoFading();
m_fadeInTime = mE.getFadeInTime();
m_fadeOutTime = mE.getFadeOutTime();
m_recordedChannel = mE.getRecordedChannel();
m_recordedDevice = mE.getRecordedDevice();
return *this;
}
// Do we use this? It looks dangerous so just commenting it out - rwb
//
//const size_t MappedEvent::streamedSize = 12 * sizeof(unsigned int);
TQDataStream&
operator<<(TQDataStream &dS, MappedEvent *mE)
{
dS << (unsigned int)mE->getTrackId();
dS << (unsigned int)mE->getInstrument();
dS << (unsigned int)mE->getType();
dS << (unsigned int)mE->getData1();
dS << (unsigned int)mE->getData2();
dS << (unsigned int)mE->getEventTime().sec;
dS << (unsigned int)mE->getEventTime().nsec;
dS << (unsigned int)mE->getDuration().sec;
dS << (unsigned int)mE->getDuration().nsec;
dS << (unsigned int)mE->getAudioStartMarker().sec;
dS << (unsigned int)mE->getAudioStartMarker().nsec;
dS << (unsigned long)mE->getDataBlockId();
dS << mE->getRuntimeSegmentId();
dS << (unsigned int)mE->isAutoFading();
dS << (unsigned int)mE->getFadeInTime().sec;
dS << (unsigned int)mE->getFadeInTime().nsec;
dS << (unsigned int)mE->getFadeOutTime().sec;
dS << (unsigned int)mE->getFadeOutTime().nsec;
dS << (unsigned int)mE->getRecordedChannel();
dS << (unsigned int)mE->getRecordedDevice();
return dS;
}
TQDataStream&
operator<<(TQDataStream &dS, const MappedEvent &mE)
{
dS << (unsigned int)mE.getTrackId();
dS << (unsigned int)mE.getInstrument();
dS << (unsigned int)mE.getType();
dS << (unsigned int)mE.getData1();
dS << (unsigned int)mE.getData2();
dS << (unsigned int)mE.getEventTime().sec;
dS << (unsigned int)mE.getEventTime().nsec;
dS << (unsigned int)mE.getDuration().sec;
dS << (unsigned int)mE.getDuration().nsec;
dS << (unsigned int)mE.getAudioStartMarker().sec;
dS << (unsigned int)mE.getAudioStartMarker().nsec;
dS << (unsigned long)mE.getDataBlockId();
dS << mE.getRuntimeSegmentId();
dS << (unsigned int)mE.isAutoFading();
dS << (unsigned int)mE.getFadeInTime().sec;
dS << (unsigned int)mE.getFadeInTime().nsec;
dS << (unsigned int)mE.getFadeOutTime().sec;
dS << (unsigned int)mE.getFadeOutTime().nsec;
dS << (unsigned int)mE.getRecordedChannel();
dS << (unsigned int)mE.getRecordedDevice();
return dS;
}
TQDataStream&
operator>>(TQDataStream &dS, MappedEvent *mE)
{
unsigned int trackId = 0, instrument = 0, type = 0, data1 = 0, data2 = 0;
long eventTimeSec = 0, eventTimeNsec = 0, durationSec = 0, durationNsec = 0,
audioSec = 0, audioNsec = 0;
std::string dataBlock;
unsigned long dataBlockId = 0;
int runtimeSegmentId = -1;
unsigned int autoFade = 0,
fadeInSec = 0, fadeInNsec = 0, fadeOutSec = 0, fadeOutNsec = 0,
recordedChannel = 0, recordedDevice = 0;
dS >> trackId;
dS >> instrument;
dS >> type;
dS >> data1;
dS >> data2;
dS >> eventTimeSec;
dS >> eventTimeNsec;
dS >> durationSec;
dS >> durationNsec;
dS >> audioSec;
dS >> audioNsec;
dS >> dataBlockId;
dS >> runtimeSegmentId;
dS >> autoFade;
dS >> fadeInSec;
dS >> fadeInNsec;
dS >> fadeOutSec;
dS >> fadeOutNsec;
dS >> recordedChannel;
dS >> recordedDevice;
mE->setTrackId((TrackId)trackId);
mE->setInstrument((InstrumentId)instrument);
mE->setType((MappedEvent::MappedEventType)type);
mE->setData1((MidiByte)data1);
mE->setData2((MidiByte)data2);
mE->setEventTime(RealTime(eventTimeSec, eventTimeNsec));
mE->setDuration(RealTime(durationSec, durationNsec));
mE->setAudioStartMarker(RealTime(audioSec, audioNsec));
mE->setDataBlockId(dataBlockId);
mE->setRuntimeSegmentId(runtimeSegmentId);
mE->setAutoFade(autoFade);
mE->setFadeInTime(RealTime(fadeInSec, fadeInNsec));
mE->setFadeOutTime(RealTime(fadeOutSec, fadeOutNsec));
mE->setRecordedChannel(recordedChannel);
mE->setRecordedDevice(recordedDevice);
return dS;
}
TQDataStream&
operator>>(TQDataStream &dS, MappedEvent &mE)
{
unsigned int trackId = 0, instrument = 0, type = 0, data1 = 0, data2 = 0;
long eventTimeSec = 0, eventTimeNsec = 0, durationSec = 0, durationNsec = 0,
audioSec = 0, audioNsec = 0;
std::string dataBlock;
unsigned long dataBlockId = 0;
int runtimeSegmentId = -1;
unsigned int autoFade = 0,
fadeInSec = 0, fadeInNsec = 0, fadeOutSec = 0, fadeOutNsec = 0,
recordedChannel = 0, recordedDevice = 0;
dS >> trackId;
dS >> instrument;
dS >> type;
dS >> data1;
dS >> data2;
dS >> eventTimeSec;
dS >> eventTimeNsec;
dS >> durationSec;
dS >> durationNsec;
dS >> audioSec;
dS >> audioNsec;
dS >> dataBlockId;
dS >> runtimeSegmentId;
dS >> autoFade;
dS >> fadeInSec;
dS >> fadeInNsec;
dS >> fadeOutSec;
dS >> fadeOutNsec;
dS >> recordedChannel;
dS >> recordedDevice;
mE.setTrackId((TrackId)trackId);
mE.setInstrument((InstrumentId)instrument);
mE.setType((MappedEvent::MappedEventType)type);
mE.setData1((MidiByte)data1);
mE.setData2((MidiByte)data2);
mE.setEventTime(RealTime(eventTimeSec, eventTimeNsec));
mE.setDuration(RealTime(durationSec, durationNsec));
mE.setAudioStartMarker(RealTime(audioSec, audioNsec));
mE.setDataBlockId(dataBlockId);
mE.setRuntimeSegmentId(runtimeSegmentId);
mE.setAutoFade(autoFade);
mE.setFadeInTime(RealTime(fadeInSec, fadeInNsec));
mE.setFadeOutTime(RealTime(fadeOutSec, fadeOutNsec));
mE.setRecordedChannel(recordedChannel);
mE.setRecordedDevice(recordedDevice);
return dS;
}
void
MappedEvent::addDataByte(MidiByte byte)
{
DataBlockRepository::getInstance()->addDataByteForEvent(byte, this);
}
void
MappedEvent::addDataString(const std::string& data)
{
DataBlockRepository::getInstance()->addDataStringForEvent(data, this);
}
//--------------------------------------------------
class DataBlockFile
{
public:
DataBlockFile(DataBlockRepository::blockid id);
~DataBlockFile();
TQString getFileName()
{
return m_fileName;
}
void addDataByte(MidiByte);
void addDataString(const std::string&);
void clear()
{
m_cleared = true;
}
bool exists();
void setData(const std::string&);
std::string getData();
protected:
void prepareToWrite();
void prepareToRead();
//--------------- Data members ---------------------------------
TQString m_fileName;
TQFile m_file;
bool m_cleared;
};
DataBlockFile::DataBlockFile(DataBlockRepository::blockid id)
: m_fileName(TDEGlobal::dirs()->resourceDirs("tmp").first() + TQString("/rosegarden_datablock_%1").arg(id)),
m_file(m_fileName),
m_cleared(false)
{
// std::cerr << "DataBlockFile " << m_fileName.latin1() << std::endl;
}
DataBlockFile::~DataBlockFile()
{
if (m_cleared) {
// std::cerr << "~DataBlockFile : removing " << m_fileName.latin1() << std::endl;
TQFile::remove
(m_fileName);
}
}
bool DataBlockFile::exists()
{
return TQFile::exists(m_fileName);
}
void DataBlockFile::setData(const std::string& s)
{
// std::cerr << "DataBlockFile::setData() : setting data to " << m_fileName << std::endl;
prepareToWrite();
TQDataStream stream(&m_file);
stream.writeRawBytes(s.data(), s.length());
}
std::string DataBlockFile::getData()
{
if (!exists())
return std::string();
prepareToRead();
TQDataStream stream(&m_file);
// std::cerr << "DataBlockFile::getData() : file size = " << m_file.size() << std::endl;
char* tmp = new char[m_file.size()];
stream.readRawBytes(tmp, m_file.size());
std::string res(tmp, m_file.size());
delete[] tmp;
return res;
}
void DataBlockFile::addDataByte(MidiByte byte)
{
prepareToWrite();
m_file.putch(byte);
}
void DataBlockFile::addDataString(const std::string& s)
{
prepareToWrite();
TQDataStream stream(&m_file);
stream.writeRawBytes(s.data(), s.length());
}
void DataBlockFile::prepareToWrite()
{
// std::cerr << "DataBlockFile[" << m_fileName << "]: prepareToWrite" << std::endl;
if (!m_file.isWritable()) {
m_file.close();
m_file.open(IO_WriteOnly | IO_Append);
assert(m_file.isWritable());
}
}
void DataBlockFile::prepareToRead()
{
// std::cerr << "DataBlockFile[" << m_fileName << "]: prepareToRead" << std::endl;
if (!m_file.isReadable()) {
m_file.close();
m_file.open(IO_ReadOnly);
assert(m_file.isReadable());
}
}
//--------------------------------------------------
DataBlockRepository* DataBlockRepository::getInstance()
{
if (!m_instance)
m_instance = new DataBlockRepository;
return m_instance;
}
std::string DataBlockRepository::getDataBlock(DataBlockRepository::blockid id)
{
DataBlockFile dataBlockFile(id);
if (dataBlockFile.exists())
return dataBlockFile.getData();
return std::string();
}
std::string DataBlockRepository::getDataBlockForEvent(MappedEvent* e)
{
blockid id = e->getDataBlockId();
if (id == 0) {
// std::cerr << "WARNING: DataBlockRepository::getDataBlockForEvent called on event with data block id 0" << std::endl;
return "";
}
return getInstance()->getDataBlock(id);
}
void DataBlockRepository::setDataBlockForEvent(MappedEvent* e, const std::string& s)
{
blockid id = e->getDataBlockId();
if (id == 0) {
// std::cerr << "Creating new datablock for event" << std::endl;
getInstance()->registerDataBlockForEvent(s, e);
} else {
// std::cerr << "Writing " << s.length() << " chars to file for datablock " << id << std::endl;
DataBlockFile dataBlockFile(id);
dataBlockFile.setData(s);
}
}
bool DataBlockRepository::hasDataBlock(DataBlockRepository::blockid id)
{
return DataBlockFile(id).exists();
}
DataBlockRepository::blockid DataBlockRepository::registerDataBlock(const std::string& s)
{
blockid id = 0;
while (id == 0 || DataBlockFile(id).exists())
id = (blockid)random();
// std::cerr << "DataBlockRepository::registerDataBlock: " << s.length() << " chars, id is " << id << std::endl;
DataBlockFile dataBlockFile(id);
dataBlockFile.setData(s);
return id;
}
void DataBlockRepository::unregisterDataBlock(DataBlockRepository::blockid id)
{
DataBlockFile dataBlockFile(id);
dataBlockFile.clear();
}
void DataBlockRepository::registerDataBlockForEvent(const std::string& s, MappedEvent* e)
{
e->setDataBlockId(registerDataBlock(s));
}
void DataBlockRepository::unregisterDataBlockForEvent(MappedEvent* e)
{
unregisterDataBlock(e->getDataBlockId());
}
DataBlockRepository::DataBlockRepository()
{}
void DataBlockRepository::clear()
{
#ifdef DEBUG_MAPPEDEVENT
std::cerr << "DataBlockRepository::clear()\n";
#endif
// Erase all 'datablock_*' files
//
TQString tmpPath = TDEGlobal::dirs()->resourceDirs("tmp").first();
TQDir segmentsDir(tmpPath, "rosegarden_datablock_*");
for (unsigned int i = 0; i < segmentsDir.count(); ++i) {
TQString segmentName = tmpPath + '/' + segmentsDir[i];
TQFile::remove
(segmentName);
}
}
void DataBlockRepository::addDataByteForEvent(MidiByte byte, MappedEvent* e)
{
DataBlockFile dataBlockFile(e->getDataBlockId());
dataBlockFile.addDataByte(byte);
}
void DataBlockRepository::addDataStringForEvent(const std::string& s, MappedEvent* e)
{
DataBlockFile dataBlockFile(e->getDataBlockId());
dataBlockFile.addDataString(s);
}
DataBlockRepository* DataBlockRepository::m_instance = 0;
}