Skip to content

Commit

Permalink
Use AVPlayer and add sound effect volume setting.
Browse files Browse the repository at this point in the history
No user interface for volume setting at this moment.
Use `defaults(1)` to change it.
```
defaults write at.niw.HapticKey SoundEffectVolume -float 0.3.
```

See also #30.
  • Loading branch information
niw committed Dec 30, 2020
1 parent cd221bc commit 6d49c74
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 90 deletions.
12 changes: 6 additions & 6 deletions HapticKey.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -50,6 +50,8 @@
546E62F41FE2449600F287C9 /* NSTouchDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSTouchDevice.h; sourceTree = "<group>"; };
546E62F51FE24A1D00F287C9 /* HTKEventTap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKEventTap.h; sourceTree = "<group>"; };
546E62F61FE24A1D00F287C9 /* HTKEventTap.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKEventTap.m; sourceTree = "<group>"; };
5480AC5E242B1BC000344DA5 /* HTKAudioPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKAudioPlayer.h; sourceTree = "<group>"; };
5480AC5F242B1BC000344DA5 /* HTKAudioPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKAudioPlayer.m; sourceTree = "<group>"; };
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 = "<group>"; };
548E153A1FD00412001C0D4C /* HTKAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKAppDelegate.m; sourceTree = "<group>"; };
Expand All @@ -60,8 +62,6 @@
549F9234200AF543003A8D7B /* HTKTimer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKTimer.h; sourceTree = "<group>"; };
549F9235200AF543003A8D7B /* HTKTimer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKTimer.m; sourceTree = "<group>"; };
54A310B71FD390EB002DE7BC /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = ../../../../../../../System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = "<group>"; };
54ACC70F201F15F70026CAFD /* HTKSystemSound.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTKSystemSound.h; sourceTree = "<group>"; };
54ACC710201F15F70026CAFD /* HTKSystemSound.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTKSystemSound.m; sourceTree = "<group>"; };
54E1E0951FE4613F007F2EE0 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
54E1E0971FE4614F007F2EE0 /* Base */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
54E1E0981FE4621E007F2EE0 /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -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 */,
Expand All @@ -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 */,
Expand Down Expand Up @@ -273,14 +273,14 @@
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 */,
544C76362017311500FF155C /* HTKLoginItem.m in Sources */,
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 */,
Expand Down
33 changes: 33 additions & 0 deletions HapticKey/Classes/HTKAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ typedef NS_ENUM(NSUInteger, HTKAppDelegateSoundEffectType) {
HTKAppDelegateSoundEffectTypeDefault
};

static NSString * const kSoundEffectVolumeDefaultsKey = @"SoundEffectVolume";

static NSString * const kScreenFlashEnabledUserDefaultsKey = @"ScreenFlashEnabled";

static const char kStatusItemVisibleKeyObservingTag;
Expand All @@ -56,6 +58,7 @@ @interface HTKAppDelegate () <NSApplicationDelegate, HTKLoginItemDelegate, HTKSt
@property (nonatomic) HTKAppDelegateListeningEventType listeningEventType;
@property (nonatomic) HTKAppDelegateFeedbackType feedbackType;
@property (nonatomic) HTKAppDelegateSoundEffectType soundEffectType;
@property (nonatomic) float soundEffectVolume;
@property (nonatomic, getter=isScreenFlashEnabled) BOOL screenFlashEnabled;
@property (nonatomic, getter=isLoginItemEnabled) BOOL loginItemEnabled;
@property (nonatomic, getter=isAutomaticallyCheckForUpdatesEnabled) BOOL automaticallyCheckForUpdatesEnabled;
Expand Down Expand Up @@ -134,6 +137,17 @@ - (void)setSoundEffectType:(HTKAppDelegateSoundEffectType)soundEffectType
}
}

- (void)setSoundEffectVolume:(float)soundEffectVolume
{
if (_soundEffectVolume != soundEffectVolume) {
_soundEffectVolume = soundEffectVolume;

[self _htk_main_updateSoundFeedbackVolume];

[self _htk_main_updateUserDefaults];
}
}

- (void)setScreenFlashEnabled:(BOOL)screenFlashEnabled
{
if (_screenFlashEnabled != screenFlashEnabled) {
Expand Down Expand Up @@ -253,6 +267,7 @@ - (void)_htk_main_updateHapticFeedback

[self _htk_main_updateHapticFeedbackType];
[self _htk_main_updateSoundFeedbackType];
[self _htk_main_updateSoundFeedbackVolume];
[self _htk_main_updateScreenFlashEnabled];
}

Expand Down Expand Up @@ -294,6 +309,15 @@ - (void)_htk_main_updateSoundFeedbackType
}
}

- (void)_htk_main_updateSoundFeedbackVolume
{
if (!self.hapticFeedback) {
return;
}

self.hapticFeedback.soundVolume = self.soundEffectVolume;
}

- (void)_htk_main_updateScreenFlashEnabled
{
if (!self.hapticFeedback) {
Expand Down Expand Up @@ -332,6 +356,7 @@ - (void)_htk_main_updateUserDefaults
[defaults setInteger:self.listeningEventType forKey:kListeningEventTypeUserDefaultsKey];
[defaults setInteger:self.feedbackType forKey:kFeedbackTypeUserDefaultsKey];
[defaults setInteger:self.soundEffectType forKey:kSoundEffectTypeUserDefaultsKey];
[defaults setFloat:self.soundEffectVolume forKey:kSoundEffectVolumeDefaultsKey];
[defaults setBool:self.screenFlashEnabled forKey:kScreenFlashEnabledUserDefaultsKey];
}

Expand Down Expand Up @@ -382,6 +407,13 @@ - (void)_htk_main_loadUserDefaults
soundEffectType = HTKAppDelegateSoundEffectTypeNone;
}

float soundEffectVolume;
if ([defaults objectForKey:kSoundEffectVolumeDefaultsKey]) {
soundEffectVolume = [defaults floatForKey:kSoundEffectVolumeDefaultsKey];
} else {
soundEffectVolume = 0.0;
}

BOOL screenFlashEnabled;
if ([defaults objectForKey:kScreenFlashEnabledUserDefaultsKey]) {
screenFlashEnabled = [defaults boolForKey:kScreenFlashEnabledUserDefaultsKey];
Expand All @@ -393,6 +425,7 @@ - (void)_htk_main_loadUserDefaults
self.listeningEventType = listeningEventType;
self.feedbackType = feedbackType;
self.soundEffectType = soundEffectType;
self.soundEffectVolume = soundEffectVolume;
self.screenFlashEnabled = screenFlashEnabled;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
//
// HTKSystemSound.h
// HTKAudioPlayer.h
// HapticKey
//
// Created by Yoshimasa Niwa on 1/29/18.
// Copyright © 2018 Yoshimasa Niwa. All rights reserved.
// Created by Yoshimasa Niwa on 3/24/20.
// Copyright © 2020 Yoshimasa Niwa. All rights reserved.
//

@import Foundation;

NS_ASSUME_NONNULL_BEGIN

@interface HTKSystemSound : NSObject
@interface HTKAudioPlayer : NSObject

@property (nonatomic, readonly) NSString *path;
@property (nonatomic) float volume;

+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
Expand Down
95 changes: 95 additions & 0 deletions HapticKey/Classes/HTKAudioPlayer.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// HTKAudioPlayer.m
// HapticKey
//
// Created by Yoshimasa Niwa on 3/24/20.
// Copyright © 2020 Yoshimasa Niwa. All rights reserved.
//

#import "HTKAudioPlayer.h"

@import AVFoundation;
@import os.log;

NS_ASSUME_NONNULL_BEGIN

static NSString * const kSystemSoundsPath = @"/System/Library/Components/CoreAudio.component/Contents/SharedSupport/SystemSounds";

@interface HTKAudioPlayer ()

@property (nonatomic, readonly) AVPlayer *player;

@end

@implementation HTKAudioPlayer

- (instancetype)init
{
[self doesNotRecognizeSelector:_cmd];
abort();
}

static const char kAVPlayerStatusObservingTag;
static NSString * const kAVPlayerStatusKeyPath = @"status";

- (instancetype)initWithPath:(NSString *)path
{
if (self = [super init]) {
_path = [path copy];

NSURL * const url = [[NSURL alloc] initFileURLWithPath:path];
_player = [[AVPlayer alloc] initWithURL:url];

// Neither key-value observer nor observable are retained. Should remove the observer on its `dealloc`.
[_player addObserver:self forKeyPath:kAVPlayerStatusKeyPath options:0 context:(void *)&kAVPlayerStatusObservingTag];
}
return self;
}

- (instancetype)initWithSystemSoundsGroup:(NSString *)group name:(NSString *)name
{
NSString * const path = [[kSystemSoundsPath stringByAppendingPathComponent:group] stringByAppendingPathComponent:name];
return [self initWithPath:path];
}

- (void)dealloc
{
[self.player removeObserver:self forKeyPath:kAVPlayerStatusKeyPath context:(void *)&kAVPlayerStatusObservingTag];
}

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey,id> *)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
1 change: 1 addition & 0 deletions HapticKey/Classes/HTKHapticFeedback.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 18 additions & 8 deletions HapticKey/Classes/HTKHapticFeedback.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,7 +25,7 @@
@interface HTKHapticFeedback () <HTKEventListenerDelegate>

@property (nonatomic, nullable) HTKTimer *timer;
@property (nonatomic, readonly) HTKSystemSound *defaultSystemSound;
@property (nonatomic, readonly) HTKAudioPlayer *defaultAudioPlayer;

@end

Expand All @@ -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;
}
Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit 6d49c74

Please sign in to comment.