// -*- c-indentation-style:"stroustrup" c-basic-offset: 4 -*- /* Rosegarden A sequencer and musical notation editor. This program is Copyright 2000-2008 Guillaume Laurent , Chris Cannam , Richard Bown 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 #include #include #include #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(BaseProperties::VELOCITY, v); m_data2 = v; m_data1 = e.get(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(KGlobal::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 = KGlobal::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; }