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.
This commit is contained in:
rogerman 2012-01-04 21:10:19 +00:00
parent 861308954f
commit 719e210de6
4 changed files with 1122 additions and 483 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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
};

View File

@ -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 <pthread.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "sndOSX.h"
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include <pthread.h>
#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);
}
///////////////////////////////////////////////////////////////////////////

View File

@ -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 <Cocoa/Cocoa.h>
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
//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);
}
}
///////////////////////////////////////////////////////////////////////////