1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A MIDI and audio sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version. See the file
12 COPYING included with this distribution for more information.
13 */
14
15 #include "MappedEvent.h"
16 #include "base/BaseProperties.h"
17 #include "Midi.h"
18 #include "base/MidiTypes.h"
19 #include "base/NotationTypes.h" // for Note::EventType
20 #include "misc/Debug.h"
21 #include "misc/TempDir.h"
22
23 #include <QDir>
24 #include <QFile>
25 #include <QFileInfo>
26 #include <QtGlobal>
27
28 #include <cstdlib>
29
30 // #define DEBUG_MAPPEDEVENT 1
31
32 namespace Rosegarden
33 {
34
MappedEvent(InstrumentId id,const Event & e,const RealTime & eventTime,const RealTime & duration)35 MappedEvent::MappedEvent(InstrumentId id,
36 const Event &e,
37 const RealTime &eventTime,
38 const RealTime &duration):
39 m_trackId((int)NoTrack),
40 m_instrument(id),
41 m_type(MidiNote),
42 m_data1(0),
43 m_data2(0),
44 m_eventTime(eventTime),
45 m_duration(duration),
46 m_audioStartMarker(0, 0),
47 m_dataBlockId(0),
48 m_runtimeSegmentId( -1),
49 m_autoFade(false),
50 m_fadeInTime(RealTime::zeroTime),
51 m_fadeOutTime(RealTime::zeroTime),
52 m_recordedChannel(0),
53 m_recordedDevice(0)
54
55 {
56 try {
57
58 // For each event type, we set the properties in a particular
59 // order: first the type, then whichever of data1 and data2 fits
60 // less well with its default value. This way if one throws an
61 // exception for no data, we still have a good event with the
62 // defaults set.
63
64 if (e.isa(Note::EventType)) {
65 long v = MidiMaxValue;
66 e.get<Int>(BaseProperties::VELOCITY, v);
67 m_data2 = v;
68 m_data1 = e.get<Int>(BaseProperties::PITCH);
69 } else if (e.isa(PitchBend::EventType)) {
70 m_type = MidiPitchBend;
71 m_data1 = e.get<Int>(PitchBend::MSB);
72 m_data2 = e.get<Int>(PitchBend::LSB);
73 } else if (e.isa(Controller::EventType)) {
74 m_type = MidiController;
75 m_data1 = e.get<Int>(Controller::NUMBER);
76 m_data2 = e.get<Int>(Controller::VALUE);
77 } else if (e.isa(ProgramChange::EventType)) {
78 m_type = MidiProgramChange;
79 m_data1 = e.get<Int>(ProgramChange::PROGRAM);
80 } else if (e.isa(KeyPressure::EventType)) {
81 m_type = MidiKeyPressure;
82 m_data1 = e.get<Int>(KeyPressure::PITCH);
83 m_data2 = e.get<Int>(KeyPressure::PRESSURE);
84 } else if (e.isa(ChannelPressure::EventType)) {
85 m_type = MidiChannelPressure;
86 m_data1 = e.get<Int>(ChannelPressure::PRESSURE);
87 } else if (e.isa(SystemExclusive::EventType)) {
88 m_type = MidiSystemMessage;
89 m_data1 = MIDI_SYSTEM_EXCLUSIVE;
90 std::string dataBlock;
91 e.get<String>(SystemExclusive::DATABLOCK, dataBlock);
92 dataBlock = SystemExclusive::toRaw(dataBlock);
93 DataBlockRepository::getInstance()->registerDataBlockForEvent(dataBlock, this);
94 } else if (e.isa(Text::EventType)) {
95 const Rosegarden::Text text(e);
96
97 // Somewhat hacky: We know that annotations and LilyPond directives
98 // aren't to be output, so we make their MappedEvents invalid.
99 // InternalSegmentMapper will then discard those.
100 if (text.getTextType() == Text::Annotation || text.getTextType() == Text::LilyPondDirective) {
101 setType(InvalidMappedEvent);
102 } else {
103 setType(MappedEvent::Text);
104
105 MidiByte midiTextType =
106 (text.getTextType() == Text::Lyric) ?
107 MIDI_LYRIC :
108 MIDI_TEXT_EVENT;
109 setData1(midiTextType);
110
111 std::string metaMessage = text.getText();
112 addDataString(metaMessage);
113 }
114 } else if (e.isa(Key::EventType)) {
115 const Rosegarden::Key key(e);
116 m_type = KeySignature;
117 int accidentals = key.getAccidentalCount();
118 if (!key.isSharp()) {
119 accidentals = -accidentals;
120 }
121 m_data1 = accidentals;
122 m_data2 = key.isMinor();
123 } else {
124 m_type = InvalidMappedEvent;
125 }
126
127 } catch (const Event::NoData &d) {
128
129 #ifdef DEBUG_MAPPEDEVENT
130 RG_WARNING << "Caught Event::NoData in MappedEvent ctor, message is:\n" << d.getMessage();
131 #endif
132
133 } catch (const Event::BadType &b) {
134
135 #ifdef DEBUG_MAPPEDEVENT
136 RG_WARNING << "Caught Event::BadType in MappedEvent ctor, message is:\n" << b.getMessage();
137 #endif
138
139 } catch (const SystemExclusive::BadEncoding &e) {
140
141 #ifdef DEBUG_MAPPEDEVENT
142 RG_WARNING << "Caught bad SysEx encoding in MappedEvent ctor";
143 #endif
144
145 }
146 }
147
148 bool
operator <(const MappedEvent & a,const MappedEvent & b)149 operator<(const MappedEvent &a, const MappedEvent &b)
150 {
151 return a.getEventTime() < b.getEventTime();
152 }
153
154 MappedEvent&
operator =(const MappedEvent & mE)155 MappedEvent::operator=(const MappedEvent &mE)
156 {
157 if (&mE == this)
158 return * this;
159
160 m_trackId = mE.getTrackId();
161 m_instrument = mE.getInstrument();
162 m_type = mE.getType();
163 m_data1 = mE.getData1();
164 m_data2 = mE.getData2();
165 m_eventTime = mE.getEventTime();
166 m_duration = mE.getDuration();
167 m_audioStartMarker = mE.getAudioStartMarker();
168 m_dataBlockId = mE.getDataBlockId();
169 m_runtimeSegmentId = mE.getRuntimeSegmentId();
170 m_autoFade = mE.isAutoFading();
171 m_fadeInTime = mE.getFadeInTime();
172 m_fadeOutTime = mE.getFadeOutTime();
173 m_recordedChannel = mE.getRecordedChannel();
174 m_recordedDevice = mE.getRecordedDevice();
175
176 return *this;
177 }
178
179 void
addDataString(const std::string & data)180 MappedEvent::addDataString(const std::string& data)
181 {
182 DataBlockRepository::getInstance()->
183 setDataBlockForEvent(this, data, true);
184 }
185
186 QDebug &
operator <<(QDebug & dbg,const MappedEvent & mE)187 operator<<(QDebug &dbg, const MappedEvent &mE)
188 {
189 dbg << "MappedEvent" << "\n";
190
191 dbg << " Track ID:";
192
193 if (mE.m_trackId != NoTrack)
194 dbg << static_cast<int>(mE.m_trackId);
195 else
196 dbg << "NO_TRACK";
197
198 dbg << "\n";
199
200 dbg << " Instrument ID:" << mE.m_instrument << "\n";
201
202 QString type;
203
204 // ToString(MappedEventType)?
205 switch(mE.m_type)
206 {
207 case MappedEvent::InvalidMappedEvent:
208 type = "InvalidMappedEvent";
209 break;
210 case MappedEvent::MidiNote:
211 type = "MidiNote";
212 break;
213 case MappedEvent::MidiNoteOneShot:
214 type = "MidiNoteOneShot";
215 break;
216 case MappedEvent::MidiProgramChange:
217 type = "MidiProgramChange";
218 break;
219 case MappedEvent::MidiKeyPressure:
220 type = "MidiKeyPressure";
221 break;
222 case MappedEvent::MidiChannelPressure:
223 type = "MidiChannelPressure";
224 break;
225 case MappedEvent::MidiPitchBend:
226 type = "MidiPitchBend";
227 break;
228 case MappedEvent::MidiController:
229 type = "MidiController";
230 break;
231 case MappedEvent::MidiSystemMessage:
232 type = "MidiSystemMessage";
233 break;
234 case MappedEvent::Audio:
235 type = "Audio";
236 break;
237 case MappedEvent::AudioCancel:
238 type = "AudioCancel";
239 break;
240 case MappedEvent::AudioLevel:
241 type = "AudioLevel";
242 break;
243 case MappedEvent::AudioStopped:
244 type = "AudioStopped";
245 break;
246 case MappedEvent::AudioGeneratePreview:
247 type = "AudioGeneratePreview";
248 break;
249 case MappedEvent::SystemUpdateInstruments:
250 type = "SystemUpdateInstruments";
251 break;
252 case MappedEvent::SystemJackTransport:
253 type = "SystemJackTransport";
254 break;
255 case MappedEvent::SystemMMCTransport:
256 type = "SystemMMCTransport";
257 break;
258 case MappedEvent::SystemMIDIClock:
259 type = "SystemMIDIClock";
260 break;
261 case MappedEvent::SystemMetronomeDevice:
262 type = "SystemMetronomeDevice";
263 break;
264 case MappedEvent::SystemAudioPortCounts:
265 type = "SystemAudioPortCounts";
266 break;
267 case MappedEvent::SystemAudioPorts:
268 type = "SystemAudioPorts";
269 break;
270 case MappedEvent::SystemFailure:
271 type = "SystemFailure";
272 break;
273 case MappedEvent::TimeSignature:
274 type = "TimeSignature";
275 break;
276 case MappedEvent::Tempo:
277 type = "Tempo";
278 break;
279 case MappedEvent::Panic:
280 type = "Panic";
281 break;
282 case MappedEvent::SystemMTCTransport:
283 type = "SystemMTCTransport";
284 break;
285 case MappedEvent::SystemMIDISyncAuto:
286 type = "SystemMIDISyncAuto";
287 break;
288 case MappedEvent::SystemAudioFileFormat:
289 type = "SystemAudioFileFormat";
290 break;
291 case MappedEvent::Marker:
292 type = "Marker";
293 break;
294 case MappedEvent::Text:
295 type = "Text";
296 break;
297 case MappedEvent::KeySignature:
298 type = "KeySignature";
299 break;
300 default:
301 // ??? This is a bitmask, so this might happen with perfectly
302 // legitimate values.
303 type = "*** Unexpected";
304 break;
305 }
306
307 dbg << " Type:" << type << "\n";
308
309 dbg << " Data 1:" << mE.m_data1 << "\n";
310 dbg << " Data 2:" << mE.m_data2 << "\n";
311 dbg << " Event Time:" << mE.m_eventTime << "\n";
312 dbg << " Duration:" << mE.m_duration << "\n";
313 dbg << " Audio Start Marker:" << mE.m_audioStartMarker << "\n";
314 dbg << " Runtime Segment ID:" << mE.m_runtimeSegmentId << "\n";
315 dbg << " Auto Fade:" << mE.m_autoFade << "\n";
316 dbg << " Fade In Time:" << mE.m_fadeInTime << "\n";
317 dbg << " Fade Out Time:" << mE.m_fadeOutTime << "\n";
318 dbg << " Recorded Channel:" << mE.m_recordedChannel << "\n";
319 dbg << " Recorded Device:" << mE.m_recordedDevice << "\n";
320
321 return dbg;
322 }
323
324 //--------------------------------------------------
325
326 class DataBlockFile
327 {
328 public:
329 DataBlockFile(DataBlockRepository::blockid id);
330 ~DataBlockFile();
331
getFileName()332 QString getFileName()
333 {
334 return m_fileName;
335 }
336
337 void addDataString(const std::string&);
338
clear()339 void clear()
340 {
341 m_cleared = true;
342 }
343 bool exists();
344 void setData(const std::string&);
345 std::string getData();
346
347 protected:
348 void prepareToWrite();
349 void prepareToRead();
350
351 //--------------- Data members ---------------------------------
352 QString m_fileName;
353 QFile m_file;
354 bool m_cleared;
355 };
356
DataBlockFile(DataBlockRepository::blockid id)357 DataBlockFile::DataBlockFile(DataBlockRepository::blockid id)
358 : m_fileName(TempDir::path() + QString("/rosegarden_datablock_%1").arg(id)),
359 m_file(m_fileName),
360 m_cleared(false)
361 {
362 // std::cerr << "DataBlockFile " << m_fileName.toLatin1().data() << std::endl;
363 }
364
~DataBlockFile()365 DataBlockFile::~DataBlockFile()
366 {
367 if (m_cleared) {
368 // std::cerr << "~DataBlockFile : removing " << m_fileName.toLatin1().data() << std::endl;
369 QFile::remove
370 (m_fileName);
371 }
372
373 }
374
exists()375 bool DataBlockFile::exists()
376 {
377 return QFile::exists(m_fileName);
378 }
379
setData(const std::string & s)380 void DataBlockFile::setData(const std::string& s)
381 {
382 // std::cerr << "DataBlockFile::setData() : setting data to " << m_fileName << std::endl;
383 prepareToWrite();
384
385 QDataStream stream(&m_file);
386 stream.writeRawData(s.data(), s.length());
387 }
388
getData()389 std::string DataBlockFile::getData()
390 {
391 if (!exists())
392 return std::string();
393
394 prepareToRead();
395
396 QDataStream stream(&m_file);
397 // std::cerr << "DataBlockFile::getData() : file size = " << m_file.size() << std::endl;
398 char* tmp = new char[m_file.size()];
399 stream.readRawData(tmp, m_file.size());
400 std::string res(tmp, m_file.size());
401 delete[] tmp;
402
403 return res;
404 }
405
addDataString(const std::string & s)406 void DataBlockFile::addDataString(const std::string& s)
407 {
408 prepareToWrite();
409 QDataStream stream(&m_file);
410 stream.writeRawData(s.data(), s.length());
411 }
412
prepareToWrite()413 void DataBlockFile::prepareToWrite()
414 {
415 // std::cerr << "DataBlockFile[" << m_fileName << "]: prepareToWrite" << std::endl;
416 if (!m_file.isWritable()) {
417 m_file.close();
418 m_file.open(QIODevice::WriteOnly | QIODevice::Append);
419 Q_ASSERT(m_file.isWritable());
420 }
421 }
422
prepareToRead()423 void DataBlockFile::prepareToRead()
424 {
425 // std::cerr << "DataBlockFile[" << m_fileName << "]: prepareToRead" << std::endl;
426 if (!m_file.isReadable()) {
427 m_file.close();
428 m_file.open(QIODevice::ReadOnly);
429 Q_ASSERT(m_file.isReadable());
430 }
431 }
432
433
434
435 //--------------------------------------------------
436
getInstance()437 DataBlockRepository* DataBlockRepository::getInstance()
438 {
439 if (!m_instance)
440 m_instance = new DataBlockRepository;
441 return m_instance;
442 }
443
getDataBlock(DataBlockRepository::blockid id)444 std::string DataBlockRepository::getDataBlock(DataBlockRepository::blockid id)
445 {
446 DataBlockFile dataBlockFile(id);
447
448 if (dataBlockFile.exists())
449 return dataBlockFile.getData();
450
451 return std::string();
452 }
453
454
getDataBlockForEvent(const MappedEvent * e)455 std::string DataBlockRepository::getDataBlockForEvent(const MappedEvent* e)
456 {
457 blockid id = e->getDataBlockId();
458 if (id == 0) {
459 // std::cerr << "WARNING: DataBlockRepository::getDataBlockForEvent called on event with data block id 0" << std::endl;
460 return "";
461 }
462 return getInstance()->getDataBlock(id);
463 }
464
setDataBlockForEvent(MappedEvent * e,const std::string & s,bool extend)465 void DataBlockRepository::setDataBlockForEvent(MappedEvent* e,
466 const std::string& s,
467 bool extend)
468 {
469 blockid id = e->getDataBlockId();
470 if (id == 0) {
471 #ifdef DEBUG_MAPPEDEVENT
472 RG_DEBUG << "Creating new datablock for event";
473 #endif
474 getInstance()->registerDataBlockForEvent(s, e);
475 } else {
476 #ifdef DEBUG_MAPPEDEVENT
477 RG_DEBUG << "Writing" << s.length()
478 << "chars to file for datablock" << id;
479 #endif
480 DataBlockFile dataBlockFile(id);
481 if (extend)
482 { dataBlockFile.addDataString(s);}
483 else
484 { dataBlockFile.setData(s); }
485 }
486 }
487
hasDataBlock(DataBlockRepository::blockid id)488 bool DataBlockRepository::hasDataBlock(DataBlockRepository::blockid id)
489 {
490 return DataBlockFile(id).exists();
491 }
492
registerDataBlock(const std::string & s)493 DataBlockRepository::blockid DataBlockRepository::registerDataBlock(const std::string& s)
494 {
495 blockid id = 0;
496 while (id == 0 || DataBlockFile(id).exists())
497 id = (blockid)random();
498
499 // std::cerr << "DataBlockRepository::registerDataBlock: " << s.length() << " chars, id is " << id << std::endl;
500
501 DataBlockFile dataBlockFile(id);
502 dataBlockFile.setData(s);
503
504 return id;
505 }
506
unregisterDataBlock(DataBlockRepository::blockid id)507 void DataBlockRepository::unregisterDataBlock(DataBlockRepository::blockid id)
508 {
509 DataBlockFile dataBlockFile(id);
510
511 dataBlockFile.clear();
512 }
513
registerDataBlockForEvent(const std::string & s,MappedEvent * e)514 void DataBlockRepository::registerDataBlockForEvent(const std::string& s, MappedEvent* e)
515 {
516 e->setDataBlockId(registerDataBlock(s));
517 }
518
unregisterDataBlockForEvent(MappedEvent * e)519 void DataBlockRepository::unregisterDataBlockForEvent(MappedEvent* e)
520 {
521 unregisterDataBlock(e->getDataBlockId());
522 }
523
524
DataBlockRepository()525 DataBlockRepository::DataBlockRepository()
526 {}
527
clear()528 void DataBlockRepository::clear()
529 {
530 #ifdef DEBUG_MAPPEDEVENT
531 RG_DEBUG << "DataBlockRepository::clear()";
532 #endif
533
534 // Erase all 'datablock_*' files
535 //
536 QString tmpPath = TempDir::path();
537
538 QDir segmentsDir(tmpPath, "rosegarden_datablock_*");
539
540 if (segmentsDir.count() > 2000) {
541 RG_DEBUG << "DataBlockRepository::clear(): A rather large number of rosegarden_datablock_*\n" <<
542 " files (" << segmentsDir.count() << " of them) have been found in " << tmpPath.toStdString() << ".\n" <<
543 " It may take a while to delete them all. Working...";
544 }
545
546 for (unsigned int i = 0; i < segmentsDir.count(); ++i) {
547 QString segmentName = tmpPath + '/' + segmentsDir[i];
548 QFile::remove
549 (segmentName);
550 }
551 }
552
553 // setDataBlockForEvent does what addDataStringForEvent used to do.
554
555
556 DataBlockRepository* DataBlockRepository::m_instance = nullptr;
557
558
559 }
560