changeset 407:c3abc4ada43d

Add support for logging YM2612 channels to WAVE files
author Mike Pavone <pavone@retrodev.com>
date Sun, 16 Jun 2013 17:57:57 -0700
parents b1bc1947d949
children a13a83934ba3
files Makefile blastem.c wave.c wave.h ym2612.c ym2612.h
diffstat 6 files changed, 124 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sun Jun 16 13:42:13 2013 -0700
+++ b/Makefile	Sun Jun 16 17:57:57 2013 -0700
@@ -7,8 +7,8 @@
 
 all : dis trans stateview blastem
 
-blastem : blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o
-	$(CC) -ggdb -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o `pkg-config --libs $(LIBS)`
+blastem : blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o wave.o
+	$(CC) -ggdb -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o wave.o `pkg-config --libs $(LIBS)`
 
 dis : dis.o 68kinst.o
 	$(CC) -o dis dis.o 68kinst.o
--- a/blastem.c	Sun Jun 16 13:42:13 2013 -0700
+++ b/blastem.c	Sun Jun 16 17:57:57 2013 -0700
@@ -1919,6 +1919,7 @@
 	int width = -1;
 	int height = -1;
 	int debug = 0;
+	int ym_log = 0;
 	FILE *address_log = NULL;
 	for (int i = 2; i < argc; i++) {
 		if (argv[i][0] == '-') {
@@ -1963,6 +1964,9 @@
 					return 1;
 				}
 				break;
+			case 'y':
+				ym_log = 1;
+				break;
 			default:
 				fprintf(stderr, "Unrecognized switch %s\n", argv[i]);
 				return 1;
@@ -1989,7 +1993,7 @@
 	init_vdp_context(&v_context);
 	
 	ym2612_context y_context;
-	ym_init(&y_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_YM, render_audio_buffer());
+	ym_init(&y_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0);
 	
 	psg_context p_context;
 	psg_init(&p_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_PSG, render_audio_buffer());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wave.c	Sun Jun 16 17:57:57 2013 -0700
@@ -0,0 +1,41 @@
+#include "wave.h"
+#include <stddef.h>
+#include <string.h>
+
+int wave_init(FILE * f, uint32_t sample_rate, uint16_t bits_per_sample, uint16_t num_channels)
+{
+	wave_header header;
+	memcpy(header.chunk.id, "RIFF", 4);
+	memcpy(header.chunk.format, "WAVE", 4);
+	header.chunk.size = 0; //This will be filled in later
+	memcpy(header.format_header.id, "fmt ", 4);
+	header.format_header.size = sizeof(wave_header) - (sizeof(header.chunk) + sizeof(header.data_header) + sizeof(header.format_header));
+	header.audio_format = 1;
+	header.num_channels = num_channels;
+	header.sample_rate = sample_rate;
+	header.byte_rate = sample_rate * num_channels * (bits_per_sample/8);
+	header.block_align = num_channels * (bits_per_sample/8);
+	header.bits_per_sample = bits_per_sample;
+	memcpy(header.data_header.id, "data", 4);
+	header.data_header.size = 0;//This will be filled in later;
+	return fwrite(&header, 1, sizeof(header), f) == sizeof(header);
+}
+
+int wave_finalize(FILE * f)
+{
+	uint32_t size = ftell(f);
+	fseek(f, offsetof(wave_header, chunk.size), SEEK_SET);
+	size -= 8;
+	if (fwrite(&size, sizeof(size), 1, f) != sizeof(size)) {
+		fclose(f);
+		return 0;
+	}
+	fseek(f, offsetof(wave_header, data_header.size), SEEK_SET);
+	size -= 36;
+	if (fwrite(&size, sizeof(size), 1, f)) {
+		fclose(f);
+		return 0;
+	}
+	fclose(f);
+	return 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wave.h	Sun Jun 16 17:57:57 2013 -0700
@@ -0,0 +1,38 @@
+#ifndef WAVE_H_
+#define WAVE_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+#pragma pack(push, 1)
+
+typedef struct {
+	char     id[4];
+	uint32_t size;
+	char     format[4];
+} riff_chunk;
+
+typedef struct {
+	char     id[4];
+	uint32_t size;
+} riff_sub_chunk;
+
+typedef struct {
+	riff_chunk     chunk;
+	riff_sub_chunk format_header;
+	uint16_t       audio_format;
+	uint16_t       num_channels;
+	uint32_t       sample_rate;
+	uint32_t       byte_rate;
+	uint16_t       block_align;
+	uint16_t       bits_per_sample;
+	riff_sub_chunk data_header;
+} wave_header;
+
+#pragma pack(pop)
+
+int wave_init(FILE * f, uint32_t sample_rate, uint16_t bits_per_sample, uint16_t num_channels);
+int wave_finalize(FILE * f);
+
+#endif //WAVE_H_
+
--- a/ym2612.c	Sun Jun 16 13:42:13 2013 -0700
+++ b/ym2612.c	Sun Jun 16 17:57:57 2013 -0700
@@ -4,6 +4,7 @@
 #include <stdlib.h>
 #include "ym2612.h"
 #include "render.h"
+#include "wave.h"
 
 //#define DO_DEBUG_PRINT
 #ifdef DO_DEBUG_PRINT
@@ -97,7 +98,18 @@
 FILE * debug_file = NULL;
 uint32_t first_key_on=0;
 
-void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit)
+ym2612_context * log_context = NULL;
+
+void ym_finalize_log()
+{
+	for (int i = 0; i < NUM_CHANNELS; i++) {
+		if (log_context->channels[i].logfile) {
+			wave_finalize(log_context->channels[i].logfile);
+		}
+	}
+}
+
+void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options)
 {
 	dfopen(debug_file, "ym_debug.txt", "w");
 	memset(context, 0, sizeof(*context));
@@ -114,6 +126,23 @@
 	//some games seem to expect that the LR flags start out as 1
 	for (int i = 0; i < NUM_CHANNELS; i++) {
 		context->channels[i].lr = 0xC0;
+		if (options & YM_OPT_WAVE_LOG) {
+			char fname[64];
+			sprintf(fname, "ym_channel_%d.wav", i);
+			FILE * f = context->channels[i].logfile = fopen(fname, "wb");
+			if (!f) {
+				fprintf(stderr, "Failed to open WAVE log file %s for writing\n", fname);
+				continue;
+			}
+			if (!wave_init(f, sample_rate, 16, 1)) {
+				fclose(f);
+				context->channels[i].logfile = NULL;
+			}
+		}
+	}
+	if (options & YM_OPT_WAVE_LOG) {
+		log_context = context;
+		atexit(ym_finalize_log);
 	}
 	if (!did_tbl_init) {
 		//populate sine table
@@ -372,6 +401,9 @@
 					if (value & 0x2000) {
 						value |= 0xC000;
 					}
+					if (context->channels[i].logfile) {
+						fwrite(&value, sizeof(value), 1, context->channels[i].logfile);
+					}
 					if (context->channels[i].lr & 0x80) {
 						context->audio_buffer[context->buffer_pos] += value / YM_VOLUME_DIVIDER;
 					}
--- a/ym2612.h	Sun Jun 16 13:42:13 2013 -0700
+++ b/ym2612.h	Sun Jun 16 17:57:57 2013 -0700
@@ -2,11 +2,14 @@
 #define YM2612_H_
 
 #include <stdint.h>
+#include <stdio.h>
 
 #define NUM_PART_REGS (0xB7-0x30)
 #define NUM_CHANNELS 6
 #define NUM_OPERATORS (4*NUM_CHANNELS)
 
+#define YM_OPT_WAVE_LOG 1
+
 typedef struct {
 	uint32_t phase_inc;
 	uint32_t phase_counter;
@@ -22,6 +25,7 @@
 } ym_operator;
 
 typedef struct {
+	FILE *   logfile;
 	uint16_t fnum;
 	int16_t  output;
 	uint8_t  block_fnum_latch;
@@ -70,7 +74,7 @@
 	uint8_t     selected_part;
 } ym2612_context;
 
-void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit);
+void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options);
 void ym_run(ym2612_context * context, uint32_t to_cycle);
 void ym_address_write_part1(ym2612_context * context, uint8_t address);
 void ym_address_write_part2(ym2612_context * context, uint8_t address);