OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_MidiFile.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23 namespace juce
24{
25
26 namespace MidiFileHelpers
27{
28 static void writeVariableLengthInt (OutputStream& out, uint32 v)
29 {
30 auto buffer = v & 0x7f;
31
32 while ((v >>= 7) != 0)
33 {
34 buffer <<= 8;
35 buffer |= ((v & 0x7f) | 0x80);
36 }
37
38 for (;;)
39 {
40 out.writeByte ((char) buffer);
41
42 if (buffer & 0x80)
43 buffer >>= 8;
44 else
45 break;
46 }
47 }
48
49 template <typename Integral>
50 struct ReadTrait;
51
52 template <>
53 struct ReadTrait<uint32> { static constexpr auto read = ByteOrder::bigEndianInt; };
54
55 template <>
56 struct ReadTrait<uint16> { static constexpr auto read = ByteOrder::bigEndianShort; };
57
58 template <typename Integral>
59 Optional<Integral> tryRead (const uint8*& data, size_t& remaining)
60 {
61 using Trait = ReadTrait<Integral>;
62 constexpr auto size = sizeof (Integral);
63
64 if (remaining < size)
65 return {};
66
67 const Optional<Integral> result { Trait::read (data) };
68
69 data += size;
70 remaining -= size;
71
72 return result;
73 }
74
75 struct HeaderDetails
76 {
77 size_t bytesRead = 0;
78 short timeFormat = 0;
79 short fileType = 0;
80 short numberOfTracks = 0;
81 };
82
83 static Optional<HeaderDetails> parseMidiHeader (const uint8* const initialData,
84 const size_t maxSize)
85 {
86 auto* data = initialData;
87 auto remaining = maxSize;
88
89 auto ch = tryRead<uint32> (data, remaining);
90
91 if (! ch.hasValue())
92 return {};
93
94 if (*ch != ByteOrder::bigEndianInt ("MThd"))
95 {
96 auto ok = false;
97
98 if (*ch == ByteOrder::bigEndianInt ("RIFF"))
99 {
100 for (int i = 0; i < 8; ++i)
101 {
102 ch = tryRead<uint32> (data, remaining);
103
104 if (! ch.hasValue())
105 return {};
106
107 if (*ch == ByteOrder::bigEndianInt ("MThd"))
108 {
109 ok = true;
110 break;
111 }
112 }
113 }
114
115 if (! ok)
116 return {};
117 }
118
119 const auto bytesRemaining = tryRead<uint32> (data, remaining);
120
121 if (! bytesRemaining.hasValue() || *bytesRemaining > remaining)
122 return {};
123
124 const auto optFileType = tryRead<uint16> (data, remaining);
125
126 if (! optFileType.hasValue() || 2 < *optFileType)
127 return {};
128
129 const auto optNumTracks = tryRead<uint16> (data, remaining);
130
131 if (! optNumTracks.hasValue() || (*optFileType == 0 && *optNumTracks != 1))
132 return {};
133
134 const auto optTimeFormat = tryRead<uint16> (data, remaining);
135
136 if (! optTimeFormat.hasValue())
137 return {};
138
139 HeaderDetails result;
140
141 result.fileType = (short) *optFileType;
142 result.timeFormat = (short) *optTimeFormat;
143 result.numberOfTracks = (short) *optNumTracks;
144 result.bytesRead = maxSize - remaining;
145
146 return { result };
147 }
148
149 static double convertTicksToSeconds (double time,
150 const MidiMessageSequence& tempoEvents,
151 int timeFormat)
152 {
153 if (timeFormat < 0)
154 return time / (-(timeFormat >> 8) * (timeFormat & 0xff));
155
156 double lastTime = 0, correctedTime = 0;
157 auto tickLen = 1.0 / (timeFormat & 0x7fff);
158 auto secsPerTick = 0.5 * tickLen;
159 auto numEvents = tempoEvents.getNumEvents();
160
161 for (int i = 0; i < numEvents; ++i)
162 {
163 auto& m = tempoEvents.getEventPointer (i)->message;
164 auto eventTime = m.getTimeStamp();
165
166 if (eventTime >= time)
167 break;
168
169 correctedTime += (eventTime - lastTime) * secsPerTick;
170 lastTime = eventTime;
171
172 if (m.isTempoMetaEvent())
173 secsPerTick = tickLen * m.getTempoSecondsPerQuarterNote();
174
175 while (i + 1 < numEvents)
176 {
177 auto& m2 = tempoEvents.getEventPointer (i + 1)->message;
178
179 if (! approximatelyEqual (m2.getTimeStamp(), eventTime))
180 break;
181
182 if (m2.isTempoMetaEvent())
183 secsPerTick = tickLen * m2.getTempoSecondsPerQuarterNote();
184
185 ++i;
186 }
187 }
188
189 return correctedTime + (time - lastTime) * secsPerTick;
190 }
191
192 template <typename MethodType>
193 static void findAllMatchingEvents (const OwnedArray<MidiMessageSequence>& tracks,
194 MidiMessageSequence& results,
195 MethodType method)
196 {
197 for (auto* track : tracks)
198 {
199 auto numEvents = track->getNumEvents();
200
201 for (int j = 0; j < numEvents; ++j)
202 {
203 auto& m = track->getEventPointer (j)->message;
204
205 if ((m.*method)())
206 results.addEvent (m);
207 }
208 }
209 }
210
211 static MidiMessageSequence readTrack (const uint8* data, int size)
212 {
213 double time = 0;
214 uint8 lastStatusByte = 0;
215
216 MidiMessageSequence result;
217
218 while (size > 0)
219 {
220 const auto delay = MidiMessage::readVariableLengthValue (data, (int) size);
221
222 if (! delay.isValid())
223 break;
224
225 data += delay.bytesUsed;
226 size -= delay.bytesUsed;
227 time += delay.value;
228
229 if (size <= 0)
230 break;
231
232 int messSize = 0;
233 const MidiMessage mm (data, size, messSize, lastStatusByte, time);
234
235 if (messSize <= 0)
236 break;
237
238 size -= messSize;
239 data += messSize;
240
241 result.addEvent (mm);
242
243 auto firstByte = *(mm.getRawData());
244
245 if ((firstByte & 0xf0) != 0xf0)
246 lastStatusByte = firstByte;
247 }
248
249 return result;
250 }
251}
252
253//==============================================================================
254 MidiFile::MidiFile() : timeFormat ((short) (unsigned short) 0xe728) {}
255
256 MidiFile::MidiFile (const MidiFile& other) : timeFormat (other.timeFormat)
257{
258 tracks.addCopiesOf (other.tracks);
259}
260
261 MidiFile& MidiFile::operator= (const MidiFile& other)
262{
263 tracks.clear();
264 tracks.addCopiesOf (other.tracks);
265 timeFormat = other.timeFormat;
266 return *this;
267}
268
269 MidiFile::MidiFile (MidiFile&& other)
270 : tracks (std::move (other.tracks)),
271 timeFormat (other.timeFormat)
272{
273}
274
275 MidiFile& MidiFile::operator= (MidiFile&& other)
276{
277 tracks = std::move (other.tracks);
278 timeFormat = other.timeFormat;
279 return *this;
280}
281
282 void MidiFile::clear()
283{
284 tracks.clear();
285}
286
287//==============================================================================
288 int MidiFile::getNumTracks() const noexcept
289{
290 return tracks.size();
291}
292
293 const MidiMessageSequence* MidiFile::getTrack (int index) const noexcept
294{
295 return tracks[index];
296}
297
298 void MidiFile::addTrack (const MidiMessageSequence& trackSequence)
299{
300 tracks.add (new MidiMessageSequence (trackSequence));
301}
302
303//==============================================================================
304 short MidiFile::getTimeFormat() const noexcept
305{
306 return timeFormat;
307}
308
309 void MidiFile::setTicksPerQuarterNote (int ticks) noexcept
310{
311 timeFormat = (short) ticks;
312}
313
314 void MidiFile::setSmpteTimeFormat (int framesPerSecond, int subframeResolution) noexcept
315{
316 timeFormat = (short) (((-framesPerSecond) << 8) | subframeResolution);
317}
318
319//==============================================================================
320 void MidiFile::findAllTempoEvents (MidiMessageSequence& results) const
321{
322 MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTempoMetaEvent);
323}
324
325 void MidiFile::findAllTimeSigEvents (MidiMessageSequence& results) const
326{
327 MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTimeSignatureMetaEvent);
328}
329
330 void MidiFile::findAllKeySigEvents (MidiMessageSequence& results) const
331{
332 MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isKeySignatureMetaEvent);
333}
334
335 double MidiFile::getLastTimestamp() const
336{
337 double t = 0.0;
338
339 for (auto* ms : tracks)
340 t = jmax (t, ms->getEndTime());
341
342 return t;
343}
344
345//==============================================================================
346 bool MidiFile::readFrom (InputStream& sourceStream,
347 bool createMatchingNoteOffs,
348 int* fileType)
349{
350 clear();
351 MemoryBlock data;
352
353 const int maxSensibleMidiFileSize = 200 * 1024 * 1024;
354
355 // (put a sanity-check on the file size, as midi files are generally small)
356 if (! sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize))
357 return false;
358
359 auto size = data.getSize();
360 auto d = static_cast<const uint8*> (data.getData());
361
362 const auto optHeader = MidiFileHelpers::parseMidiHeader (d, size);
363
364 if (! optHeader.hasValue())
365 return false;
366
367 const auto header = *optHeader;
368 timeFormat = header.timeFormat;
369
370 d += header.bytesRead;
371 size -= (size_t) header.bytesRead;
372
373 for (int track = 0; track < header.numberOfTracks; ++track)
374 {
375 const auto optChunkType = MidiFileHelpers::tryRead<uint32> (d, size);
376
377 if (! optChunkType.hasValue())
378 return false;
379
380 const auto optChunkSize = MidiFileHelpers::tryRead<uint32> (d, size);
381
382 if (! optChunkSize.hasValue())
383 return false;
384
385 const auto chunkSize = *optChunkSize;
386
387 if (size < chunkSize)
388 return false;
389
390 if (*optChunkType == ByteOrder::bigEndianInt ("MTrk"))
391 readNextTrack (d, (int) chunkSize, createMatchingNoteOffs);
392
393 size -= chunkSize;
394 d += chunkSize;
395 }
396
397 const auto successful = (size == 0);
398
399 if (successful && fileType != nullptr)
400 *fileType = header.fileType;
401
402 return successful;
403}
404
405 void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNoteOffs)
406{
407 auto sequence = MidiFileHelpers::readTrack (data, size);
408
409 // sort so that we put all the note-offs before note-ons that have the same time
410 std::stable_sort (sequence.list.begin(), sequence.list.end(),
411 [] (const MidiMessageSequence::MidiEventHolder* a,
412 const MidiMessageSequence::MidiEventHolder* b)
413 {
414 auto t1 = a->message.getTimeStamp();
415 auto t2 = b->message.getTimeStamp();
416
417 if (t1 < t2) return true;
418 if (t2 < t1) return false;
419
420 return a->message.isNoteOff() && b->message.isNoteOn();
421 });
422
423 if (createMatchingNoteOffs)
424 sequence.updateMatchedPairs();
425
426 addTrack (sequence);
427}
428
429//==============================================================================
430 void MidiFile::convertTimestampTicksToSeconds()
431{
432 MidiMessageSequence tempoEvents;
433 findAllTempoEvents (tempoEvents);
434 findAllTimeSigEvents (tempoEvents);
435
436 if (timeFormat != 0)
437 {
438 for (auto* ms : tracks)
439 {
440 for (int j = ms->getNumEvents(); --j >= 0;)
441 {
442 auto& m = ms->getEventPointer (j)->message;
443 m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat));
444 }
445 }
446 }
447}
448
449//==============================================================================
450 bool MidiFile::writeTo (OutputStream& out, int midiFileType) const
451{
452 jassert (midiFileType >= 0 && midiFileType <= 2);
453
454 if (! out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd"))) return false;
455 if (! out.writeIntBigEndian (6)) return false;
456 if (! out.writeShortBigEndian ((short) midiFileType)) return false;
457 if (! out.writeShortBigEndian ((short) tracks.size())) return false;
458 if (! out.writeShortBigEndian (timeFormat)) return false;
459
460 for (auto* ms : tracks)
461 if (! writeTrack (out, *ms))
462 return false;
463
464 out.flush();
465 return true;
466}
467
468 bool MidiFile::writeTrack (OutputStream& mainOut, const MidiMessageSequence& ms) const
469{
470 MemoryOutputStream out;
471
472 int lastTick = 0;
473 uint8 lastStatusByte = 0;
474 bool endOfTrackEventWritten = false;
475
476 for (int i = 0; i < ms.getNumEvents(); ++i)
477 {
478 auto& mm = ms.getEventPointer (i)->message;
479
480 if (mm.isEndOfTrackMetaEvent())
481 endOfTrackEventWritten = true;
482
483 auto tick = roundToInt (mm.getTimeStamp());
484 auto delta = jmax (0, tick - lastTick);
485 MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta);
486 lastTick = tick;
487
488 auto* data = mm.getRawData();
489 auto dataSize = mm.getRawDataSize();
490 auto statusByte = data[0];
491
492 if (statusByte == lastStatusByte
493 && (statusByte & 0xf0) != 0xf0
494 && dataSize > 1
495 && i > 0)
496 {
497 ++data;
498 --dataSize;
499 }
500 else if (statusByte == 0xf0) // Write sysex message with length bytes.
501 {
502 out.writeByte ((char) statusByte);
503
504 ++data;
505 --dataSize;
506
507 MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize);
508 }
509
510 out.write (data, (size_t) dataSize);
511 lastStatusByte = statusByte;
512 }
513
514 if (! endOfTrackEventWritten)
515 {
516 out.writeByte (0); // (tick delta)
517 auto m = MidiMessage::endOfTrack();
518 out.write (m.getRawData(), (size_t) m.getRawDataSize());
519 }
520
521 if (! mainOut.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MTrk"))) return false;
522 if (! mainOut.writeIntBigEndian ((int) out.getDataSize())) return false;
523
524 mainOut << out;
525
526 return true;
527}
528
529//==============================================================================
530//==============================================================================
531#if JUCE_UNIT_TESTS
532
533 struct MidiFileTest final : public UnitTest
534{
535 MidiFileTest()
536 : UnitTest ("MidiFile", UnitTestCategories::midi)
537 {}
538
539 void runTest() override
540 {
541 beginTest ("ReadTrack respects running status");
542 {
543 const auto sequence = parseSequence ([] (OutputStream& os)
544 {
545 MidiFileHelpers::writeVariableLengthInt (os, 100);
546 writeBytes (os, { 0x90, 0x40, 0x40 });
547 MidiFileHelpers::writeVariableLengthInt (os, 200);
548 writeBytes (os, { 0x40, 0x40 });
549 MidiFileHelpers::writeVariableLengthInt (os, 300);
550 writeBytes (os, { 0xff, 0x2f, 0x00 });
551 });
552
553 expectEquals (sequence.getNumEvents(), 3);
554 expect (sequence.getEventPointer (0)->message.isNoteOn());
555 expect (sequence.getEventPointer (1)->message.isNoteOn());
556 expect (sequence.getEventPointer (2)->message.isEndOfTrackMetaEvent());
557 }
558
559 beginTest ("ReadTrack returns available messages if input is truncated");
560 {
561 {
562 const auto sequence = parseSequence ([] (OutputStream& os)
563 {
564 // Incomplete delta time
565 writeBytes (os, { 0xff });
566 });
567
568 expectEquals (sequence.getNumEvents(), 0);
569 }
570
571 {
572 const auto sequence = parseSequence ([] (OutputStream& os)
573 {
574 // Complete delta with no following event
575 MidiFileHelpers::writeVariableLengthInt (os, 0xffff);
576 });
577
578 expectEquals (sequence.getNumEvents(), 0);
579 }
580
581 {
582 const auto sequence = parseSequence ([] (OutputStream& os)
583 {
584 // Complete delta with malformed following event
585 MidiFileHelpers::writeVariableLengthInt (os, 0xffff);
586 writeBytes (os, { 0x90, 0x40 });
587 });
588
589 expectEquals (sequence.getNumEvents(), 1);
590 expect (sequence.getEventPointer (0)->message.isNoteOff());
591 expectEquals (sequence.getEventPointer (0)->message.getNoteNumber(), 0x40);
592 expectEquals (sequence.getEventPointer (0)->message.getVelocity(), (uint8) 0x00);
593 }
594 }
595
596 beginTest ("Header parsing works");
597 {
598 {
599 // No data
600 const auto header = parseHeader ([] (OutputStream&) {});
601 expect (! header.hasValue());
602 }
603
604 {
605 // Invalid initial byte
606 const auto header = parseHeader ([] (OutputStream& os)
607 {
608 writeBytes (os, { 0xff });
609 });
610
611 expect (! header.hasValue());
612 }
613
614 {
615 // Type block, but no header data
616 const auto header = parseHeader ([] (OutputStream& os)
617 {
618 writeBytes (os, { 'M', 'T', 'h', 'd' });
619 });
620
621 expect (! header.hasValue());
622 }
623
624 {
625 // We (ll-formed header, but track type is 0 and channels != 1
626 const auto header = parseHeader ([] (OutputStream& os)
627 {
628 writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 0, 0, 16, 0, 1 });
629 });
630
631 expect (! header.hasValue());
632 }
633
634 {
635 // Well-formed header, but track type is 5
636 const auto header = parseHeader ([] (OutputStream& os)
637 {
638 writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 5, 0, 16, 0, 1 });
639 });
640
641 expect (! header.hasValue());
642 }
643
644 {
645 // Well-formed header
646 const auto header = parseHeader ([] (OutputStream& os)
647 {
648 writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 16, 0, 1 });
649 });
650
651 expect (header.hasValue());
652
653 expectEquals (header->fileType, (short) 1);
654 expectEquals (header->numberOfTracks, (short) 16);
655 expectEquals (header->timeFormat, (short) 1);
656 expectEquals ((int) header->bytesRead, 14);
657 }
658 }
659
660 beginTest ("Read from stream");
661 {
662 {
663 // Empty input
664 const auto file = parseFile ([] (OutputStream&) {});
665 expect (! file.hasValue());
666 }
667
668 {
669 // Malformed header
670 const auto file = parseFile ([] (OutputStream& os)
671 {
672 writeBytes (os, { 'M', 'T', 'h', 'd' });
673 });
674
675 expect (! file.hasValue());
676 }
677
678 {
679 // Header, no channels
680 const auto file = parseFile ([] (OutputStream& os)
681 {
682 writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 0, 0, 1 });
683 });
684
685 expect (file.hasValue());
686 expectEquals (file->getNumTracks(), 0);
687 }
688
689 {
690 // Header, one malformed channel
691 const auto file = parseFile ([] (OutputStream& os)
692 {
693 writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 });
694 writeBytes (os, { 'M', 'T', 'r', '?' });
695 });
696
697 expect (! file.hasValue());
698 }
699
700 {
701 // Header, one channel with malformed message
702 const auto file = parseFile ([] (OutputStream& os)
703 {
704 writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 });
705 writeBytes (os, { 'M', 'T', 'r', 'k', 0, 0, 0, 1, 0xff });
706 });
707
708 expect (file.hasValue());
709 expectEquals (file->getNumTracks(), 1);
710 expectEquals (file->getTrack (0)->getNumEvents(), 0);
711 }
712
713 {
714 // Header, one channel with incorrect length message
715 const auto file = parseFile ([] (OutputStream& os)
716 {
717 writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 });
718 writeBytes (os, { 'M', 'T', 'r', 'k', 0x0f, 0, 0, 0, 0xff });
719 });
720
721 expect (! file.hasValue());
722 }
723
724 {
725 // Header, one channel, all well-formed
726 const auto file = parseFile ([] (OutputStream& os)
727 {
728 writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 });
729 writeBytes (os, { 'M', 'T', 'r', 'k', 0, 0, 0, 4 });
730
731 MidiFileHelpers::writeVariableLengthInt (os, 0x0f);
732 writeBytes (os, { 0x80, 0x00, 0x00 });
733 });
734
735 expect (file.hasValue());
736 expectEquals (file->getNumTracks(), 1);
737
738 auto& track = *file->getTrack (0);
739 expectEquals (track.getNumEvents(), 1);
740 expect (track.getEventPointer (0)->message.isNoteOff());
741 expectEquals (track.getEventPointer (0)->message.getTimeStamp(), (double) 0x0f);
742 }
743 }
744 }
745
746 template <typename Fn>
747 static MidiMessageSequence parseSequence (Fn&& fn)
748 {
749 MemoryOutputStream os;
750 fn (os);
751
752 return MidiFileHelpers::readTrack (reinterpret_cast<const uint8*> (os.getData()),
753 (int) os.getDataSize());
754 }
755
756 template <typename Fn>
757 static Optional<MidiFileHelpers::HeaderDetails> parseHeader (Fn&& fn)
758 {
759 MemoryOutputStream os;
760 fn (os);
761
762 return MidiFileHelpers::parseMidiHeader (reinterpret_cast<const uint8*> (os.getData()),
763 os.getDataSize());
764 }
765
766 template <typename Fn>
767 static Optional<MidiFile> parseFile (Fn&& fn)
768 {
769 MemoryOutputStream os;
770 fn (os);
771
772 MemoryInputStream is (os.getData(), os.getDataSize(), false);
773 MidiFile mf;
774
775 int fileType = 0;
776
777 if (mf.readFrom (is, true, &fileType))
778 return mf;
779
780 return {};
781 }
782
783 static void writeBytes (OutputStream& os, const std::vector<uint8>& bytes)
784 {
785 for (const auto& byte : bytes)
786 os.writeByte ((char) byte);
787 }
788};
789
790 static MidiFileTest midiFileTests;
791
792#endif
793
794} // namespace juce
juce::ByteOrder::bigEndianInt
static constexpr uint32 bigEndianInt(const void *bytes) noexcept
Definition juce_ByteOrder.h:207
juce::ByteOrder::bigEndianShort
static constexpr uint16 bigEndianShort(const void *bytes) noexcept
Definition juce_ByteOrder.h:206
juce::InputStream
Definition juce_InputStream.h:37
juce::InputStream::readIntoMemoryBlock
virtual size_t readIntoMemoryBlock(MemoryBlock &destBlock, ssize_t maxNumBytesToRead=-1)
Definition juce_InputStream.cpp:223
juce::MemoryBlock
Definition juce_MemoryBlock.h:33
juce::MemoryBlock::getData
void * getData() noexcept
Definition juce_MemoryBlock.h:91
juce::MemoryBlock::getSize
size_t getSize() const noexcept
Definition juce_MemoryBlock.h:127
juce::MemoryOutputStream
Definition juce_MemoryOutputStream.h:36
juce::MemoryOutputStream::getDataSize
size_t getDataSize() const noexcept
Definition juce_MemoryOutputStream.h:80
juce::MemoryOutputStream::write
bool write(const void *, size_t) override
Definition juce_MemoryOutputStream.cpp:102
juce::MidiFile
Definition juce_MidiFile.h:42
juce::MidiFile::convertTimestampTicksToSeconds
void convertTimestampTicksToSeconds()
Definition juce_MidiFile.cpp:430
juce::MidiFile::addTrack
void addTrack(const MidiMessageSequence &trackSequence)
Definition juce_MidiFile.cpp:298
juce::MidiFile::setTicksPerQuarterNote
void setTicksPerQuarterNote(int ticksPerQuarterNote) noexcept
Definition juce_MidiFile.cpp:309
juce::MidiFile::getNumTracks
int getNumTracks() const noexcept
Definition juce_MidiFile.cpp:288
juce::MidiFile::getTimeFormat
short getTimeFormat() const noexcept
Definition juce_MidiFile.cpp:304
juce::MidiFile::setSmpteTimeFormat
void setSmpteTimeFormat(int framesPerSecond, int subframeResolution) noexcept
Definition juce_MidiFile.cpp:314
juce::MidiFile::getLastTimestamp
double getLastTimestamp() const
Definition juce_MidiFile.cpp:335
juce::MidiFile::readFrom
bool readFrom(InputStream &sourceStream, bool createMatchingNoteOffs=true, int *midiFileType=nullptr)
Definition juce_MidiFile.cpp:346
juce::MidiFile::operator=
MidiFile & operator=(const MidiFile &)
Definition juce_MidiFile.cpp:261
juce::MidiFile::MidiFile
MidiFile()
Definition juce_MidiFile.cpp:254
juce::MidiFile::findAllTimeSigEvents
void findAllTimeSigEvents(MidiMessageSequence &timeSigEvents) const
Definition juce_MidiFile.cpp:325
juce::MidiFile::findAllKeySigEvents
void findAllKeySigEvents(MidiMessageSequence &keySigEvents) const
Definition juce_MidiFile.cpp:330
juce::MidiFile::findAllTempoEvents
void findAllTempoEvents(MidiMessageSequence &tempoChangeEvents) const
Definition juce_MidiFile.cpp:320
juce::MidiFile::clear
void clear()
Definition juce_MidiFile.cpp:282
juce::MidiFile::getTrack
const MidiMessageSequence * getTrack(int index) const noexcept
Definition juce_MidiFile.cpp:293
juce::MidiFile::writeTo
bool writeTo(OutputStream &destStream, int midiFileType=1) const
Definition juce_MidiFile.cpp:450
juce::MidiMessageSequence::MidiEventHolder
Definition juce_MidiMessageSequence.h:65
juce::MidiMessageSequence::MidiEventHolder::message
MidiMessage message
Definition juce_MidiMessageSequence.h:69
juce::MidiMessageSequence
Definition juce_MidiMessageSequence.h:38
juce::MidiMessageSequence::getEventPointer
MidiEventHolder * getEventPointer(int index) const noexcept
Definition juce_MidiMessageSequence.cpp:80
juce::MidiMessageSequence::getNumEvents
int getNumEvents() const noexcept
Definition juce_MidiMessageSequence.cpp:75
juce::MidiMessage::isKeySignatureMetaEvent
bool isKeySignatureMetaEvent() const noexcept
Definition juce_MidiMessage.cpp:879
juce::MidiMessage::isTimeSignatureMetaEvent
bool isTimeSignatureMetaEvent() const noexcept
Definition juce_MidiMessage.cpp:839
juce::MidiMessage::isTempoMetaEvent
bool isTempoMetaEvent() const noexcept
Definition juce_MidiMessage.cpp:784
juce::MidiMessage::endOfTrack
static MidiMessage endOfTrack() noexcept
Definition juce_MidiMessage.cpp:901
juce::MidiMessage::readVariableLengthValue
static VariableLengthValue readVariableLengthValue(const uint8 *data, int maxBytesToUse) noexcept
Definition juce_MidiMessage.cpp:60
juce::OutputStream
Definition juce_OutputStream.h:38
juce::OutputStream::writeByte
virtual bool writeByte(char byte)
Definition juce_OutputStream.cpp:83
juce::OutputStream::writeIntBigEndian
virtual bool writeIntBigEndian(int value)
Definition juce_OutputStream.cpp:115
juce::OutputStream::writeShortBigEndian
virtual bool writeShortBigEndian(short value)
Definition juce_OutputStream.cpp:103
juce::OutputStream::flush
virtual void flush()=0