如何在 WatchOS 3 中播放将在手表扬声器上播放的自定义声音

How can I play a custom sound in WatchOS 3 that will playback on the watch speakers

我了解到我们现在可以在 watchos 3 的 Apple Watch 上播放自定义声音。

根据 Apple 的公告,显然有但我没有测试它的示例:使用 SCNAudioSource 或 SCNAudioPlayer 实现的 3D 空间音频。相反,请使用 playAudioSource:waitForCompletion: 或 WatchKit 声音或触觉 API。在这里找到:https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInwatchOS/Articles/watchOS3.html

有人可以举一个简单的例子吗?我没有在我的应用程序中使用 SceneKit,因为我不需要它,但如果这是播放自定义声音的唯一方法,那么我想知道完成此操作所需的最少代码。最好在 Objective c 中,但我会采用任何形状。我可以使用 SpriteKit,如果这样也更容易的话。

这是我目前拥有的,但它不起作用:

SCNNode * audioNode = [[SCNNode alloc] init];

SCNAudioSource * audioSource = [SCNAudioSource audioSourceNamed:@"mysound.mp3"];
SCNAudioPlayer * audioPlayer = [SCNAudioPlayer audioPlayerWithSource:audioSource];  
[audioNode addAudioPlayer:audioPlayer];

SCNAction * play = [SCNAction playAudioSource:audioSource waitForCompletion:YES];
[audioNode runAction:play];

这是Objective-c但是可以翻译成Swift

我最终使用 AVAudioEngineAVAudioPlayerNode 在 Apple Watch 上播放音频。

操作要点如下:

我在 AudioPlayer 的 init 方法中调用了以下代码(它是封装功能的 NSObject 子类)

_audioPlayer = [[AVAudioPlayerNode alloc] init];
_audioEngine = [[AVAudioEngine alloc] init];
[_audioEngine attachNode:_audioPlayer];

AVAudioFormat *stereoFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100 channels:2];
[_audioEngine connect:_audioPlayer to:_audioEngine.mainMixerNode format:stereoFormat];

if (!_audioEngine.isRunning) {
    NSError* error;
    [_audioEngine startAndReturnError:&error];
}

我有一个缓存设置,所以我不会在每次想要播放声音时都重新创建 AVAudioFile 资源,但您不需要。

接下来创建一个 AVAudioFile 对象:

NSError *error;
NSBundle* appBundle = [NSBundle mainBundle];
NSURL *url = [NSURL fileURLWithPath:[appBundle pathForResource:key ofType:@"aifc"]];
AVAudioFile *asset = [[AVAudioFile alloc] initForReading:url &error];

然后播放该文件:

[_audioPlayer scheduleFile:asset atTime:nil completionHandler:nil];
[_audioPlayer play];

更新:如果应用进入休眠状态或进入后台,音频可能会停止 playing/fade。通过激活音频会话,这将被阻止。

    NSError *error;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
    if (error) {
      NSLog(@"AVAudioSession setCategory ERROR: %@", error.localizedDescription);
    }
    [[AVAudioSession sharedInstance] setActive:YES error:&error];
    if (error) {
      NSLog(@"AVAudioSession setActive ERROR: %@", error.localizedDescription);
    }

我没有去处理任何错误,但这应该可行。不要忘记在实施文件的顶部 #import <AVFoundation/AVFoundation.h>

我可以确认,@ApperleyA 解决方案确实有效!

这里是 swift 版本:

var _audioPlayer : AVAudioPlayerNode!
var _audioEngine : AVAudioEngine!

func playAudio()
{
    if (_audioPlayer==nil) {
        _audioPlayer = AVAudioPlayerNode()
        _audioEngine = AVAudioEngine()
        _audioEngine.attach(_audioPlayer)

        let stereoFormat = AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 2)
        _audioEngine.connect(_audioPlayer, to: _audioEngine.mainMixerNode, format: stereoFormat)

        do {

            if !_audioEngine.isRunning {
                try _audioEngine.start()
            }

        } catch {}

    }


    if let path = Bundle.main.path(forResource: "test", ofType: "mp3") {

        let fileUrl = URL(fileURLWithPath: path)

        do {
            let asset = try AVAudioFile(forReading: fileUrl)

            _audioPlayer.scheduleFile(asset, at: nil, completionHandler: nil)
            _audioPlayer.play()

        } catch {
            print ("asset error")
        }

    }


}

这在模拟器中对我有用

  let soundPath = Bundle.main.path(forResource: "cheerMP3", ofType: "mp3")
  let soundPathURL = URL(fileURLWithPath: soundPath!)
  let audioFile = WKAudioFileAsset(url: soundPathURL)
  let audioItem = WKAudioFilePlayerItem(asset: audioFile)

  let audioPlayer = WKAudioFilePlayer.init(playerItem: audioItem)

  if audioPlayer.status == .readyToPlay
  {
    audioPlayer.play()
  }
  else
  {
    print("Not ready!!")
  }

但前提是我在 audioPlayer.play() 和最后一个 } 之后都有断点。

dunqan,你在文件的顶部放了什么,导入语句?我无法包含

导入 AVFoundation

使用Xcode 8.2.1

没有错误