Platforms to show: All Mac Windows Linux Cross-Platform

/AVFoundation/AVAudioEngine/Player


Required plugins for this example: MBS MacBase Plugin, MBS Main Plugin, MBS AVFoundation Plugin

You find this example project in your Plugins Download as a Xojo project file within the examples folder: /AVFoundation/AVAudioEngine/Player

This example is the version from Thu, 25th Dec 2019.

Project "Player.xojo_binary_project"
Class App Inherits Application
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
End Class
Class Window1 Inherits Window
Control LoadButton Inherits PushButton
ControlInstance LoadButton Inherits PushButton
EventHandler Sub Action() dim f as FolderItem = GetOpenFolderItem("") if f = nil then Return dim error as NSErrorMBS dim a as new AVAudioFileMBS(f, error) if error <> nil then MsgBox error.LocalizedDescription return end if NoEvents = true dim ff as AVAudioFormatMBS = a.fileFormat dim d as Double = a.Length/a.fileFormat.sampleRate FileInfo.Text = f.DisplayName+": "+str(ff.sampleRate)+"Hz, "+str(ff.channelCount)+" channels, "+str(d, "0.00")+" seconds" self.fileFormat = ff self.AudioFile = a self.File = f PositionSlider.Value = 0 player.scheduleFile(a, nil) StartEngine if not player.Playing then player.play end if Finally NoEvents = false End EventHandler
End Control
Control FileInfo Inherits Label
ControlInstance FileInfo Inherits Label
End Control
Control PlayButton Inherits PushButton
ControlInstance PlayButton Inherits PushButton
EventHandler Sub Action() if player.Playing then player.pause else player.play end if End EventHandler
End Control
Control PlayStatus Inherits Label
ControlInstance PlayStatus Inherits Label
End Control
Control Timer1 Inherits Timer
ControlInstance Timer1 Inherits Timer
EventHandler Sub Action() NoEvents = true dim lines() as string if player.Playing then lines.Append "playing" else lines.Append "paused" end if if audiofile <> nil and fileFormat <> nil then lines.Append str(audiofile.FramePosition / fileFormat.sampleRate)+" seconds" end if PlayStatus.Text = Join(lines, ", ") if audiofile <> nil then dim nodetime as AVAudioTimeMBS = self.player.lastRenderTime if nodetime <> nil then dim playerTime as AVAudioTimeMBS = self.player.playerTimeForNodeTime(nodetime) if playerTime <> nil then PositionSlider.Value = 1000.0 * (StartPos + playerTime.sampleTime) / audiofile.Length end if end if end if LabelVolume.Text = str(player.volume, "-0.0%") labelpan.Text = str(mixer.pan, "-0.0") LabelPitch.Text = str(PitchNode.pitch, "-0.00") LabelRate.Text = str(PitchNode.rate, "-0.00") Finally NoEvents = False End EventHandler
End Control
Control VolumeSlider Inherits Slider
ControlInstance VolumeSlider Inherits Slider
EventHandler Sub ValueChanged() if NoEvents then Return player.volume = me.Value / 100.0 End EventHandler
End Control
Control Label1 Inherits Label
ControlInstance Label1 Inherits Label
End Control
Control VolumePan Inherits Slider
ControlInstance VolumePan Inherits Slider
EventHandler Sub ValueChanged() if NoEvents then Return mixer.pan = me.Value / 100.0 End EventHandler
End Control
Control Label2 Inherits Label
ControlInstance Label2 Inherits Label
End Control
Control PositionSlider Inherits Slider
ControlInstance PositionSlider Inherits Slider
EventHandler Sub ValueChanged() if NoEvents then Return dim playing as Boolean = player.Playing player.stop dim l as Int64 = audiofile.Length dim x as Int64 = (me.Value * l) / 1000.0 dim r as int64 = audiofile.Length - x player.scheduleSegment(audiofile, nil, x, r) StartPos = x if Playing then player.play end if End EventHandler
End Control
Control VolumeRate Inherits Slider
ControlInstance VolumeRate Inherits Slider
EventHandler Sub ValueChanged() if NoEvents then Return PitchNode.bypass = false dim v as integer = me.Value if v >= 1 then PitchNode.rate = v elseif v = 0 then PitchNode.rate = 1 elseif v < 0 then PitchNode.rate = 1 / abs(v) else Break // impossible? end if End EventHandler
End Control
Control Label3 Inherits Label
ControlInstance Label3 Inherits Label
End Control
Control VolumePitch Inherits Slider
ControlInstance VolumePitch Inherits Slider
EventHandler Sub ValueChanged() if NoEvents then Return PitchNode.bypass = false PitchNode.pitch = me.Value End EventHandler
End Control
Control Label4 Inherits Label
ControlInstance Label4 Inherits Label
End Control
Control LabelVolume Inherits Label
ControlInstance LabelVolume Inherits Label
End Control
Control LabelPan Inherits Label
ControlInstance LabelPan Inherits Label
End Control
Control LabelRate Inherits Label
ControlInstance LabelRate Inherits Label
End Control
Control LabelPitch Inherits Label
ControlInstance LabelPitch Inherits Label
End Control
Control Label5 Inherits Label
ControlInstance Label5 Inherits Label
End Control
Control PopupOutput Inherits PopupMenu
ControlInstance PopupOutput Inherits PopupMenu
EventHandler Sub Change() if NoEvents then Return 'engine.stop output.CurrentDeviceID = me.RowTag(me.ListIndex) 'dim e as NSErrorMBS 'if engine.startAndReturnError(e) then '// ok ' 'else 'MsgBox "Failed: "+e.localizedDescription 'end if ' End EventHandler
End Control
Control LevelChannel0 Inherits ProgressBar
ControlInstance LevelChannel0 Inherits ProgressBar
End Control
Control LevelChannel1 Inherits ProgressBar
ControlInstance LevelChannel1 Inherits ProgressBar
End Control
EventHandler Sub Close() player.removeTapOnBus 0 End EventHandler
EventHandler Sub Open() NoEvents = true // Needs OS X 10.10 or newer Engine = new AVAudioEngineMBS player = new myAVAudioPlayerNodeMBS mixer = new AVAudioMixerNodeMBS PitchNode = new AVAudioUnitTimePitchMBS output = Engine.outputNode Engine.attachNode player Engine.attachNode mixer Engine.attachNode PitchNode AddHandler player.Tap, WeakAddressOf OutputTap 'dim f as AVAudioFormatMBS = mixer.outputFormatForBus(0) player.installTapOnBus 0, 8192, nil PitchNode.bypass = true // you can build a channel layout and use it 'dim qlayout as new QTAudioChannelLayoutMBS ' 'qlayout.ChannelLayoutTag = qlayout.kAudioChannelLayoutTag_Mono ' 'dim layout as new AVAudioChannelLayoutMBS(qlayout) 'dim outputFormat as new AVAudioFormatMBS(48100.0, layout) dim outputFormat as AVAudioFormatMBS = nil // default engine.connect(player, PitchNode, nil) engine.connect(PitchNode, mixer, nil) engine.connect(mixer, engine.outputNode, outputFormat) dim devices as Dictionary = output.OutputDevices dim DefaultDevice as integer = output.CurrentDeviceID for each key as Variant in devices.keys dim name as String = devices.Value(key) PopupOutput.AddRow name PopupOutput.RowTag(PopupOutput.ListCount-1) = key if key.IntegerValue = DefaultDevice then PopupOutput.ListIndex = PopupOutput.ListCount-1 end if next Finally NoEvents = false End EventHandler
Sub OutputTap(m as avaudioNodeMBS, bus as integer, bufferSize as UInt32, format as AVAudioFormatMBS, buffer as AVAudioPCMBufferMBS, time as AVAudioTimeMBS, tag as variant) dim l0 as Double = buffer.level(0) dim l1 as Double = buffer.level(1) 'dim l2 as Double = buffer.level(2) 'dim l3 as Double = buffer.level(3) 'dim mm as MemoryBlock = buffer.floatChannelDataCopy(0) // -74 to 90 maybe LevelChannel0.Value = 10 + l0 LevelChannel1.Value = 10 + l1 End Sub
Sub StartEngine() if not engine.running then dim e as NSErrorMBS if engine.startAndReturnError(e) then // ok PopupOutput.Enabled = false // switching while playing doesn't work right now? else MsgBox "Failed: "+e.localizedDescription end if end if End Sub
Property Engine As AVAudioEngineMBS
Property NoEvents As Boolean
Property PitchNode As AVAudioUnitTimePitchMBS
Property StartPos As int64
Property audiofile As AVAudioFileMBS
Property file As FolderItem
Property fileFormat As AVAudioFormatMBS
Property mixer As AVAudioMixerNodeMBS
Property output As AVAudioOutputNodeMBS
Property player As AVAudioPlayerNodeMBS
End Class
MenuBar MainMenuBar
MenuItem FileMenu = "&File"
MenuItem FileQuit = "#App.kFileQuit"
MenuItem EditMenu = "&Edit"
MenuItem EditUndo = "&Undo"
MenuItem EditSeparator1 = "-"
MenuItem EditCut = "Cu&t"
MenuItem EditCopy = "&Copy"
MenuItem EditPaste = "&Paste"
MenuItem EditClear = "#App.kEditClear"
MenuItem EditSeparator2 = "-"
MenuItem EditSelectAll = "Select &All"
End MenuBar
Class MyAVAudioPlayerNodeMBS Inherits AVAudioPlayerNodeMBS
EventHandler Sub scheduleFileCompleted(callbackType as integer, file as AVAudioFileMBS, time as AVAudioTimeMBS, tag as Variant) MsgBox "Playing finished." End EventHandler
End Class
End Project

See also:

The items on this page are in the following plugins: MBS AVFoundation Plugin.


The biggest plugin in space...