首页 > 解决方案 > Midi 音乐音高转换不适用于 Swift 中的 iOS

问题描述

我有一个简单的应用程序,带有键盘来播放音符。我的问题是我正在尝试添加一个滑块来弯曲音符音高,但音高没有变化。没有错误或任何东西,音符继续播放,音高没有任何变化。

我知道还有其他类似的问题,我已经阅读了我能找到的所有内容,并且我所实施的内容是基于该研究,但它不起作用。

请帮助我找出我做错了什么,或者为我指出一个更好的方向。

在我的工作代码中,我使用的是 AudioToolbox。我创建并打开一个新的 AUGraph。我在图中添加了一个 kAudioUnitSubType_RemoteIO 和一个 kAudioUnitSubType_MIDISynth 节点。我把它全部连接起来,我可以弹奏音符。

为了添加弯音,我创建了一个 kAudioUnitSubType_NewTimePitch AUNode,并将它连接在合成器和输出节点之间。然后我从效果节点获取音高 AudioUnit,当我的滑块值更改时,我尝试使用带有 kNewTimePitchParam_Pitch 的 AudioUnitSetParameter 更改音高。一切仍然有效,但是当我弹奏一个音符并拖动滑块时,音符的音高没有任何变化。我的滑块值在 -1000 和 +1000 之间变化,我看到打印出来的值,所以我知道正在调用 pitchBend 函数。

这是我的相关代码。这段代码,除了我试图添加的音高效果部分,大量复制了我在这里找到的内容: https ://rollout.io/blog/building-a-midi-music-app-for-ios-in -迅速/

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))
    }
  }
}

谢谢。任何帮助都会得到帮助。

标签: iosswiftaudiotoolboxpitch-shifting

解决方案


推荐阅读