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);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////