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.
592 lines
16 KiB
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;
|
|
|
|
}
|