diff --git a/HapticKey.xcodeproj/project.pbxproj b/HapticKey.xcodeproj/project.pbxproj index d2e30dd..d95d645 100644 --- a/HapticKey.xcodeproj/project.pbxproj +++ b/HapticKey.xcodeproj/project.pbxproj @@ -16,13 +16,13 @@ 5451D76C2569093000758615 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 5451D76B2569093000758615 /* Sparkle */; }; 545E325A2426BC5E00FBF9B9 /* HTKStatusItemMenuController.m in Sources */ = {isa = PBXBuildFile; fileRef = 545E32592426BC5E00FBF9B9 /* HTKStatusItemMenuController.m */; }; 546E62F71FE24A1D00F287C9 /* HTKEventTap.m in Sources */ = {isa = PBXBuildFile; fileRef = 546E62F61FE24A1D00F287C9 /* HTKEventTap.m */; }; + 5480AC60242B1BC000344DA5 /* HTKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5480AC5F242B1BC000344DA5 /* HTKAudioPlayer.m */; }; 548E153B1FD00412001C0D4C /* HTKAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 548E153A1FD00412001C0D4C /* HTKAppDelegate.m */; }; 548E15401FD00412001C0D4C /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 548E153E1FD00412001C0D4C /* MainMenu.xib */; }; 548E15431FD00412001C0D4C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 548E15421FD00412001C0D4C /* main.m */; }; 54942E882021B149003507D8 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 54942E872021B149003507D8 /* dsa_pub.pem */; }; 549F9236200AF543003A8D7B /* HTKTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 549F9235200AF543003A8D7B /* HTKTimer.m */; }; 54A310B81FD390EB002DE7BC /* MultitouchSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54A310B71FD390EB002DE7BC /* MultitouchSupport.framework */; }; - 54ACC711201F15F70026CAFD /* HTKSystemSound.m in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC710201F15F70026CAFD /* HTKSystemSound.m */; }; 54E1E0941FE4613F007F2EE0 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 54E1E0961FE4613F007F2EE0 /* Localizable.strings */; }; 54FBCF291FD4CDE0000EB4D3 /* HTKMultitouchActuator.m in Sources */ = {isa = PBXBuildFile; fileRef = 54FBCF281FD4CDE0000EB4D3 /* HTKMultitouchActuator.m */; }; 54FBCF321FD52483000EB4D3 /* AppIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 54FBCF301FD52483000EB4D3 /* AppIcon.icns */; }; @@ -50,6 +50,8 @@ 546E62F41FE2449600F287C9 /* NSTouchDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSTouchDevice.h; sourceTree = ""; }; 546E62F51FE24A1D00F287C9 /* HTKEventTap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKEventTap.h; sourceTree = ""; }; 546E62F61FE24A1D00F287C9 /* HTKEventTap.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKEventTap.m; sourceTree = ""; }; + 5480AC5E242B1BC000344DA5 /* HTKAudioPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKAudioPlayer.h; sourceTree = ""; }; + 5480AC5F242B1BC000344DA5 /* HTKAudioPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKAudioPlayer.m; sourceTree = ""; }; 548E15361FD00412001C0D4C /* HapticKey.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HapticKey.app; sourceTree = BUILT_PRODUCTS_DIR; }; 548E15391FD00412001C0D4C /* HTKAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKAppDelegate.h; sourceTree = ""; }; 548E153A1FD00412001C0D4C /* HTKAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKAppDelegate.m; sourceTree = ""; }; @@ -60,8 +62,6 @@ 549F9234200AF543003A8D7B /* HTKTimer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKTimer.h; sourceTree = ""; }; 549F9235200AF543003A8D7B /* HTKTimer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKTimer.m; sourceTree = ""; }; 54A310B71FD390EB002DE7BC /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = ../../../../../../../System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = ""; }; - 54ACC70F201F15F70026CAFD /* HTKSystemSound.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKSystemSound.h; sourceTree = ""; }; - 54ACC710201F15F70026CAFD /* HTKSystemSound.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKSystemSound.m; sourceTree = ""; }; 54E1E0951FE4613F007F2EE0 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 54E1E0971FE4614F007F2EE0 /* Base */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; 54E1E0981FE4621E007F2EE0 /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; @@ -126,6 +126,8 @@ children = ( 548E15391FD00412001C0D4C /* HTKAppDelegate.h */, 548E153A1FD00412001C0D4C /* HTKAppDelegate.m */, + 5480AC5E242B1BC000344DA5 /* HTKAudioPlayer.h */, + 5480AC5F242B1BC000344DA5 /* HTKAudioPlayer.m */, 5443CEFD1FE38F6C002D4086 /* HTKEvent.h */, 5443CEFE1FE38F6C002D4086 /* HTKEvent.m */, 5443CEFA1FE38EA8002D4086 /* HTKEventListener.h */, @@ -142,8 +144,6 @@ 54FBCF281FD4CDE0000EB4D3 /* HTKMultitouchActuator.m */, 545E32582426BC5E00FBF9B9 /* HTKStatusItemMenuController.h */, 545E32592426BC5E00FBF9B9 /* HTKStatusItemMenuController.m */, - 54ACC70F201F15F70026CAFD /* HTKSystemSound.h */, - 54ACC710201F15F70026CAFD /* HTKSystemSound.m */, 5443CF061FE39293002D4086 /* HTKTapGestureEventListener.h */, 5443CF071FE39293002D4086 /* HTKTapGestureEventListener.m */, 549F9234200AF543003A8D7B /* HTKTimer.h */, @@ -273,6 +273,7 @@ buildActionMask = 2147483647; files = ( 5443CF0D1FE394BB002D4086 /* HTKHapticFeedback.m in Sources */, + 5480AC60242B1BC000344DA5 /* HTKAudioPlayer.m in Sources */, 549F9236200AF543003A8D7B /* HTKTimer.m in Sources */, 5443CEFF1FE38F6C002D4086 /* HTKEvent.m in Sources */, 548E15431FD00412001C0D4C /* main.m in Sources */, @@ -280,7 +281,6 @@ 5443CF081FE39293002D4086 /* HTKTapGestureEventListener.m in Sources */, 548E153B1FD00412001C0D4C /* HTKAppDelegate.m in Sources */, 5443CEFC1FE38EA8002D4086 /* HTKEventListener.m in Sources */, - 54ACC711201F15F70026CAFD /* HTKSystemSound.m in Sources */, 546E62F71FE24A1D00F287C9 /* HTKEventTap.m in Sources */, 5443CF051FE39049002D4086 /* HTKFunctionKeyEventListener.m in Sources */, 54FBCF291FD4CDE0000EB4D3 /* HTKMultitouchActuator.m in Sources */, diff --git a/HapticKey/Classes/HTKAppDelegate.m b/HapticKey/Classes/HTKAppDelegate.m index dd37648..7d55413 100644 --- a/HapticKey/Classes/HTKAppDelegate.m +++ b/HapticKey/Classes/HTKAppDelegate.m @@ -42,6 +42,8 @@ typedef NS_ENUM(NSUInteger, HTKAppDelegateSoundEffectType) { HTKAppDelegateSoundEffectTypeDefault }; +static NSString * const kSoundEffectVolumeDefaultsKey = @"SoundEffectVolume"; + static NSString * const kScreenFlashEnabledUserDefaultsKey = @"ScreenFlashEnabled"; static const char kStatusItemVisibleKeyObservingTag; @@ -56,6 +58,7 @@ @interface HTKAppDelegate () *)change context:(nullable void *)context +{ + if ([keyPath isEqualToString:kAVPlayerStatusKeyPath] && object == self.player) { + switch (self.player.status) { + case AVPlayerStatusFailed: + os_log_error(OS_LOG_DEFAULT, "Player is not ready to play sound at path: %{public}@ error: %{public}@", self.path, self.player.error); + return; + case AVPlayerStatusReadyToPlay: + os_log_info(OS_LOG_DEFAULT, "Player is ready to play sound at path: %{public}@", self.path); + break; + default: + break; + } + } +} + +- (void)setVolume:(float)volume +{ + self.player.volume = volume; +} + +- (float)volume +{ + return self.player.volume; +} + +- (void)play +{ + [self.player pause]; + [self.player seekToTime:kCMTimeZero]; + [self.player playImmediatelyAtRate:1.0]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/HapticKey/Classes/HTKHapticFeedback.h b/HapticKey/Classes/HTKHapticFeedback.h index a7d5575..2e4beca 100644 --- a/HapticKey/Classes/HTKHapticFeedback.h +++ b/HapticKey/Classes/HTKHapticFeedback.h @@ -30,6 +30,7 @@ typedef NS_ENUM(NSUInteger, HTKSoundFeedbackType) { @property (nonatomic, getter=isEnabled) BOOL enabled; @property (nonatomic) HTKHapticFeedbackType type; @property (nonatomic) HTKSoundFeedbackType soundType; +@property (nonatomic) float soundVolume; @property (nonatomic, getter=isScreenFlashEnabled) BOOL screenFlashEnabled; + (instancetype)new NS_UNAVAILABLE; diff --git a/HapticKey/Classes/HTKHapticFeedback.m b/HapticKey/Classes/HTKHapticFeedback.m index 2e558fb..3cad4b0 100644 --- a/HapticKey/Classes/HTKHapticFeedback.m +++ b/HapticKey/Classes/HTKHapticFeedback.m @@ -6,11 +6,11 @@ // Copyright © 2017 Yoshimasa Niwa. All rights reserved. // +#import "HTKAudioPlayer.h" #import "HTKEvent.h" #import "HTKEventListener.h" #import "HTKHapticFeedback.h" #import "HTKMultitouchActuator.h" -#import "HTKSystemSound.h" #import "HTKTimer.h" @import AudioToolbox; @@ -25,7 +25,7 @@ @interface HTKHapticFeedback () @property (nonatomic, nullable) HTKTimer *timer; -@property (nonatomic, readonly) HTKSystemSound *defaultSystemSound; +@property (nonatomic, readonly) HTKAudioPlayer *defaultAudioPlayer; @end @@ -44,7 +44,7 @@ - (instancetype)initWithEventListener:(HTKEventListener *)eventListener _eventListener.delegate = self; _type = HTKHapticFeedbackTypeMedium; - _defaultSystemSound = [[HTKSystemSound alloc] initWithSystemSoundsGroup:kDefaultSystemSoundsGroup name:kDefaultSystemSoundsName]; + _defaultAudioPlayer = [[HTKAudioPlayer alloc] initWithSystemSoundsGroup:kDefaultSystemSoundsGroup name:kDefaultSystemSoundsName]; } return self; } @@ -59,6 +59,16 @@ - (BOOL)isEnabled return self.eventListener.enabled; } +- (void)setSoundVolume:(float)soundVolume +{ + self.defaultAudioPlayer.volume = soundVolume; +} + +- (float)soundVolume +{ + return self.defaultAudioPlayer.volume; +} + // MARK: - HTKEventListenerDelegate - (void)eventListener:(HTKEventListener *)eventListener didListenEvent:(HTKEvent *)event @@ -70,14 +80,14 @@ - (void)eventListener:(HTKEventListener *)eventListener didListenEvent:(HTKEvent self.timer = [[HTKTimer alloc] initWithTimeInterval:kMinimumActuationInterval repeats:NO target:self selector:@selector(_htk_timer_didFire:)]; const SInt32 actuationID = [self _htk_main_actuationID]; - HTKSystemSound * const systemSound = [self _htk_main_systemSound]; + HTKAudioPlayer * const audioPlayer = [self _htk_main_audioPlayer]; switch (event.phase) { case HTKEventPhaseBegin: if (actuationID != 0) { [[HTKMultitouchActuator sharedActuator] actuateActuationID:actuationID unknown1:0 unknown2:0.0 unknown3:2.0]; } - if (systemSound) { - [systemSound play]; + if (audioPlayer) { + [audioPlayer play]; } if (self.screenFlashEnabled) { AudioServicesPlaySystemSoundWithCompletion(kSystemSoundID_FlashScreen, NULL); @@ -117,13 +127,13 @@ - (SInt32)_htk_main_actuationID return 0; } -- (nullable HTKSystemSound *)_htk_main_systemSound +- (nullable HTKAudioPlayer *)_htk_main_audioPlayer { switch (self.soundType) { case HTKSoundFeedbackTypeNone: return nil; case HTKSoundFeedbackTypeDefault: - return self.defaultSystemSound; + return self.defaultAudioPlayer; } return nil; } diff --git a/HapticKey/Classes/HTKSystemSound.m b/HapticKey/Classes/HTKSystemSound.m deleted file mode 100644 index 6f99a03..0000000 --- a/HapticKey/Classes/HTKSystemSound.m +++ /dev/null @@ -1,72 +0,0 @@ -// -// HTKSystemSound.m -// HapticKey -// -// Created by Yoshimasa Niwa on 1/29/18. -// Copyright © 2018 Yoshimasa Niwa. All rights reserved. -// - -#import "HTKSystemSound.h" - -@import AudioToolbox; -@import os.log; - -NS_ASSUME_NONNULL_BEGIN - -static NSString * const kSystemSoundsPath = @"/System/Library/Components/CoreAudio.component/Contents/SharedSupport/SystemSounds"; - -@interface HTKSystemSound () - -@property (nonatomic, readonly) SystemSoundID systemSoundID; - -@end - -@implementation HTKSystemSound - -- (instancetype)init -{ - [self doesNotRecognizeSelector:_cmd]; - abort(); -} - -- (instancetype)initWithPath:(NSString *)path -{ - if (self = [super init]) { - _path = [path copy]; - - SystemSoundID systemSoundID; - NSURL *url = [[NSURL alloc] initFileURLWithPath:path]; - OSStatus error = AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &systemSoundID); - if (error != noErr) { - os_log_error(OS_LOG_DEFAULT, "Fail to create system sound at path: %{public}@ code: 0x%lx", path, (long)error); - return nil; - } else { - os_log_info(OS_LOG_DEFAULT, "Create system sound at path: %{public}@ id: 0x%lx", path, (long)systemSoundID); - } - _systemSoundID = systemSoundID; - } - return self; -} - -- (instancetype)initWithSystemSoundsGroup:(NSString *)group name:(NSString *)name -{ - NSString * const path = [[kSystemSoundsPath stringByAppendingPathComponent:group] stringByAppendingPathComponent:name]; - return [self initWithPath:path]; -} - -- (void)dealloc -{ - OSStatus error = AudioServicesDisposeSystemSoundID(self.systemSoundID); - if (error != noErr) { - os_log_error(OS_LOG_DEFAULT, "Fail to dispose system sound id: %lu code: 0x%lx", (long)self.systemSoundID, (long)error); - } -} - -- (void)play -{ - AudioServicesPlaySystemSoundWithCompletion(self.systemSoundID, NULL); -} - -@end - -NS_ASSUME_NONNULL_END