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.