summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libayumi/ayumi_render.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libayumi/ayumi_render.c')
-rw-r--r--lib/rbcodec/codecs/libayumi/ayumi_render.c328
1 files changed, 328 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libayumi/ayumi_render.c b/lib/rbcodec/codecs/libayumi/ayumi_render.c
new file mode 100644
index 0000000000..9bf0204597
--- /dev/null
+++ b/lib/rbcodec/codecs/libayumi/ayumi_render.c
@@ -0,0 +1,328 @@
1#include "ayumi_render.h"
2
3#include <stdlib.h>
4#include <string.h>
5#include <stdint.h>
6
7#include "ayumi.h"
8#include "lzh.h"
9#include "codeclib.h"
10
11ayumi_render_t ay;
12
13/* default panning settings, 7 stereo types */
14static const double default_pan[7][3] = {
15/* A, B, C */
16
17 {0.50, 0.50, 0.50}, /* MONO */
18 {0.10, 0.50, 0.90}, /* ABC */
19 {0.10, 0.90, 0.50}, /* ACB */
20 {0.50, 0.10, 0.90}, /* BAC */
21 {0.90, 0.10, 0.50}, /* BCA */
22 {0.50, 0.90, 0.10}, /* CAB */
23 {0.90, 0.50, 0.10} /* CBA */
24};
25
26static const char *chiptype_name[3] = {
27 "AY-3-8910",
28 "YM2149",
29 "Unknown"
30};
31
32static const char *layout_name[9] = {
33 "Mono",
34 "ABC Stereo",
35 "ACB Stereo",
36 "BAC Stereo",
37 "BCA Stereo",
38 "CAB Stereo",
39 "CBA Stereo",
40 "Custom",
41 "Unknown"
42};
43
44/* reader */
45
46#define VTX_STRING_MAX 254
47
48typedef struct {
49 uchar *ptr;
50 uint size;
51} reader_t;
52
53reader_t reader;
54
55void Reader_Init(void *pBlock) {
56 reader.ptr = (uchar *) pBlock;
57 reader.size = 0;
58}
59
60uint Reader_ReadByte(void) {
61 uint res;
62 res = *reader.ptr++;
63 reader.size += 1;
64 return res;
65}
66
67uint Reader_ReadWord(void) {
68 uint res;
69 res = *reader.ptr++;
70 res += *reader.ptr++ << 8;
71 reader.size += 2;
72 return res;
73}
74
75uint Reader_ReadDWord(void) {
76 uint res;
77 res = *reader.ptr++;
78 res += *reader.ptr++ << 8;
79 res += *reader.ptr++ << 16;
80 res += *reader.ptr++ << 24;
81 reader.size += 4;
82 return res;
83}
84
85char *Reader_ReadString(void) {
86 char *res;
87 if (reader.ptr == NULL)
88 return NULL;
89 int len = strlen((const char *)reader.ptr);
90 if (len > VTX_STRING_MAX)
91 return NULL;
92 res = reader.ptr;
93 reader.ptr += len + 1;
94 reader.size += len + 1;
95 return res;
96}
97
98uchar *Reader_GetPtr(void) {
99 return reader.ptr;
100}
101
102uint Reader_GetSize(void) {
103 return reader.size;
104}
105
106/* ayumi_render */
107
108static int AyumiRender_LoadInfo(void *pBlock, uint size)
109{
110 if (size < 20)
111 return 0;
112
113 Reader_Init(pBlock);
114
115 uint hdr = Reader_ReadWord();
116
117 if (hdr == 0x7961)
118 ay.info.chiptype = VTX_CHIP_AY;
119 else if (hdr == 0x6d79)
120 ay.info.chiptype = VTX_CHIP_YM;
121 else {
122 return 0;
123 }
124
125 ay.info.layout = (vtx_layout_t)
126 Reader_ReadByte();
127 ay.info.loop = Reader_ReadWord();
128 ay.info.chipfreq = Reader_ReadDWord();
129 ay.info.playerfreq = Reader_ReadByte();
130 ay.info.year = Reader_ReadWord();
131 ay.data.regdata_size = Reader_ReadDWord();
132 ay.info.frames = ay.data.regdata_size / 14;
133 ay.info.title = Reader_ReadString();
134 ay.info.author = Reader_ReadString();
135 ay.info.from = Reader_ReadString();
136 ay.info.tracker = Reader_ReadString();
137 ay.info.comment = Reader_ReadString();
138
139 ay.data.lzhdata_size = size - Reader_GetSize();
140 ay.data.lzhdata = (uchar *)codec_malloc(ay.data.lzhdata_size);
141 memcpy(ay.data.lzhdata, Reader_GetPtr(), ay.data.lzhdata_size);
142
143 return 1;
144}
145
146int AyumiRender_LoadFile(void *pBlock, uint size)
147{
148 if (!AyumiRender_LoadInfo(pBlock, size))
149 return 0;
150
151 ay.data.regdata = (uchar *)codec_malloc(ay.data.regdata_size);
152 if (ay.data.regdata == NULL)
153 return 0;
154
155 int bRet = LzUnpack(ay.data.lzhdata, ay.data.lzhdata_size,
156 ay.data.regdata, ay.data.regdata_size);
157
158 if (bRet)
159 return 0;
160
161 return 1;
162}
163
164const char *AyumiRender_GetChipTypeName(vtx_chiptype_t chiptype)
165{
166 if (chiptype > VTX_CHIP_YM)
167 chiptype = (vtx_chiptype_t) (VTX_CHIP_YM + 1);
168 return chiptype_name[chiptype];
169}
170
171const char *AyumiRender_GetLayoutName(vtx_layout_t layout)
172{
173 if (layout > VTX_LAYOUT_CUSTOM)
174 layout = (vtx_layout_t) (VTX_LAYOUT_CUSTOM + 1);
175 return layout_name[layout];
176}
177
178int AyumiRender_AyInit(vtx_chiptype_t chiptype, uint samplerate,
179 uint chipfreq, double playerfreq, uint dcfilter)
180{
181 if (chiptype > VTX_CHIP_YM)
182 return 0;
183 if ((samplerate < 8000) || (samplerate > 768000))
184 return 0;
185 if ((chipfreq < 1000000) || (chipfreq > 2000000))
186 return 0;
187 if ((playerfreq < 1) || (playerfreq > 100))
188 return 0;
189
190 ay.is_ym = (chiptype == VTX_CHIP_YM) ? 1 : 0;
191 ay.clock_rate = chipfreq;
192 ay.sr = samplerate;
193
194 ay.dc_filter_on = dcfilter ? 1 : 0;
195
196 ay.frame = 0;
197 ay.isr_counter = 1;
198 ay.isr_step = playerfreq / samplerate;
199
200 if (!ayumi_configure(&ay.ay, ay.is_ym, ay.clock_rate, ay.sr))
201 return 0;
202
203 return 1;
204}
205
206int AyumiRender_SetLayout(vtx_layout_t layout, uint eqpower)
207{
208 if (layout > VTX_LAYOUT_CUSTOM)
209 return 0;
210 ay.is_eqp = eqpower ? 1 : 0;
211
212 switch (layout) {
213 case VTX_LAYOUT_MONO:
214 case VTX_LAYOUT_ABC:
215 case VTX_LAYOUT_ACB:
216 case VTX_LAYOUT_BAC:
217 case VTX_LAYOUT_BCA:
218 case VTX_LAYOUT_CAB:
219 case VTX_LAYOUT_CBA:
220 for (int i = 0; i < 3; i++)
221 ay.pan[i] = default_pan[layout][i];
222 break;
223 case VTX_LAYOUT_CUSTOM:
224 for (int i = 0; i < 3; i++)
225 ay.pan[i] = 0; // no custom layout
226 break;
227 default:
228 return 0;
229 }
230
231 for (int i = 0; i < 3; i++)
232 ayumi_set_pan(&ay.ay, i, ay.pan[i], ay.is_eqp);
233
234 return 1;
235}
236
237uint AyumiRender_GetPos(void)
238{
239 return ay.frame;
240}
241
242uint AyumiRender_GetMaxPos(void)
243{
244 return ay.info.frames;
245}
246
247static void AyumiRender_UpdateAyumiState(void)
248{
249 int r[16];
250
251 if (ay.frame < ay.info.frames) {
252 uchar *ptr = ay.data.regdata + ay.frame;
253 for (int n = 0; n < 14; n++) {
254 r[n] = *ptr;
255 ptr += ay.info.frames;
256 }
257 } else {
258 for (int n = 0; n < 14; n++) {
259 r[n] = 0;
260 }
261 }
262
263 ayumi_set_tone(&ay.ay, 0, (r[1] << 8) | r[0]);
264 ayumi_set_tone(&ay.ay, 1, (r[3] << 8) | r[2]);
265 ayumi_set_tone(&ay.ay, 2, (r[5] << 8) | r[4]);
266 ayumi_set_noise(&ay.ay, r[6]);
267 ayumi_set_mixer(&ay.ay, 0, r[7] & 1, (r[7] >> 3) & 1, r[8] >> 4);
268 ayumi_set_mixer(&ay.ay, 1, (r[7] >> 1) & 1, (r[7] >> 4) & 1, r[9] >> 4);
269 ayumi_set_mixer(&ay.ay, 2, (r[7] >> 2) & 1, (r[7] >> 5) & 1, r[10] >> 4);
270 ayumi_set_volume(&ay.ay, 0, r[8] & 0xf);
271 ayumi_set_volume(&ay.ay, 1, r[9] & 0xf);
272 ayumi_set_volume(&ay.ay, 2, r[10] & 0xf);
273 ayumi_set_envelope(&ay.ay, (r[12] << 8) | r[11]);
274 if (r[13] != 255) {
275 ayumi_set_envelope_shape(&ay.ay, r[13]);
276 }
277}
278
279int AyumiRender_Seek(ulong nSample)
280{
281 ulong samples = 0;
282
283 ay.frame = 0;
284 ay.isr_counter = 1;
285
286 ayumi_configure(&ay.ay, ay.is_ym, ay.clock_rate, ay.sr);
287
288 for (int i = 0; i < 3; i++)
289 ayumi_set_pan(&ay.ay, i, ay.pan[i], ay.is_eqp);
290
291 while (samples < nSample) {
292 ay.isr_counter += ay.isr_step;
293 if (ay.isr_counter >= 1) {
294 ay.isr_counter -= 1;
295 AyumiRender_UpdateAyumiState();
296 ay.frame += 1;
297 }
298 ayumi_seek(&ay.ay);
299 samples++;
300 }
301
302 return 1;
303}
304
305ulong AyumiRender_AySynth(void *pBuffer, ulong nSamples)
306{
307 ulong samples = 0;
308 short *out = (int16_t *) pBuffer;
309
310 for (ulong i = 0; i < nSamples; i++) {
311 ay.isr_counter += ay.isr_step;
312 if (ay.isr_counter >= 1) {
313 ay.isr_counter -= 1;
314 AyumiRender_UpdateAyumiState();
315 ay.frame += 1;
316 }
317 ayumi_process(&ay.ay);
318 if (ay.dc_filter_on) {
319 ayumi_remove_dc(&ay.ay);
320 }
321 out[0] = (int16_t)(ay.ay.left * 16383);
322 out[1] = (int16_t)(ay.ay.right * 16383);
323 out += 2;
324 samples++;
325 }
326
327 return samples;
328}