From 8a7fd2445f7d2dac69e68415aa43c9213c052098 Mon Sep 17 00:00:00 2001 From: rogerman Date: Sat, 3 Mar 2012 02:16:24 +0000 Subject: [PATCH] Cocoa Port: - Refactor audio playback code for better code modularity. - Improve overall performance of audio playback. - Fix compile issue with the legacy build. - Add a method for setting the emulator core's file paths directly by using a dictionary. This method can be used as an alternative to reading FileTypeInfo.plist. - Add file handling support for the upcoming OpenEmu Plug-in build target. --- .../project.pbxproj | 24 + .../project.pbxproj | 18 +- .../project.pbxproj | 25 +- desmume/src/cocoa/FileTypeInfo.plist | 7 + desmume/src/cocoa/cocoa_file.h | 5 + desmume/src/cocoa/cocoa_file.mm | 255 +++++- desmume/src/cocoa/cocoa_globals.h | 6 +- desmume/src/cocoa/cocoa_output.h | 4 - desmume/src/cocoa/cocoa_output.mm | 91 +-- desmume/src/cocoa/coreaudiosound.cpp | 211 +++++ desmume/src/cocoa/coreaudiosound.h | 58 ++ desmume/src/cocoa/nds_control_legacy.mm | 6 - desmume/src/cocoa/ringbuffer.cpp | 184 +++++ desmume/src/cocoa/ringbuffer.h | 49 ++ desmume/src/cocoa/sndOSX.cpp | 736 ++---------------- desmume/src/cocoa/sndOSX.h | 37 +- .../src/cocoa/userinterface/appDelegate.mm | 3 +- .../cocoa/userinterface/appDelegate_legacy.mm | 4 - 18 files changed, 936 insertions(+), 787 deletions(-) create mode 100644 desmume/src/cocoa/coreaudiosound.cpp create mode 100644 desmume/src/cocoa/coreaudiosound.h create mode 100644 desmume/src/cocoa/ringbuffer.cpp create mode 100644 desmume/src/cocoa/ringbuffer.h diff --git a/desmume/src/cocoa/DeSmuME (Legacy).xcodeproj/project.pbxproj b/desmume/src/cocoa/DeSmuME (Legacy).xcodeproj/project.pbxproj index f6f0c4f0e..872af2f0a 100644 --- a/desmume/src/cocoa/DeSmuME (Legacy).xcodeproj/project.pbxproj +++ b/desmume/src/cocoa/DeSmuME (Legacy).xcodeproj/project.pbxproj @@ -392,6 +392,14 @@ ABBF053114B5436E00E505A0 /* cocoa_file.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABBF052F14B5436E00E505A0 /* cocoa_file.mm */; }; ABBF053214B5436E00E505A0 /* cocoa_file.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABBF052F14B5436E00E505A0 /* cocoa_file.mm */; }; ABBF053314B5436E00E505A0 /* cocoa_file.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABBF052F14B5436E00E505A0 /* cocoa_file.mm */; }; + ABD0A5661501AC5C0074A094 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5621501AC5C0074A094 /* coreaudiosound.cpp */; }; + ABD0A5671501AC5C0074A094 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5631501AC5C0074A094 /* ringbuffer.cpp */; }; + ABD0A5681501AC5C0074A094 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5621501AC5C0074A094 /* coreaudiosound.cpp */; }; + ABD0A5691501AC5C0074A094 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5631501AC5C0074A094 /* ringbuffer.cpp */; }; + ABD0A56A1501AC5C0074A094 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5621501AC5C0074A094 /* coreaudiosound.cpp */; }; + ABD0A56B1501AC5C0074A094 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5631501AC5C0074A094 /* ringbuffer.cpp */; }; + ABD0A56C1501AC5C0074A094 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5621501AC5C0074A094 /* coreaudiosound.cpp */; }; + ABD0A56D1501AC5C0074A094 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5631501AC5C0074A094 /* ringbuffer.cpp */; }; ABE240FF14BE3169006EA2D5 /* cocoa_input_legacy.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABE240F414BE3169006EA2D5 /* cocoa_input_legacy.mm */; }; ABE2410014BE3169006EA2D5 /* input_legacy.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABE240F614BE3169006EA2D5 /* input_legacy.mm */; }; ABE2410114BE3169006EA2D5 /* nds_control_legacy.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABE240F814BE3169006EA2D5 /* nds_control_legacy.mm */; }; @@ -787,6 +795,10 @@ ABBF04CD14B51BC900E505A0 /* Romanian */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Romanian; path = translations/Romanian.lproj/Localizable.strings; sourceTree = ""; }; ABBF052F14B5436E00E505A0 /* cocoa_file.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = cocoa_file.mm; sourceTree = ""; }; ABBF053B14B543B600E505A0 /* cocoa_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_file.h; sourceTree = ""; }; + ABD0A5621501AC5C0074A094 /* coreaudiosound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coreaudiosound.cpp; sourceTree = ""; }; + ABD0A5631501AC5C0074A094 /* ringbuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ringbuffer.cpp; sourceTree = ""; }; + ABD0A5641501AC5C0074A094 /* coreaudiosound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coreaudiosound.h; sourceTree = ""; }; + ABD0A5651501AC5C0074A094 /* ringbuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ringbuffer.h; sourceTree = ""; }; ABE240E514BE30FC006EA2D5 /* Info (Legacy Debug).plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info (Legacy Debug).plist"; sourceTree = ""; }; ABE240E614BE30FC006EA2D5 /* Info (Legacy).plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info (Legacy).plist"; sourceTree = ""; }; ABE240F314BE3169006EA2D5 /* cocoa_input_legacy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_input_legacy.h; sourceTree = ""; }; @@ -870,15 +882,19 @@ isa = PBXGroup; children = ( ABC3ADEC14B7DC6E00D5B13D /* userinterface */, + ABD0A5621501AC5C0074A094 /* coreaudiosound.cpp */, + ABD0A5631501AC5C0074A094 /* ringbuffer.cpp */, ABF4007E14B4F1C000578AE7 /* sndOSX.cpp */, ABBF053B14B543B600E505A0 /* cocoa_file.h */, ABF95B4714B4F4FC007912B8 /* cocoa_globals.h */, ABE240F314BE3169006EA2D5 /* cocoa_input_legacy.h */, AB8FE37414B652EC009E20B1 /* cocoa_util.h */, + ABD0A5641501AC5C0074A094 /* coreaudiosound.h */, ABE240F514BE3169006EA2D5 /* input_legacy.h */, AB8FE30A14B647D6009E20B1 /* macosx_10_4_compat.h */, ABE240F714BE3169006EA2D5 /* nds_control_legacy.h */, ABE240F914BE3169006EA2D5 /* preferences_legacy.h */, + ABD0A5651501AC5C0074A094 /* ringbuffer.h */, ABE240FB14BE3169006EA2D5 /* screen_state_legacy.h */, AB06CB49135B8A4D00E977B3 /* sndOSX.h */, ABE240FD14BE3169006EA2D5 /* video_output_view_legacy.h */, @@ -1744,6 +1760,8 @@ ABE2410E14BE3169006EA2D5 /* preferences_legacy.mm in Sources */, ABE2410F14BE3169006EA2D5 /* screen_state_legacy.m in Sources */, ABE2411014BE3169006EA2D5 /* video_output_view_legacy.mm in Sources */, + ABD0A5681501AC5C0074A094 /* coreaudiosound.cpp in Sources */, + ABD0A5691501AC5C0074A094 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1851,6 +1869,8 @@ ABE2411414BE3169006EA2D5 /* preferences_legacy.mm in Sources */, ABE2411514BE3169006EA2D5 /* screen_state_legacy.m in Sources */, ABE2411614BE3169006EA2D5 /* video_output_view_legacy.mm in Sources */, + ABD0A56C1501AC5C0074A094 /* coreaudiosound.cpp in Sources */, + ABD0A56D1501AC5C0074A094 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1958,6 +1978,8 @@ ABE2410214BE3169006EA2D5 /* preferences_legacy.mm in Sources */, ABE2410314BE3169006EA2D5 /* screen_state_legacy.m in Sources */, ABE2410414BE3169006EA2D5 /* video_output_view_legacy.mm in Sources */, + ABD0A5661501AC5C0074A094 /* coreaudiosound.cpp in Sources */, + ABD0A5671501AC5C0074A094 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2065,6 +2087,8 @@ ABE2410814BE3169006EA2D5 /* preferences_legacy.mm in Sources */, ABE2410914BE3169006EA2D5 /* screen_state_legacy.m in Sources */, ABE2410A14BE3169006EA2D5 /* video_output_view_legacy.mm in Sources */, + ABD0A56A1501AC5C0074A094 /* coreaudiosound.cpp in Sources */, + ABD0A56B1501AC5C0074A094 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/desmume/src/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj b/desmume/src/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj index 6f884c4e8..b321508f6 100644 --- a/desmume/src/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj +++ b/desmume/src/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj @@ -296,6 +296,10 @@ ABC5720D1344346600E7B0B1 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97324FDCFA39411CA2CEA /* AppKit.framework */; }; ABC572101344347000E7B0B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97325FDCFA39411CA2CEA /* Foundation.framework */; }; ABC719E2138CB25E002827A9 /* DefaultKeyMappings.plist in Resources */ = {isa = PBXBuildFile; fileRef = ABC719E1138CB25E002827A9 /* DefaultKeyMappings.plist */; }; + ABD0A5381501AA5A0074A094 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5341501AA5A0074A094 /* coreaudiosound.cpp */; }; + ABD0A5391501AA5A0074A094 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5351501AA5A0074A094 /* ringbuffer.cpp */; }; + ABD0A53A1501AA5A0074A094 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5341501AA5A0074A094 /* coreaudiosound.cpp */; }; + ABD0A53B1501AA5A0074A094 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD0A5351501AA5A0074A094 /* ringbuffer.cpp */; }; ABD1041C1346652500AF11D1 /* cocoa_input.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABD104111346652500AF11D1 /* cocoa_input.mm */; }; ABD1041D1346652500AF11D1 /* cocoa_core.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABD104121346652500AF11D1 /* cocoa_core.mm */; }; ABD1041E1346652500AF11D1 /* cocoa_rom.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABD104131346652500AF11D1 /* cocoa_rom.mm */; }; @@ -530,6 +534,10 @@ ABC570D0134431CE00E7B0B1 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; ABC570D4134431DA00E7B0B1 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; ABC719E1138CB25E002827A9 /* DefaultKeyMappings.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DefaultKeyMappings.plist; sourceTree = ""; }; + ABD0A5341501AA5A0074A094 /* coreaudiosound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coreaudiosound.cpp; sourceTree = ""; }; + ABD0A5351501AA5A0074A094 /* ringbuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ringbuffer.cpp; sourceTree = ""; }; + ABD0A5361501AA5A0074A094 /* coreaudiosound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coreaudiosound.h; sourceTree = ""; }; + ABD0A5371501AA5A0074A094 /* ringbuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ringbuffer.h; sourceTree = ""; }; ABD103FE1346652500AF11D1 /* cocoa_core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_core.h; sourceTree = ""; }; ABD103FF1346652500AF11D1 /* cocoa_input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_input.h; sourceTree = ""; }; ABD104001346652500AF11D1 /* cocoa_rom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_rom.h; sourceTree = ""; }; @@ -776,6 +784,8 @@ isa = PBXGroup; children = ( AB3ACB6514C2361100D7D192 /* userinterface */, + ABD0A5341501AA5A0074A094 /* coreaudiosound.cpp */, + ABD0A5351501AA5A0074A094 /* ringbuffer.cpp */, ABD104141346652500AF11D1 /* sndOSX.cpp */, AB817A35143EE2DB00A7DFE9 /* videofilter.cpp */, ABA6574914511EC90077E5E9 /* cocoa_cheat.h */, @@ -790,13 +800,15 @@ ABD104001346652500AF11D1 /* cocoa_rom.h */, AB80E050142BC4FA00A52038 /* cocoa_util.h */, ABE5DFE3143FB1DA00835AD8 /* cocoa_videofilter.h */, + ABD0A5361501AA5A0074A094 /* coreaudiosound.h */, + ABD0A5371501AA5A0074A094 /* ringbuffer.h */, ABD104011346652500AF11D1 /* sndOSX.h */, AB817A34143EE2DB00A7DFE9 /* videofilter.h */, - AB350B691478A5B3007165AC /* cocoa_hid.mm */, ABA6574A14511EC90077E5E9 /* cocoa_cheat.mm */, ABD104121346652500AF11D1 /* cocoa_core.mm */, AB58F32C1364F44B0074C376 /* cocoa_file.mm */, ABE7F53D13EE1C7900FD3A71 /* cocoa_firmware.mm */, + AB350B691478A5B3007165AC /* cocoa_hid.mm */, ABD104111346652500AF11D1 /* cocoa_input.mm */, ABD9A46413DB99B300777194 /* cocoa_mic.mm */, AB3E34C8134AF4500056477A /* cocoa_output.mm */, @@ -1665,6 +1677,8 @@ ABFE150B14C92FF5005D6699 /* hq4x.cpp in Sources */, ABFE150D14C92FF5005D6699 /* lq2x.cpp in Sources */, ABFE150E14C92FF5005D6699 /* scanline.cpp in Sources */, + ABD0A5381501AA5A0074A094 /* coreaudiosound.cpp in Sources */, + ABD0A5391501AA5A0074A094 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1787,6 +1801,8 @@ ABFE151314C92FF5005D6699 /* hq4x.cpp in Sources */, ABFE151514C92FF5005D6699 /* lq2x.cpp in Sources */, ABFE151614C92FF5005D6699 /* scanline.cpp in Sources */, + ABD0A53A1501AA5A0074A094 /* coreaudiosound.cpp in Sources */, + ABD0A53B1501AA5A0074A094 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/desmume/src/cocoa/DeSmuME (XCode 4).xcodeproj/project.pbxproj b/desmume/src/cocoa/DeSmuME (XCode 4).xcodeproj/project.pbxproj index 9b985bd70..06bba96c1 100644 --- a/desmume/src/cocoa/DeSmuME (XCode 4).xcodeproj/project.pbxproj +++ b/desmume/src/cocoa/DeSmuME (XCode 4).xcodeproj/project.pbxproj @@ -104,6 +104,12 @@ AB0F29C914BE7213009ABC6F /* Icon_Speaker_420x420.png in Resources */ = {isa = PBXBuildFile; fileRef = AB0F29A514BE7213009ABC6F /* Icon_Speaker_420x420.png */; }; AB15CA7214A29EE500B8A6A4 /* cpu_detect_x86_gcc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABAD3E6613AF1D6D00502E1E /* cpu_detect_x86_gcc.cpp */; }; AB181D0013B66889006CA82D /* datetime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD1FF1F1345ACBF00AF11D1 /* datetime.cpp */; }; + AB1B9E631501A78000464647 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB1B9E5F1501A78000464647 /* coreaudiosound.cpp */; }; + AB1B9E641501A78000464647 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB1B9E5F1501A78000464647 /* coreaudiosound.cpp */; }; + AB1B9E651501A78000464647 /* coreaudiosound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB1B9E5F1501A78000464647 /* coreaudiosound.cpp */; }; + AB1B9E661501A78000464647 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB1B9E601501A78000464647 /* ringbuffer.cpp */; }; + AB1B9E671501A78000464647 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB1B9E601501A78000464647 /* ringbuffer.cpp */; }; + AB1B9E681501A78000464647 /* ringbuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB1B9E601501A78000464647 /* ringbuffer.cpp */; }; AB1F468413A0ADE400B80DE6 /* SndOut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD1FF7A1345ACFA00AF11D1 /* SndOut.cpp */; }; AB1F469813A0AE2F00B80DE6 /* Timestretcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD1FF9B1345ACFA00AF11D1 /* Timestretcher.cpp */; }; AB350B6A1478A5B3007165AC /* cocoa_hid.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB350B691478A5B3007165AC /* cocoa_hid.mm */; }; @@ -648,6 +654,10 @@ AB0F29A314BE7213009ABC6F /* Icon_RotateCW_420x420.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon_RotateCW_420x420.png; path = images/Icon_RotateCW_420x420.png; sourceTree = ""; }; AB0F29A414BE7213009ABC6F /* Icon_ShowHUD_420x420.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon_ShowHUD_420x420.png; path = images/Icon_ShowHUD_420x420.png; sourceTree = ""; }; AB0F29A514BE7213009ABC6F /* Icon_Speaker_420x420.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon_Speaker_420x420.png; path = images/Icon_Speaker_420x420.png; sourceTree = ""; }; + AB1B9E5F1501A78000464647 /* coreaudiosound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coreaudiosound.cpp; sourceTree = SOURCE_ROOT; }; + AB1B9E601501A78000464647 /* ringbuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ringbuffer.cpp; sourceTree = SOURCE_ROOT; }; + AB1B9E611501A78000464647 /* coreaudiosound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coreaudiosound.h; sourceTree = SOURCE_ROOT; }; + AB1B9E621501A78000464647 /* ringbuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ringbuffer.h; sourceTree = SOURCE_ROOT; }; AB350B681478A5B3007165AC /* cocoa_hid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_hid.h; sourceTree = ""; }; AB350B691478A5B3007165AC /* cocoa_hid.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = cocoa_hid.mm; sourceTree = ""; }; AB350BA41478AC96007165AC /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; @@ -985,6 +995,8 @@ isa = PBXGroup; children = ( AB3ACB6514C2361100D7D192 /* userinterface */, + AB1B9E5F1501A78000464647 /* coreaudiosound.cpp */, + AB1B9E601501A78000464647 /* ringbuffer.cpp */, ABD104141346652500AF11D1 /* sndOSX.cpp */, AB817A35143EE2DB00A7DFE9 /* videofilter.cpp */, ABA6574914511EC90077E5E9 /* cocoa_cheat.h */, @@ -999,13 +1011,15 @@ ABD104001346652500AF11D1 /* cocoa_rom.h */, AB80E050142BC4FA00A52038 /* cocoa_util.h */, ABE5DFE3143FB1DA00835AD8 /* cocoa_videofilter.h */, + AB1B9E611501A78000464647 /* coreaudiosound.h */, + AB1B9E621501A78000464647 /* ringbuffer.h */, ABD104011346652500AF11D1 /* sndOSX.h */, AB817A34143EE2DB00A7DFE9 /* videofilter.h */, - AB350B691478A5B3007165AC /* cocoa_hid.mm */, ABA6574A14511EC90077E5E9 /* cocoa_cheat.mm */, ABD104121346652500AF11D1 /* cocoa_core.mm */, AB58F32C1364F44B0074C376 /* cocoa_file.mm */, ABE7F53D13EE1C7900FD3A71 /* cocoa_firmware.mm */, + AB350B691478A5B3007165AC /* cocoa_hid.mm */, ABD104111346652500AF11D1 /* cocoa_input.mm */, ABD9A46413DB99B300777194 /* cocoa_mic.mm */, AB3E34C8134AF4500056477A /* cocoa_output.mm */, @@ -1554,7 +1568,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0420; + LastUpgradeCheck = 0430; ORGANIZATIONNAME = "DeSmuME Team"; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "DeSmuME (XCode 4)" */; @@ -1990,6 +2004,8 @@ ABFE150B14C92FF5005D6699 /* hq4x.cpp in Sources */, ABFE150D14C92FF5005D6699 /* lq2x.cpp in Sources */, ABFE150E14C92FF5005D6699 /* scanline.cpp in Sources */, + AB1B9E631501A78000464647 /* coreaudiosound.cpp in Sources */, + AB1B9E661501A78000464647 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2112,6 +2128,8 @@ ABFE151314C92FF5005D6699 /* hq4x.cpp in Sources */, ABFE151514C92FF5005D6699 /* lq2x.cpp in Sources */, ABFE151614C92FF5005D6699 /* scanline.cpp in Sources */, + AB1B9E641501A78000464647 /* coreaudiosound.cpp in Sources */, + AB1B9E671501A78000464647 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2234,6 +2252,8 @@ ABB715FB14E386740027FE88 /* hq4x.cpp in Sources */, ABB715FC14E386740027FE88 /* lq2x.cpp in Sources */, ABB715FD14E386740027FE88 /* scanline.cpp in Sources */, + AB1B9E651501A78000464647 /* coreaudiosound.cpp in Sources */, + AB1B9E681501A78000464647 /* ringbuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2389,7 +2409,6 @@ GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Info.plist; - LLVM_LTO = NO; PRODUCT_NAME = DeSmuME; SDKROOT = macosx10.7; STRIP_INSTALLED_PRODUCT = NO; diff --git a/desmume/src/cocoa/FileTypeInfo.plist b/desmume/src/cocoa/FileTypeInfo.plist index b2e9e62e1..19fcdf879 100644 --- a/desmume/src/cocoa/FileTypeInfo.plist +++ b/desmume/src/cocoa/FileTypeInfo.plist @@ -39,6 +39,13 @@ Save State ${APPSUPPORT} + OpenEmu + + ROM Save + ${OPENEMU} + Save State + ${OPENEMU} + DirectoryNames diff --git a/desmume/src/cocoa/cocoa_file.h b/desmume/src/cocoa/cocoa_file.h index 04a2b9f89..60f9794d9 100644 --- a/desmume/src/cocoa/cocoa_file.h +++ b/desmume/src/cocoa/cocoa_file.h @@ -28,6 +28,9 @@ } ++ (NSMutableDictionary *) URLDictionary; ++ (void) addURLToURLDictionary:(NSURL *)theURL groupKey:(NSString *)groupKey fileKind:(NSString *)fileKind; ++ (void) removeURLFromURLDictionaryByGroupKey:(NSString *)groupKey fileKind:(NSString *)fileKind; + (BOOL) loadState:(NSURL *)saveStateURL; + (BOOL) saveState:(NSURL *)saveStateURL; + (BOOL) loadRom:(NSURL *)romURL; @@ -38,6 +41,8 @@ + (BOOL) romSaveExists:(NSURL *)romURL; + (BOOL) romSaveExistsWithRom:(NSURL *)romURL; + (void) setupAllFilePaths; ++ (void) setupAllFilePathsWithURLDictionary:(NSString *)URLDictionaryKey; ++ (void) setupAllFilePathsForVersion:(NSString *)versionString port:(NSString *)portString; + (BOOL) setupAllAppDirectories; + (NSURL *) saveStateURL; + (BOOL) saveScreenshot:(NSURL *)fileURL bitmapData:(NSBitmapImageRep *)bitmapImageRep fileType:(NSBitmapImageFileType)fileType; diff --git a/desmume/src/cocoa/cocoa_file.mm b/desmume/src/cocoa/cocoa_file.mm index f0365fad9..d2ddcbcea 100644 --- a/desmume/src/cocoa/cocoa_file.mm +++ b/desmume/src/cocoa/cocoa_file.mm @@ -28,6 +28,110 @@ @implementation CocoaDSFile +// Global dictionary that can be used for storing URLs. +// +// Usually, it's best to add directory paths to FileTypeInfo.plist, and then +// use directoryByKind:version:port for getting a URL, but this method only +// works for application build targets. Other target types, such as plug-in +// targets, don't read FileTypeInfo.plist correctly, so we include a global +// URL dictionary so that we can manually set and get URLs at runtime. +static NSMutableDictionary *_gURLDictionary = nil; + +/******************************************************************************************** + URLDictionary + + Returns the global URL dictionary. + + Takes: + Nothing. + + Returns: + A reference to the NSMutableDictionary URLDictionary. + + Details: + This should always be used for getting the global URL dictionary. Never try to + reference the global URL dictionary directly, since this may change between + versions. The first time this method is called, it will automatically allocate + the memory for the global URL dictionary. + ********************************************************************************************/ ++ (NSMutableDictionary *) URLDictionary +{ + if (_gURLDictionary == nil) + { + _gURLDictionary = [[NSMutableDictionary alloc] initWithCapacity:8]; + } + + return _gURLDictionary; +} + +/******************************************************************************************** + addURLToURLDictionary:groupKey:fileKind: + + Adds a URL to the global URL dictionary. + + Takes: + theURL - An NSURL used to store a URL into the dictionary. + groupKey - An NSString that represents the key used to group a set URLs together. + fileKind - An NSString that represents the file type. + + Returns: + Nothing. + + Details: + This should always be used for adding a URL to the global URL dictionary. Never + try to add a URL directly, since this may change between versions. + ********************************************************************************************/ ++ (void) addURLToURLDictionary:(NSURL *)theURL groupKey:(NSString *)groupKey fileKind:(NSString *)fileKind +{ + if (theURL == nil || groupKey == nil || fileKind == nil) + { + return; + } + + NSMutableDictionary *urlDictionary = [CocoaDSFile URLDictionary]; + + NSMutableDictionary *groupDictionary = (NSMutableDictionary *)[urlDictionary valueForKey:groupKey]; + if (groupDictionary == nil) + { + groupDictionary = [NSMutableDictionary dictionaryWithCapacity:16]; + [urlDictionary setValue:groupDictionary forKey:groupKey]; + } + + [groupDictionary setValue:theURL forKey:fileKind]; +} + +/******************************************************************************************** + removeURLFromURLDictionaryByGroupKey:fileKind: + + Removes a URL from the global URL dictionary. + + Takes: + groupKey - An NSString that represents the key used to group a set URLs together. + fileKind - An NSString that represents the file type. + + Returns: + Nothing. + + Details: + This should always be used for removing a URL from the global URL dictionary. + Never try to remove a URL directly, since this may change between versions. + ********************************************************************************************/ ++ (void) removeURLFromURLDictionaryByGroupKey:(NSString *)groupKey fileKind:(NSString *)fileKind +{ + if (groupKey == nil || fileKind == nil) + { + return; + } + + NSMutableDictionary *urlDictionary = [CocoaDSFile URLDictionary]; + + NSMutableDictionary *groupDictionary = (NSMutableDictionary *)[urlDictionary valueForKey:groupKey]; + if (groupDictionary != nil) + { + [groupDictionary setValue:nil forKey:fileKind]; + } +} + + (BOOL) loadState:(NSURL *)saveStateURL { BOOL result = NO; @@ -107,6 +211,7 @@ switch (fileTypeID) { +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 case ROMSAVEFORMAT_DESMUME: { NSString *destinationPath = [[destinationURL path] stringByAppendingPathExtension:@FILE_EXT_ROM_SAVE]; @@ -115,7 +220,7 @@ [fileManager release]; break; } - +#endif case ROMSAVEFORMAT_NOGBA: { const char *destinationPath = [[[destinationURL path] stringByAppendingPathExtension:@FILE_EXT_ROM_SAVE_NOGBA] cStringUsingEncoding:NSUTF8StringEncoding]; @@ -191,57 +296,174 @@ return exists; } +/******************************************************************************************** + setupAllFilePaths + + Sets up all application file paths using the current version of the default port. + + Takes: + Nothing. + + Returns: + Nothing. + + Details: + This method uses setupAllFilePathsForVersion:port: for its implementation. + ********************************************************************************************/ + (void) setupAllFilePaths { - NSURL *romURL = [CocoaDSFile directoryURLByKind:@"ROM"]; + [CocoaDSFile setupAllFilePathsForVersion:nil port:nil]; +} + +/******************************************************************************************** + setupAllFilePathsForVersion:port: + + Sets up all application file paths, reading the paths from FileTypeInfo.plist. + + Takes: + versionString - An NSString that represents the application version. If nil is + used, this method assumes the current version. + portString - An NSString that represents the port version. If nil is used, this + method assumes the default port version. + + Returns: + Nothing. + + Details: + This is an Objective-C to C wrapper function for assigning file paths to the + emulation layer. + ********************************************************************************************/ ++ (void) setupAllFilePathsForVersion:(NSString *)versionString port:(NSString *)portString +{ + NSURL *romURL = [CocoaDSFile directoryURLByKind:@"ROM" version:versionString port:portString]; if (romURL != nil) { strlcpy(path.pathToRoms, [[romURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); } - NSURL *romSaveURL = [CocoaDSFile directoryURLByKind:@"ROM Save"]; + NSURL *romSaveURL = [CocoaDSFile directoryURLByKind:@"ROM Save" version:versionString port:portString]; if (romSaveURL != nil) { strlcpy(path.pathToBattery, [[romSaveURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); } - NSURL *saveStateURL = [CocoaDSFile directoryURLByKind:@"Save State"]; + NSURL *saveStateURL = [CocoaDSFile directoryURLByKind:@"Save State" version:versionString port:portString]; if (saveStateURL != nil) { strlcpy(path.pathToStates, [[saveStateURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); } - NSURL *screenshotURL = [CocoaDSFile directoryURLByKind:@"Screenshot"]; + NSURL *screenshotURL = [CocoaDSFile directoryURLByKind:@"Screenshot" version:versionString port:portString]; if (screenshotURL != nil) { strlcpy(path.pathToScreenshots, [[screenshotURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); } - NSURL *aviURL = [CocoaDSFile directoryURLByKind:@"Video"]; + NSURL *aviURL = [CocoaDSFile directoryURLByKind:@"Video" version:versionString port:portString]; if (aviURL != nil) { strlcpy(path.pathToAviFiles, [[aviURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); } - NSURL *cheatURL = [CocoaDSFile directoryURLByKind:@"Cheat"]; + NSURL *cheatURL = [CocoaDSFile directoryURLByKind:@"Cheat" version:versionString port:portString]; if (cheatURL != nil) { strlcpy(path.pathToCheats, [[cheatURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); } - NSURL *soundSamplesURL = [CocoaDSFile directoryURLByKind:@"Sound Sample"]; + NSURL *soundSamplesURL = [CocoaDSFile directoryURLByKind:@"Sound Sample" version:versionString port:portString]; if (soundSamplesURL != nil) { strlcpy(path.pathToSounds, [[soundSamplesURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); } - NSURL *firmwareURL = [CocoaDSFile directoryURLByKind:@"Firmware Configuration"]; + NSURL *firmwareURL = [CocoaDSFile directoryURLByKind:@"Firmware Configuration" version:versionString port:portString]; if (firmwareURL != nil) { strlcpy(path.pathToFirmware, [[firmwareURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); } - NSURL *luaURL = [CocoaDSFile directoryURLByKind:@"Lua Script"]; + NSURL *luaURL = [CocoaDSFile directoryURLByKind:@"Lua Script" version:versionString port:portString]; + if (luaURL != nil) + { + strlcpy(path.pathToLua, [[luaURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } +} + +/******************************************************************************************** + setupAllFilePathsWithURLDictionary: + + Sets up all application file paths using the global URLDictionary. This method exists + in the case where reading the paths from FileTypeInfo.plist impossible or impractical. + + Takes: + URLDictionaryKey - An NSString that is a group key to the URLDictionary. + + Returns: + Nothing. + + Details: + This is an Objective-C to C wrapper function for assigning file paths to the + emulation layer. + ********************************************************************************************/ ++ (void) setupAllFilePathsWithURLDictionary:(NSString *)URLDictionaryKey +{ + if (URLDictionaryKey == nil) + { + return; + } + + NSDictionary *URLDictionary = (NSDictionary *)[(NSDictionary *)[CocoaDSFile URLDictionary] valueForKey:URLDictionaryKey]; + + NSURL *romURL = (NSURL *)[URLDictionary valueForKey:@"ROM"]; + if (romURL != nil) + { + strlcpy(path.pathToRoms, [[romURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } + + NSURL *romSaveURL = (NSURL *)[URLDictionary valueForKey:@"ROM Save"]; + if (romSaveURL != nil) + { + strlcpy(path.pathToBattery, [[romSaveURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } + + NSURL *saveStateURL = (NSURL *)[URLDictionary valueForKey:@"Save State"]; + if (saveStateURL != nil) + { + strlcpy(path.pathToStates, [[saveStateURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } + + NSURL *screenshotURL = (NSURL *)[URLDictionary valueForKey:@"Screenshot"]; + if (screenshotURL != nil) + { + strlcpy(path.pathToScreenshots, [[screenshotURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } + + NSURL *aviURL = (NSURL *)[URLDictionary valueForKey:@"Video"]; + if (aviURL != nil) + { + strlcpy(path.pathToAviFiles, [[aviURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } + + NSURL *cheatURL = (NSURL *)[URLDictionary valueForKey:@"Cheat"]; + if (cheatURL != nil) + { + strlcpy(path.pathToCheats, [[cheatURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } + + NSURL *soundSamplesURL = (NSURL *)[URLDictionary valueForKey:@"Sound Sample"]; + if (soundSamplesURL != nil) + { + strlcpy(path.pathToSounds, [[soundSamplesURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } + + NSURL *firmwareURL = (NSURL *)[URLDictionary valueForKey:@"Firmware Configuration"]; + if (firmwareURL != nil) + { + strlcpy(path.pathToFirmware, [[firmwareURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); + } + + NSURL *luaURL = (NSURL *)[URLDictionary valueForKey:@"Lua Script"]; if (luaURL != nil) { strlcpy(path.pathToLua, [[luaURL path] cStringUsingEncoding:NSUTF8StringEncoding], MAX_PATH); @@ -283,6 +505,11 @@ result = NO; } } + else if ([dirPath isEqualToString:@PATH_OPEN_EMU]) + { + // OpenEmu uses its own directory structure, so no need to setup here. + continue; + } else if ([dirPath isEqualToString:@PATH_WITH_ROM]) { continue; @@ -583,6 +810,14 @@ { fileURL = [CocoaDSFile userAppSupportURL:dirName version:lookupVersionStr]; } + else if ([dirPath isEqualToString:@PATH_OPEN_EMU]) + { + NSMutableDictionary *urlDictionary = (NSMutableDictionary *)[[CocoaDSFile URLDictionary] valueForKey:dirPath]; + if (urlDictionary != nil) + { + fileURL = (NSURL *)[urlDictionary valueForKey:fileKind]; + } + } else if ([dirPath isEqualToString:@PATH_WITH_ROM]) { return fileURL; diff --git a/desmume/src/cocoa/cocoa_globals.h b/desmume/src/cocoa/cocoa_globals.h index a1cecc660..2bdf79892 100644 --- a/desmume/src/cocoa/cocoa_globals.h +++ b/desmume/src/cocoa/cocoa_globals.h @@ -113,6 +113,7 @@ #define PATH_CONFIG_DIRECTORY_0_9_6 "~/.config/desmume" #define PATH_USER_APP_SUPPORT "${APPSUPPORT}" +#define PATH_OPEN_EMU "${OPENEMU}" #define PATH_WITH_ROM "${WITHROM}" #define FILE_EXT_FIRMWARE_CONFIG "dfc" @@ -162,8 +163,9 @@ #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 SPU_NUMBER_CHANNELS 2 // Number of channels +#define SPU_SAMPLE_SIZE ((SPU_SAMPLE_RESOLUTION / 8) * SPU_NUMBER_CHANNELS) // Bytes per sample, multiplied by the number of channels +#define SPU_BUFFER_BYTES ((SPU_SAMPLE_RATE / DS_FRAMES_PER_SECOND) * SPU_SAMPLE_SIZE) #define CLOCKWISE_DEGREES(x) (360.0 - x) // Converts an angle in degrees from normal-direction to clockwise-direction. diff --git a/desmume/src/cocoa/cocoa_output.h b/desmume/src/cocoa/cocoa_output.h index 819cb75af..bd73d8eae 100644 --- a/desmume/src/cocoa/cocoa_output.h +++ b/desmume/src/cocoa/cocoa_output.h @@ -62,10 +62,6 @@ - (id) initWithVolume:(CGFloat)vol; - (void) dealloc; -+ (BOOL) startupSPU; -+ (void) shutdownSPU; -+ (BOOL) isSPUStarted; - - (void) setVolume:(float)vol; - (float) volume; - (void) setAudioOutputEngine:(NSInteger)methodID; diff --git a/desmume/src/cocoa/cocoa_output.mm b/desmume/src/cocoa/cocoa_output.mm index 8ed4fce27..73f1641c7 100644 --- a/desmume/src/cocoa/cocoa_output.mm +++ b/desmume/src/cocoa/cocoa_output.mm @@ -22,7 +22,6 @@ #import "cocoa_util.h" #include -#include "sndOSX.h" #include "../NDSSystem.h" #include "../GPU.h" @@ -40,12 +39,6 @@ GPU3DInterface *core3DList[] = { NULL }; -SoundInterface_struct *SNDCoreList[] = { - &SNDDummy, - &SNDOSX, - NULL -}; - @implementation CocoaDSOutput @synthesize isStateChanged; @@ -133,8 +126,6 @@ SoundInterface_struct *SNDCoreList[] = { @synthesize bufferSize; -static BOOL isSPUStarted = NO; - - (id)init { return [self initWithVolume:MAX_VOLUME]; @@ -161,7 +152,7 @@ static BOOL isSPUStarted = NO; [property setValue:[NSNumber numberWithFloat:(float)vol] forKey:@"volume"]; [property setValue:[NSNumber numberWithBool:NO] forKey:@"mute"]; [property setValue:[NSNumber numberWithInteger:0] forKey:@"filter"]; - [property setValue:[NSNumber numberWithInteger:SNDCORE_OSX] forKey:@"audioOutputEngine"]; + [property setValue:[NSNumber numberWithInteger:SNDCORE_DUMMY] forKey:@"audioOutputEngine"]; [property setValue:[NSNumber numberWithBool:NO] forKey:@"spuAdvancedLogic"]; [property setValue:[NSNumber numberWithInteger:SPUInterpolation_None] forKey:@"spuInterpolationMode"]; [property setValue:[NSNumber numberWithInteger:SPU_SYNC_MODE_DUAL_SYNC_ASYNC] forKey:@"spuSyncMode"]; @@ -175,43 +166,6 @@ static BOOL isSPUStarted = NO; [super dealloc]; } -+ (BOOL) startupSPU -{ - NSInteger result = -1; - - if (isSPUStarted) - { - return isSPUStarted; - } - - SNDOSXStartup(); - - result = SPU_ChangeSoundCore(SNDCORE_OSX, (int)SPU_BUFFER_BYTES); - if(result == -1) - { - SPU_ChangeSoundCore(SNDCORE_DUMMY, 0); - isSPUStarted = NO; - return isSPUStarted; - } - - SPU_SetVolume(0); - isSPUStarted = YES; - - return isSPUStarted; -} - -+ (void) shutdownSPU -{ - SPU_ChangeSoundCore(SNDCORE_DUMMY, 0); - SNDOSXShutdown(); - isSPUStarted = NO; -} - -+ (BOOL) isSPUStarted -{ - return isSPUStarted; -} - - (void) setVolume:(float)vol { if (vol < 0.0f) @@ -227,12 +181,7 @@ static BOOL isSPUStarted = NO; [property setValue:[NSNumber numberWithFloat:vol] forKey:@"volume"]; OSSpinLockUnlock(&spinlockVolume); - if (isSPUStarted) - { - //pthread_mutex_lock(self.mutexOutputFrame); - SPU_SetVolume((int)vol); - //pthread_mutex_unlock(self.mutexOutputFrame); - } + SPU_SetVolume((int)vol); } - (float) volume @@ -253,15 +202,10 @@ static BOOL isSPUStarted = NO; pthread_mutex_lock(self.mutexOutputFrame); NSInteger result = -1; - switch (methodID) + + if (methodID != SNDCORE_DUMMY) { - case SNDCORE_OSX: - result = SPU_ChangeSoundCore(methodID, (int)SPU_BUFFER_BYTES); - break; - - default: - SPU_ChangeSoundCore(SNDCORE_DUMMY, 0); - break; + result = SPU_ChangeSoundCore(methodID, (int)SPU_BUFFER_BYTES); } if(result == -1) @@ -377,16 +321,13 @@ static BOOL isSPUStarted = NO; { [property setValue:[NSNumber numberWithBool:mute] forKey:@"mute"]; - if (isSPUStarted) + if (mute) { - if (mute) - { - SPU_SetVolume(0); - } - else - { - SPU_SetVolume((int)[self volume]); - } + SPU_SetVolume(0); + } + else + { + SPU_SetVolume((int)[self volume]); } } @@ -400,16 +341,6 @@ static BOOL isSPUStarted = NO; [property setValue:[NSNumber numberWithInteger:filter] forKey:@"filter"]; } -- (void) runThread:(id)object -{ - [CocoaDSSpeaker startupSPU]; - SPU_SetVolume((int)[self volume]); - - [super runThread:object]; - - [CocoaDSSpeaker shutdownSPU]; -} - - (void)handlePortMessage:(NSPortMessage*)portMessage { NSInteger message = (NSInteger)[portMessage msgid]; diff --git a/desmume/src/cocoa/coreaudiosound.cpp b/desmume/src/cocoa/coreaudiosound.cpp new file mode 100644 index 000000000..2c9f9040f --- /dev/null +++ b/desmume/src/cocoa/coreaudiosound.cpp @@ -0,0 +1,211 @@ +/* + Copyright (C) 2012 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 "coreaudiosound.h" +#include "cocoa_globals.h" + +CoreAudioSound::CoreAudioSound(size_t bufferSamples, size_t sampleSize) +{ + OSStatus error = noErr; + + _spinlockAU = (OSSpinLock *)malloc(sizeof(OSSpinLock)); + *_spinlockAU = OS_SPINLOCK_INIT; + + _buffer = new RingBuffer(bufferSamples, sampleSize); + _volume = 1.0f; + + // Create a new 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; + } + + error = OpenAComponent(comp, &_au); + if (comp == NULL) + { + return; + } + + // Set the render callback + AURenderCallbackStruct callback; + callback.inputProc = &RenderCallback; + callback.inputProcRefCon = _buffer; + + error = AudioUnitSetProperty(_au, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &callback, + sizeof(callback) ); + + if(error != noErr) + { + return; + } + + // Set up the audio unit for audio streaming + AudioStreamBasicDescription audio_format; + audio_format.mSampleRate = SPU_SAMPLE_RATE; + audio_format.mFormatID = kAudioFormatLinearPCM; + audio_format.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked; + audio_format.mBytesPerPacket = SPU_SAMPLE_SIZE; + audio_format.mFramesPerPacket = 1; + audio_format.mBytesPerFrame = SPU_SAMPLE_SIZE; + audio_format.mChannelsPerFrame = SPU_NUMBER_CHANNELS; + audio_format.mBitsPerChannel = SPU_SAMPLE_RESOLUTION; + + error = AudioUnitSetProperty(_au, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &audio_format, + sizeof(audio_format) ); + + if(error != noErr) + { + return; + } + + // Initialize our new audio unit + error = AudioUnitInitialize(_au); + if(error != noErr) + { + return; + } +} + +CoreAudioSound::~CoreAudioSound() +{ + OSSpinLockLock(_spinlockAU); + + if(_au != NULL) + { + AudioOutputUnitStop(_au); + AudioUnitUninitialize(_au); + _au = NULL; + } + + OSSpinLockUnlock(_spinlockAU); + + delete _buffer; + _buffer = NULL; + + free(_spinlockAU); + _spinlockAU = NULL; +} + +RingBuffer* CoreAudioSound::getBuffer() +{ + return this->_buffer; +} + +void CoreAudioSound::start() +{ + this->clearBuffer(); + + OSSpinLockLock(this->_spinlockAU); + AudioUnitReset(this->_au, kAudioUnitScope_Global, 0); + AudioOutputUnitStart(this->_au); + OSSpinLockUnlock(this->_spinlockAU); +} + +void CoreAudioSound::stop() +{ + OSSpinLockLock(this->_spinlockAU); + AudioOutputUnitStop(this->_au); + OSSpinLockUnlock(this->_spinlockAU); + + this->clearBuffer(); +} + +void CoreAudioSound::writeToBuffer(const void *buffer, size_t numberBytes) +{ + this->getBuffer()->write(buffer, numberBytes); +} + +void CoreAudioSound::clearBuffer() +{ + this->_buffer->clear(); +} + +void CoreAudioSound::mute() +{ + OSSpinLockLock(this->_spinlockAU); + AudioUnitSetParameter(this->_au, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, 0.0f, 0); + OSSpinLockUnlock(this->_spinlockAU); +} + +void CoreAudioSound::unmute() +{ + OSSpinLockLock(this->_spinlockAU); + AudioUnitSetParameter(this->_au, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, this->_volume, 0); + OSSpinLockUnlock(this->_spinlockAU); +} + +size_t CoreAudioSound::getAvailableSamples() +{ + return this->_buffer->getAvailableElements(); +} + +float CoreAudioSound::getVolume() +{ + return this->_volume; +} + +void CoreAudioSound::setVolume(float vol) +{ + this->_volume = vol; + + OSSpinLockLock(this->_spinlockAU); + AudioUnitSetParameter(this->_au, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, vol, 0); + OSSpinLockUnlock(this->_spinlockAU); +} + +OSStatus RenderCallback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + RingBuffer *__restrict__ audioBuffer = (RingBuffer *)inRefCon; + UInt8 *__restrict__ playbackBuffer = (UInt8 *)ioData->mBuffers[0].mData; + const size_t totalReadSize = inNumberFrames * audioBuffer->getElementSize(); + const size_t bytesRead = audioBuffer->read(playbackBuffer, totalReadSize); + + // Pad any remaining samples. + if (bytesRead < totalReadSize) + { + memset(playbackBuffer + bytesRead, 0, totalReadSize - bytesRead); + } + + // Copy to other channels. + for (UInt32 channel = 1; channel < ioData->mNumberBuffers; channel++) + { + memcpy(ioData->mBuffers[channel].mData, playbackBuffer, ioData->mBuffers[0].mDataByteSize); + } + + return noErr; +} diff --git a/desmume/src/cocoa/coreaudiosound.h b/desmume/src/cocoa/coreaudiosound.h new file mode 100644 index 000000000..86852e642 --- /dev/null +++ b/desmume/src/cocoa/coreaudiosound.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2012 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 . + */ + +#ifndef _COREAUDIOSOUND_ +#define _COREAUDIOSOUND_ + +#include +#include +#include +#include +#include "ringbuffer.h" + +class CoreAudioSound +{ +private: + AudioUnit _au; + RingBuffer *_buffer; + OSSpinLock *_spinlockAU; + float _volume; + +public: + CoreAudioSound(size_t bufferSamples, size_t sampleSize); + ~CoreAudioSound(); + + void start(); + void stop(); + void writeToBuffer(const void *buffer, size_t numberBytes); + void clearBuffer(); + size_t getAvailableSamples(); + RingBuffer* getBuffer(); + void mute(); + void unmute(); + float getVolume(); + void setVolume(float vol); +}; + +OSStatus RenderCallback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); + +#endif diff --git a/desmume/src/cocoa/nds_control_legacy.mm b/desmume/src/cocoa/nds_control_legacy.mm index 97fb93a66..4e1f54eb2 100644 --- a/desmume/src/cocoa/nds_control_legacy.mm +++ b/desmume/src/cocoa/nds_control_legacy.mm @@ -53,12 +53,6 @@ GPU3DInterface *core3DList[] = { NULL }; -SoundInterface_struct *SNDCoreList[] = { -&SNDDummy, -&SNDOSX, -NULL -}; - struct NDS_fw_config_data macDS_firmware; diff --git a/desmume/src/cocoa/ringbuffer.cpp b/desmume/src/cocoa/ringbuffer.cpp new file mode 100644 index 000000000..75df6403c --- /dev/null +++ b/desmume/src/cocoa/ringbuffer.cpp @@ -0,0 +1,184 @@ +/* + Copyright (C) 2012 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 "ringbuffer.h" + + +RingBuffer::RingBuffer(size_t numberElements, size_t newBufferElementSize) +{ + _buffer = (uint8_t *)calloc(numberElements + 1, newBufferElementSize); + _bufferSize = (numberElements + 1) * newBufferElementSize; + _numElements = numberElements; + _elementSize = newBufferElementSize; + + _readPosition = newBufferElementSize - 1; + _writePosition = newBufferElementSize; + _bufferFillSize = 0; +} + +RingBuffer::~RingBuffer() +{ + free(_buffer); + _buffer = NULL; +} + +void RingBuffer::clear() +{ + this->_readPosition = this->_elementSize - 1; + this->_writePosition = this->_elementSize; + this->_bufferFillSize = 0; + memset(_buffer, 0, this->_bufferSize); +} + +size_t RingBuffer::read(void *__restrict__ destBuffer, size_t requestedNumberBytes) +{ + if (destBuffer == NULL) + { + return 0; + } + + size_t hiBufferAvailable = 0; + size_t loBufferAvailable = 0; + + const uint8_t *__restrict__ inputData = this->_buffer; + size_t inputDataReadPos = this->_readPosition; + const size_t inputDataWritePos = this->_writePosition; + const size_t inputDataSize = this->_bufferSize; + + // Check buffer availability + if (inputDataReadPos < inputDataWritePos) + { + hiBufferAvailable = inputDataWritePos - inputDataReadPos - 1; + } + else if (inputDataReadPos > inputDataWritePos) + { + hiBufferAvailable = inputDataSize - inputDataReadPos - 1; + loBufferAvailable = inputDataWritePos; + } + + // Bounds check for buffer overrun + if (requestedNumberBytes > hiBufferAvailable + loBufferAvailable) + { + requestedNumberBytes = hiBufferAvailable + loBufferAvailable; + requestedNumberBytes -= requestedNumberBytes % this->_elementSize; + } + + // Copy ring buffer to destination buffer + if (requestedNumberBytes <= hiBufferAvailable) + { + memcpy(destBuffer, inputData + inputDataReadPos + 1, requestedNumberBytes); + } + else + { + memcpy(destBuffer, inputData + inputDataReadPos + 1, hiBufferAvailable); + memcpy((uint8_t *)destBuffer + hiBufferAvailable, inputData, requestedNumberBytes - hiBufferAvailable); + } + + // Advance the read position + inputDataReadPos += requestedNumberBytes; + if (inputDataReadPos >= inputDataSize) + { + inputDataReadPos -= inputDataSize; + } + + this->_readPosition = inputDataReadPos; + + // Decrease the fill size now that we're done reading. + OSAtomicAdd32Barrier(-(int32_t)requestedNumberBytes, &this->_bufferFillSize); + + return requestedNumberBytes; +} + +size_t RingBuffer::write(const void *__restrict__ srcBuffer, size_t requestedNumberBytes) +{ + if (srcBuffer == NULL) + { + return 0; + } + + size_t hiBufferAvailable = 0; + size_t loBufferAvailable = 0; + + uint8_t *__restrict__ inputData = this->_buffer; + const size_t inputDataReadPos = this->_readPosition; + size_t inputDataWritePos = this->_writePosition; + const size_t inputDataSize = this->_bufferSize; + + // Check buffer availability. + 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; + } + + // Bounds check for buffer overrun + if (requestedNumberBytes > hiBufferAvailable + loBufferAvailable) + { + requestedNumberBytes = hiBufferAvailable + loBufferAvailable; + requestedNumberBytes -= requestedNumberBytes % this->_elementSize; + } + + // Increase the fill size before writing anything. + OSAtomicAdd32Barrier((int32_t)requestedNumberBytes, &this->_bufferFillSize); + + // Copy source buffer to ring buffer. + if (requestedNumberBytes <= hiBufferAvailable) + { + memcpy(inputData + inputDataWritePos, srcBuffer, requestedNumberBytes); + } + else + { + memcpy(inputData + inputDataWritePos, srcBuffer, hiBufferAvailable); + memcpy(inputData, (uint8_t *)srcBuffer + hiBufferAvailable, requestedNumberBytes - hiBufferAvailable); + + } + + // Advance the write position. + inputDataWritePos += requestedNumberBytes; + if (inputDataWritePos >= inputDataSize) + { + inputDataWritePos -= inputDataSize; + } + + this->_writePosition = inputDataWritePos; + + return requestedNumberBytes; +} + +size_t RingBuffer::getAvailableElements() +{ + return ((this->_bufferSize - this->_bufferFillSize) / this->_elementSize) - 1; +} + +size_t RingBuffer::getElementSize() +{ + return this->_elementSize; +} diff --git a/desmume/src/cocoa/ringbuffer.h b/desmume/src/cocoa/ringbuffer.h new file mode 100644 index 000000000..2d0b86741 --- /dev/null +++ b/desmume/src/cocoa/ringbuffer.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2012 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 . + */ + +#ifndef _RINGBUFFER_ +#define _RINGBUFFER_ + +#include +#include +#include + + +class RingBuffer +{ +private: + uint8_t *_buffer; + uint8_t *_bufferEnd; + size_t _bufferSize; + size_t _numElements; + size_t _elementSize; + int32_t _bufferFillSize; + size_t _readPosition; + size_t _writePosition; + +public: + RingBuffer(size_t numberElements, size_t newBufferElementSize); + ~RingBuffer(); + + void clear(); + size_t read(void *__restrict__ destBuffer, size_t requestedNumberBytes); + size_t write(const void *__restrict__ srcBuffer, size_t requestedNumberBytes); + size_t getAvailableElements(); + size_t getElementSize(); +}; + +#endif diff --git a/desmume/src/cocoa/sndOSX.cpp b/desmume/src/cocoa/sndOSX.cpp index 15e26d2a8..b1e97e036 100644 --- a/desmume/src/cocoa/sndOSX.cpp +++ b/desmume/src/cocoa/sndOSX.cpp @@ -18,592 +18,17 @@ #include "sndOSX.h" -#include -#include -#include -#include - +#include "coreaudiosound.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); - UInt8 *__restrict__ outputData = (UInt8 *)ioData->mBuffers[0].mData; - const size_t copySize = inNumberFrames * SPU_STEREO_SAMPLE_SIZE; - size_t hiBufferAvailable = 0; - size_t loBufferAvailable = 0; - - 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); - } - - 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); -} - -////////////////////////////////////////////////////////////////////////////// +// Global sound playback manager +static CoreAudioSound *coreAudioPlaybackManager = NULL; +// Sound interface to the SPU SoundInterface_struct SNDOSX = { SNDCORE_OSX, - "Core Audio Sound Interface", + "OS X Core Audio Sound Interface", SNDOSXInit, SNDOSXDeInit, SNDOSXUpdateAudio, @@ -614,99 +39,102 @@ SoundInterface_struct SNDOSX = { SNDOSXClearBuffer }; -////////////////////////////////////////////////////////////////////////////// -//Sound recording -////////////////////////////////////////////////////////////////////////////// +SoundInterface_struct *SNDCoreList[] = { + &SNDDummy, + &SNDOSX, + NULL +}; -bool SNDOSXOpenFile(void *fname) +int SNDOSXInit(int buffer_size) { - /* - 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) + if (coreAudioPlaybackManager != NULL) { - SNDOSXStopRecording(); - return false; + CoreAudioSound *oldcoreAudioPlaybackManager = coreAudioPlaybackManager; + coreAudioPlaybackManager = new CoreAudioSound(buffer_size / SPU_SAMPLE_SIZE, SPU_SAMPLE_SIZE); + delete oldcoreAudioPlaybackManager; } - - 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; + else + { + coreAudioPlaybackManager = new CoreAudioSound(buffer_size / SPU_SAMPLE_SIZE, SPU_SAMPLE_SIZE); + } + + coreAudioPlaybackManager->start(); + + return 0; } -void SNDOSXStartRecording() +void SNDOSXDeInit() { - pthread_mutex_lock(mutexSoundData); - - if(sound_data == NULL) - { - pthread_mutex_unlock(mutexSoundData); - return; - } - - pthread_mutex_unlock(mutexSoundData); + delete coreAudioPlaybackManager; + coreAudioPlaybackManager = NULL; } -void SNDOSXStopRecording() +int SNDOSXReset() { - pthread_mutex_lock(mutexSoundData); - - if(sound_data == NULL) - { - pthread_mutex_unlock(mutexSoundData); - return; - } - - pthread_mutex_unlock(mutexSoundData); + SNDOSXClearBuffer(); + + return 0; } -void SNDOSXCloseFile() +void SNDOSXUpdateAudio(s16 *buffer, u32 num_samples) { - pthread_mutex_lock(mutexSoundData); - - if(sound_data == NULL) + if (coreAudioPlaybackManager != NULL) { - pthread_mutex_unlock(mutexSoundData); - return; + coreAudioPlaybackManager->writeToBuffer(buffer, coreAudioPlaybackManager->getBuffer()->getElementSize() * (size_t)num_samples); } +} - if(file_open) +u32 SNDOSXGetAudioSpace() +{ + u32 availableSamples = 0; + + if (coreAudioPlaybackManager != NULL) { - file_open = false; + availableSamples = (u32)coreAudioPlaybackManager->getAvailableSamples(); + } + + return availableSamples; +} - //if it's rendering sound, wait until it's not - //so we dont close the file while writing to it +void SNDOSXMuteAudio() +{ + if (coreAudioPlaybackManager != NULL) + { + coreAudioPlaybackManager->mute(); + } +} + +void SNDOSXUnMuteAudio() +{ + if (coreAudioPlaybackManager != NULL) + { + coreAudioPlaybackManager->unmute(); + } +} + +void SNDOSXSetVolume(int volume) +{ + if (coreAudioPlaybackManager != NULL) + { + float newVolumeScalar = (float)volume / 100.0f; - // Do something here, just not implemented yet... - - //ExtAudioFileDispose(outfile); + if(volume > 100) + { + newVolumeScalar = 1.0f; + } + else if(volume < 0) + { + newVolumeScalar = 0.0f; + } + + coreAudioPlaybackManager->setVolume(newVolumeScalar); } - - pthread_mutex_unlock(mutexSoundData); } -/////////////////////////////////////////////////////////////////////////// +void SNDOSXClearBuffer() +{ + if (coreAudioPlaybackManager != NULL) + { + coreAudioPlaybackManager->clearBuffer(); + } +} diff --git a/desmume/src/cocoa/sndOSX.h b/desmume/src/cocoa/sndOSX.h index 20501c0ce..3b2d9758c 100644 --- a/desmume/src/cocoa/sndOSX.h +++ b/desmume/src/cocoa/sndOSX.h @@ -1,6 +1,6 @@ /* Copyright (C) 2007 Jeff Bland - Copyright (C) 2007-2011 DeSmuME team + Copyright (C) 2007-2012 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 @@ -16,30 +16,25 @@ along with the this software. If not, see . */ +#ifndef _OSXSOUNDINTERFACE_ +#define _OSXSOUNDINTERFACE_ + #include "../SPU.h" #define SNDCORE_OSX 58325 //hopefully this is unique number - -// This is the sound interface so the emulator core can send us sound info and whatnot +// Sound interface to the SPU extern SoundInterface_struct SNDOSX; -// Sound interface extensions for CoreAudio -void SNDOSXStartup(); -void SNDOSXShutdown(); -int SNDOSXInit(int buffer_size); -void SNDOSXDeInit(); -int SNDOSXReset(); -void SNDOSXUpdateAudio(s16 *buffer, u32 num_samples); -u32 SNDOSXGetAudioSpace(); -void SNDOSXMuteAudio(); -void SNDOSXUnMuteAudio(); -void SNDOSXSetVolume(int volume); -void SNDOSXClearBuffer(); +// Core Audio functions for the sound interface +int SNDOSXInit(int buffer_size); +void SNDOSXDeInit(); +int SNDOSXReset(); +void SNDOSXUpdateAudio(s16 *buffer, u32 num_samples); +u32 SNDOSXGetAudioSpace(); +void SNDOSXMuteAudio(); +void SNDOSXUnMuteAudio(); +void SNDOSXSetVolume(int volume); +void SNDOSXClearBuffer(); -// Recording -// Not supported as of 2011/12/28 - rogerman -bool SNDOSXOpenFile(void *fname); //opens a file for recording (if filename is the currently opened one, it will restart the file), fname is an NSString -void SNDOSXStartRecording(); //begins recording to the currently open file if there is an open file -void SNDOSXStopRecording(); //pauses recording (you can continue recording later) -void SNDOSXCloseFile(); //closes the file, making sure it's saved \ No newline at end of file +#endif // _OSXSOUNDINTERFACE_ diff --git a/desmume/src/cocoa/userinterface/appDelegate.mm b/desmume/src/cocoa/userinterface/appDelegate.mm index 2bba34a25..0db69f46e 100644 --- a/desmume/src/cocoa/userinterface/appDelegate.mm +++ b/desmume/src/cocoa/userinterface/appDelegate.mm @@ -188,8 +188,7 @@ [NSThread detachNewThreadSelector:@selector(runThread:) toTarget:newSpeaker withObject:nil]; // Wait until the GPU and SPU are finished starting up. - while (!([CocoaDSSpeaker isSPUStarted] && - [newComboDisplay thread] != nil && [newSpeaker thread] != nil)) + while ([newComboDisplay thread] == nil || [newSpeaker thread] == nil) { [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; } diff --git a/desmume/src/cocoa/userinterface/appDelegate_legacy.mm b/desmume/src/cocoa/userinterface/appDelegate_legacy.mm index a69ac45ae..3b2c4c7f9 100644 --- a/desmume/src/cocoa/userinterface/appDelegate_legacy.mm +++ b/desmume/src/cocoa/userinterface/appDelegate_legacy.mm @@ -117,8 +117,6 @@ void joinThread_gdb(void *thread_handle) [aboutWindowController setContent:aboutWindowProperties]; - SNDOSXStartup(); - //Set default values for all preferences //(this wont override saved preferences as //they work in different preference domains) @@ -192,8 +190,6 @@ void joinThread_gdb(void *thread_handle) [cdsCore release]; [cdsCoreController setContent:nil]; - - SNDOSXShutdown(); } - (IBAction) showSupportFolderInFinder:(id)sender