Deluge Firmware 1.3.0
Build date: 2025.04.16
Loading...
Searching...
No Matches
DX7Cartridge.h
1
20
21#pragma once
22
23#include "gui/l10n/strings.h"
24#include "util/misc.h"
25#include <algorithm>
26#include <cstdint>
27#include <cstring>
28#include <span>
29
30std::byte sysexChecksum(std::span<std::byte> sysex);
31void exportSysexPgm(uint8_t* dest, uint8_t* src);
32
33constexpr std::array kSysexHeader = {0xF0_b, 0x43_b, 0x00_b, 0x09_b, 0x20_b, 0x00_b};
34constexpr std::size_t kSysexSize = 4104;
35constexpr std::size_t kSmallSysexSize = 163;
36
37class DX7Cartridge {
38 std::array<std::byte, kSysexSize> voiceData;
39
40 void setHeader() {
41 std::ranges::copy(kSysexHeader, voiceData.begin());
42 voiceData[4102] = sysexChecksum({&voiceData[6], 4096});
43 voiceData[4103] = std::byte{0xF7};
44 }
45
46public:
47 DX7Cartridge() = default;
48
49 static void normalizePgmName(char buffer[11], const char* sysexName) {
50 memcpy(buffer, sysexName, 10);
51
52 for (int j = 0; j < 10; j++) {
53 char c = (unsigned char)buffer[j];
54 c &= 0x7F; // strip don't care most-significant bit from name
55 switch (c) {
56 case 92:
57 c = 'Y';
58 break; /* yen */
59 case 126:
60 c = '>';
61 break; /* >> */
62 case 127:
63 c = '<';
64 break; /* << */
65 default:
66 if (c < 32 || c > 127)
67 c = 32;
68 break;
69 }
70 buffer[j] = c;
71 }
72 buffer[10] = 0;
73
74 // trim spaces at the end
75 for (int j = 9; j >= 0; j--) {
76 if (buffer[j] != 32) {
77 break;
78 }
79 buffer[j] = 0;
80 }
81 }
87 deluge::l10n::String load(std::span<std::byte> stream) {
88 using deluge::l10n::String;
89 const std::byte* pos = stream.data();
90
91 size_t minMsgSize = 163;
92
93 if (stream.size() < minMsgSize) {
94 std::ranges::copy(stream, &voiceData[6]);
95 return String::STRING_FOR_DX_ERROR_FILE_TOO_SMALL;
96 }
97
98 if (pos[0] != std::byte{0xF0}) {
99 // it is not, just copy the first 4096 bytes
100 std::copy_n(pos, 4096, &voiceData[6]);
101 return String::STRING_FOR_DX_ERROR_NO_SYSEX_START;
102 }
103
104 size_t i;
105 // check if this is the size of a DX7 sysex cartridge
106 for (i = 0; i < stream.size(); ++i) {
107 if (pos[i] == std::byte{0xF7}) {
108 break;
109 }
110 }
111 if (i == stream.size()) {
112 return String::STRING_FOR_DX_ERROR_NO_SYSEX_END;
113 }
114
115 int msgSize = i + 1;
116
117 if (msgSize != kSysexSize && msgSize != kSmallSysexSize) {
118 return String::STRING_FOR_DX_ERROR_INVALID_LEN;
119 }
120
121 std::copy_n(pos, msgSize, voiceData.begin());
122 size_t dataSize = (msgSize == kSysexSize) ? 4096 : 155;
123 if (sysexChecksum({&voiceData[6], dataSize}) != pos[msgSize - 2]) {
124 return String::STRING_FOR_DX_ERROR_CHECKSUM_FAIL;
125 }
126
127 if (voiceData[1] != std::byte{67} || (voiceData[3] != std::byte{9} && voiceData[3] != std::byte{0})) {
128 return String::STRING_FOR_DX_ERROR_SYSEX_ID;
129 }
130
131 return String::EMPTY_STRING;
132 }
133
134 bool isCartridge() { return voiceData[3] == std::byte{9}; }
135 int numPatches() { return isCartridge() ? 32 : 1; }
136
137 std::span<const std::byte> saveVoice() {
138 setHeader();
139 return voiceData;
140 }
141
142 std::byte* getRawVoice() { return &voiceData[6]; }
143
144 void getProgramNames(char dest[32][11]) { // 10 chars + NUL
145 for (int i = 0; i < numPatches(); i++) {
146 getProgramName(i, dest[i]);
147 }
148 }
149
150 void getProgramName(int32_t i, char dest[11]) {
151 normalizePgmName(dest,
152 reinterpret_cast<const char*>(getRawVoice() + ((i * 128) + (isCartridge() ? 118 : 145))));
153 }
154
155 void unpackProgram(std::span<std::uint8_t>, size_t idx);
156 void packProgram(uint8_t* src, int idx, char* name, char* opSwitch);
157};
void packProgram(uint8_t *src, int idx, char *name, char *opSwitch)
Definition DX7Cartridge.cpp:54
deluge::l10n::String load(std::span< std::byte > stream)
Definition DX7Cartridge.h:87