Platforms to show: All Mac Windows Linux Cross-Platform
/AVFoundation/AVAudioEngine manual rendering
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 manual rendering
This example is the version from Sun, 2nd Jan 2021.
Project "AVAudioEngine manual rendering.xojo_binary_project"
Class App Inherits Application
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
EventHandler Sub Open()
Dim InputFile As FolderItem = GetFolderItem("/Users/cs/Desktop/test.MOV", FolderItem.PathTypeNative)
If Not InputFile.Exists Then
// please change path!
Break
return
End If
Dim outputFile As FolderItem = SpecialFolder.Desktop.Child("test.aac")
Call export(InputFile, 1.0, 1.0, outputFile)
End EventHandler
Function export(InputFile as FolderItem, floRate as double, floPitch as Double, DestFile as FolderItem) As Boolean
// floRate = Float of playback rate (e.g. 0.5 = 50% speed)
// floPitch = Float for pitch change required
// DestFile = Filename of file where to put
System.debuglog("Exporting...")
Dim engine As New AVAudioEngineMBS
Dim player As New AVAudioPlayerNodeMBS
Dim pitchEffect As New AVAudioUnitTimePitchMBS
// We found the file in the folder.
system.debuglog("Audio file obtained")
Dim error As NSErrorMBS
Dim file As New AVAudioFileMBS(InputFile, error)
If error <> Nil Then
Dim e As String = error.LocalizedDescription
System.DebugLog "Failed to open file: "+e
Break
Return false
End If
system.debuglog("File opened OK")
Dim Format As AVAudioFormatMBS = file.processingFormat
// Set the pitch-shift amount (100 cents = 1 semitone).
pitchEffect.pitch = floPitch // semitone
pitchEffect.overlap = 32.0 // more overlapping windows = less artifacts - Max = 32
pitchEffect.rate = floRate // Rate
// Add nodes to engine.
engine.attachNode(player)
engine.attachNode(pitchEffect)
// Connect the player's output to the effect's input.
engine.connect(player, pitchEffect, file.processingFormat)
// Connect the effect's output to the main mixer input.
engine.connect(pitchEffect, engine.mainMixerNode, file.processingFormat)
// Schedule the file and start the engine.
player.scheduleFile(file, Nil)
// Set engine to manual rendering.
// The maximum number of frames the engine renders in any single render call.
Dim maxFrames As Integer = 4096
Dim mode As Integer = AVAudioEngineMBS.ManualRenderingModeOffline
Dim r As Boolean
r = engine.enableManualRenderingMode(mode, Format, maxFrames, error)
If error <> Nil Then
Dim e As String = error.LocalizedDescription
System.DebugLog "Failed to enable manual rendering mode: "+e
Break
Return False
End If
// Start engine.
If engine.startAndReturnError(error) then
System.debuglog("Engine started")
Else
Dim e As String = error.LocalizedDescription
System.DebugLog "Engine failed to start: "+e
Break
Return False
End If
// Play the file.
player.play
// The output buffer to which the engine renders the processed data.
dim buffer as new AVAudioPCMBufferMBS(engine.manualRenderingFormat, engine.manualRenderingMaximumFrameCount)
// Setup our output file to export to.
Dim outputFile As New AVAudioFileMBS(DestFile, file.fileFormat.settings, error)
If error <> Nil Then
Dim e As String = error.LocalizedDescription
System.DebugLog "Failed to create output file: "+e
Break
Return false
End If
// Need to work out what the new length would be based on the rate passed.
// This prevents output ending early or having silence at end of output.
Dim newLength As Double = file.length * (1.0 / floRate)
newLength = Ceil(newLength) // round up
Dim newLengthInt As Integer = newLength
// Render output to buffer and output buffer content to file in loop.
While engine.manualRenderingSampleTime < newLength
// Work out how many frames to render.
Dim frameCount As Int64 = newLengthInt - engine.manualRenderingSampleTime
Dim framesToRender As Int64 = Min(frameCount, buffer.frameCapacity)
// Render offline to buffer.
Dim status As Integer = engine.renderOffline(framesToRender, buffer, error)
If error <> Nil Then
Dim e As String = error.LocalizedDescription
System.DebugLog "Failed to render offline: "+e
Break
Return False
End If
// Check if we had success.
Select Case status
Case AVAudioEngineMBS.ManualRenderingStatusSuccess
// The data rendered successfully. Write it to the output file.
Call outputFile.writeFromBuffer(buffer, error)
If error <> Nil Then
Dim e As String = error.LocalizedDescription
System.DebugLog "Failed to write buffer: "+e
Break
Return False
End If
Case AVAudioEngineMBS.ManualRenderingStatusInsufficientDataFromInputNode
// Applicable only when using the input node as one of the sources.
Break
Case AVAudioEngineMBS.ManualRenderingStatusCannotDoInCurrentContext
// The engine couldn't render in the current render call.
// Retry in the next iteration.
Break
Case AVAudioEngineMBS.ManualRenderingStatusError
// An error occurred while rendering the audio.
System.DebugLog("Manual offline rendering failed.")
Break
Else
// Something unexpected.
// We'll retry in the next iteration.
Break
End Select
Wend
// Stop the player node and engine.
player.stop
engine.stop
player = Nil
engine = Nil
System.debuglog("Audio file exported")
return true
// catch all exceptions
Exception re As RuntimeException
System.DebugLog "Got exception: "+Introspection.GetType(re).name
System.DebugLog Str(re.errorNumber)+": "+re.message
Break
Return False
End Function
End Class
Class MainWindow Inherits Window
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
End Project
The items on this page are in the following plugins: MBS AVFoundation Plugin.