I have a simple app with a keyboard to play music notes. My issue is that I am trying to add a slider to bend the note pitch, but the pitch is not shifting. No errors or anything, the note continues to play without any change in pitch.
I know there are other similar questions on this, I've read all I could find and what I've implemented is based on that research, but it isn't working.
Please help me find what I'm doing wrong, or point me in a better direction.
In my working code, I am using AudioToolbox. I create and open a new AUGraph. I add a kAudioUnitSubType_RemoteIO, and a kAudioUnitSubType_MIDISynth node to the graph. I hook it all up and I can play notes.
To add the pitch bend I created a kAudioUnitSubType_NewTimePitch AUNode and I connect it between the synth and the output node. Then I get the pitch AudioUnit from the effect node and when my slider value changes I try to change the pitch using AudioUnitSetParameter with kNewTimePitchParam_Pitch. Everything still works but when I play a note, and drag the slider nothing happens to the note's pitch. My slider value changes between -1000 and +1000, and I see the value printed out, so I know the pitchBend function is being called.
Here is my pertinent code. This code, except for the pitch effect part I'm trying to add, was heavily copied from what I found here:
https://rollout.io/blog/building-a-midi-music-app-for-ios-in-swift/
import Foundation
import AudioToolbox
class AudioSynth
{
var audioGraph: AUGraph?
var synthNode = AUNode()
var effectNode = AUNode()
var outputNode = AUNode()
var synthUnit: AudioUnit?
var effectUnit: AudioUnit?
var patch = UInt32(0)
func initAudio() {
checkError(osstatus: NewAUGraph(&audioGraph))
createOutputNode(audioGraph: audioGraph!, outputNode: &outputNode)
createSynthNode()
createEffectNode()
checkError(osstatus: AUGraphOpen(audioGraph!))
// get the synth unit
checkError(osstatus: AUGraphNodeInfo(audioGraph!, synthNode, nil, &synthUnit))
checkError(osstatus: AUGraphNodeInfo(audioGraph!, effectNode, nil, &effectUnit))
let synthOutputElement: AudioUnitElement = 0
let effectOutputElement: AudioUnitElement = 0
let ioUnitInputElement: AudioUnitElement = 0
checkError(osstatus:
AUGraphConnectNodeInput(audioGraph!, synthNode, synthOutputElement,
effectNode, effectOutputElement))
checkError(osstatus:
AUGraphConnectNodeInput(audioGraph!, effectNode, effectOutputElement,
outputNode, ioUnitInputElement))
checkError(osstatus: AUGraphInitialize(audioGraph!))
checkError(osstatus: AUGraphStart(audioGraph!))
loadSoundFont()
loadPatch(patchNo: 0)
}
// Mark: - Audio Init Utility Methods
func createOutputNode(audioGraph: AUGraph, outputNode: UnsafeMutablePointer<AUNode>) {
var cd = AudioComponentDescription(
componentType: OSType(kAudioUnitType_Output),
componentSubType: OSType(kAudioUnitSubType_RemoteIO),
componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
componentFlags: 0,componentFlagsMask: 0)
checkError(osstatus: AUGraphAddNode(audioGraph, &cd, outputNode))
}
func createSynthNode() {
var cd = AudioComponentDescription(
componentType: OSType(kAudioUnitType_MusicDevice),
componentSubType: OSType(kAudioUnitSubType_MIDISynth),
componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
componentFlags: 0,componentFlagsMask: 0)
checkError(osstatus: AUGraphAddNode(audioGraph!, &cd, &synthNode))
}
func createEffectNode() {
var cd = AudioComponentDescription(
componentType: OSType(kAudioUnitType_Effect),
componentSubType: OSType(kAudioUnitSubType_NewTimePitch),
componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
componentFlags: 0,componentFlagsMask: 0)
checkError(osstatus: AUGraphAddNode(audioGraph!, &cd, &effectNode))
}
func pitchBend(pitchBend: Double) {
print("Bend pitch by " + String(pitchBend))
checkError(osstatus: AudioUnitSetParameter(effectUnit!, AudioUnitPropertyID(kNewTimePitchParam_Pitch), AudioUnitScope(kAudioUnitScope_Global), 0, AudioUnitParameterValue(pitchBend), 0))
}
func playNoteOn(channel: Int, note: UInt32, midiVelocity: Int) {
let noteCommand = UInt32(0x90 | channel)
checkError(osstatus: MusicDeviceMIDIEvent(synthUnit!, noteCommand, note, UInt32(midiVelocity), 0))
}
func playNoteOff(channel: Int, note: UInt32, midiVelocity: Int) {
let noteCommand = UInt32(0x80 | channel)
checkError(osstatus: MusicDeviceMIDIEvent(synthUnit!, noteCommand, note, 0, 0))
}
// In the simulator this takes a long time, so we
// call it in a background thread in the controller
func loadSoundFont() {
var bankURL = Bundle.main.url(forResource: "FluidR3_GM", withExtension: "sf2")
checkError(osstatus: AudioUnitSetProperty(synthUnit!, AudioUnitPropertyID(kMusicDeviceProperty_SoundBankURL), AudioUnitScope(kAudioUnitScope_Global), 0, &bankURL, UInt32(MemoryLayout<URL>.size)))
}
func loadPatch(patchNo: Int) {
let channel = UInt32(0)
var enabled = UInt32(1)
var disabled = UInt32(0)
patch = UInt32(patchNo)
checkError(osstatus: AudioUnitSetProperty(
synthUnit!,
AudioUnitPropertyID(kAUMIDISynthProperty_EnablePreload),
AudioUnitScope(kAudioUnitScope_Global),
0,
&enabled,
UInt32(MemoryLayout<UInt32>.size)))
let programChangeCommand = UInt32(0xC0 | channel)
checkError(osstatus: MusicDeviceMIDIEvent(self.synthUnit!, programChangeCommand, patch, 0, 0))
checkError(osstatus: AudioUnitSetProperty(
synthUnit!,
AudioUnitPropertyID(kAUMIDISynthProperty_EnablePreload),
AudioUnitScope(kAudioUnitScope_Global),
0,
&disabled,
UInt32(MemoryLayout<UInt32>.size)))
// the previous programChangeCommand just triggered a preload
// this one actually changes to the new voice
checkError(osstatus: MusicDeviceMIDIEvent(synthUnit!, programChangeCommand, patch, 0, 0))
}
func checkError(osstatus: OSStatus) {
if osstatus != noErr {
print(SoundError.GetErrorMessage(osstatus))
}
}
}
Thanks. Any help is appriciated.
question from:
https://stackoverflow.com/questions/65863277/midi-music-pitch-shift-not-working-for-ios-in-swift