|
|
1.1 root 1: /* Generator is (c) James Ponder, 1997-2001 http://www.squish.net/generator/ */
2:
1.1.1.2 root 3: #include <stdlib.h>
1.1 root 4:
5: #include "generator.h"
6: #include "gensound.h"
1.1.1.2 root 7: #include "gensoundp.h"
1.1 root 8: #include "vdp.h"
9: #include "ui.h"
1.1.1.2 root 10: #include "sn76496.h"
1.1 root 11:
12: #ifdef JFM
13: # include "jfm.h"
14: #else
15: # include "support.h"
16: # include "fm.h"
17: #endif
18:
19: /*** variables externed ***/
20:
1.1.1.2 root 21: int sound_debug = 0; /* debug mode */
22: int sound_feedback = 0; /* -1, running out of sound
23: +0, lots of sound, do something */
24: unsigned int sound_minfields = 5; /* min fields to try to buffer */
25: unsigned int sound_maxfields = 10; /* max fields before blocking */
1.1.1.3 root 26: unsigned int sound_speed = SOUND_SAMPLERATE; /* sample rate */
1.1.1.2 root 27: unsigned int sound_sampsperfield; /* samples per field */
28: unsigned int sound_threshold; /* samples in buffer aiming for */
1.1 root 29: uint8 sound_regs1[256];
30: uint8 sound_regs2[256];
31: uint8 sound_address1 = 0;
32: uint8 sound_address2 = 0;
33: uint8 sound_keys[8];
1.1.1.3 root 34: int sound_logsample = 0; /* sample to log or -1 if none */
35: unsigned int sound_on = 1; /* sound enabled */
36: unsigned int sound_psg = 1; /* psg enabled */
37: unsigned int sound_fm = 1; /* fm enabled */
1.1.1.4 ! root 38: unsigned int sound_filter = 50; /* low-pass filter percentage (0-100) */
1.1 root 39:
1.1.1.2 root 40: /* pal is lowest framerate */
41: uint16 sound_soundbuf[2][SOUND_MAXRATE / 50];
1.1 root 42:
43: /*** forward references ***/
44:
1.1.1.3 root 45: static void sound_process(void);
46: static void sound_writetolog(unsigned char c);
47:
1.1 root 48: /*** file scoped variables ***/
49:
50: static int sound_active = 0;
1.1.1.3 root 51: static uint8 *sound_logdata; /* log data */
52: static unsigned int sound_logdata_size; /* sound_logdata size */
53: static unsigned int sound_logdata_p; /* current log data offset */
54: static unsigned int sound_fieldhassamples; /* flag if field has samples */
1.1 root 55:
56: #ifdef JFM
1.1.1.2 root 57: static t_jfm_ctx *sound_ctx;
1.1 root 58: #endif
59:
60: /*** sound_init - initialise this sub-unit ***/
61:
62: int sound_init(void)
63: {
64: int ret;
65:
1.1.1.2 root 66: /* The sound_minfields parameter specifies how many fields worth of sound we
67: should try and keep around (a display field is 1/50th or 1/60th of a
68: second) - generator works by trying to keep sound_minfields number of
69: fields worth of sound around, and drops plotting frames to keep up on slow
70: machines */
71:
72: sound_sampsperfield = sound_speed / vdp_framerate;
73: sound_threshold = sound_sampsperfield * sound_minfields;
74:
1.1 root 75: ret = sound_start();
76: if (ret)
77: return ret;
78: #ifdef JFM
1.1.1.2 root 79: if ((sound_ctx = jfm_init(0, 2612, vdp_clock / 7, sound_speed,
80: NULL, NULL)) == NULL) {
81: #else
82: if (YM2612Init(1, vdp_clock / 7, sound_speed, NULL, NULL)) {
83: #endif
84: LOG_VERBOSE(("YM2612 failed init"));
85: sound_stop();
86: return 1;
87: }
88: if (SN76496Init(0, vdp_clock / 15, 0, sound_speed)) {
89: LOG_VERBOSE(("SN76496 failed init"));
90: sound_stop();
91: #ifdef JFM
92: jfm_final(sound_ctx);
1.1 root 93: #else
1.1.1.2 root 94: YM2612Shutdown();
1.1 root 95: #endif
96: return 1;
97: }
1.1.1.3 root 98: if (sound_logdata)
99: free(sound_logdata);
100: sound_logdata_size = 8192;
101: sound_logdata = malloc(sound_logdata_size);
102: if (!sound_logdata)
103: ui_err("out of memory");
1.1 root 104: LOG_VERBOSE(("YM2612 Initialised @ sample rate %d", sound_speed));
105: return 0;
106: }
107:
108: /*** sound_final - finalise this sub-unit ***/
109:
110: void sound_final(void)
111: {
112: sound_stop();
113: #ifdef JFM
114: jfm_final(sound_ctx);
115: #else
116: YM2612Shutdown();
117: #endif
118: }
119:
1.1.1.2 root 120: /*** sound_start - start sound ***/
1.1 root 121:
122: int sound_start(void)
123: {
124: if (sound_active)
125: sound_stop();
1.1.1.2 root 126: LOG_VERBOSE(("Starting sound..."));
1.1.1.4 ! root 127: if (soundp_start() != 0) {
1.1.1.2 root 128: LOG_VERBOSE(("Failed to start sound hardware"));
129: return -1;
1.1 root 130: }
131: sound_active = 1;
1.1.1.2 root 132: LOG_VERBOSE(("Started sound."));
1.1 root 133: return 0;
134: }
1.1.1.2 root 135:
1.1 root 136: /*** sound_stop - stop sound ***/
137:
138: void sound_stop(void)
139: {
140: if (!sound_active)
141: return;
142: LOG_VERBOSE(("Stopping sound..."));
1.1.1.2 root 143: soundp_stop();
1.1 root 144: sound_active = 0;
145: LOG_VERBOSE(("Stopped sound."));
146: }
1.1.1.2 root 147:
1.1 root 148: /*** sound_reset - reset sound sub-unit ***/
149:
150: int sound_reset(void)
151: {
152: sound_final();
153: return sound_init();
154: }
155:
1.1.1.3 root 156: /*** sound_startfield - start of frame ***/
157:
158: void sound_startfield(void)
159: {
160: sound_logdata_p = 0;
161: if (gen_musiclog == musiclog_gnm) {
162: sound_writetolog(0);
163: sound_writetolog((vdp_totlines >> 8) & 0xff);
164: sound_writetolog(vdp_totlines & 0xff);
165: sound_fieldhassamples = 0;
166: }
167: }
1.1 root 168: /*** sound_endfield - end frame and output sound ***/
169:
170: void sound_endfield(void)
171: {
1.1.1.2 root 172: int pending;
1.1.1.3 root 173: uint8 *p, *o;
174:
175: if (gen_musiclog) {
176: if (gen_musiclog == musiclog_gym) {
177: /* GYM format ends a field with a 0 byte */
178: sound_writetolog(0);
179: } else {
180: /* GNM format requires sifting through the data */
181: if (!sound_fieldhassamples) {
182: /* we're only removing data, so we're going to write to the buffer
183: we're reading from */
184: o = sound_logdata + 3;
185: for (p = sound_logdata + 3;
186: p < (sound_logdata + sound_logdata_p); p++) {
187: if ((*p & 0xF0) != 0x00 || *p == 4)
188: ui_err("assertion of no samples failed");
189: switch (*p) {
190: case 0:
191: ui_err("field marker in middle of field data");
192: case 1:
193: case 2:
194: /* FM data, copy 2 and two bytes to output */
195: *o++ = *p++;
196: *o++ = *p++;
197: *o++ = *p;
198: break;
199: case 3:
200: /* PSG data, copy 3 and one byte to output */
201: *o++ = *p++;
202: *o++ = *p;
203: break;
204: case 5:
205: /* these are the bytes we're trying to strip */
206: /* do nothing */
207: break;
208: default:
209: ui_err("invalid data in sound log buffer");
210: }
211: }
212: /* update new end of buffer */
213: sound_logdata_p = o - sound_logdata;
214: sound_logdata[1] = 0; /* high byte number of samples */
215: sound_logdata[2] = 0; /* low byte number of samples */
216: }
217: }
218: /* write data */
219: ui_musiclog(sound_logdata, sound_logdata_p);
220: /* sound_startfield resets everything */
221: }
222:
223: if (!sound_on) {
224: /* sound is turned off - let generator continue */
225: sound_feedback = 0;
226: return;
227: }
1.1.1.2 root 228:
229: /* work out feedback from sound system */
230:
231: if ((pending = soundp_samplesbuffered()) == -1)
232: ui_err("Failed to read pending bytes in output sound buffer");
233: if ((unsigned int)pending < sound_threshold)
1.1 root 234: sound_feedback = -1;
235: else
236: sound_feedback = 0;
1.1.1.2 root 237:
1.1 root 238: if (sound_debug) {
1.1.1.2 root 239: LOG_VERBOSE(("End of field - sound system says %d bytes buffered",
240: pending));
241: LOG_VERBOSE(("Threshold %d, therefore feedback = %d ", sound_threshold,
242: sound_feedback));
1.1 root 243: }
1.1.1.2 root 244: soundp_output(sound_soundbuf[0], sound_soundbuf[1], sound_sampsperfield);
1.1 root 245: }
246:
247: /*** sound_ym2612fetch - fetch byte from ym2612 chip ***/
248:
249: uint8 sound_ym2612fetch(uint8 addr)
250: {
251: #ifdef JFM
252: return jfm_read(sound_ctx, addr);
253: #else
254: return YM2612Read(0, addr);
255: #endif
256: }
257:
258: /*** sound_ym2612store - store a byte to the ym2612 chip ***/
259:
260: void sound_ym2612store(uint8 addr, uint8 data)
261: {
262: switch (addr) {
263: case 0:
264: sound_address1 = data;
265: break;
266: case 1:
267: if (sound_address1 == 0x2a) {
1.1.1.3 root 268: /* sample data */
1.1 root 269: sound_keys[7] = 0;
1.1.1.3 root 270: sound_logsample = data;
271: } else {
272: if (gen_musiclog != musiclog_off) {
273: /* GYM and GNM */
274: sound_writetolog(1);
275: sound_writetolog(sound_address1);
276: sound_writetolog(data);
277: }
1.1 root 278: }
1.1.1.3 root 279: if (sound_address1 == 0x28 && (data & 3) != 3)
280: sound_keys[data & 7] = data >> 4;
1.1 root 281: if (sound_address1 == 0x2b)
282: sound_keys[7] = data & 0x80 ? 0xf : 0;
283: sound_regs1[sound_address1] = data;
284: break;
285: case 2:
1.1.1.3 root 286: if (gen_musiclog != musiclog_off) {
287: /* GYM and GNM */
288: sound_writetolog(2);
289: sound_writetolog(sound_address2);
290: sound_writetolog(data);
291: }
1.1 root 292: sound_address2 = data;
293: break;
294: case 3:
295: sound_regs2[sound_address2] = data;
296: break;
297: }
298: #ifdef JFM
299: jfm_write(sound_ctx, addr, data);
300: #else
301: YM2612Write(0, addr, data);
302: #endif
303: }
304:
1.1.1.2 root 305: /*** sound_sn76496store - store a byte to the sn76496 chip ***/
306:
307: void sound_sn76496store(uint8 data)
308: {
1.1.1.3 root 309: if (gen_musiclog != musiclog_off) {
310: /* GYM and GNM */
311: sound_writetolog(3);
312: sound_writetolog(data);
313: }
1.1.1.2 root 314: SN76496Write(0, data);
315: }
316:
317: /*** sound_genreset - reset genesis sound ***/
318:
319: void sound_genreset(void)
320: {
321: #ifdef JFM
322: jfm_reset(sound_ctx);
323: #else
324: YM2612ResetChip(0);
325: #endif
326: }
327:
1.1.1.3 root 328: /*** sound_line - called at end of line ***/
329:
330: void sound_line(void)
331: {
332: if (gen_musiclog == musiclog_gnm) {
333: /* GNM log */
334: if (sound_logsample == -1) {
335: sound_writetolog(5);
336: } else {
337: if ((sound_logsample & 0xF0) == 0) {
338: sound_writetolog(4);
339: }
340: sound_writetolog(sound_logsample);
341: sound_logsample = -1;
342: /* mark the fact that we have encountered a sample this field */
343: sound_fieldhassamples = 1;
344: }
345: }
346: sound_process();
347: }
348:
1.1.1.2 root 349: /*** sound_process - process sound ***/
350:
1.1.1.3 root 351: static void sound_process(void)
1.1.1.2 root 352: {
353: static sint16 *tbuf[2];
354: int s1 = (sound_sampsperfield * (vdp_line)) / vdp_totlines;
355: int s2 = (sound_sampsperfield * (vdp_line + 1)) / vdp_totlines;
356: /* pal is lowest framerate */
357: uint16 sn76496buf[SOUND_MAXRATE / 50]; /* far too much but who cares */
358: unsigned int samples = s2 - s1;
359: unsigned int i;
1.1.1.4 ! root 360: static sint32 ll, rr;
! 361: uint32 factora = (0x10000 * sound_filter) / 100;
! 362: uint32 factorb = 0x10000 - factora;
1.1.1.2 root 363:
364: tbuf[0] = sound_soundbuf[0] + s1;
365: tbuf[1] = sound_soundbuf[1] + s1;
366:
367: if (s2 > s1) {
1.1.1.3 root 368: if (sound_fm)
1.1.1.2 root 369: #ifdef JFM
1.1.1.3 root 370: jfm_update(sound_ctx, (void **)tbuf, samples1);
1.1.1.2 root 371: #else
1.1.1.3 root 372: YM2612UpdateOne(0, tbuf, samples);
1.1.1.2 root 373: #endif
374:
375: /* SN76496 outputs sound in range -0x4000 to 0x3fff
376: YM2612 ouputs sound in range -0x8000 to 0x7fff - therefore
377: we take 3/4 of the YM2612 and add half the SN76496 */
1.1.1.3 root 378:
1.1.1.4 ! root 379: /* this uses a single-pole low-pass filter (6 dB/octave) curtesy of
! 380: Jon Watte <[email protected]> */
! 381:
1.1.1.3 root 382: if (sound_fm && sound_psg) {
1.1.1.4 ! root 383: SN76496Update(0, sn76496buf, samples);
1.1.1.3 root 384: for (i = 0; i < samples; i++) {
1.1.1.4 ! root 385: sint16 snsample = ((sint16)sn76496buf[i] - 0x4000) >> 1;
! 386: sint32 l = snsample + ((tbuf[0][i] * 3) >> 2); /* left channel */
! 387: sint32 r = snsample + ((tbuf[1][i] * 3) >> 2); /* right channel */
! 388: ll = (ll >> 16) * factora + (l * factorb);
! 389: rr = (rr >> 16) * factora + (r * factorb);
! 390: tbuf[0][i] = ll >> 16;
! 391: tbuf[1][i] = rr >> 16;
1.1.1.3 root 392: }
393: } else if (!sound_fm && !sound_psg) {
394: /* no sound */
395: memset(tbuf[0], 0, 2 * samples);
396: memset(tbuf[1], 0, 2 * samples);
397: } else if (sound_fm) {
398: /* fm only */
399: for (i = 0; i < samples; i++) {
400: sint32 l = (tbuf[0][i] * 3) >> 2; /* left channel */
401: sint32 r = (tbuf[1][i] * 3) >> 2; /* right channel */
1.1.1.4 ! root 402: ll = (ll >> 16) * factora + (l * factorb);
! 403: rr = (rr >> 16) * factora + (r * factorb);
! 404: tbuf[0][i] = ll >> 16;
! 405: tbuf[1][i] = rr >> 16;
1.1.1.3 root 406: }
407: } else {
408: /* psg only */
1.1.1.4 ! root 409: SN76496Update(0, sn76496buf, samples);
1.1.1.3 root 410: for (i = 0; i < samples; i++) {
1.1.1.4 ! root 411: sint16 snsample = ((sint16)(sn76496buf[i] - 0x4000)) >> 1;
! 412: ll = (ll >> 16) * factora + (snsample * factorb);
! 413: rr = (rr >> 16) * factora + (snsample * factorb);
! 414: tbuf[0][i] = ll >> 16;
! 415: tbuf[1][i] = rr >> 16;
1.1.1.3 root 416: }
1.1.1.2 root 417: }
418: }
419: }
1.1.1.3 root 420:
421: /*** sound_writetolog - write to music log buffer ***/
422:
423: static void sound_writetolog(unsigned char c)
424: {
425: /* write the byte to the self-expanding memory log - when we have the whole
426: field's worth of data we then sift through the data (see the
427: documentation on the GNM log format) and then pass it to ui_writelog */
428:
429: sound_logdata[sound_logdata_p++] = c;
430: if (sound_logdata_p >= sound_logdata_size) {
431: LOG_VERBOSE(("sound log buffer limit increased"));
432: sound_logdata_size+= 8192;
433: sound_logdata = realloc(sound_logdata, sound_logdata_size);
434: if (!sound_logdata)
435: ui_err("out of memory");
436: }
437: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.