forked from christofmuc/juce-utils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Sysex.cpp
110 lines (96 loc) · 3.61 KB
/
Sysex.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
Copyright (c) 2019 Christof Ruch. All rights reserved.
Dual licensed: Distributed under Affero GPL license by default, an MIT license is available for purchase
*/
#include "Sysex.h"
#include "Logger.h"
std::vector<MidiMessage> Sysex::loadSysex(std::string const &filename)
{
std::vector<MidiMessage> messages;
File sysexFile = File::createFileWithoutCheckingPath(filename);
if (sysexFile.existsAsFile()) {
// This could be a ZIP file?
if (sysexFile.getFileExtension().toLowerCase() == ".zip") {
ZipFile zip(sysexFile);
for (int i = 0; i < zip.getNumEntries(); i++) {
auto entry = zip.getEntry(i);
File zipEntry = File::createFileWithoutCheckingPath(entry->filename);
if (zipEntry.getFileExtension().toLowerCase() == ".mid" || zipEntry.getFileExtension().toLowerCase() == ".syx") {
// That's an interesting entry
SimpleLogger::instance()->postMessage(String("Opening ") + String(entry->filename));
auto zipStream = zip.createStreamForEntry(i);
if (zipStream) {
auto newMessages = loadSysex(*zipStream);
std::copy(newMessages.cbegin(), newMessages.cend(), std::back_inserter(messages));
delete zipStream;
}
}
}
}
else {
// Single file
FileInputStream inputStream(sysexFile);
return loadSysex(inputStream);
}
}
return messages;
}
std::vector<juce::MidiMessage> Sysex::loadSysex(InputStream &inputStream)
{
std::vector<MidiMessage> messages;
// It could be SMF (Standard Midi File). Then there is a bit more structure we need to parse
MidiFile smfFile;
if (smfFile.readFrom(inputStream, false)) {
// Loop over all tracks
for (int track = 0; track < smfFile.getNumTracks(); track++) {
auto t = smfFile.getTrack(track);
MidiMessageSequence::MidiEventHolder* const* currentMsg;
for (currentMsg = t->begin(); currentMsg != t->end(); currentMsg++) {
// If this is a sysex message, keep it
if ((*currentMsg)->message.isSysEx()) {
messages.push_back((*currentMsg)->message);
}
}
}
}
else {
// More likely, this is a pure sysex file where we read the raw messages from a binary stream
inputStream.setPosition(0);
std::vector<uint8> data((size_t) inputStream.getTotalLength());
inputStream.read(&data[0], (int)inputStream.getTotalLength()); // 4 GB Limit
// Now produce the Sysex messages from the file
size_t inPointer = 0;
uint8 lastStatusByte = 0xf0; // Sysex message
while (inPointer < data.size()) {
int bytesUsed = 0;
//TODO - this crashes in case the data is not well formed sysex (example: Depeche Mode TI sound set, there is a message which ends on 0xff 0xff).
messages.push_back(MidiMessage(&data[inPointer], (int)(data.size()) - inPointer, bytesUsed, lastStatusByte, 0.0, false));
inPointer += bytesUsed;
}
}
return messages;
}
void Sysex::saveSysex(std::string const &filename, std::vector<juce::MidiMessage> const &messages) {
File sysExFile(filename);
if (sysExFile.existsAsFile()) {
sysExFile.deleteFile();
}
File sysexFile = File::createFileWithoutCheckingPath(filename);
FileOutputStream outputStream(sysexFile);
if (sysexFile.existsAsFile()) {
for (auto message : messages) {
outputStream.write(message.getRawData(), message.getRawDataSize());
}
}
}
std::string Sysex::saveSysexIntoNewFile(std::string const &directory, std::string const &desiredFileName, std::vector<juce::MidiMessage> const &messages)
{
File dir(directory);
if (dir.isDirectory()) {
File newFile = dir.getNonexistentChildFile(desiredFileName, ".syx", false);
saveSysex(newFile.getFullPathName().toStdString(), messages);
return newFile.getFullPathName().toStdString();
}
jassert(false);
return "Failure";
}