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