Deluge Firmware
1.3.0
Build date: 2026.08.23
Toggle main menu visibility
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
30
std::byte sysexChecksum(std::span<std::byte> sysex);
31
void
exportSysexPgm(uint8_t* dest, uint8_t* src);
32
33
constexpr
std::array kSysexHeader = {0xF0_b, 0x43_b, 0x00_b, 0x09_b, 0x20_b, 0x00_b};
34
constexpr
std::size_t kSysexSize = 4104;
35
constexpr
std::size_t kSmallSysexSize = 163;
36
37
class
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
46
public
:
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
};
DX7Cartridge::packProgram
void packProgram(uint8_t *src, int idx, char *name, char *opSwitch)
Definition
DX7Cartridge.cpp:54
DX7Cartridge::load
deluge::l10n::String load(std::span< std::byte > stream)
Definition
DX7Cartridge.h:87