From cb498c415c1e92e7e16027ae07d3558f9ba87537 Mon Sep 17 00:00:00 2001 From: tudnai Date: Sat, 27 Jun 2020 16:54:02 -0700 Subject: [PATCH] Fixed sound issues and resource leaks related to that --- A2Mac.xcodeproj/project.pbxproj | 6 -- src/cpu/6502.h | 4 +- src/dev/audio/speaker.c | 112 +++++++++++++++++++------------- src/dev/disk/woz.c | 17 ++++- src/dev/disk/woz.h | 42 ++++++++++-- src/dev/mem/mmio.h | 15 +++-- 6 files changed, 132 insertions(+), 64 deletions(-) diff --git a/A2Mac.xcodeproj/project.pbxproj b/A2Mac.xcodeproj/project.pbxproj index df383ba..712c942 100644 --- a/A2Mac.xcodeproj/project.pbxproj +++ b/A2Mac.xcodeproj/project.pbxproj @@ -126,9 +126,6 @@ 32F8A8A724A3A98D00EE6735 /* XPS Diagnostic IIe.woz in Copy Disk Images */ = {isa = PBXBuildFile; fileRef = 32440B8B247F86D5000F9DA1 /* XPS Diagnostic IIe.woz */; }; 32F8A8A824A3A98D00EE6735 /* LOCKSMITH_V7_REV_B.woz in Copy Disk Images */ = {isa = PBXBuildFile; fileRef = 32440BA0247F9F99000F9DA1 /* LOCKSMITH_V7_REV_B.woz */; }; 32F8A8A924A3A98D00EE6735 /* locksmith_v6.0.woz in Copy Disk Images */ = {isa = PBXBuildFile; fileRef = 32F2C144249218A400FDC61B /* locksmith_v6.0.woz */; }; - 32F8A8AB24A3AAD300EE6735 /* diskmotor.sfx in Copy SFX Files */ = {isa = PBXBuildFile; fileRef = 323D043D248F70930086A901 /* diskmotor.sfx */; }; - 32F8A8AC24A3AAD300EE6735 /* diskarm.sfx in Copy SFX Files */ = {isa = PBXBuildFile; fileRef = 323D0441248F711F0086A901 /* diskarm.sfx */; }; - 32F8A8AD24A3AAD300EE6735 /* diskioerr.sfx in Copy SFX Files */ = {isa = PBXBuildFile; fileRef = 323D043F248F70A10086A901 /* diskioerr.sfx */; }; 32F8A8AF24A3AB5900EE6735 /* apple-rainbow.png in Copy Image Files */ = {isa = PBXBuildFile; fileRef = 32E21BE62491BF8B006C0C72 /* apple-rainbow.png */; }; 32F8A8B024A3AB5D00EE6735 /* scanlines.png in Copy Image Files */ = {isa = PBXBuildFile; fileRef = 323D04452490BA1E0086A901 /* scanlines.png */; }; /* End PBXBuildFile section */ @@ -247,9 +244,6 @@ 32E3126724A7194900E61891 /* disk_ii_arm.sfx in Copy SFX Files */, 3296223A24A710E5002DEB78 /* disk_ii_io_error.sfx in Copy SFX Files */, 3296223624A709EE002DEB78 /* disk_ii_motor_w_floppy.sfx in Copy SFX Files */, - 32F8A8AB24A3AAD300EE6735 /* diskmotor.sfx in Copy SFX Files */, - 32F8A8AC24A3AAD300EE6735 /* diskarm.sfx in Copy SFX Files */, - 32F8A8AD24A3AAD300EE6735 /* diskioerr.sfx in Copy SFX Files */, ); name = "Copy SFX Files"; runOnlyForDeploymentPostprocessing = 0; diff --git a/src/cpu/6502.h b/src/cpu/6502.h index d6584e9..2c04ee7 100644 --- a/src/cpu/6502.h +++ b/src/cpu/6502.h @@ -181,8 +181,8 @@ extern double mhz; #define DEF_VIDEO_DIV 1U #define DEF_SPKR_DIV 1U -#define GAME_FPS 300U -#define GAME_VIDEO_DIV 5U // 300 / 5 = 60 FPS +#define GAME_FPS 600U +#define GAME_VIDEO_DIV 10U // 600 / 10 = 60 FPS extern unsigned int video_fps_divider; extern unsigned int fps; diff --git a/src/dev/audio/speaker.c b/src/dev/audio/speaker.c index 74f901b..58227c4 100644 --- a/src/dev/audio/speaker.c +++ b/src/dev/audio/speaker.c @@ -58,6 +58,7 @@ int spkr_level = SPKR_LEVEL_ZERO; #define BUFFER_COUNT 256 #define SOURCES_COUNT 4 +#define SPKR_CHANNELS 2 ALuint spkr_src [SOURCES_COUNT] = { 0, 0, 0, 0 }; @@ -77,9 +78,9 @@ const unsigned spkr_sample_rate = 44100; const unsigned sfx_sample_rate = 22050; // original sample rate //const unsigned sfx_sample_rate = 26000; // bit higher pitch int spkr_extra_buf = 0; // 800 / spkr_fps; -const unsigned spkr_buf_alloc_size = spkr_seconds * spkr_sample_rate * 2 / DEFAULT_FPS; +const unsigned spkr_buf_alloc_size = spkr_seconds * spkr_sample_rate * SPKR_CHANNELS / DEFAULT_FPS; unsigned spkr_buf_size = spkr_buf_alloc_size; -int16_t spkr_samples [ spkr_buf_alloc_size * DEFAULT_FPS * BUFFER_COUNT * 2]; // stereo +int16_t spkr_samples [ spkr_buf_alloc_size * DEFAULT_FPS * BUFFER_COUNT * SPKR_CHANNELS]; // stereo unsigned spkr_sample_idx = 0; unsigned spkr_play_timeout = 8; // increase to 32 for 240 fps, normally 8 for 30 fps @@ -95,6 +96,8 @@ int diskarm_sfx_len = 0; uint8_t * diskioerr_sfx = NULL; int diskioerr_sfx_len = 0; +ALint freeBuffers = BUFFER_COUNT; + static int load_sfx( const char * bundlePath, const char * filename, uint8_t ** buf ) { char fullPath[256]; @@ -237,11 +240,48 @@ void spkr_init() { freeBuffers = BUFFER_COUNT; } + +int spkr_unqueue( ALuint src ) { + ALint processed = 0; + + alGetSourcei ( src, AL_BUFFERS_PROCESSED, &processed ); + al_check_error(); + printf("%s alGetSourcei(%d)\n", __FUNCTION__, src); + + if ( processed ) { + alSourceUnqueueBuffers( src, processed, &spkr_buffers[freeBuffers]); + al_check_error(); + } + + return processed; +} + +void spkr_unqueueAll() { + for ( int i = 0; i < SOURCES_COUNT; i++ ) { + spkr_unqueue( spkr_src[i] ); + } +} + + // Dealloc OpenAL void spkr_exit() { if ( spkr_src[SPKR_SRC_GAME_SFX] ) { - alSourceStop( spkr_src[SPKR_SRC_GAME_SFX] ); + spkr_stopAll(); + spkr_unqueueAll(); + // delete buffers + alDeleteBuffers(BUFFER_COUNT, spkr_buffers); + al_check_error(); + alDeleteBuffers(1, &spkr_disk_motor_buf); + al_check_error(); + alDeleteBuffers(1, &spkr_disk_arm_buf); + al_check_error(); + + // delete sound source and play buffer + alDeleteSources(SOURCES_COUNT, spkr_src); + al_check_error(); + + ALCdevice *dev = NULL; ALCcontext *ctx = NULL; ctx = alcGetCurrentContext(); @@ -280,7 +320,7 @@ void spkr_toggle() { // push a click into the speaker buffer // (we will play the entire buffer at the end of the frame) - spkr_sample_idx = ( (spkr_clk + clkfrm) / ( MHZ(default_MHz_6502) / spkr_sample_rate)) * 2; + spkr_sample_idx = ( (spkr_clk + clkfrm) / ( MHZ(default_MHz_6502) / spkr_sample_rate)) * SPKR_CHANNELS; if ( spkr_state ) { // down edge @@ -290,7 +330,7 @@ void spkr_toggle() { while ( fadeLevel > +1 ) { spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_MIN + fadeLevel; - spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_MIN + fadeLevel; + spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_MIN + fadeLevel; // stereo // how smooth we want the speeker to decay, so we will hear no pops and crackles // 0.9 gives you a kind of saw wave at 1KHz (beep) @@ -307,7 +347,7 @@ void spkr_toggle() { while ( fadeLevel < -1 ) { spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_MAX + fadeLevel; - spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_MAX + fadeLevel; + spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_MAX + fadeLevel; // stereo // how smooth we want the speeker to decay, so we will hear no pops and crackles // 0.9 gives you a kind of saw wave at 1KHz (beep) @@ -328,25 +368,8 @@ void spkr_toggle() { } - -ALint freeBuffers = BUFFER_COUNT; -//ALuint alBuffers[BUFFER_COUNT]; - -int spkr_unqueue( ALuint src ) { - ALint processed = 0; - - alGetSourcei ( src, AL_BUFFERS_PROCESSED, &processed ); - al_check_error(); - - if ( processed ) { - alSourceUnqueueBuffers( src, processed, &spkr_buffers[freeBuffers]); - al_check_error(); - } - - return processed; -} - -int playDelay = 4; +#define SPKR_PLAY_DELAY 2 +int playDelay = SPKR_PLAY_DELAY; void spkr_update() { @@ -354,12 +377,7 @@ void spkr_update() { spkr_frame_cntr = 0; if ( spkr_play_time ) { - // free up unused buffers - freeBuffers += spkr_unqueue( spkr_src[SPKR_SRC_GAME_SFX] ); - freeBuffers = clamp( 1, freeBuffers, BUFFER_COUNT ); - if ( freeBuffers ) { - // in Game Mode do not fade out and stop playing if ( ( cpuMode_game != cpuMode ) && ( --spkr_play_time == 0 ) ) { float fadeLevel = spkr_level - SPKR_LEVEL_ZERO; @@ -369,7 +387,7 @@ void spkr_update() { while ( ( fadeLevel < -1 ) || ( fadeLevel > 1 ) ) { spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_ZERO + fadeLevel; - spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_ZERO + fadeLevel; + spkr_samples[ spkr_sample_idx++ ] = SPKR_LEVEL_ZERO + fadeLevel; // stereo // how smooth we want the speeker to decay, so we will hear no pops and crackles fadeLevel *= 0.999; @@ -402,7 +420,7 @@ void spkr_update() { case AL_PAUSED: if ( --playDelay <= 0 ) { alSourcePlay(spkr_src[SPKR_SRC_GAME_SFX]); - playDelay = 4; + playDelay = SPKR_PLAY_DELAY; } break; @@ -429,6 +447,10 @@ void spkr_update() { } spkr_clk = 0; + + // free up unused buffers + freeBuffers += spkr_unqueue( spkr_src[SPKR_SRC_GAME_SFX] ); + freeBuffers = clamp( 1, freeBuffers, BUFFER_COUNT ); } else { spkr_clk += clkfrm; @@ -438,15 +460,16 @@ void spkr_update() { void spkr_playqueue_sfx( ALuint src, uint8_t * sfx, int len ) { -// freeBuffers += spkr_unqueue( src ); -// freeBuffers = clamp( 1, freeBuffers, BUFFER_COUNT ); - +// printf("%s freeBuffers:%d\n", __FUNCTION__, freeBuffers); + if ( freeBuffers ) { ALenum queued; alGetSourcei( src, AL_BUFFERS_QUEUED, &queued ); // printf("Q:%u\n", queued); - if ( queued < 32 ) { +// printf("%s queued:%d\n", __FUNCTION__, queued); + + if ( queued < 16 ) { freeBuffers--; alBufferData( spkr_buffers[freeBuffers], AL_FORMAT_STEREO16, sfx, len, sfx_sample_rate ); al_check_error(); @@ -472,6 +495,9 @@ void spkr_playqueue_sfx( ALuint src, uint8_t * sfx, int len ) { void spkr_play_sfx( ALuint src, uint8_t * sfx, int len ) { + +// printf("%s freeBuffers:%d\n", __FUNCTION__, freeBuffers); + if ( freeBuffers ) { ALenum state; alGetSourcei( src, AL_SOURCE_STATE, &state ); @@ -509,13 +535,16 @@ void spkr_stop_sfx( ALuint src ) { case AL_PAUSED: case AL_PLAYING: alSourceStop( src ); - freeBuffers += spkr_unqueue( src ); - freeBuffers = clamp( 1, freeBuffers, BUFFER_COUNT ); break; default: break; } + + // free up unused buffers + freeBuffers += spkr_unqueue( src ); + freeBuffers = clamp( 1, freeBuffers, BUFFER_COUNT ); + } @@ -577,12 +606,7 @@ void spkr_update_disk_sfx() { update_disk_sfx( &spkr_play_disk_motor_time, spkr_src[SPKR_SRC_DISK_MOTOR_SFX] ); update_disk_sfx( &spkr_play_disk_arm_time, spkr_src[SPKR_SRC_DISK_ARM_SFX] ); + update_disk_sfx( &spkr_play_disk_ioerr_time, spkr_src[SPKR_SRC_DISK_IOERR_SFX] ); - // we do not need to stop playing, - // however, counter needed to eliminate arm movement noise while in io error - if ( spkr_play_disk_ioerr_time ) { - spkr_play_disk_ioerr_time--; - } - } diff --git a/src/dev/disk/woz.c b/src/dev/disk/woz.c index eb4d4d7..4f206af 100644 --- a/src/dev/disk/woz.c +++ b/src/dev/disk/woz.c @@ -517,7 +517,22 @@ int woz_loadFile( const char * filename ) { fread( woz_file_buffer, woz_file_size, 1, f); fclose(f); - if ( woz_header->magic != WOZ1_MAGIC ) { + + // if this really a WOZ file? + switch ( woz_header->magic ) { + case WOZ1_MAGIC: + break; + + case WOZ2_MAGIC: + break; + + default: + woz_free_buffer(); + return WOZ_ERR_NOT_WOZ_FILE; + } + + // check if bits and line ends not been altered by data transmission protocols + if ( woz_header->bit_correctness != 0x0A0D0AFF ) { woz_free_buffer(); return WOZ_ERR_NOT_WOZ_FILE; } diff --git a/src/dev/disk/woz.h b/src/dev/disk/woz.h index 0585b54..a1e69a1 100644 --- a/src/dev/disk/woz.h +++ b/src/dev/disk/woz.h @@ -24,6 +24,7 @@ #define WOZ_TMAP_CHUNK_ID 0x50414D54 #define WOZ_TRKS_CHUNK_ID 0x534B5254 #define WOZ_META_CHUNK_ID 0x4154454D +#define WOZ_WRIT_CHUNK_ID 0x54495257 #define WOZ_ERR_OK 0 #define WOZ_ERR_FILE_NOT_FOUND -1 @@ -37,8 +38,13 @@ typedef struct woz_header_s { uint32_t magic; - uint8_t no7; - char lineend [3]; + union { + struct { + uint8_t no7; + char lineend [3]; + }; + uint32_t bit_correctness; + }; uint32_t crc; } woz_header_t; @@ -49,12 +55,39 @@ typedef struct woz_chunk_header_s { // chunk data only typedef struct woz_info_s { - uint8_t version; + uint8_t version; // Version number of the INFO chunk. + // WOZ1 version is 1 + // WOZ2 version is 2 uint8_t disk_type; // 1 = 5.25, 2 = 3.5 - uint8_t is_write_protected; + uint8_t is_write_protected; // 1 = Floppy is write protected uint8_t sync; // 1 = Cross track sync uint8_t cleaned; // 1 = MC3470 fake bits removed char creator [32]; // Name of software created this file (UTF-8, 0x20 padded, NOT zero terminated) + uint8_t disk_sides; // The number of disk sides contained within this image. A 5.25 disk will always be 1. A 3.5 disk can be 1 or 2. + uint8_t boot_sec_format; // The type of boot sector found on this disk. This is only for 5.25 disks. 3.5 disks should just set this to 0. + // 0 = Unknown + // 1 = Contains boot sector for 16-sector + // 2 = Contains boot sector for 13-sector + // 3 = Contains boot sectors for both + uint8_t opt_bit_timing; // The ideal rate that bits should be delivered to the disk controller card. + // This value is in 125 nanosecond increments, so 8 is equal to 1 microsecond. + // And a standard bit rate for a 5.25 disk would be 32 (4µs). + uint16_t compatible_hw; // Bit field with a 1 indicating known compatibility. Multiple compatibility flags are possible. + // A 0 value represents that the compatible hardware list is unknown. + // 0x0001 = Apple ][ + // 0x0002 = Apple ][ Plus + // 0x0004 = Apple //e (unenhanced) + // 0x0008 = Apple //c + // 0x0010 = Apple //e Enhanced + // 0x0020 = Apple IIgs + // 0x0040 = Apple //c Plus + // 0x0080 = Apple /// + // 0x0100 = Apple /// Plus + uint16_t required_ram; // Minimum RAM size needed for this software. This value is in K (1024 bytes). + // If the minimum size is unknown, this value should be set to 0. So, a requirement + // of 64K would be indicated by the value 64 here. + uint16_t largest_track; // The number of blocks (512 bytes) used by the largest track. + // Can be used to allocate a buffer with a size safe for all tracks. } woz_info_t; // chunk data only @@ -108,6 +141,7 @@ typedef struct woz_flags_s { extern WOZread_t WOZread; +extern WOZread_t WOZwrite; extern uint8_t WOZlatch; extern char woz_filename[MAXFILENAME]; extern woz_flags_t woz_flags; diff --git a/src/dev/mem/mmio.h b/src/dev/mem/mmio.h index cfa4340..46f84e0 100644 --- a/src/dev/mem/mmio.h +++ b/src/dev/mem/mmio.h @@ -741,34 +741,34 @@ INLINE uint8_t ioRead( uint16_t addr ) { case (uint8_t)io_DISK_PHASE2_OFF + SLOT6: case (uint8_t)io_DISK_PHASE3_OFF + SLOT6: disk_phase_off( (addr - io_DISK_PHASE0_OFF - SLOT6) / 2 ); - return 0; + return disk_read(); case (uint8_t)io_DISK_PHASE0_ON + SLOT6: case (uint8_t)io_DISK_PHASE1_ON + SLOT6: case (uint8_t)io_DISK_PHASE2_ON + SLOT6: case (uint8_t)io_DISK_PHASE3_ON + SLOT6: disk_phase_on( (addr - io_DISK_PHASE0_ON - SLOT6) / 2 ); - return 0; + return disk_read(); case (uint8_t)io_DISK_POWER_OFF + SLOT6: dbgPrintf2("io_DISK_POWER_OFF (S%u)\n", 6); disk_motor_off(); - return 0; + return disk_read(); case (uint8_t)io_DISK_POWER_ON + SLOT6: dbgPrintf2("io_DISK_POWER_ON (S%u)\n", 6); disk_motor_on(); - return 0; + return disk_read(); case (uint8_t)io_DISK_SELECT_1 + SLOT6: dbgPrintf2("io_DISK_SELECT_1 (S%u)\n", 6); disk.drive = 0; - return 0; + return disk_read(); case (uint8_t)io_DISK_SELECT_2 + SLOT6: dbgPrintf2("io_DISK_SELECT_2 (S%u)\n", 6); disk.drive = 1; - return 0; + return disk_read(); case (uint8_t)io_DISK_READ + SLOT6: if ( writeState ) { @@ -784,6 +784,7 @@ INLINE uint8_t ioRead( uint16_t addr ) { case (uint8_t)io_DISK_WRITE + SLOT6: dbgPrintf2("io_DISK_WRITE (S%u)\n", 6); // Apple2_64K_RAM[io_DISK_CLEAR + SLOT6] |= 1 << 7; // mark disk as write protected + WOZwrite.latch = WOZread.latch = 0; Apple2_64K_RAM[io_DISK_CLEAR + SLOT6] &= ~(1 << 7); // mark disk as write enabled return Apple2_64K_RAM[io_DISK_WRITE + SLOT6]; @@ -793,7 +794,7 @@ INLINE uint8_t ioRead( uint16_t addr ) { case (uint8_t)io_DISK_SHIFT + SLOT6: dbgPrintf2("io_DISK_SHIFT (S%u)\n", 6); - return 0; + return disk_read();