This repository was archived by the owner on Jul 22, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathcpc_player.cpp
More file actions
131 lines (118 loc) · 3.01 KB
/
Copy pathcpc_player.cpp
File metadata and controls
131 lines (118 loc) · 3.01 KB
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "cpc_player.h"
#include "mixer.h"
#include "util.h"
static const char *_tunes[] = {
"Options.Cpc",
"Jungle.Cpc",
"Subway.Cpc",
"TVshow.Cpc",
"City.Cpc",
"Alien.Cpc"
};
CpcPlayer::CpcPlayer(Mixer *mixer, FileSystem *fs)
: _mix(mixer), _fs(fs) {
}
CpcPlayer::~CpcPlayer() {
}
bool CpcPlayer::playTrack(int num) {
_compression[0] = 0;
const int tuneNum = num - 2;
if (tuneNum >= 0 && tuneNum < ARRAYSIZE(_tunes) && _f.open(_tunes[tuneNum], "rb", _fs)) {
_pos = 0;
_sampleL = _sampleR = 0;
while (nextChunk()) {
if (_compression[0]) {
_restartPos = _nextPos;
_mix->setPremixHook(mixCallback, this);
return true;
}
}
}
return false;
}
void CpcPlayer::stopTrack() {
_f.close();
}
void CpcPlayer::pauseTrack() {
_mix->setPremixHook(0, 0);
}
void CpcPlayer::resumeTrack() {
_mix->setPremixHook(mixCallback, this);
}
bool CpcPlayer::nextChunk() {
bool found = false;
while (!_f.ioErr() && !found) {
const uint32_t pos = _pos;
char tag[4];
_f.read(tag, sizeof(tag));
const uint32_t len = _f.readUint32BE();
_nextPos = pos + len;
if (memcmp(tag, "SNDS", 4) == 0) {
_f.readUint32BE();
_f.readUint32BE();
char type[4];
_f.read(type, sizeof(type));
if (memcmp(type, "SHDR", 4) == 0) {
uint8_t buf[24];
_f.read(buf, sizeof(buf));
const uint32_t rate = _f.readUint32BE();
const uint32_t channels = _f.readUint32BE();
if (channels != 2 || rate != _mix->getSampleRate()) {
warning("Unsupported CPC tune channels %d rate %d", channels, rate);
break;
}
_f.read(_compression, sizeof(_compression) - 1);
_compression[sizeof(_compression) - 1] = 0;
if (strcmp(_compression, "SDX2") != 0) {
warning("Unsupported CPC compression '%s'", _compression);
break;
}
found = true;
} else if (memcmp(type, "SSMP", 4) == 0) {
_samplesLeft = _f.readUint32BE();
found = true;
break;
} else {
warning("Unhandled SNDS chunk '%c%c%c%c'", type[0], type[1], type[2], type[3]);
}
} else if (memcmp(tag, "SHDR", 4) == 0 || memcmp(tag, "FILL", 4) == 0 || memcmp(tag, "CTRL", 4) == 0) {
// ignore
} else {
warning("Unhandled chunk '%c%c%c%c' size %d", tag[0], tag[1], tag[2], tag[3], len);
}
_f.seek(_nextPos);
_pos = _nextPos;
}
return found;
}
static int16_t decodeSDX2(int16_t prev, int8_t data) {
const int sqr = data * ABS(data) * 2;
return (data & 1) != 0 ? prev + sqr : sqr;
}
int8_t CpcPlayer::readSampleData() {
if (_samplesLeft <= 0) {
_f.seek(_nextPos);
_pos = _nextPos;
if (!nextChunk()) {
// rewind
_f.seek(_restartPos);
nextChunk();
_sampleL = _sampleR = 0;
}
}
const int8_t data = _f.readByte();
--_samplesLeft;
return data;
}
bool CpcPlayer::mix(int16_t *buf, int len) {
for (int i = 0; i < len; ++i) {
_sampleL = decodeSDX2(_sampleL, readSampleData());
_sampleR = decodeSDX2(_sampleR, readSampleData());
*buf++ = _sampleL;
*buf++ = _sampleR;
}
return true;
}
bool CpcPlayer::mixCallback(void *param, int16_t *buf, int len) {
return ((CpcPlayer *)param)->mix(buf, len);
}