From cf2325e6faa2d72edc65277439cd9a13605e1ea9 Mon Sep 17 00:00:00 2001 From: tudnai Date: Sat, 9 May 2020 23:40:37 -0700 Subject: [PATCH] Speaker - First implementation --- A2Mac.xcodeproj/project.pbxproj | 16 +++++ src/cpu/6502.c | 9 +++ src/dev/audio/speaker.c | 115 ++++++++++++++++++++++++++++++++ src/dev/audio/speaker.h | 23 +++++++ src/dev/mem/mmio.h | 9 ++- 5 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 src/dev/audio/speaker.c create mode 100644 src/dev/audio/speaker.h diff --git a/A2Mac.xcodeproj/project.pbxproj b/A2Mac.xcodeproj/project.pbxproj index 40fd6da..31857ac 100644 --- a/A2Mac.xcodeproj/project.pbxproj +++ b/A2Mac.xcodeproj/project.pbxproj @@ -70,6 +70,8 @@ 3262F37723E169F8008BDB95 /* spk_up.wav in Resources */ = {isa = PBXBuildFile; fileRef = 3262F37523E169F8008BDB95 /* spk_up.wav */; }; 32A9F72C24668D26004902A1 /* apple-rainbow.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 32A9F72B24668D26004902A1 /* apple-rainbow.jpg */; }; 32A9F72D24668D26004902A1 /* apple-rainbow.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 32A9F72B24668D26004902A1 /* apple-rainbow.jpg */; }; + 32A9F74A2467B60B004902A1 /* speaker.c in Sources */ = {isa = PBXBuildFile; fileRef = 32A9F7492467B60B004902A1 /* speaker.c */; }; + 32A9F74B2467B60B004902A1 /* speaker.c in Sources */ = {isa = PBXBuildFile; fileRef = 32A9F7492467B60B004902A1 /* speaker.c */; }; 32BFFB5B22EACC630003B53F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BFFB5A22EACC630003B53F /* AppDelegate.swift */; }; 32BFFB5D22EACC630003B53F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BFFB5C22EACC630003B53F /* ViewController.swift */; }; 32BFFB5F22EACC660003B53F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32BFFB5E22EACC660003B53F /* Assets.xcassets */; }; @@ -189,6 +191,8 @@ 326426112328ADF4008B615F /* Apple_II_ROM.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = Apple_II_ROM.s; sourceTree = ""; }; 326ED2EE232D7A0000A41337 /* 6502_functional_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = 6502_functional_test.bin; sourceTree = SOURCE_ROOT; }; 32A9F72B24668D26004902A1 /* apple-rainbow.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "apple-rainbow.jpg"; sourceTree = ""; }; + 32A9F7482467B60B004902A1 /* speaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = speaker.h; sourceTree = ""; }; + 32A9F7492467B60B004902A1 /* speaker.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = speaker.c; sourceTree = ""; }; 32B18435233F10BC00DBB4AB /* Shaders.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = ""; }; 32B18438233FAB3900DBB4AB /* verticies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = verticies.swift; sourceTree = ""; }; 32BFFB5722EACC630003B53F /* A2Mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = A2Mac.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -310,6 +314,7 @@ 325EB63223F8862A00C6B4A4 /* dev */ = { isa = PBXGroup; children = ( + 32A9F7472467B5DF004902A1 /* audio */, 325EB63B23FA57C000C6B4A4 /* mem */, 325EB63323F8863100C6B4A4 /* disk */, ); @@ -419,6 +424,15 @@ path = Resources; sourceTree = ""; }; + 32A9F7472467B5DF004902A1 /* audio */ = { + isa = PBXGroup; + children = ( + 32A9F7492467B60B004902A1 /* speaker.c */, + 32A9F7482467B60B004902A1 /* speaker.h */, + ); + path = audio; + sourceTree = ""; + }; 32BFFB4E22EACC630003B53F = { isa = PBXGroup; children = ( @@ -816,6 +830,7 @@ buildActionMask = 2147483647; files = ( 325EB67623FBC44400C6B4A4 /* common.c in Sources */, + 32A9F74B2467B60B004902A1 /* speaker.c in Sources */, 325EB67523FBC43E00C6B4A4 /* 6502.c in Sources */, 325EB67823FBC45300C6B4A4 /* disk.c in Sources */, 325EB64723FBBACF00C6B4A4 /* ViewController.swift in Sources */, @@ -849,6 +864,7 @@ 32439F8722ECD8AD0077AAE0 /* 6502.c in Sources */, 325EB63623F8F78300C6B4A4 /* disk.c in Sources */, 325EB63923F9E48100C6B4A4 /* common.c in Sources */, + 32A9F74A2467B60B004902A1 /* speaker.c in Sources */, 32BFFB5D22EACC630003B53F /* ViewController.swift in Sources */, 325EB69323FE6C6200C6B4A4 /* HiRes.swift in Sources */, 32C4532E233345430000EBA1 /* MonitorView.swift in Sources */, diff --git a/src/cpu/6502.c b/src/cpu/6502.c index cbde300..47c17fe 100644 --- a/src/cpu/6502.c +++ b/src/cpu/6502.c @@ -15,6 +15,7 @@ #include #include "6502.h" #include "../dev/disk/woz.h" +#include "speaker.h" void ViewController_spk_up_play(void); @@ -712,6 +713,10 @@ void softReset() { void m6502_Run() { + // clear speaker buffer, so we can fill it up by new data + memset(spkr_samples, 127, spkr_buf_size); + + // init time //#ifdef CLK_WAIT // unsigned long long elpased = (unsigned long long)-1LL; @@ -772,6 +777,8 @@ void m6502_Run() { } } + spkr_play(); + } void read_rom( const char * bundlePath, const char * filename, uint8_t * rom, const uint16_t addr ) { @@ -845,6 +852,8 @@ void m6502_ColdReset( const char * bundlePath, const char * romFileName ) { inst_cnt = 0; mhz = (double)MHz_6502 / M; + spkr_init(); + unsigned long long saved_frm_set = clk_6502_per_frm_set; clk_6502_per_frm = clk_6502_per_frm_max = diff --git a/src/dev/audio/speaker.c b/src/dev/audio/speaker.c new file mode 100644 index 0000000..a78b434 --- /dev/null +++ b/src/dev/audio/speaker.c @@ -0,0 +1,115 @@ +// +// speaker.c +// A2Mac +// +// Created by Tamas Rudnai on 5/9/20. +// Copyright © 2020 GameAlloy. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include + +#include "speaker.h" + + +#define CASE_RETURN(err) case (err): return #err +const char* al_err_str(ALenum err) { + switch(err) { + CASE_RETURN(AL_NO_ERROR); + CASE_RETURN(AL_INVALID_NAME); + CASE_RETURN(AL_INVALID_ENUM); + CASE_RETURN(AL_INVALID_VALUE); + CASE_RETURN(AL_INVALID_OPERATION); + CASE_RETURN(AL_OUT_OF_MEMORY); + } + return "unknown"; +} +#undef CASE_RETURN + +#define __al_check_error(file,line) \ + do { \ + ALenum err = alGetError(); \ + for(; err != AL_NO_ERROR; err = alGetError()) { \ + printf( "AL Error %s at %s:%d\n", al_err_str(err), file, line ); \ + } \ + } while(0) + +#define al_check_error() \ + __al_check_error(__FILE__, __LINE__) + + +ALCdevice *dev = NULL; +ALCcontext *ctx = NULL; +ALuint spkr_buf = 0; +ALuint spkr_src = 0; + +const int spkr_seconds = 1; +const unsigned spkr_sample_rate = 44100; +const unsigned spkr_buf_size = spkr_seconds * spkr_sample_rate; +char spkr_samples [ spkr_buf_size ]; + +/* initialize OpenAL */ +void spkr_init() { + + const char *defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); + printf( "Default device: %s\n", defname ); + + dev = alcOpenDevice(defname); + ctx = alcCreateContext(dev, NULL); + alcMakeContextCurrent(ctx); + + // Fill buffer with zeros + memset( spkr_samples, 127, spkr_buf_size ); + +} + +/* Dealloc OpenAL */ +void spkr_exit() { + ALCdevice *dev = NULL; + ALCcontext *ctx = NULL; + ctx = alcGetCurrentContext(); + dev = alcGetContextsDevice(ctx); + + alcMakeContextCurrent(NULL); + alcDestroyContext(ctx); + alcCloseDevice(dev); + + al_check_error(); +} + +void spkr_play() { + if ( spkr_src ) { + alSourceStop(spkr_src); + al_check_error(); + alSourcei(spkr_src, AL_BUFFER, 0); + al_check_error(); + } + + if ( spkr_buf ) { + alDeleteBuffers(1, &spkr_buf); + al_check_error(); + } + + /* Create buffer to store samples */ + spkr_buf = 0; + alGenBuffers(1, &spkr_buf); + al_check_error(); + + + /* Download buffer to OpenAL */ + alBufferData(spkr_buf, AL_FORMAT_MONO8, spkr_samples, spkr_buf_size, spkr_sample_rate); + al_check_error(); + + /* Set-up sound source and play buffer */ + spkr_src = 0; + alGenSources(1, &spkr_src); + alSourcei(spkr_src, AL_BUFFER, spkr_buf); + alSourcei(spkr_src, AL_LOOPING, 0); + + alSourcePlay(spkr_src); +} diff --git a/src/dev/audio/speaker.h b/src/dev/audio/speaker.h new file mode 100644 index 0000000..c7ef43a --- /dev/null +++ b/src/dev/audio/speaker.h @@ -0,0 +1,23 @@ +// +// speaker.h +// A2Mac +// +// Created by Tamas Rudnai on 5/9/20. +// Copyright © 2020 GameAlloy. All rights reserved. +// + +#ifndef speaker_h +#define speaker_h + +#include + + +extern void spkr_init(void); +extern void spkr_exit(void); +extern void spkr_play(void); + +extern const unsigned spkr_buf_size; +extern char spkr_samples []; + + +#endif /* speaker_h */ diff --git a/src/dev/mem/mmio.h b/src/dev/mem/mmio.h index 3800042..b4fab5e 100644 --- a/src/dev/mem/mmio.h +++ b/src/dev/mem/mmio.h @@ -13,6 +13,7 @@ #include "6502.h" #include "disk.h" #include "woz.h" +#include "speaker.h" typedef union address16_u { @@ -418,9 +419,14 @@ INLINE uint8_t ioRead( uint16_t addr ) { Apple2_64K_RAM[io_KBD] &= 0x7F; return Apple2_64K_RAM[io_KBDSTRB]; - case (uint8_t)io_SPKR: + case (uint8_t)io_SPKR: { // TODO: This is very slow! // printf("io_KBDSTRB\n"); + + // push a click into the speaker buffer + // (we will play the entire buffer at the end of the frame) + int sample_idx = clkfrm / 22; + spkr_samples[sample_idx] = 0; //ViewController_spk_up_play(); @@ -434,6 +440,7 @@ INLINE uint8_t ioRead( uint16_t addr ) { // videoMode.col80 = 1; // break; // + } case (uint8_t)io_VID_RDTEXT: return videoMode.text << 7;