问题描述
我有一个使用Swift UI开发的非常简单的iOS应用,该应用使用AVMIDIPlayer播放一系列MIDI事件。
我在本地测试了该事件序列,创建了一个.midi文件,并使用Garageband执行该文件。输出按预期工作。
但是,当我的应用程序播放它时,我没有收到任何错误,但也没有声音。相关代码:
import SwiftUI
let WELLCOME_MESSAGE = "hey there! Guess the sequence!"
let PLAYING_SEQUENCE = "Playing the Sequence..."
let MAJOR_THIRD = "Guessed Major Third"
let MInor_THIRD = "Guessed Minor Third"
let CORRECT_GUESS = "You guessed correctly! Hurray!"
let INCORRECT_GUESS = "You guessed wrongly! Looser!"
enum Note: UInt8,CaseIterable {
case MajorThird = 4
case MinorThird = 3
}
struct ContentView: View {
@State var score: Int = 0
@State var message: String = WELLCOME_MESSAGE
@State var intervalPlayer: IntervalPlayer! = IntervalPlayer.init(root: UInt8.random(in: 60...71),interval: Note.allCases.randomElement()!.rawValue)
fileprivate func processGuess(note: Note,resetAction: (String) -> Void) {
var result: String = ""
if self.intervalPlayer.interval == note.rawValue {
self.score += 1
result = CORRECT_GUESS
} else {
result = INCORRECT_GUESS
}
resetAction(result)
}
var body: some View {
let resetAction: (String) -> Void = { message in
self.message = message
self.intervalPlayer.setInterval(root: UInt8.random(in: 60...71),interval: Note.allCases.randomElement()!.rawValue)
}
vstack {
Text("\(self.message)")
.padding()
Button("Play Sequence",action: {
self.message = PLAYING_SEQUENCE
self.intervalPlayer.playInterval()
})
.padding(.bottom)
HStack {
Button("Guess Major Third",action: {
self.message = MAJOR_THIRD
dispatchQueue.main.asyncAfter(deadline: .Now() + .seconds(5)) {
processGuess(note: Note.MajorThird,resetAction: resetAction)
}
})
.padding(.trailing)
Button("Guess Minor Third",action: {
self.message = MInor_THIRD;
dispatchQueue.main.asyncAfter(deadline: .Now() + .seconds(5)) {
processGuess(note: Note.MinorThird,resetAction: resetAction)
}
})
.padding(.leading)
}
Text("Your score is \(self.score)")
.padding(.top)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import AVFoundation
import Foundation
let INTERVAL_MIDI_HEADER: [UInt8] = [
0x4D,0x54,0x68,0x64,// MIDI header: MThd
0x00,0x00,0x06,// MIDI header length: 6 bytes
0x00,// Single channel
0x00,0x01,// Number of channels
0x00,// Number of ticks per beat: 1 tick per beat
0x4D,0x72,0x6b,// Track header: MThd
]
let INTERVAL_MIDI_TRACK_MetaDATA: [UInt8] = [
0x00,0x3A,// Track length: 4 bytes * 8 events + 4 bytes for end of header + 22 for the headers = 58
0x00,0xFF,0x58,0x04,// Specify time signature,and clarify that the next four bytes will contain that information
0x04,0x18,0x08,// Time signature is 4/4,and 0x18 MIDI clocks in a metronome clock
0x00,0x51,0x03,// Specify the tempo of the track.
0x08,0x7a,0x23,// 1,000,000 microseconds per quarter note. Because it is in 4/4,this means 60 bpm.
0x00,0xc0,0x4f,// Set MIDI instrument to 79 (Flute)
0x00,0xb0,0x07,0x20,// Main volume
]
let END_OF_TRACK_BYTES: [UInt8] = [0x00,0xff,0x2f,0x00]
struct IntervalPlayer {
var midiPlayer: AVMIDIPlayer?
var root: UInt8!
var interval: UInt8!
init(root: UInt8,interval: UInt8) {
self.setInterval(root: root,interval: interval)
}
mutating func setInterval(root: UInt8,interval: UInt8) {
self.root = root
self.interval = interval
let intervalNote = root + interval
let sequence: [UInt8] = INTERVAL_MIDI_HEADER + INTERVAL_MIDI_TRACK_MetaDATA + [
0x00,0x90,root,0x40,// Play the root at beat 1.
0x01,0x80,// Stop playing the root at beat 2.
0x00,intervalNote,// Play the interval at beat 2.
0x01,// Stop playing the interval at beat 3.
0x01,// Play the interval at beat 4.
0x01,// Stop playing the interval at beat 5.
0x00,// Play the root at beat 5.
0x01,// Stop playing the root at beat 6.
] + END_OF_TRACK_BYTES
let data = Data.init(sequence)
guard let bankURL = Bundle.main.url(forResource: "FluidR3_GM",withExtension: "sf2") else {
fatalError("soundbank file not found.")
}
do {
self.midiPlayer = try AVMIDIPlayer.init(data: data,soundBankURL: bankURL)
print("created midi player with sound bank url \(bankURL)")
self.midiPlayer!.preparetoPlay()
print("PREPARES TO PLAY")
} catch let error as NSError {
print("Error \(error.localizedDescription)")
}
}
func playInterval() {
if !self.midiPlayer!.isPlaying {
self.midiPlayer!.currentPosition = 0
self.midiPlayer!.play({
print("FINISHED")
})
print("PLAYS?")
}
}
}
当我用Google搜索类似问题时,似乎每个人都认为问题是midiPlayer
寿命不足。但是,这里似乎并非如此。当我单击“播放序列...”按钮时,在终端中,我看到以下消息:
PLAYS?
FINISHED
符合预期。 FINISHED
会在PLAYS
之后大约3秒钟出现,这也是音频持续的时间。但是,再次没有声音播放。
感谢您的帮助!
谢谢。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)