LMMS
Loading...
Searching...
No Matches
juce_MidiFile.cpp
Go to the documentation of this file.
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
23namespace juce
24{
25
27{
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
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
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 (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>
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
212 {
213 double time = 0;
214 uint8 lastStatusByte = 0;
215
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//==============================================================================
254MidiFile::MidiFile() : timeFormat ((short) (unsigned short) 0xe728) {}
255
257{
258 tracks.addCopiesOf (other.tracks);
259}
260
261MidiFile& MidiFile::operator= (const MidiFile& other)
262{
263 tracks.clear();
264 tracks.addCopiesOf (other.tracks);
265 timeFormat = other.timeFormat;
266 return *this;
267}
268
270 : tracks (std::move (other.tracks)),
271 timeFormat (other.timeFormat)
272{
273}
274
275MidiFile& MidiFile::operator= (MidiFile&& other)
276{
277 tracks = std::move (other.tracks);
278 timeFormat = other.timeFormat;
279 return *this;
280}
281
283{
284 tracks.clear();
285}
286
287//==============================================================================
289{
290 return tracks.size();
291}
292
293const MidiMessageSequence* MidiFile::getTrack (int index) const noexcept
294{
295 return tracks[index];
296}
297
298void MidiFile::addTrack (const MidiMessageSequence& trackSequence)
299{
300 tracks.add (new MidiMessageSequence (trackSequence));
301}
302
303//==============================================================================
305{
306 return timeFormat;
307}
308
309void MidiFile::setTicksPerQuarterNote (int ticks) noexcept
310{
311 timeFormat = (short) ticks;
312}
313
314void MidiFile::setSmpteTimeFormat (int framesPerSecond, int subframeResolution) noexcept
315{
316 timeFormat = (short) (((-framesPerSecond) << 8) | subframeResolution);
317}
318
319//==============================================================================
324
329
334
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//==============================================================================
346bool MidiFile::readFrom (InputStream& sourceStream,
347 bool createMatchingNoteOffs,
348 int* fileType)
349{
350 clear();
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
405void 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(),
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//==============================================================================
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//==============================================================================
450bool 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
469{
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);
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
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
533struct MidiFileTest : 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
790static MidiFileTest midiFileTests;
791
792#endif
793
794} // namespace juce
Type jmax(const Type a, const Type b)
Definition MathsFunctions.h:48
#define noexcept
Definition DistrhoDefines.h:72
uint8_t a
Definition Spc_Cpu.h:141
uint8_t uint8
Definition basics.h:86
static constexpr uint32 bigEndianInt(const void *bytes) noexcept
Definition juce_ByteOrder.h:211
static constexpr uint16 bigEndianShort(const void *bytes) noexcept
Definition juce_ByteOrder.h:210
Definition juce_InputStream.h:37
virtual size_t readIntoMemoryBlock(MemoryBlock &destBlock, ssize_t maxNumBytesToRead=-1)
Definition juce_InputStream.cpp:223
Definition juce_MemoryBlock.h:33
Definition juce_MemoryOutputStream.h:36
void convertTimestampTicksToSeconds()
Definition juce_MidiFile.cpp:430
void addTrack(const MidiMessageSequence &trackSequence)
Definition juce_MidiFile.cpp:298
void setTicksPerQuarterNote(int ticksPerQuarterNote) noexcept
Definition juce_MidiFile.cpp:309
int getNumTracks() const noexcept
Definition juce_MidiFile.cpp:288
short getTimeFormat() const noexcept
Definition juce_MidiFile.cpp:304
void setSmpteTimeFormat(int framesPerSecond, int subframeResolution) noexcept
Definition juce_MidiFile.cpp:314
double getLastTimestamp() const
Definition juce_MidiFile.cpp:335
bool readFrom(InputStream &sourceStream, bool createMatchingNoteOffs=true, int *midiFileType=nullptr)
Definition juce_MidiFile.cpp:346
bool writeTrack(OutputStream &, const MidiMessageSequence &) const
Definition juce_MidiFile.cpp:468
MidiFile()
Definition juce_MidiFile.cpp:254
void findAllTimeSigEvents(MidiMessageSequence &timeSigEvents) const
Definition juce_MidiFile.cpp:325
void findAllKeySigEvents(MidiMessageSequence &keySigEvents) const
Definition juce_MidiFile.cpp:330
void findAllTempoEvents(MidiMessageSequence &tempoChangeEvents) const
Definition juce_MidiFile.cpp:320
void readNextTrack(const uint8 *, int, bool)
Definition juce_MidiFile.cpp:405
void clear()
Definition juce_MidiFile.cpp:282
const MidiMessageSequence * getTrack(int index) const noexcept
Definition juce_MidiFile.cpp:293
OwnedArray< MidiMessageSequence > tracks
Definition juce_MidiFile.h:189
short timeFormat
Definition juce_MidiFile.h:190
bool writeTo(OutputStream &destStream, int midiFileType=1) const
Definition juce_MidiFile.cpp:450
Definition juce_MidiMessage.h:35
bool isKeySignatureMetaEvent() const noexcept
Definition juce_MidiMessage.cpp:874
bool isTimeSignatureMetaEvent() const noexcept
Definition juce_MidiMessage.cpp:834
bool isTempoMetaEvent() const noexcept
Definition juce_MidiMessage.cpp:779
double getTempoSecondsPerQuarterNote() const noexcept
Definition juce_MidiMessage.cpp:788
static MidiMessage endOfTrack() noexcept
Definition juce_MidiMessage.cpp:896
static VariableLengthValue readVariableLengthValue(const uint8 *data, int maxBytesToUse) noexcept
Definition juce_MidiMessage.cpp:60
Definition juce_MidiMessageSequence.h:65
MidiMessage message
Definition juce_MidiMessageSequence.h:69
Definition juce_MidiMessageSequence.h:38
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
Definition juce_MidiMessageSequence.cpp:167
MidiEventHolder * getEventPointer(int index) const noexcept
Definition juce_MidiMessageSequence.cpp:80
int getNumEvents() const noexcept
Definition juce_MidiMessageSequence.cpp:75
Definition juce_Optional.h:74
Definition juce_OutputStream.h:38
virtual bool writeIntBigEndian(int value)
Definition juce_OutputStream.cpp:115
Definition juce_OwnedArray.h:51
Definition juce_UnitTest.h:70
unsigned * m
Definition inflate.c:1559
struct huft * t
Definition inflate.c:943
register unsigned j
Definition inflate.c:1576
unsigned v[N_MAX]
Definition inflate.c:1584
unsigned d
Definition inflate.c:940
register unsigned i
Definition inflate.c:1575
JSAMPIMAGE data
Definition jpeglib.h:945
#define jassert(expression)
float out
Definition lilv_test.c:1461
Definition juce_MidiFile.cpp:27
static void writeVariableLengthInt(OutputStream &out, uint32 v)
Definition juce_MidiFile.cpp:28
static double convertTicksToSeconds(double time, const MidiMessageSequence &tempoEvents, int timeFormat)
Definition juce_MidiFile.cpp:149
static void findAllMatchingEvents(const OwnedArray< MidiMessageSequence > &tracks, MidiMessageSequence &results, MethodType method)
Definition juce_MidiFile.cpp:193
static Optional< HeaderDetails > parseMidiHeader(const uint8 *const initialData, const size_t maxSize)
Definition juce_MidiFile.cpp:83
Optional< Integral > tryRead(const uint8 *&data, size_t &remaining)
Definition juce_MidiFile.cpp:59
static MidiMessageSequence readTrack(const uint8 *data, int size)
Definition juce_MidiFile.cpp:211
Definition juce_UnitTestCategories.h:27
Definition carla_juce.cpp:31
unsigned short uint16
Definition juce_MathsFunctions.h:41
unsigned int uint32
Definition juce_MathsFunctions.h:45
unsigned char uint8
Definition juce_MathsFunctions.h:37
int roundToInt(const FloatType value) noexcept
Definition juce_MathsFunctions.h:465
Definition juce_Uuid.h:141
Definition juce_MidiFile.cpp:76
short fileType
Definition juce_MidiFile.cpp:79
short numberOfTracks
Definition juce_MidiFile.cpp:80
short timeFormat
Definition juce_MidiFile.cpp:78
size_t bytesRead
Definition juce_MidiFile.cpp:77
static constexpr auto read
Definition juce_MidiFile.cpp:56
static constexpr auto read
Definition juce_MidiFile.cpp:53
Definition juce_MidiFile.cpp:50
const char const char const char const char char * fn
Definition swell-functions.h:168
b
Definition crypt.c:628
ulg size
Definition extract.c:2350
int result
Definition process.c:1455
struct zdirent * file
Definition win32.c:1500
static ZCONST char Far * method[NUM_METHODS]
Definition zipinfo.c:1008
static ZCONST char Far * os[NUM_HOSTS]
Definition zipinfo.c:1001
mm
Definition zipinfo.c:2291
#define const
Definition zconf.h:137