-
Notifications
You must be signed in to change notification settings - Fork 4
/
sndh.h
331 lines (282 loc) · 8.28 KB
/
sndh.h
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Fredrik Noring
*/
#ifndef PSGPLAY_SNDH_H
#define PSGPLAY_SNDH_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define SNDH_PROLOGUE_SIZE 20
#define SNDH_FLAG(f) \
f('y', PSG, "YM2149 PSG") \
f('e', DMA, "STE/TT DMA sound") \
f('a', MFP_TA, "MFP timer A") \
f('b', MFP_TB, "MFP timer B") \
f('c', MFP_TC, "MFP timer C") \
f('d', MFP_TD, "MFP timer D") \
f('p', AGA, "Amiga AGA") \
f('l', LMC, "STE/TT LMC1992") \
f('s', DSP, "Falcon DSP 56001") \
f('t', BLT, "BLITTER") \
f('h', HBL, "Horizontal blank")
enum sndh_timer_type {
SNDH_TIMER_A = 'A',
SNDH_TIMER_B = 'B',
SNDH_TIMER_C = 'C',
SNDH_TIMER_D = 'D',
SNDH_TIMER_V = 'V', /* Vertical blank (VBL) */
};
struct sndh_timer {
enum sndh_timer_type type;
int frequency;
};
/**
* sndh_identify - is data SNDH?
* @data: data
* @size: size in bytes of data, or greater
*
* Note that @size must be at least SNDH_PROLOGUE_SIZE bytes.
*
* Return: %true if data seems to be SNDH, otherwise %false
*/
bool sndh_identify(const void *data, size_t size);
/**
* sndh_tag_subtune_count - get SNDH subtune count
* @subtune_count: SNDH subtune count result, if determined
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Return: %true on success, otherwise %false
*/
bool sndh_tag_subtune_count(int *subtune_count,
const void *data, const size_t size);
/**
* sndh_tag_default_subtune - get SNDH default subtune
* @default_subtune: default SNDH subtune result, if determined
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Return: %true on success, otherwise %false
*/
bool sndh_tag_default_subtune(int *default_subtune,
const void *data, const size_t size);
/**
* sndh_tag_subtune_time - get SNDH subtune duration time
* @duration: result of SNDH subtune duration in seconds, if determined
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Return: %true on success, otherwise %false
*/
bool sndh_tag_subtune_time(float *duration, int subtune,
const void *data, const size_t size);
/**
* sndh_tag_timer - get SNDH timer
* @timer: SNDH timer result, if determined
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Return: %true on success, otherwise %false
*/
bool sndh_tag_timer(struct sndh_timer *timer,
const void *data, const size_t size);
/**
* sndh_tag_title - get SNDH title
* @title: SNDH title result, if determined
* @length: maximum length of title buffer including NUL
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Return: %true on success, otherwise %false
*/
bool sndh_tag_title(char *title, size_t length,
const void *data, const size_t size);
/**
* sndh_init_address - SNDH init address relative start of data
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Return: address relative start of data, otherwise zero
*/
size_t sndh_init_address(const void *data, const size_t size);
/**
* sndh_exit_address - SNDH exit address relative start of data
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Return: address relative start of data, otherwise zero
*/
size_t sndh_exit_address(const void *data, const size_t size);
/**
* sndh_play_address - SNDH play address relative start of data
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Return: address relative start of data, otherwise zero
*/
size_t sndh_play_address(const void *data, const size_t size);
/** sndh_tag_name - SNDH tag name */
#define sndh_tag_name (sndh_cursor__.tag->name)
/** sndh_tag_value - SNDH tag string representation */
#define sndh_tag_value (sndh_cursor__.value)
/** sndh_tag_integer - SNDH tag integer representation */
#define sndh_tag_integer (sndh_cursor__.integer)
/**
* sndh_for_each_tag - iterate over SNDH tags
* @data: SNDH data
* @size: size in bytes of SNDH data
*
* Note that some tags such as "TIME" and "#!SN" may have multiple values
* and be given more than once.
*/
#define sndh_for_each_tag(data, size) \
sndh_for_each_tag_with_header_size_and_diagnostic((data), (size), NULL, NULL)
/**
* sndh_for_each_tag_with_diagnostic - iterate over SNDH tags with diagnostic
* @data: SNDH data
* @size: size in bytes of SNDH data
* @diag: pointer to &struct sndh_diagnostic with warning and error callbacks,
* or %NULL to ignore
*
* Note that some tags such as "TIME" and "#!SN" may have multiple values
* and be given more than once.
*/
#define sndh_for_each_tag_with_diagnostic(data, size, diag) \
sndh_for_each_tag_with_header_size_and_diagnostic((data), (size), NULL, (diag))
/**
* sndh_for_each_tag_with_header_size - iterate over SNDH tags and compute
* SNDH header size
* @data: SNDH data
* @size: size in bytes of SNDH data
* @header_size: pointer to &size_t to store SNDH header size, or %NULL to
* ignore
*
* Note that some tags such as "TIME" and "#!SN" may have multiple values
* and be given more than once.
*/
#define sndh_for_each_tag_with_header_size(data, size, header_size) \
sndh_for_each_tag_with_header_size_and_diagnostic((data), (size), (header_size), NULL)
/**
* sndh_for_each_tag_with_header_size_and_diagnostic - iterate over SNDH tags
* and compute size with diagnostic
* @data: SNDH data
* @size: size in bytes of SNDH data
* @header_size: pointer to &size_t to store SNDH header size, or %NULL to
* ignore
* @diag: pointer to &struct sndh_diagnostic with warning and error callbacks,
* or %NULL to ignore
*
* Note that some tags such as "TIME" and "#!SN" may have multiple values
* and be given more than once.
*/
#define sndh_for_each_tag_with_header_size_and_diagnostic(data, size, header_size, diag)\
for (struct sndh_cursor sndh_cursor__ = \
sndh_first_tag((data), (size), (header_size), (diag)); \
sndh_valid_tag(&sndh_cursor__); \
sndh_next_tag(&sndh_cursor__))
/**
* struct sndh_diagnostic - SNDH tag diagnostic message callbacks
* @warn: diagnostic warning message
* @error: diagnostic error message
* @arg: optional callback argument, can be %NULL
*/
struct sndh_diagnostic {
void (*warn)(void *arg, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
void (*error)(void *arg, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
void *arg;
};
/* Private SNDH structures and functions. */
struct sndh_cursor;
struct sndh_tag {
const char *name;
bool (*read)(struct sndh_cursor *cursor);
size_t length;
};
struct sndh_cursor {
const struct {
size_t size;
const void *data;
} file;
struct {
size_t *size;
} header;
const size_t bound;
size_t offset;
bool valid;
const struct sndh_tag *tag;
int integer;
const char *value;
char buffer[32];
int subtunes;
struct {
bool (*read)(struct sndh_cursor *cursor);
size_t start;
size_t bound;
} subtag;
bool hdns;
const struct sndh_diagnostic diag;
};
struct sndh_cursor sndh_first_tag(const void *data, const size_t size,
size_t *header_size, const struct sndh_diagnostic *diag);
size_t sndh_header_size(const void *data, const size_t size);
bool sndh_valid_tag(const struct sndh_cursor *cursor);
void sndh_next_tag(struct sndh_cursor *cursor);
struct sndh_file {
size_t size;
struct {
uint32_t init;
uint32_t exit;
uint32_t play;
uint8_t data[];
} *sndh;
};
static inline uint32_t sndh_timer_to_u32(const struct sndh_timer timer)
{
return (timer.frequency << 8) | (timer.type & 0xff);
}
static inline struct sndh_timer u32_to_sndh_timer(const uint32_t timer)
{
return (struct sndh_timer) {
.type = timer & 0xff,
.frequency = timer >> 8,
};
}
#if defined(__m68k__)
static inline void sndh_init(int track, struct sndh_file *file)
{
__asm__ __volatile__ (
" movem.l %%d0-%%a6,-(%%sp)\n"
" move.l %1,%%d0\n"
" jsr (%0)\n"
" movem.l (%%sp)+,%%d0-%%a6\n"
:
: "a" (&file->sndh->init), "d" (track)
: "memory");
}
static inline void sndh_exit(struct sndh_file *file)
{
__asm__ __volatile__ (
" movem.l %%d0-%%a6,-(%%sp)\n"
" jsr (%0)\n"
" movem.l (%%sp)+,%%d0-%%a6\n"
:
: "a" (&file->sndh->exit)
: "memory");
}
static inline void sndh_play(struct sndh_file *file)
{
__asm__ __volatile__ (
/* Enable interrupts and other timers for effects. */
" move #0x2200,%%sr\n"
" movem.l %%d0-%%a6,-(%%sp)\n"
" jsr (%0)\n"
" movem.l (%%sp)+,%%d0-%%a6\n"
:
: "a" (&file->sndh->play)
: "memory");
}
#endif /* defined(__m68k__) */
#endif /* PSGPLAY_SNDH_H */