From 6bc64a99026a33da5c9762ebf6d4d7eb7806d39b Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 10 Jun 2016 15:28:50 +0300 Subject: [PATCH] Added stereo support. Correct some PCM register behavior. --- Cocoa/Document.m | 7 ++-- Cocoa/{AudioClient.h => GBAudioClient.h} | 7 ++-- Cocoa/{AudioClient.m => GBAudioClient.m} | 20 +++++------ Core/apu.c | 42 ++++++++++++++---------- Core/apu.h | 11 +++++-- Core/gb.c | 2 +- Core/gb.h | 2 +- SDL/main.c | 4 +-- 8 files changed, 51 insertions(+), 44 deletions(-) rename Cocoa/{AudioClient.h => GBAudioClient.h} (72%) rename Cocoa/{AudioClient.m => GBAudioClient.m} (89%) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index bf56895..2fb1710 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -1,5 +1,5 @@ #include -#include "AudioClient.h" +#include "GBAudioClient.h" #include "Document.h" #include "AppDelegate.h" #include "gb.h" @@ -15,7 +15,7 @@ NSString *lastConsoleInput; } -@property AudioClient *audioClient; +@property GBAudioClient *audioClient; - (void) vblank; - (void) log: (const char *) log withAttributes: (gb_log_attributes) attributes; - (const char *) getDebuggerInput; @@ -103,8 +103,7 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, unsigned char r, unsigned char g, un gb_set_pixels_output(&gb, self.view.pixels); self.view.gb = &gb; gb_set_sample_rate(&gb, 96000); - self.audioClient = [[AudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, SInt16 *buffer) { - //apu_render(&gb, sampleRate, nFrames, buffer); + self.audioClient = [[GBAudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer) { apu_copy_buffer(&gb, buffer, nFrames); } andSampleRate:96000]; [self.audioClient start]; diff --git a/Cocoa/AudioClient.h b/Cocoa/GBAudioClient.h similarity index 72% rename from Cocoa/AudioClient.h rename to Cocoa/GBAudioClient.h index 9c61896..19b0b84 100644 --- a/Cocoa/AudioClient.h +++ b/Cocoa/GBAudioClient.h @@ -1,11 +1,12 @@ #import +#import "apu.h" -@interface AudioClient : NSObject -@property (strong) void (^renderBlock)(UInt32 sampleRate, UInt32 nFrames, SInt16 *buffer); +@interface GBAudioClient : NSObject +@property (strong) void (^renderBlock)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer); @property (readonly) UInt32 rate; @property (readonly, getter=isPlaying) bool playing; -(void) start; -(void) stop; --(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, SInt16 *buffer)) block +-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block andSampleRate:(UInt32) rate; @end diff --git a/Cocoa/AudioClient.m b/Cocoa/GBAudioClient.m similarity index 89% rename from Cocoa/AudioClient.m rename to Cocoa/GBAudioClient.m index e143693..81ddec4 100644 --- a/Cocoa/AudioClient.m +++ b/Cocoa/GBAudioClient.m @@ -1,9 +1,9 @@ #import #import -#import "AudioClient.h" +#import "GBAudioClient.h" static OSStatus render( - AudioClient *self, + GBAudioClient *self, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, @@ -11,23 +11,19 @@ static OSStatus render( AudioBufferList *ioData) { - // This is a mono tone generator so we only need the first buffer - const int channel = 0; - SInt16 *buffer = (SInt16 *)ioData->mBuffers[channel].mData; - + GB_sample_t *buffer = (GB_sample_t *)ioData->mBuffers[0].mData; self.renderBlock(self.rate, inNumberFrames, buffer); - return noErr; } -@implementation AudioClient +@implementation GBAudioClient { AudioComponentInstance audioUnit; } --(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, SInt16 *buffer)) block +-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block andSampleRate:(UInt32) rate { if(!(self = [super init])) @@ -70,10 +66,10 @@ static OSStatus render( streamFormat.mFormatID = kAudioFormatLinearPCM; streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian; - streamFormat.mBytesPerPacket = 2; + streamFormat.mBytesPerPacket = 4; streamFormat.mFramesPerPacket = 1; - streamFormat.mBytesPerFrame = 2; - streamFormat.mChannelsPerFrame = 1; + streamFormat.mBytesPerFrame = 4; + streamFormat.mChannelsPerFrame = 2; streamFormat.mBitsPerChannel = 2 * 8; err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, diff --git a/Core/apu.c b/Core/apu.c index d49f0c1..ad1dd79 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -60,10 +60,10 @@ static int16_t step_lfsr(uint16_t lfsr, bool uses_7_bit) /* General Todo: The APU emulation seems to fail many accuracy tests. It might require a rewrite with these tests in mind. */ -void apu_render(GB_gameboy_t *gb, unsigned long sample_rate, unsigned long n_samples, int16_t *samples) +void apu_render(GB_gameboy_t *gb, unsigned long sample_rate, unsigned long n_samples, GB_sample_t *samples) { for (; n_samples--; samples++) { - *samples = 0; + samples->left = samples->right = 0; if (!gb->apu.global_enable) { continue; } @@ -71,39 +71,45 @@ void apu_render(GB_gameboy_t *gb, unsigned long sample_rate, unsigned long n_sam gb->io_registers[GB_IO_PCM_12] = 0; gb->io_registers[GB_IO_PCM_34] = 0; - // Todo: Stereo support - - if (gb->apu.left_on[0] || gb->apu.right_on[0]) { + { int16_t sample = generate_square(gb->apu.wave_channels[0].phase, gb->apu.wave_channels[0].amplitude, gb->apu.wave_channels[0].duty); - *samples += sample; + if (gb->apu.left_on [0]) samples->left += sample; + if (gb->apu.right_on[0]) samples->right += sample; gb->io_registers[GB_IO_PCM_12] = ((int)sample) * 0xF / MAX_CH_AMP; } - if (gb->apu.left_on[1] || gb->apu.right_on[1]) { + + { int16_t sample = generate_square(gb->apu.wave_channels[1].phase, gb->apu.wave_channels[1].amplitude, gb->apu.wave_channels[1].duty); - *samples += sample; + if (gb->apu.left_on [1]) samples->left += sample; + if (gb->apu.right_on[1]) samples->right += sample; gb->io_registers[GB_IO_PCM_12] |= (((int)sample) * 0xF / MAX_CH_AMP) << 4; } - if (gb->apu.wave_enable && (gb->apu.left_on[2] || gb->apu.right_on[2])) { + + { int16_t sample = generate_wave(gb->apu.wave_channels[2].phase, MAX_CH_AMP, gb->apu.wave_form, gb->apu.wave_shift); - *samples += sample; + if (gb->apu.left_on [2]) samples->left += sample; + if (gb->apu.right_on[2]) samples->right += sample; gb->io_registers[GB_IO_PCM_34] = ((int)sample) * 0xF / MAX_CH_AMP; } - if (gb->apu.left_on[3] || gb->apu.right_on[3]) { + + { int16_t sample = generate_noise(gb->apu.wave_channels[3].phase, gb->apu.wave_channels[3].amplitude, gb->apu.lfsr); - *samples += sample; + if (gb->apu.left_on [3]) samples->left += sample; + if (gb->apu.right_on[3]) samples->right += sample; gb->io_registers[GB_IO_PCM_34] |= (((int)sample) * 0xF / MAX_CH_AMP) << 4; } - *samples *= gb->apu.left_volume; + samples->left *= gb->apu.left_volume; + samples->right *= gb->apu.right_volume; for (unsigned char i = 0; i < 4; i++) { /* Phase */ @@ -168,7 +174,7 @@ void apu_run(GB_gameboy_t *gb) while (gb->audio_copy_in_progress); double ticks_per_sample = (double) CPU_FREQUENCY / gb->sample_rate; while (gb->apu_cycles > ticks_per_sample) { - int16_t sample = 0; + GB_sample_t sample = {0, }; apu_render(gb, gb->sample_rate, 1, &sample); gb->apu_cycles -= ticks_per_sample; if (gb->audio_position == gb->buffer_size) { @@ -186,7 +192,7 @@ void apu_run(GB_gameboy_t *gb) } } -void apu_copy_buffer(GB_gameboy_t *gb, int16_t *dest, unsigned int count) +void apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count) { gb->audio_copy_in_progress = true; @@ -198,11 +204,11 @@ void apu_copy_buffer(GB_gameboy_t *gb, int16_t *dest, unsigned int count) if (count > gb->audio_position) { // gb_log(gb, "Audio underflow: %d\n", count - gb->audio_position); - memset(dest + gb->audio_position, 0, (count - gb->audio_position) * 2); + memset(dest + gb->audio_position, 0, (count - gb->audio_position) * sizeof(*gb->audio_buffer)); count = gb->audio_position; } - memcpy(dest, gb->audio_buffer, count * 2); - memmove(gb->audio_buffer, gb->audio_buffer + count, (gb->audio_position - count) * 2); + memcpy(dest, gb->audio_buffer, count * sizeof(*gb->audio_buffer)); + memmove(gb->audio_buffer, gb->audio_buffer + count, (gb->audio_position - count) * sizeof(*gb->audio_buffer)); gb->audio_position -= count; gb->audio_copy_in_progress = false; diff --git a/Core/apu.h b/Core/apu.h index 003f1ca..a0f7036 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -11,6 +11,12 @@ struct GB_gameboy_s; typedef struct GB_gameboy_s GB_gameboy_t; +typedef struct +{ + int16_t left; + int16_t right; +} GB_sample_t; + /* Not all used on all channels */ typedef struct { @@ -48,12 +54,11 @@ typedef struct bool global_enable; } GB_apu_t; -void apu_render(GB_gameboy_t *gb, unsigned long sample_rate, unsigned long n_samples, int16_t *samples); -void apu_copy_buffer(GB_gameboy_t *gb, int16_t *dest, unsigned int count); +void apu_render(GB_gameboy_t *gb, unsigned long sample_rate, unsigned long n_samples, GB_sample_t *samples); +void apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count); void apu_write(GB_gameboy_t *gb, unsigned char reg, unsigned char value); unsigned char apu_read(GB_gameboy_t *gb, unsigned char reg); void apu_init(GB_gameboy_t *gb); -void apu_copy_buffer(GB_gameboy_t *gb, int16_t *dest, unsigned int count); void apu_run(GB_gameboy_t *gb); #endif /* apu_h */ diff --git a/Core/gb.c b/Core/gb.c index 5cd48f1..8937546 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -445,7 +445,7 @@ void gb_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate) free(gb->audio_buffer); } gb->buffer_size = sample_rate / 25; // 40ms delay - gb->audio_buffer = malloc(gb->buffer_size * 2); + gb->audio_buffer = malloc(gb->buffer_size * sizeof(*gb->audio_buffer)); gb->sample_rate = sample_rate; gb->audio_position = 0; } diff --git a/Core/gb.h b/Core/gb.h index f4214bf..1207e61 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -239,7 +239,7 @@ typedef struct GB_gameboy_s{ /* APU */ GB_apu_t apu; - int16_t *audio_buffer; + GB_sample_t *audio_buffer; unsigned int buffer_size; unsigned int sample_rate; unsigned int audio_position; diff --git a/SDL/main.c b/SDL/main.c index 2d2a604..0dbdb71 100644 --- a/SDL/main.c +++ b/SDL/main.c @@ -136,7 +136,7 @@ static void debugger_interrupt(int ignore) static void audio_callback(void *gb, Uint8 *stream, int len) { - apu_copy_buffer(gb, (int16_t *) stream, len / sizeof(int16_t)); + apu_copy_buffer(gb, (GB_sample_t *) stream, len / sizeof(GB_sample_t)); } #ifdef __APPLE__ @@ -206,7 +206,7 @@ usage: SDL_memset(&want, 0, sizeof(want)); want.freq = 96000; want.format = AUDIO_S16SYS; - want.channels = 1; + want.channels = 2; want.samples = 512; want.callback = audio_callback; want.userdata = &gb;