如何监控设备的音频输出以判断声音是不是来自扬声器/耳机插孔?
Posted
技术标签:
【中文标题】如何监控设备的音频输出以判断声音是不是来自扬声器/耳机插孔?【英文标题】:How can I monitor the device's audio output to be able to tell if sound is coming out of the speakers/headphone jack?如何监控设备的音频输出以判断声音是否来自扬声器/耳机插孔? 【发布时间】:2015-11-17 08:49:03 【问题描述】:我需要在设备开始输出音频时立即启动操作。我正在使用 AVPlayer 并正在从 Parse 流式传输音频文件。使用等待 (AVPlayer.currentTime() != nil) 和 (AVPlayer.rate > 0) 等其他方法不够准确,我需要确切知道从设备输出音频的时间。我尝试使用 AVAudioEngine,然后附加一个具有 AVAudioNodeBus 的 AVAudioNode,但我无法让它工作。任何建议或技术都会非常好,谢谢!
这是我的 AudioEngine 代码。我在实例级别实例化 AudioEngine。创建standardFormat 时,我不知道standardFormatWithSampleRate 或通道块使用什么。当我尝试 installTapOnBus 时,我不知道该块使用什么,所以我输入了 nil,但这也会触发错误。任何帮助将不胜感激,我对 ios 开发人员非常陌生,并且已多次阅读 Apple 的文档,但我无法完全理解它,也无法在网上找到任何最近的示例。
class TableViewController: UITableViewController, AVAudioPlayerDelegate
var iDArray = [String]()
var NameArray = [String]()
var durationInSeconds = Double()
var currentSong = String()
override func viewDidLoad()
super.viewDidLoad()
let ObjectIDQuery = PFQuery(className: "Songs")
ObjectIDQuery.findObjectsInBackgroundWithBlock
(objectsArray: [PFObject]?, error: NSError?) -> Void in
//objectsArray!.count != 0
var objectIDs = objectsArray!
for i in 0...objectIDs.count-1
self.iDArray.append(objectIDs[i].valueForKey("objectId") as! String)
self.NameArray.append(objectIDs[i].valueForKey("SongName") as! String)
self.tableView.reloadData()
do
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
print("AVAudioSession Category Playback OK")
do
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
catch let error as NSError
print(error.localizedDescription)
catch let error as NSError
print(error.localizedDescription)
func grabSong ()
let songQuery = PFQuery(className: "Songs")
songQuery.getObjectInBackgroundWithId(iDArray[SelectedSongNumber], block:
(object: PFObject?, error : NSError?) -> Void in
if let audioFile = object?["SongFile"] as? PFFile
let audioFileUrlString: String = audioFile.url!
let audioFileUrl = NSURL(string: audioFileUrlString)!
AudioPlayer = AVPlayer(URL: audioFileUrl)
AudioPlayer.play()
)
func audioFunction()
var audioPlayerNode = AVAudioNode()
var audioBus = AVAudioNodeBus()
var standardFormat = AVAudioFormat(standardFormatWithSampleRate: <#T##Double#>, channels: <#T##AVAudioChannelCount#>)
AudioEngine.attachNode(audioPlayerNode)
audioPlayerNode.outputFormatForBus(audioBus)
audioPlayerNode.installTapOnBus(audioBus, bufferSize: 100, format: standardFormat, block: nil)
if AudioEngine.running == true
print("the audio engine is running")
else
print("the audio engine is NOTTT running")
func attachNode(audioNode : AVAudioNode)
AudioEngine.attachNode(audioNode)
AudioEngine.outputNode
print(AudioEngine.outputNode.description)
if AudioEngine.running == true
print("the audio engine is running")
else
print("the audio engine is NOTTT running")
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return iDArray.count
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
cell?.textLabel!.text = NameArray[indexPath.row]
return cell!
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
SelectedSongNumber = indexPath.row
grabSong()
我应该改用 AVAudioSession 吗?还是 AVCaptureSession?
【问题讨论】:
我很想给AVPlayer
添加一个水龙头。为什么不显示您的AVAudioEngine
代码?我很好奇为什么这不起作用。
抱歉,刚刚添加了它
您实际上是如何使用AVAudioEngine
播放流式音频文件的?
我更新了更多我的代码,因为它有助于清除任何东西。我实际上并没有使用 AudioEngine 播放流式音频文件。我使用 AVPlayer 构建了应用程序,现在我意识到要获得我正在寻找的精度,我需要以某种方式监控音频输出,而我发现这样做的唯一方法(我认为)是附加一个 AVAudioNode
我真的只想知道音频何时开始从设备输出。它不必连接到我的 AVPlayer,我只是想监控硬件的某些部分何时开始使用......这甚至可能吗?
【参考方案1】:
我会在AVPlayer
上使用音频点击来了解音频何时实际播放/即将播放。基本上,您会在音频播放到扬声器/耳机插孔之前收到音频点击回调。
一些复杂情况:我不确定如何获取某些流媒体类型(pls、icecast)的 AVAsset
曲目,但远程(和本地)mp3 文件可以正常工作。
var player: AVPlayer?
func doit()
let url = NSURL(string: "URL TO YOUR POSSIBLY REMOTE AUDIO FILE")!
let asset = AVURLAsset(URL:url)
let playerItem = AVPlayerItem(asset: asset)
let tapProcess: @convention(c) (MTAudioProcessingTap, CMItemCount, MTAudioProcessingTapFlags, UnsafeMutablePointer<AudioBufferList>, UnsafeMutablePointer<CMItemCount>, UnsafeMutablePointer<MTAudioProcessingTapFlags>) -> Void =
(tap, numberFrames, flags, bufferListInOut, numberFramesOut, flagsOut) -> Void in
// Audio coming out!
let status = MTAudioProcessingTapGetSourceAudio(tap, numberFrames, bufferListInOut, flagsOut, nil, numberFramesOut)
print("get audio: \(status)\n")
var callbacks = MTAudioProcessingTapCallbacks(
version: kMTAudioProcessingTapCallbacksVersion_0,
clientInfo: UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque()),
`init`: nil,
finalize: nil,
prepare: nil,
unprepare: nil,
process: tapProcess)
var tap: Unmanaged<MTAudioProcessingTap>?
let err = MTAudioProcessingTapCreate(kCFAllocatorDefault, &callbacks, kMTAudioProcessingTapCreationFlag_PostEffects, &tap)
if err != noErr
// TODO: something
let audioTrack = playerItem.asset.tracksWithMediaType(AVMediaTypeAudio).first!
let inputParams = AVMutableAudioMixInputParameters(track: audioTrack)
inputParams.audioTapProcessor = tap?.takeUnretainedValue()
let audioMix = AVMutableAudioMix()
audioMix.inputParameters = [inputParams]
playerItem.audioMix = audioMix
player = AVPlayer(playerItem: playerItem)
player?.play()
【讨论】:
谢谢,我会试试这个并报告以上是关于如何监控设备的音频输出以判断声音是不是来自扬声器/耳机插孔?的主要内容,如果未能解决你的问题,请参考以下文章