commit 70fec21a8e418a59563ac9b0fe7cbd8b4e2d87ef
parent 82ad36aa39114ecf69b00ab336646883d460a49e
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Sun, 4 Dec 2022 02:33:19 +0100
implement SysEx to midi converter
Diffstat:
3 files changed, 131 insertions(+), 0 deletions(-)
diff --git a/source/synthLib/CMakeLists.txt b/source/synthLib/CMakeLists.txt
@@ -13,6 +13,7 @@ set(SOURCES
plugin.cpp plugin.h
resampler.cpp resampler.h
resamplerInOut.cpp resamplerInOut.h
+ sysexToMidi.cpp sysexToMidi.h
wavWriter.cpp wavWriter.h
)
diff --git a/source/synthLib/sysexToMidi.cpp b/source/synthLib/sysexToMidi.cpp
@@ -0,0 +1,109 @@
+#include "sysexToMidi.h"
+
+#include <fstream>
+
+namespace synthLib
+{
+ constexpr uint16_t g_ppq = 96;
+ constexpr uint16_t g_beatsBetweenMessages = 1;
+
+ bool SysexToMidi::write(const char* _filename, const std::vector<std::vector<uint8_t>>& _messages)
+ {
+ std::ofstream f(_filename, std::ios::binary);
+ if(!f.is_open())
+ return false;
+ try
+ {
+ write(f, _messages);
+ }
+ catch (...)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ void SysexToMidi::write(std::ostream& _dst, const std::vector<std::vector<uint8_t>>& _messages)
+ {
+ write(_dst, "MThd"); // header
+ writeUInt32(_dst, 6); // chunk length = 6
+ writeUInt16(_dst, 0); // format = single multi-channel track
+ writeUInt16(_dst, 1); // number of tracks = 1
+ writeUInt16(_dst, 96); // ticks per quarter note = 96
+
+ // track chunk
+ write(_dst, "MTrk");
+ const auto trackChunkBegin = _dst.tellp();
+ writeUInt32(_dst, 0); // will be replaced later
+
+ for (const auto& message : _messages)
+ {
+ writeVarLen(_dst, g_ppq * g_beatsBetweenMessages); // delta time
+ writeUInt8(_dst, message.front()); // f0 comes first...
+ writeVarLen(_dst, static_cast<uint32_t>(message.size() - 1)); // ...then the length
+ _dst.write(reinterpret_cast<const char*>(&message[1]), static_cast<std::streamsize>(message.size() - 1));
+ }
+ const auto newPos = _dst.tellp();
+ const auto trackChunkLength = newPos - trackChunkBegin;
+
+ writeBuf(_dst, {0xff,0x2f,0x00}); // end of track
+
+ _dst.seekp(trackChunkBegin);
+ writeUInt32(_dst, static_cast<uint32_t>(trackChunkLength));
+ }
+
+ void SysexToMidi::writeBuf(std::ostream& _dst, const std::vector<uint8_t>& _data)
+ {
+ _dst.write(reinterpret_cast<const char*>(&_data[0]), static_cast<std::streamsize>(_data.size()));
+ }
+
+ void SysexToMidi::writeUInt8(std::ostream& _dst, uint8_t _data)
+ {
+ _dst.write(reinterpret_cast<const char*>(&_data), 1);
+ }
+
+ void SysexToMidi::writeUInt32(std::ostream& _dst, const uint32_t& _data)
+ {
+ writeBuf(_dst,
+ {
+ static_cast<unsigned char>((_data >> 24) & 0xff),
+ static_cast<unsigned char>((_data >> 16) & 0xff),
+ static_cast<unsigned char>((_data >> 8) & 0xff),
+ static_cast<unsigned char>(_data & 0xff)
+ });
+ }
+
+ void SysexToMidi::writeUInt16(std::ostream& _dst, const uint16_t& _data)
+ {
+ writeBuf(_dst,
+ {
+ static_cast<unsigned char>((_data >> 8) & 0xff),
+ static_cast<unsigned char>(_data & 0xff)
+ });
+ }
+
+ void SysexToMidi::write(std::ostream& _dst, const std::string& _data)
+ {
+ _dst << _data;
+ }
+
+ void SysexToMidi::writeVarLen(std::ostream& _dst, uint32_t _len)
+ {
+ auto buffer = _len & 0x7f;
+
+ while ( (_len >>= 7) )
+ {
+ buffer <<= 8;
+ buffer |= ((_len & 0x7f) | 0x80);
+ }
+
+ while (true)
+ {
+ const auto byte = static_cast<uint8_t>(buffer & 0xff);
+ writeUInt8(_dst, byte);
+ if ((byte & 0x80) == 0)
+ break;
+ buffer >>= 8;
+ }
+ }
+}
diff --git a/source/synthLib/sysexToMidi.h b/source/synthLib/sysexToMidi.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <ostream>
+#include <vector>
+
+namespace synthLib
+{
+ class SysexToMidi
+ {
+ public:
+ static bool write(const char* _filename, const std::vector<std::vector<uint8_t>>& _messages);
+ static void write(std::ostream& _dst, const std::vector<std::vector<uint8_t>>& _messages);
+ private:
+ static void writeBuf(std::ostream& _dst, const std::vector<uint8_t>& _data);
+ static void writeUInt8(std::ostream& _dst, uint8_t _data);
+ static void writeUInt32(std::ostream& _dst, const uint32_t& _data);
+ static void writeUInt16(std::ostream& _dst, const uint16_t& _data);
+ static void write(std::ostream& _dst, const std::string& _data);
+ static void writeVarLen(std::ostream& _dst, uint32_t _len);
+ };
+}