如何解决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-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,&effectUnit))
let synthOutputElement: AudioUnitElement = 0
let effectOutputElement: AudioUnitElement = 0
let IoUnitInputElement: AudioUnitElement = 0
checkerror(osstatus:
AUGraphConnectNodeInput(audioGraph!,synthOutputElement,effectOutputElement))
checkerror(osstatus:
AUGraphConnectNodeInput(audioGraph!,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),componentFlagsMask: 0)
checkerror(osstatus: AUGraphAddNode(audioGraph!,&synthNode))
}
func createEffectNode() {
var cd = AudioComponentDescription(
componentType: OSType(kAudioUnitType_Effect),componentSubType: OSType(kAudioUnitSubType_NewTimePitch),componentFlagsMask: 0)
checkerror(osstatus: AUGraphAddNode(audioGraph!,&effectNode))
}
func pitchBend(pitchBend: Double) {
print("Bend pitch by " + String(pitchBend))
checkerror(osstatus: AudioUnitSetParameter(effectUnit!,AudioUnitPropertyID(kNewTimePitchParam_Pitch),AudioUnitScope(kAudioUnitScope_Global),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,midiveLocity: Int) {
let noteCommand = UInt32(0x80 | channel)
checkerror(osstatus: MusicDeviceMIDIEvent(synthUnit!,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),&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),&enabled,UInt32(MemoryLayout<UInt32>.size)))
let programChangeCommand = UInt32(0xC0 | channel)
checkerror(osstatus: MusicDeviceMIDIEvent(self.synthUnit!,programChangeCommand,patch,0))
checkerror(osstatus: AudioUnitSetProperty(
synthUnit!,&disabled,UInt32(MemoryLayout<UInt32>.size)))
// the prevIoUs programChangeCommand just triggered a preload
// this one actually changes to the new voice
checkerror(osstatus: MusicDeviceMIDIEvent(synthUnit!,0))
}
func checkerror(osstatus: Osstatus) {
if osstatus != noErr {
print(SoundError.GetErrorMessage(osstatus))
}
}
}
谢谢。任何帮助是appriciated。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。