summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pavone <pavone@retrodev.com>2013-06-16 17:57:57 -0700
committerMike Pavone <pavone@retrodev.com>2013-06-16 17:57:57 -0700
commit4a5e8b3bb9c88a802c2b7744c3766e81b0a02c6c (patch)
tree7f194ccd2765e5d51f6a7fd58606b11446bdced0
parent0a7995ec919cc21fe19a8b8a53512b2c979bba5f (diff)
Add support for logging YM2612 channels to WAVE files
-rw-r--r--Makefile4
-rw-r--r--blastem.c6
-rw-r--r--wave.c41
-rw-r--r--wave.h38
-rw-r--r--ym2612.c34
-rw-r--r--ym2612.h6
6 files changed, 124 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index 3dee3fa..b9d0c08 100644
--- a/Makefile
+++ b/Makefile
@@ -7,8 +7,8 @@ endif
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
diff --git a/blastem.c b/blastem.c
index 531f72d..7262591 100644
--- a/blastem.c
+++ b/blastem.c
@@ -1919,6 +1919,7 @@ int main(int argc, char ** argv)
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 @@ int main(int argc, char ** argv)
return 1;
}
break;
+ case 'y':
+ ym_log = 1;
+ break;
default:
fprintf(stderr, "Unrecognized switch %s\n", argv[i]);
return 1;
@@ -1989,7 +1993,7 @@ int main(int argc, char ** argv)
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());
diff --git a/wave.c b/wave.c
new file mode 100644
index 0000000..cf3d11e
--- /dev/null
+++ b/wave.c
@@ -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;
+}
diff --git a/wave.h b/wave.h
new file mode 100644
index 0000000..4fa2440
--- /dev/null
+++ b/wave.h
@@ -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_
+
diff --git a/ym2612.c b/ym2612.c
index 899230c..df4520a 100644
--- a/ym2612.c
+++ b/ym2612.c
@@ -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 @@ uint16_t round_fixed_point(double value, int dec_bits)
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 @@ void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clo
//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 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
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;
}
diff --git a/ym2612.h b/ym2612.h
index 41182d0..4a146c4 100644
--- a/ym2612.h
+++ b/ym2612.h
@@ -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 @@ typedef struct {
} ym_operator;
typedef struct {
+ FILE * logfile;
uint16_t fnum;
int16_t output;
uint8_t block_fnum_latch;
@@ -70,7 +74,7 @@ typedef struct {
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);