From 719e210de6ea65382f0d96ab80688dff60942aca Mon Sep 17 00:00:00 2001 From: rogerman Date: Wed, 4 Jan 2012 21:10:19 +0000 Subject: [PATCH] Cocoa Port: - Major rewrite of the OS X Core Audio backend code! This brings performance improvements, bug fixes, and general code cleanup. - Remove all Objective-C code from sndOSX.mm, and change into a pure C++ file. - Fixed audio noise issues that intermittently occurred while the emulation was running. This most often occurred after a ROM has been loaded. - Fix possible crash on application exit. --- desmume/src/cocoa/cocoa_globals.h | 398 +++++++++++++++++ desmume/src/cocoa/main.mm | 7 + desmume/src/cocoa/sndOSX.cpp | 717 ++++++++++++++++++++++++++++++ desmume/src/cocoa/sndOSX.mm | 483 -------------------- 4 files changed, 1122 insertions(+), 483 deletions(-) create mode 100644 desmume/src/cocoa/cocoa_globals.h create mode 100644 desmume/src/cocoa/sndOSX.cpp delete mode 100644 desmume/src/cocoa/sndOSX.mm diff --git a/desmume/src/cocoa/cocoa_globals.h b/desmume/src/cocoa/cocoa_globals.h new file mode 100644 index 000000000..361dbd472 --- /dev/null +++ b/desmume/src/cocoa/cocoa_globals.h @@ -0,0 +1,398 @@ +/* + Copyright (C) 2011 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#define STRING_DESMUME_WEBSITE "http://www.desmume.org" +#define STRING_DESMUME_FORUM_SITE "http://forums.desmume.org/index.php" +#define STRING_DESMUME_BUG_SITE "http://sourceforge.net/tracker/?group_id=164579&atid=832291" + +#define NSSTRING_TITLE_OPEN_ROM_PANEL NSLocalizedString(@"Open ROM", nil) +#define NSSTRING_TITLE_OPEN_STATE_FILE_PANEL NSLocalizedString(@"Open State File", nil) +#define NSSTRING_TITLE_SAVE_STATE_FILE_PANEL NSLocalizedString(@"Save State File", nil) +#define NSSTRING_TITLE_IMPORT_BACKUP_MEMORY_PANEL NSLocalizedString(@"Import Backup Memory", nil) +#define NSSTRING_TITLE_EXPORT_BACKUP_MEMORY_PANEL NSLocalizedString(@"Export Backup Memory", nil) +#define NSSTRING_TITLE_SELECT_ROM_PANEL NSLocalizedString(@"Select ROM", nil) +#define NSSTRING_TITLE_SELECT_ADVANSCENE_DB_PANEL NSLocalizedString(@"Select ADVANsCEne Database", nil) +#define NSSTRING_TITLE_SELECT_R4_CHEAT_DB_PANEL NSLocalizedString(@"Select R4 Cheat Database", nil) +#define NSSTRING_TITLE_SELECT_ARM7_IMAGE_PANEL NSLocalizedString(@"Select ARM7 BIOS Image", nil) +#define NSSTRING_TITLE_SELECT_ARM9_IMAGE_PANEL NSLocalizedString(@"Select ARM9 BIOS Image", nil) +#define NSSTRING_TITLE_SELECT_FIRMWARE_IMAGE_PANEL NSLocalizedString(@"Select Firmware Image", nil) + +#define NSSTRING_TITLE_EXECUTE_CONTROL NSLocalizedString(@"Execute", nil) +#define NSSTRING_TITLE_PAUSE_CONTROL NSLocalizedString(@"Pause", nil) +#define NSSTRING_TITLE_DISABLE_SPEED_LIMIT NSLocalizedString(@"Disable Speed Limit", nil) +#define NSSTRING_TITLE_ENABLE_SPEED_LIMIT NSLocalizedString(@"Enable Speed Limit", nil) +#define NSSTRING_TITLE_DISABLE_AUTO_FRAME_SKIP NSLocalizedString(@"Disable Auto Frame Skip", nil) +#define NSSTRING_TITLE_ENABLE_AUTO_FRAME_SKIP NSLocalizedString(@"Enable Auto Frame Skip", nil) +#define NSSTRING_TITLE_DISABLE_CHEATS NSLocalizedString(@"Disable Cheats", nil) +#define NSSTRING_TITLE_ENABLE_CHEATS NSLocalizedString(@"Enable Cheats", nil) +#define NSSTRING_TITLE_DISABLE_HUD NSLocalizedString(@"Disable HUD", nil) +#define NSSTRING_TITLE_ENABLE_HUD NSLocalizedString(@"Enable HUD", nil) +#define NSSTRING_TITLE_SPEED_1X NSLocalizedString(@"Speed 1x", nil) +#define NSSTRING_TITLE_SPEED_2X NSLocalizedString(@"Speed 2x", nil) +#define NSSTRING_TITLE_SLOT_NUMBER NSLocalizedString(@"Slot %d", nil) + +#define NSSTRING_ALERT_CRITICAL_FILE_MISSING_PRI NSLocalizedString(@"A critical file is missing. DeSmuME will now quit.", nil) +#define NSSTRING_ALERT_CRITICAL_FILE_MISSING_SEC NSLocalizedString(@"The file \"DefaultUserPrefs.plist\" is missing. Please reinstall DeSmuME.", nil) + +#define NSSTRING_STATUS_READY NSLocalizedString(@"Ready.", nil) +#define NSSTRING_STATUS_SAVESTATE_LOADING_FAILED NSLocalizedString(@"Save state file loading failed!", nil) +#define NSSTRING_STATUS_SAVESTATE_LOADED NSLocalizedString(@"Save state file loaded.", nil) +#define NSSTRING_STATUS_SAVESTATE_SAVING_FAILED NSLocalizedString(@"Save state file saving failed!", nil) +#define NSSTRING_STATUS_SAVESTATE_SAVED NSLocalizedString(@"Save state file saved.", nil) +#define NSSTRING_STATUS_SAVESTATE_REVERTING_FAILED NSLocalizedString(@"Save state file reverting failed!", nil) +#define NSSTRING_STATUS_SAVESTATE_REVERTED NSLocalizedString(@"Save state file reverted.", nil) +#define NSSTRING_STATUS_BATTERY_IMPORT_FAILED NSLocalizedString(@"Battery file import failed!", nil) +#define NSSTRING_STATUS_BATTERY_IMPORTED NSLocalizedString(@"Battery save file imported.", nil) +#define NSSTRING_STATUS_BATTERY_EXPORT_FAILED NSLocalizedString(@"Battery file export failed!", nil) +#define NSSTRING_STATUS_BATTERY_EXPORTED NSLocalizedString(@"Battery save file exported.", nil) +#define NSSTRING_STATUS_ROM_LOADING NSLocalizedString(@"Loading ROM...", nil) +#define NSSTRING_STATUS_ROM_LOADING_FAILED NSLocalizedString(@"ROM loading failed!", nil) +#define NSSTRING_STATUS_ROM_LOADED NSLocalizedString(@"ROM loaded.", nil) +#define NSSTRING_STATUS_ROM_UNLOADING NSLocalizedString(@"Unloading ROM...", nil) +#define NSSTRING_STATUS_ROM_UNLOADED NSLocalizedString(@"ROM unloaded.", nil) +#define NSSTRING_STATUS_EMULATOR_RESETTING NSLocalizedString(@"Emulator resetting...", nil) +#define NSSTRING_STATUS_EMULATOR_RESET NSLocalizedString(@"Emulator reset.", nil) +#define NSSTRING_STATUS_CANNOT_GENERATE_SAVE_PATH NSLocalizedString(@"Cannot generate the save file path!", nil) +#define NSSTRING_STATUS_CANNOT_CREATE_SAVE_DIRECTORY NSLocalizedString(@"Cannot create the save directory!", nil) + +#define NSSTRING_STATUS_SPEED_LIMIT_DISABLED NSLocalizedString(@"Speed limit disabled.", nil) +#define NSSTRING_STATUS_SPEED_LIMIT_ENABLED NSLocalizedString(@"Speed limit enabled.", nil) +#define NSSTRING_STATUS_AUTO_FRAME_SKIP_DISABLED NSLocalizedString(@"Auto frame skip disabled.", nil) +#define NSSTRING_STATUS_AUTO_FRAME_SKIP_ENABLED NSLocalizedString(@"Auto frame skip enabled.", nil) +#define NSSTRING_STATUS_CHEATS_DISABLED NSLocalizedString(@"Cheats disabled.", nil) +#define NSSTRING_STATUS_CHEATS_ENABLED NSLocalizedString(@"Cheats enabled.", nil) +#define NSSTRING_STATUS_HUD_DISABLED NSLocalizedString(@"HUD disabled.", nil) +#define NSSTRING_STATUS_HUD_ENABLED NSLocalizedString(@"HUD enabled.", nil) +#define NSSTRING_STATUS_VOLUME NSLocalizedString(@"Volume: %1.1f%%", nil) +#define NSSTRING_STATUS_NO_ROM_LOADED NSLocalizedString(@"No ROM loaded.", nil) +#define NSSTRING_STATUS_SIZE_BYTES NSLocalizedString(@"%i bytes", nil) + +#define NSSTRING_DISPLAYMODE_MAIN NSLocalizedString(@"Main", nil) +#define NSSTRING_DISPLAYMODE_TOUCH NSLocalizedString(@"Touch", nil) +#define NSSTRING_DISPLAYMODE_COMBO NSLocalizedString(@"Combo", nil) + +#define PATH_CONFIG_DIRECTORY_0_9_6 "~/.config/desmume" + +#define FILE_EXT_FIRMWARE_CONFIG "dfc" +#define FILE_EXT_SAVE_STATE "dst" +#define FILE_EXT_ROM_SAVE "dsv" +#define FILE_EXT_CHEAT "dct" +#define FILE_EXT_ROM_SAVE_NOGBA "sav" +#define FILE_EXT_ACTION_REPLAY_SAVE "duc" +#define FILE_EXT_ROM_DS "nds" +#define FILE_EXT_ROM_GBA "ds.gba" +#define FILE_EXT_HW_IMAGE_FILE "bin" +#define FILE_EXT_ADVANSCENE_DB "xml" +#define FILE_EXT_R4_CHEAT_DB "dat" + +#define MAX_SAVESTATE_SLOTS 10 + +#define MAX_VOLUME 100.0f +#define MAX_BRIGHTNESS 100.0f + +#define CHEAT_DESCRIPTION_LENGTH 1024 + +#define GPU_DISPLAY_WIDTH 256 +#define GPU_DISPLAY_HEIGHT 192 +#define GPU_DISPLAY_COLOR_DEPTH sizeof(UInt16) +#define GPU_SCREEN_SIZE_BYTES (GPU_DISPLAY_WIDTH * GPU_DISPLAY_HEIGHT * GPU_DISPLAY_COLOR_DEPTH) // The numbers are: 256px width, 192px height, 16bit color depth + +#define WINDOW_STATUS_BAR_HEIGHT 24 // Height of an emulation window status bar in pixels. + +#define SPEED_SCALAR_HALF 0.5 // Speed scalar for half execution speed. +#define SPEED_SCALAR_NORMAL 1.0 // Speed scalar for normal execution speed. +#define SPEED_SCALAR_DOUBLE 2.0 // Speed scalar for double execution speed. +#define SPEED_SCALAR_MIN 0.005 // Lower limit for the speed multiplier. + +#define DS_FRAMES_PER_SECOND 59.73 // Number of DS frames per second. +#define DS_SECONDS_PER_FRAME (1.0 / DS_FRAMES_PER_SECOND) // The length of time in seconds that, ideally, a frame should be processed within. + +#define FRAME_SKIP_AGGRESSIVENESS 2.0 +#define FRAME_SKIP_BIAS 0.5 +#define MAX_FRAME_SKIP (DS_FRAMES_PER_SECOND * 2.0) + +#define SPU_SAMPLE_RATE 44100 // Samples per second +#define SPU_SAMPLE_RESOLUTION 16 // Bits per sample; must be a multiple of 8 +#define SPU_STEREO_SAMPLE_SIZE ((SPU_SAMPLE_RESOLUTION / 8) * 2) // Bytes per sample, multiply by 2 for stereo +#define SPU_BUFFER_BYTES ((SPU_SAMPLE_RATE / DS_FRAMES_PER_SECOND) * SPU_STEREO_SAMPLE_SIZE) + +#define CLOCKWISE_DEGREES(x) (360.0 - x) // Converts an angle in degrees from normal-direction to clockwise-direction. + +#define VOLUME_THRESHOLD_LOW 35.0f +#define VOLUME_THRESHOLD_HIGH 75.0f + +#define ROM_ICON_WIDTH 32 +#define ROM_ICON_HEIGHT 32 + +#define ROMINFO_GAME_TITLE_LENGTH 12 +#define ROMINFO_GAME_CODE_LENGTH 4 +#define ROMINFO_GAME_BANNER_LENGTH 128 + +#define MIC_NULL_SAMPLE_VALUE 0 +#define MIC_SAMPLE_RATE 16000 +#define MIC_MAX_BUFFER_SAMPLES 320 +#define MIC_BUFFER_SIZE (sizeof(UInt8) * MIC_MAX_BUFFER_SAMPLES) + +#define COCOA_DIALOG_CANCEL 0 +#define COCOA_DIALOG_DEFAULT 1 +#define COCOA_DIALOG_OK 1 +#define COCOA_DIALOG_OPTION 2 + +enum +{ + ROMAUTOLOADOPTION_LOAD_LAST = 0, + ROMAUTOLOADOPTION_LOAD_SELECTED = 1 +}; + +enum +{ + REASONFORCLOSE_NORMAL = 0, + REASONFORCLOSE_OPEN, + REASONFORCLOSE_TERMINATE +}; + +enum +{ + EMULATION_ENSATA_BIT = 0, + EMULATION_ADVANCED_BUS_LEVEL_TIMING_BIT = 1, + EMULATION_USE_EXTERNAL_BIOS_BIT = 2, + EMULATION_BIOS_SWI_BIT = 3, + EMULATION_PATCH_DELAY_LOOP_BIT = 4, + EMULATION_USE_EXTERNAL_FIRMWARE_BIT = 5, + EMULATION_BOOT_FROM_FIRMWARE_BIT = 6, + EMULATION_SLEEP_BIT = 7, + EMULATION_CARD_EJECT_BIT = 8, + EMULATION_DEBUG_CONSOLE_BIT = 9 +}; + +enum +{ + EMULATION_ENSATA_MASK = 1 << EMULATION_ENSATA_BIT, + EMULATION_ADVANCED_BUS_LEVEL_TIMING_MASK = 1 << EMULATION_ADVANCED_BUS_LEVEL_TIMING_BIT, + EMULATION_USE_EXTERNAL_BIOS_MASK = 1 << EMULATION_USE_EXTERNAL_BIOS_BIT, + EMULATION_BIOS_SWI_MASK = 1 << EMULATION_BIOS_SWI_BIT, + EMULATION_PATCH_DELAY_LOOP_MASK = 1 << EMULATION_PATCH_DELAY_LOOP_BIT, + EMULATION_USE_EXTERNAL_FIRMWARE_MASK = 1 << EMULATION_USE_EXTERNAL_FIRMWARE_BIT, + EMULATION_BOOT_FROM_FIRMWARE_MASK = 1 << EMULATION_BOOT_FROM_FIRMWARE_BIT, + EMULATION_SLEEP_MASK = 1 << EMULATION_SLEEP_BIT, + EMULATION_CARD_EJECT_MASK = 1 << EMULATION_CARD_EJECT_BIT, + EMULATION_DEBUG_CONSOLE_MASK = 1 << EMULATION_DEBUG_CONSOLE_BIT +}; + +enum +{ + GPUSTATE_MAIN_GPU_BIT = 0, + GPUSTATE_MAIN_BG0_BIT = 1, + GPUSTATE_MAIN_BG1_BIT = 2, + GPUSTATE_MAIN_BG2_BIT = 3, + GPUSTATE_MAIN_BG3_BIT = 4, + GPUSTATE_MAIN_OBJ_BIT = 5, + GPUSTATE_SUB_GPU_BIT = 6, + GPUSTATE_SUB_BG0_BIT = 7, + GPUSTATE_SUB_BG1_BIT = 8, + GPUSTATE_SUB_BG2_BIT = 9, + GPUSTATE_SUB_BG3_BIT = 10, + GPUSTATE_SUB_OBJ_BIT = 11 +}; + +enum +{ + GPUSTATE_MAIN_GPU_MASK = 1 << GPUSTATE_MAIN_GPU_BIT, + GPUSTATE_MAIN_BG0_MASK = 1 << GPUSTATE_MAIN_BG0_BIT, + GPUSTATE_MAIN_BG1_MASK = 1 << GPUSTATE_MAIN_BG1_BIT, + GPUSTATE_MAIN_BG2_MASK = 1 << GPUSTATE_MAIN_BG2_BIT, + GPUSTATE_MAIN_BG3_MASK = 1 << GPUSTATE_MAIN_BG3_BIT, + GPUSTATE_MAIN_OBJ_MASK = 1 << GPUSTATE_MAIN_OBJ_BIT, + GPUSTATE_SUB_GPU_MASK = 1 << GPUSTATE_SUB_GPU_BIT, + GPUSTATE_SUB_BG0_MASK = 1 << GPUSTATE_SUB_BG0_BIT, + GPUSTATE_SUB_BG1_MASK = 1 << GPUSTATE_SUB_BG1_BIT, + GPUSTATE_SUB_BG2_MASK = 1 << GPUSTATE_SUB_BG2_BIT, + GPUSTATE_SUB_BG3_MASK = 1 << GPUSTATE_SUB_BG3_BIT, + GPUSTATE_SUB_OBJ_MASK = 1 << GPUSTATE_SUB_OBJ_BIT +}; + +enum +{ + SPU_SYNC_MODE_DUAL_SYNC_ASYNC = 0, + SPU_SYNC_MODE_SYNCHRONOUS = 1 +}; + +enum +{ + SPU_SYNC_METHOD_N = 0, + SPU_SYNC_METHOD_Z = 1, + SPU_SYNC_METHOD_P = 2 +}; + +enum +{ + CHEAT_TYPE_INTERNAL = 0, + CHEAT_TYPE_ACTION_REPLAY = 1, + CHEAT_TYPE_CODE_BREAKER = 2 +}; + +enum +{ + CHEATSEARCH_SEARCHSTYLE_EXACT_VALUE = 0, + CHEATSEARCH_SEARCHSTYLE_COMPARATIVE = 1 +}; + +enum +{ + CHEATSEARCH_COMPARETYPE_GREATER_THAN = 0, + CHEATSEARCH_COMPARETYPE_LESSER_THAN = 1, + CHEATSEARCH_COMPARETYPE_EQUALS_TO = 2, + CHEATSEARCH_COMPARETYPE_NOT_EQUALS_TO = 3 +}; + +enum +{ + CHEATSEARCH_UNSIGNED = 0, + CHEATSEARCH_SIGNED = 1 +}; + +enum +{ + CHEATEXPORT_ERROR_FILE_NOT_FOUND = 1, + CHEATEXPORT_ERROR_WRONG_FILE_FORMAT = 2, + CHEATEXPORT_ERROR_SERIAL_NOT_FOUND = 3, + CHEATEXPORT_ERROR_EXPORT_FAILED = 4 +}; + +/* + PORT MESSAGES + */ +enum +{ + MESSAGE_CHECK_FOR_RESPONSE = 100, // Message to check if a port is responding. Usually sent to make sure that a thread is alive. + MESSAGE_CHECK_RESPONSE_ECHO, // Response message when another port sends MESSAGE_CHECK_FOR_RESPONSE. Sent to confirm that a thread is indeed alive. + MESSAGE_EXIT_THREAD, // Sent whenever there is a need to stop a thread. + + MESSAGE_EMU_FRAME_PROCESSED = 1000, // Sent whenever an emulation frame is fully processed. This is mostly used to signal outputs to update themselves based on the new emulation frame. + MESSAGE_OUTPUT_FINISHED_FRAME, // Sent from an output device whenever it finishes processing the frame data. + MESSAGE_SET_EMULATION_FLAGS, + + // Video Messages + MESSAGE_RESIZE_VIEW, + MESSAGE_REDRAW_VIEW, + MESSAGE_SET_GPU_STATE_FLAGS, + MESSAGE_CHANGE_DISPLAY_TYPE, + MESSAGE_CHANGE_BILINEAR_OUTPUT, + MESSAGE_CHANGE_VIDEO_FILTER, + MESSAGE_SET_RENDER3D_METHOD, + MESSAGE_SET_RENDER3D_HIGH_PRECISION_COLOR_INTERPOLATION, + MESSAGE_SET_RENDER3D_EDGE_MARKING, + MESSAGE_SET_RENDER3D_FOG, + MESSAGE_SET_RENDER3D_TEXTURES, + MESSAGE_SET_RENDER3D_DEPTH_COMPARISON_THRESHOLD, + MESSAGE_SET_RENDER3D_THREADS, + MESSAGE_SET_RENDER3D_LINE_HACK, + MESSAGE_SET_VIEW_TO_BLACK, + MESSAGE_SET_VIEW_TO_WHITE, + + MESSAGE_SET_AUDIO_PROCESS_METHOD, + MESSAGE_SET_SPU_ADVANCED_LOGIC, + MESSAGE_SET_SPU_SYNC_MODE, + MESSAGE_SET_SPU_SYNC_METHOD, + MESSAGE_SET_SPU_INTERPOLATION_MODE, + MESSAGE_SET_VOLUME, + + MESSAGE_COPY_TO_PASTEBOARD +}; + +/* + DS DISPLAY TYPES + */ +enum +{ + DS_DISPLAY_TYPE_MAIN = 0, + DS_DISPLAY_TYPE_TOUCH, + DS_DISPLAY_TYPE_COMBO +}; + +/* + DS GPU TYPES + */ +enum +{ + DS_GPU_TYPE_MAIN = 0, + DS_GPU_TYPE_SUB, + DS_GPU_TYPE_COMBO +}; + +/* + COCOA DS CORE STATES + */ +enum +{ + CORESTATE_EXECUTE = 0, + CORESTATE_PAUSE +}; + +/* + DESMUME 3D RENDERER TYPES + */ +enum +{ + CORE3DLIST_NULL = 0, + CORE3DLIST_SWRASTERIZE, + CORE3DLIST_OPENGL +}; + +/* + MICROPHONE MODE + */ +enum +{ + MICMODE_NONE = 0, + MICMODE_INTERNAL_NOISE, + MICMODE_SOUND_FILE, + MICMODE_WHITE_NOISE, + MICMODE_PHYSICAL +}; + +/* + CONTROL TAGS FOR PREFERENCE WINDOW INPUT CONFIG BUTTONS + */ +enum +{ + PREF_INPUT_BUTTON_UP = 1000, + PREF_INPUT_BUTTON_DOWN, + PREF_INPUT_BUTTON_LEFT, + PREF_INPUT_BUTTON_RIGHT, + PREF_INPUT_BUTTON_A, + PREF_INPUT_BUTTON_B, + PREF_INPUT_BUTTON_X, + PREF_INPUT_BUTTON_Y, + PREF_INPUT_BUTTON_L, + PREF_INPUT_BUTTON_R, + PREF_INPUT_BUTTON_START, + PREF_INPUT_BUTTON_SELECT, + + PREF_INPUT_BUTTON_SIM_MIC = 2000, + + PREF_INPUT_BUTTON_LID = 3000, + PREF_INPUT_BUTTON_DEBUG, + PREF_INPUT_BUTTON_SPEED_HALF, + PREF_INPUT_BUTTON_SPEED_DOUBLE, + PREF_INPUT_BUTTON_TOGGLE_HUD, + PREF_INPUT_BUTTON_EXECUTE, + PREF_INPUT_BUTTON_PAUSE, + PREF_INPUT_BUTTON_RESET, + + PREF_INPUT_BUTTON_TOUCH = 4000 +}; \ No newline at end of file diff --git a/desmume/src/cocoa/main.mm b/desmume/src/cocoa/main.mm index 90475001b..d3b4c0e6f 100644 --- a/desmume/src/cocoa/main.mm +++ b/desmume/src/cocoa/main.mm @@ -27,6 +27,8 @@ Mac related questions can go to osx@desmume.org #import "main_window.h" #import "preferences.h" +#include "sndOSX.h" + #ifdef GDB_STUB #include #endif @@ -576,6 +578,8 @@ int main(int argc, char *argv[]) - (void)applicationWillFinishLaunching:(NSNotification*)notification { + SNDOSXStartup(); + //Set default values for all preferences //(this wont override saved preferences as //they work in different preference domains) @@ -623,7 +627,10 @@ int main(int argc, char *argv[]) - (void)applicationWillTerminate:(NSNotification*)notification { + [main_window pause]; [main_window release]; + + SNDOSXShutdown(); } @end diff --git a/desmume/src/cocoa/sndOSX.cpp b/desmume/src/cocoa/sndOSX.cpp new file mode 100644 index 000000000..0245483a0 --- /dev/null +++ b/desmume/src/cocoa/sndOSX.cpp @@ -0,0 +1,717 @@ +/* + Copyright (C) 2007 Jeff Bland + Copyright (C) 2007-2011 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#include "sndOSX.h" + +#include +#include +#include +#include + +#include "cocoa_globals.h" + + +//globals +static AudioUnit output_unit = NULL; //pointer to our audio device +static UInt8 *sound_data = NULL; //buffer where we hold data between getting it from the emulator and sending it to the device +static size_t sound_buffer_size = 0; //size in bytes of sound_data +static size_t sound_offset = SPU_STEREO_SAMPLE_SIZE; //position in the buffer that we have copied to from the emu +static size_t sound_position = 0; //position in the buffer that we have played to +static float current_volume_scalar = 1.0f; //for volume/muting + +//file output +static bool file_open = false; +//static ExtAudioFileRef outfile; + +static pthread_mutex_t *mutexSoundData = NULL; +static pthread_mutex_t *mutexAudioUnit = NULL; + +////////////////////////////////////////////////////////////////////////////// + +//This is the callback where we will stick the sound data we've gotten from +//the emulator into a core audio buffer to be processed and sent to the sound driver + +OSStatus soundMixer(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ +//printf("SOUND CALLBACK %u off%u pos%u\n", inNumberFrames * 4, sound_offset, sound_position); + const size_t copySize = inNumberFrames * SPU_STEREO_SAMPLE_SIZE; + size_t hiBufferAvailable = 0; + size_t loBufferAvailable = 0; + + pthread_mutex_lock(mutexAudioUnit); + + UInt8 *__restrict__ outputData = (UInt8 *)ioData->mBuffers[0].mData; + + pthread_mutex_lock(mutexSoundData); + + if(sound_data != NULL) + { + const UInt8 *__restrict__ inputData = sound_data; + const size_t inputDataReadPos = sound_position; + const size_t inputDataWritePos = sound_offset; + const size_t inputDataSize = sound_buffer_size; + + // Determine buffer availability + if (inputDataReadPos < inputDataWritePos) + { + hiBufferAvailable = inputDataWritePos - inputDataReadPos - 1; + } + else if (inputDataReadPos > inputDataWritePos) + { + hiBufferAvailable = inputDataSize - inputDataReadPos - 1; + loBufferAvailable = inputDataWritePos; + } + + // Copy sound data from buffer + if (copySize <= hiBufferAvailable) + { + memcpy(outputData, inputData + inputDataReadPos + 1, copySize); + sound_position += copySize; + } + else + { + memcpy(outputData, inputData + inputDataReadPos + 1, hiBufferAvailable); + + if (copySize - hiBufferAvailable <= loBufferAvailable) + { + memcpy(outputData + hiBufferAvailable, inputData, copySize - hiBufferAvailable); + sound_position = copySize - hiBufferAvailable - 1; + } + else + { + memcpy(outputData + hiBufferAvailable, inputData, loBufferAvailable); + if (inputDataWritePos == 0) + { + sound_position = inputDataSize - 1; + } + else + { + sound_position = inputDataWritePos - 1; + } + + // Pad any remaining samples with null samples + const size_t totalAvailable = hiBufferAvailable + loBufferAvailable; + if (copySize > totalAvailable) + { + memset(outputData + totalAvailable, 0, copySize - totalAvailable); + } + } + } + } + else + { + memset(outputData, 0, copySize); + } + + pthread_mutex_unlock(mutexSoundData); + + //copy to other channels + for (UInt32 channel = 1; channel < ioData->mNumberBuffers; channel++) + { + memcpy(ioData->mBuffers[channel].mData, outputData, ioData->mBuffers[0].mDataByteSize); + } + + //record to file + if(file_open) + { + //ExtAudioFileWrite(outfile, inNumberFrames, ioData); + } + + pthread_mutex_unlock(mutexAudioUnit); + + return noErr; +} + +////////////////////////////////////////////////////////////////////////////// + +void SNDOSXStartup() +{ + OSStatus error = noErr; + + if (mutexSoundData == NULL) + { + mutexSoundData = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(mutexSoundData, NULL); + } + + if (mutexAudioUnit == NULL) + { + mutexAudioUnit = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(mutexAudioUnit, NULL); + } + + //Setup the sound buffer ------------------------------------------- + pthread_mutex_lock(mutexSoundData); + + sound_data = (UInt8 *)calloc((SPU_SAMPLE_RATE / DS_FRAMES_PER_SECOND) + 1, SPU_STEREO_SAMPLE_SIZE); + if(sound_data == NULL) + { + pthread_mutex_unlock(mutexSoundData); + return; + } + + sound_position = 0; + sound_offset = SPU_STEREO_SAMPLE_SIZE; + sound_buffer_size = ((SPU_SAMPLE_RATE / DS_FRAMES_PER_SECOND) + 1) * SPU_STEREO_SAMPLE_SIZE; + + pthread_mutex_unlock(mutexSoundData); + + //grab the default audio unit ------------------------- + + pthread_mutex_lock(mutexAudioUnit); + + current_volume_scalar = 1.0f; + + ComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + Component comp = FindNextComponent(NULL, &desc); + if (comp == NULL) + { + pthread_mutex_unlock(mutexAudioUnit); + return; + } + + error = OpenAComponent(comp, &output_unit); + if (comp == NULL) + { + pthread_mutex_unlock(mutexAudioUnit); + return; + } + + //then setup the callback where we will send the audio ------- + + AURenderCallbackStruct callback; + callback.inputProc = soundMixer; + callback.inputProcRefCon = NULL; + + error = AudioUnitSetProperty(output_unit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &callback, + sizeof(callback) ); + + if(error != noErr) + { + pthread_mutex_unlock(mutexAudioUnit); + return; + } + + //now begin running the audio unit-- ---------------------- + + AudioStreamBasicDescription audio_format; + audio_format.mSampleRate = SPU_SAMPLE_RATE; + audio_format.mFormatID = kAudioFormatLinearPCM; + audio_format.mFormatFlags = kAudioFormatFlagIsSignedInteger + | kAudioFormatFlagsNativeEndian + | kLinearPCMFormatFlagIsPacked; + audio_format.mBytesPerPacket = 4; + audio_format.mFramesPerPacket = 1; + audio_format.mBytesPerFrame = 4; + audio_format.mChannelsPerFrame = 2; + audio_format.mBitsPerChannel = SPU_SAMPLE_RESOLUTION; + + error = AudioUnitSetProperty(output_unit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &audio_format, + sizeof(audio_format) ); + + if(error != noErr) + { + pthread_mutex_unlock(mutexAudioUnit); + return; + } + + // Initialize unit + error = AudioUnitInitialize(output_unit); + if(error != noErr) + { + pthread_mutex_unlock(mutexAudioUnit); + return; + } + + pthread_mutex_unlock(mutexAudioUnit); + + //we call the CFRunLoopRunInMode to service any notifications that the audio + //system has to deal with + //CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2, false); + //verify_noerr (AudioOutputUnitStop (output_unit)); +} + +void SNDOSXShutdown() +{ + pthread_mutex_lock(mutexAudioUnit); + + //closes the audio unit (errors are ignored here) + if(output_unit != NULL) + { + AudioOutputUnitStop(output_unit); + AudioUnitUninitialize(output_unit); + output_unit = NULL; + } + + pthread_mutex_unlock(mutexAudioUnit); + + pthread_mutex_lock(mutexSoundData); + + if(sound_data != NULL) + { + free(sound_data); + sound_data = NULL; + sound_position = 0; + sound_offset = SPU_STEREO_SAMPLE_SIZE; + sound_buffer_size = 0; + } + + pthread_mutex_unlock(mutexSoundData); + + if (mutexSoundData != NULL) + { + pthread_mutex_destroy(mutexSoundData); + free(mutexSoundData); + mutexSoundData = NULL; + } + + if (mutexAudioUnit != NULL) + { + pthread_mutex_destroy(mutexAudioUnit); + free(mutexAudioUnit); + mutexAudioUnit = NULL; + } +} + +int SNDOSXInit(int buffer_size) +{ + OSStatus error = noErr; + const size_t singleSampleSize = SPU_STEREO_SAMPLE_SIZE; + UInt8 *newSoundData = NULL; + + //Setup the sound buffer ------------------------------------------- + pthread_mutex_lock(mutexSoundData); + + //add one more since sound position can never catch up to + //sound_offset - because if they were the same it would signify + //that the buffer is empty + + sound_buffer_size = buffer_size + singleSampleSize; + + newSoundData = (UInt8 *)realloc(sound_data, sound_buffer_size); + if(newSoundData == NULL) + { + free(sound_data); + pthread_mutex_unlock(mutexSoundData); + return -1; + } + + memset(newSoundData, 0, sound_buffer_size); + sound_data = newSoundData; + sound_position = 0; + sound_offset = singleSampleSize; + + pthread_mutex_unlock(mutexSoundData); + + //------------------------------------------------------------------ + + //Start the rendering + //The DefaultOutputUnit will do any format conversions to the format of the default device + pthread_mutex_lock(mutexAudioUnit); + + if (output_unit != NULL) + { + AudioUnitReset(output_unit, kAudioUnitScope_Global, 0); + + error = AudioOutputUnitStart(output_unit); + if(error != noErr) + { + pthread_mutex_unlock(mutexAudioUnit); + return -1; + } + } + + pthread_mutex_unlock(mutexAudioUnit); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void SNDOSXDeInit() +{ + pthread_mutex_lock(mutexAudioUnit); + + if(output_unit != NULL) + { + AudioOutputUnitStop(output_unit); + } + + pthread_mutex_unlock(mutexAudioUnit); + + SNDOSXClearBuffer(); + SNDOSXCloseFile(); //end recording to file if needed +} + +////////////////////////////////////////////////////////////////////////////// + +int SNDOSXReset() +{ + SNDOSXClearBuffer(); + + return 0; +} + +void SNDOSXUpdateAudio(s16 *buffer, u32 num_samples) +{ + const size_t singleSampleSize = SPU_STEREO_SAMPLE_SIZE; + size_t copySize = num_samples * singleSampleSize; + size_t hiBufferAvailable = 0; // Buffer space ahead of offset + size_t loBufferAvailable = 0; // Buffer space before read position + + pthread_mutex_lock(mutexSoundData); + + if(sound_data == NULL) + { + pthread_mutex_unlock(mutexSoundData); + return; + } + + UInt8 *__restrict__ inputData = sound_data; + const size_t inputDataReadPos = sound_position; + const size_t inputDataWritePos = sound_offset; + const size_t inputDataSize = sound_buffer_size; + + if (inputDataWritePos >= inputDataReadPos) + { + hiBufferAvailable = inputDataSize - inputDataWritePos; + loBufferAvailable = inputDataReadPos; + + // Subtract a sample's worth of bytes + if (loBufferAvailable > 0) + { + loBufferAvailable -= 1; + } + else + { + hiBufferAvailable -= 1; + } + } + else // (inputDataWritePos < inputDataReadPos) + { + hiBufferAvailable = inputDataReadPos - inputDataWritePos - 1; + } + + if (copySize > hiBufferAvailable + loBufferAvailable) + { + //this shouldn't happen as the emulator core generally asks how much + //space is available before sending stuff, but just in case + int bytesShort = copySize - hiBufferAvailable - loBufferAvailable; + + printf("SNDOSXUpdateAudio() ERROR: Not enough space in buffer -- %i bytes short.\n", bytesShort); + + copySize = hiBufferAvailable + loBufferAvailable; + copySize -= copySize % singleSampleSize; + } + + if (copySize <= hiBufferAvailable) + { + memcpy(inputData + inputDataWritePos, buffer, copySize); + } + else + { + memcpy(inputData + inputDataWritePos, buffer, hiBufferAvailable); + memcpy(inputData, ((UInt8 *)buffer) + hiBufferAvailable, copySize - hiBufferAvailable); + } + + // Advance the offset + sound_offset += copySize; + if (sound_offset >= inputDataSize) + { + sound_offset -= inputDataSize; + } + + pthread_mutex_unlock(mutexSoundData); +} + +////////////////////////////////////////////////////////////////////////////// + +u32 SNDOSXGetAudioSpace() +{ + const size_t singleSampleSize = SPU_STEREO_SAMPLE_SIZE; + size_t free_space = 0; + + pthread_mutex_lock(mutexSoundData); + + if(sound_data == NULL) + { + pthread_mutex_unlock(mutexSoundData); + return 0; + } + + if(sound_offset >= sound_position) + { + free_space = (sound_buffer_size - sound_offset) + sound_position; + } + else // (sound_offset < sound_position) + { + free_space = sound_position - sound_offset; + } + + pthread_mutex_unlock(mutexSoundData); + + if (free_space >= singleSampleSize) + { + free_space -= singleSampleSize; + } + else + { + free_space = 0; + } + + return (u32)(free_space / singleSampleSize); +} + +////////////////////////////////////////////////////////////////////////////// + +void SNDOSXMuteAudio() +{ + OSStatus error = noErr; + + pthread_mutex_lock(mutexAudioUnit); + + if(output_unit == NULL) + { + pthread_mutex_unlock(mutexAudioUnit); + return; + } + + error = AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, 0, 0); + + pthread_mutex_unlock(mutexAudioUnit); + + if(error != noErr) + { + printf("SNDOSXMuteAudio() ERROR: Could not set Audio Unit parameter -- Volume=0.0%%\n"); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void SNDOSXUnMuteAudio() +{ + OSStatus error = noErr; + float volumeScalar = 0.0f; + + pthread_mutex_lock(mutexAudioUnit); + + if(output_unit == NULL) + { + pthread_mutex_unlock(mutexAudioUnit); + return; + } + + volumeScalar = current_volume_scalar; + error = AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, volumeScalar, 0); + + pthread_mutex_unlock(mutexAudioUnit); + + if(error != noErr) + { + printf("SNDOSXUnMuteAudio() ERROR: Could not set Audio Unit parameter -- Volume=%1.1f%%\n", volumeScalar * 100.0f); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void SNDOSXSetVolume(int volume) +{ + OSStatus error = noErr; + float newVolumeScalar = (float)volume / 100.0f; + + if(volume > 100) + { + newVolumeScalar = 1.0f; + } + else if(volume < 0) + { + newVolumeScalar = 0.0f; + } + + pthread_mutex_lock(mutexAudioUnit); + + if(output_unit == NULL) + { + pthread_mutex_unlock(mutexAudioUnit); + return; + } + + current_volume_scalar = newVolumeScalar; + error = AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, newVolumeScalar, 0); + + pthread_mutex_unlock(mutexAudioUnit); + + if(error != noErr) + { + printf("SNDOSXSetVolume() ERROR: Could not set Audio Unit parameter -- Volume=%1.1f%%\n", newVolumeScalar * 100.0f); + } +} + +void SNDOSXClearBuffer() +{ + pthread_mutex_lock(mutexSoundData); + + if(sound_data != NULL) + { + memset(sound_data, 0, sound_buffer_size); + sound_position = 0; + sound_offset = SPU_STEREO_SAMPLE_SIZE; + } + + pthread_mutex_unlock(mutexSoundData); + + pthread_mutex_lock(mutexAudioUnit); + + if(output_unit != NULL) + { + AudioUnitReset(output_unit, kAudioUnitScope_Global, 0); + } + + pthread_mutex_unlock(mutexAudioUnit); +} + +////////////////////////////////////////////////////////////////////////////// + +SoundInterface_struct SNDOSX = { + SNDCORE_OSX, + "Core Audio Sound Interface", + SNDOSXInit, + SNDOSXDeInit, + SNDOSXUpdateAudio, + SNDOSXGetAudioSpace, + SNDOSXMuteAudio, + SNDOSXUnMuteAudio, + SNDOSXSetVolume, + SNDOSXClearBuffer +}; + +////////////////////////////////////////////////////////////////////////////// +//Sound recording +////////////////////////////////////////////////////////////////////////////// + +bool SNDOSXOpenFile(void *fname) +{ + /* + if(sound_data == NULL)return false; + + SNDOSXCloseFile(); + + if(!fname)return false; + + NSString *filename = (NSString*)fname; + FSRef ref; + + if(FSPathMakeRef((const UInt8*)[[filename stringByDeletingLastPathComponent] fileSystemRepresentation], &ref, NULL) != noErr) + { + SNDOSXStopRecording(); + return false; + } + + AudioStreamBasicDescription audio_format; + audio_format.mSampleRate = SPU_SAMPLE_RATE; + audio_format.mFormatID = kAudioFormatLinearPCM; + audio_format.mFormatFlags = kAudioFormatFlagIsSignedInteger + | kAudioFormatFlagsNativeEndian + | kLinearPCMFormatFlagIsPacked; + audio_format.mBytesPerPacket = 4; + audio_format.mFramesPerPacket = 1; + audio_format.mBytesPerFrame = 4; + audio_format.mChannelsPerFrame = 2; + audio_format.mBitsPerChannel = SPU_SAMPLE_RESOLUTION; + + if(ExtAudioFileCreateNew(&ref, (CFStringRef)[[filename pathComponents] lastObject], kAudioFileWAVEType, &audio_format, NULL, &outfile) != noErr) + return false; + + file_open = true; + + return true; + */ + return false; +} + +void SNDOSXStartRecording() +{ + pthread_mutex_lock(mutexSoundData); + + if(sound_data == NULL) + { + pthread_mutex_unlock(mutexSoundData); + return; + } + + pthread_mutex_unlock(mutexSoundData); +} + +void SNDOSXStopRecording() +{ + pthread_mutex_lock(mutexSoundData); + + if(sound_data == NULL) + { + pthread_mutex_unlock(mutexSoundData); + return; + } + + pthread_mutex_unlock(mutexSoundData); +} + +void SNDOSXCloseFile() +{ + pthread_mutex_lock(mutexSoundData); + + if(sound_data == NULL) + { + pthread_mutex_unlock(mutexSoundData); + return; + } + + if(file_open) + { + file_open = false; + + //if it's rendering sound, wait until it's not + //so we dont close the file while writing to it + + // Do something here, just not implemented yet... + + //ExtAudioFileDispose(outfile); + } + + pthread_mutex_unlock(mutexSoundData); +} + +/////////////////////////////////////////////////////////////////////////// diff --git a/desmume/src/cocoa/sndOSX.mm b/desmume/src/cocoa/sndOSX.mm deleted file mode 100644 index e81ef0779..000000000 --- a/desmume/src/cocoa/sndOSX.mm +++ /dev/null @@ -1,483 +0,0 @@ -/* Copyright 2007 Jeff Bland - - This file is part of DeSmuME - - DeSmuME is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - DeSmuME is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with DeSmuME; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -//external includes - -#include "stdlib.h" -#include "stdio.h" - -#include - -#include -#include -#include - -//desmume includes -#include "sndOSX.h" - -//globals -AudioUnit output_unit = NULL; //pointer to our audio device -UInt16 *sound_data = NULL; //buffer where we hold data between getting it from the emulator and sending it to the device -u32 sound_buffer_size; //size in bytes of sound_data -volatile u32 sound_offset; //position in the buffer that we have copied to from the emu -volatile u32 sound_position; //position in the buffer that we have played to - -volatile bool mix_sound = false; //if false, our sound callback will do nothing -volatile bool in_mix = false; //should probably use a real mutex instead but this seems to work - -float current_volume_scalar = 100; //for volume/muting - -//file output -volatile bool file_open = false; -ExtAudioFileRef outfile; - -//defines -#define SAMPLE_SIZE (sizeof(s16) * 2) //16 bit samples, stereo sound - -////////////////////////////////////////////////////////////////////////////// - -//This is the callback where we will stick the sound data we've gotten from -//the emulator into a core audio buffer to be processed and sent to the sound driver - -OSStatus soundMixer( - void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData) -{ -//printf("SOUND CALLBACK %u off%u pos%u\n", inNumberFrames * 4, sound_offset, sound_position); - - if(!mix_sound) - return noErr; - - in_mix = true; - - UInt32 bytes_to_copy = inNumberFrames * 4; - UInt8 *data_output = (UInt8*)ioData->mBuffers[0].mData; - UInt8 *data_input = (UInt8*)sound_data; - - if(sound_position == sound_offset) - {//buffer empty - - memset(data_output, 0, bytes_to_copy); - - } else - { //buffer is not empty - - int x; - for(x = 0; x < bytes_to_copy; x++) - { - sound_position++; - - if(sound_position >= sound_buffer_size)sound_position = 0; - - if(sound_position == sound_offset) - { - sound_position --; - memset(&data_output[x], 0, bytes_to_copy - x); - break; - } - - data_output[x] = data_input[sound_position]; - - } - } - - //copy to other channels - UInt32 channel; - for (channel = 1; channel < ioData->mNumberBuffers; channel++) - memcpy(ioData->mBuffers[channel].mData, ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize); - - //record to file - if(file_open) - ;//ExtAudioFileWrite(outfile, inNumberFrames, ioData); - - in_mix = false; - return noErr; -} - -////////////////////////////////////////////////////////////////////////////// - -int SNDOSXInit(int buffer_size) -{ - OSStatus err = noErr; - - //Setup the sound buffer ------------------------------------------- - - //add one more since sound position can never catch up to - //sound_offset - because if they were the same it would signify - //that the buffer is emptys - buffer_size += 1; - - if((sound_data = (UInt16*)malloc(buffer_size)) == NULL) - return -1; - - sound_buffer_size = buffer_size; - - SNDOSXReset(); - - //grab the default audio unit ------------------------- - - ComponentDescription desc; - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_DefaultOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - Component comp = FindNextComponent(NULL, &desc); - if (comp == NULL) - return -1; - - err = OpenAComponent(comp, &output_unit); - if (comp == NULL) - return -1; - - //then setup the callback where we will send the audio ------- - - AURenderCallbackStruct callback; - callback.inputProc = soundMixer; - callback.inputProcRefCon = NULL; - - err = AudioUnitSetProperty( - output_unit, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, - 0, - &callback, - sizeof(callback) - ); - - if(err != noErr) - return -1; - - //now begin running the audio unit-- ---------------------- - - AudioStreamBasicDescription audio_format; - audio_format.mSampleRate = 44100; - audio_format.mFormatID = kAudioFormatLinearPCM; - audio_format.mFormatFlags = kAudioFormatFlagIsSignedInteger - | kAudioFormatFlagsNativeEndian - | kLinearPCMFormatFlagIsPacked; - audio_format.mBytesPerPacket = 4; - audio_format.mFramesPerPacket = 1; - audio_format.mBytesPerFrame = 4; - audio_format.mChannelsPerFrame = 2; - audio_format.mBitsPerChannel = 16; - - err = AudioUnitSetProperty( - output_unit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 0, - &audio_format, - sizeof(audio_format) - ); - - if(err != noErr){return -1;} - - // Initialize unit - err = AudioUnitInitialize(output_unit); - if(err != noErr) - return -1; - - //Start the rendering - //The DefaultOutputUnit will do any format conversions to the format of the default device - err = AudioOutputUnitStart(output_unit); - if(err != noErr) - return -1; - - // - current_volume_scalar = 100; - - //we call the CFRunLoopRunInMode to service any notifications that the audio - //system has to deal with - //CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2, false); - //verify_noerr (AudioOutputUnitStop (output_unit)); - - mix_sound = true; - - //------------------------------------------------------------------ - - return 0; -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDOSXDeInit() -{ - if(output_unit != NULL) - { - //wait for mix to end - mix_sound = false; - while(in_mix); - - //closes the audio unit (errors are ignored here) - AudioOutputUnitStop(output_unit); - AudioUnitUninitialize(output_unit); - - output_unit = NULL; - } - - SNDOSXCloseFile(); //end recording to file if needed - - if(sound_data != NULL) - { - free(sound_data); - sound_data = NULL; - } -} - -////////////////////////////////////////////////////////////////////////////// - -int SNDOSXReset() -{ - if(sound_data == NULL)return 0; - - memset(sound_data, 0, sound_buffer_size); - - sound_offset = 0; - sound_position = 0; - - return 0; -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDOSXUpdateAudio(s16 *buffer, u32 num_samples) -{ -//printf("NEW SOUND DATA %u ", num_samples * SAMPLE_SIZE); - - if(sound_data == NULL)return; - - //get the avilable bufferspace ------------------------------------------------ - - //in these calculatations: - //1 is subtracted from sound_buffer_size since if the size is 2, we can only go to index 1 - //1 is subtracted from sound_position since sound_offset should never catch up to it - //since them being equal means that the buffer is empty, rather than full - - u32 bytes_to_copy = num_samples * SAMPLE_SIZE; - - int sound_pos = sound_position; //incase the sound render thread changes it - - u32 copy1size, copy2size; - - if(sound_offset >= sound_pos) - { - if(sound_pos > 0) - { - copy1size = sound_buffer_size - sound_offset; - copy2size = (sound_pos-1); - } else - { - copy1size = sound_buffer_size - sound_offset - 1; - copy2size = 0; - } - } else if(sound_offset < sound_pos) - { - copy1size = sound_pos - sound_offset - 1; - copy2size = 0; - } - -//printf("copy1:%u copy2:%u both:%u\n",copy1size, copy2size, copy2size+copy1size); - //truncate the copy sizes to however much we've been been given to fill -------- - - if(copy1size + copy2size < bytes_to_copy) - //this shouldn't happen as the emulator core generally asks how much - //space is available before sending stuff, but just incase - printf("NDOSXUpdateAudio() error: not enough space in buffer"); - else - { - u32 diff = copy1size + copy2size - bytes_to_copy; - - if(diff <= copy2size) - { - copy2size -= diff; - } else - { - diff -= copy2size; - copy2size = 0; - copy1size -= diff; - } - - } - - //copy the data ------------------------------------------------------------- - - memcpy((((u8 *)sound_data)+sound_offset), buffer, copy1size); - - if(copy2size) - memcpy(sound_data, ((u8 *)buffer)+copy1size, copy2size); - - //change our sound_offset - sound_offset += copy1size + copy2size; - while(sound_offset >= sound_buffer_size)sound_offset -= sound_buffer_size; - -//printf("cop1%u cop2%u off%u size%u \n", copy1size, copy2size, sound_offset, sound_buffer_size); -} - -////////////////////////////////////////////////////////////////////////////// - -u32 SNDOSXGetAudioSpace() -{ - if(sound_data == NULL)return 0; - - int sound_pos = sound_position; //incase the sound render thread changes it - - u32 free_space; - - if(sound_offset > sound_pos) - free_space = sound_buffer_size + sound_pos - 1 - sound_offset; - else if(sound_offset < sound_pos) - free_space = sound_pos - sound_offset - 1; - else - free_space = sound_buffer_size-1; //sound_offset == sound_data, meaning the buffer is empty - - return (free_space / SAMPLE_SIZE); -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDOSXMuteAudio() -{ - if(sound_data == NULL)return; - - OSStatus err = AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, 0, 0); - - if(err != noErr) - printf("SNDOSXMuteAudio error\n"); -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDOSXUnMuteAudio() -{ - if(sound_data == NULL)return; - - OSStatus err = AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, current_volume_scalar, 0); - - if(err != noErr) - printf("SNDOSXUnMuteAudio %f error\n",current_volume_scalar); -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDOSXSetVolume(int volume) -{ - if(sound_data == NULL)return; - - if(volume > 100)volume = 100; - if(volume < 0)volume = 0; - - current_volume_scalar = volume; - current_volume_scalar /= 100.0; - - OSStatus err = AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, current_volume_scalar, 0); - - if(err != noErr) - printf("SNDOSXSetVolume %f error\n",current_volume_scalar); -} - -////////////////////////////////////////////////////////////////////////////// - -SoundInterface_struct SNDOSX = { - SNDCORE_OSX, - "Core Audio Sound Interface", - SNDOSXInit, - SNDOSXDeInit, - SNDOSXUpdateAudio, - SNDOSXGetAudioSpace, - SNDOSXMuteAudio, - SNDOSXUnMuteAudio, - SNDOSXSetVolume -}; - -////////////////////////////////////////////////////////////////////////////// -//Sound recording -////////////////////////////////////////////////////////////////////////////// - -bool SNDOSXOpenFile(void *fname) -{ - if(sound_data == NULL)return false; - - SNDOSXCloseFile(); - - if(!fname)return false; - - NSString *filename = (NSString*)fname; - FSRef ref; - - if(FSPathMakeRef((const UInt8*)[[filename stringByDeletingLastPathComponent] fileSystemRepresentation], &ref, NULL) != noErr) - { - SNDOSXStopRecording(); - return false; - } - - AudioStreamBasicDescription audio_format; - audio_format.mSampleRate = 44100; - audio_format.mFormatID = kAudioFormatLinearPCM; - audio_format.mFormatFlags = kAudioFormatFlagIsSignedInteger - | kAudioFormatFlagsNativeEndian - | kLinearPCMFormatFlagIsPacked; - audio_format.mBytesPerPacket = 4; - audio_format.mFramesPerPacket = 1; - audio_format.mBytesPerFrame = 4; - audio_format.mChannelsPerFrame = 2; - audio_format.mBitsPerChannel = 16; -/* - if(ExtAudioFileCreateNew(&ref, (CFStringRef)[[filename pathComponents] lastObject], kAudioFileWAVEType, &audio_format, NULL, &outfile) != noErr) - return false; - - file_open = true; - - return true; -*/ -return false; -} - -void SNDOSXStartRecording() -{ - if(sound_data == NULL)return; -} - -void SNDOSXStopRecording() -{ - if(sound_data == NULL)return; -} - -void SNDOSXCloseFile() -{ - if(sound_data == NULL)return; - - if(file_open) - { - file_open = false; - - //if it's rendering sound, wait until it's not - //so we dont close the file while writing to it - while(in_mix); - - //ExtAudioFileDispose(outfile); - } -} - -///////////////////////////////////////////////////////////////////////////