Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
163 views
in Technique[技术] by (71.8m points)

Midi music pitch shift not working for iOS in Swift

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...