如何在 Swift 中播放内存中的 [Int16] 音频样本数组

How to play an array of [Int16] audio samples from memory in Swift

尝试使用 GME 库为 Mac 构建游戏音乐播放器(NSF、SPC 等)。

我在 SO 上花了几个小时来测试如此多的解决方案和技巧,但似乎没有一个解决方案效果很好。我已经尝试了 AVAudioEngine/AVAudioPlayerNode/scheduleBuffer 路线的许多变体,但由于其中 none 有效,我只是切换到将样本转换为 Wav 数据并从内存中播放。这确实有效,但是,从 [Int16] 转换为 [UInt8](以便为波形阵列创建数据)非常慢。至少对于更高的采样率和超过几秒钟的歌曲而言。下面的“干净”代码示例。非常欢迎反馈和建议。

已测试:

  1. (无法开始工作,例如未找到输入设备、没有声音等)
  2. Buffer to wav data example(有效,但速度慢)

override func viewDidLoad() {
    super.viewDidLoad()

    gme_type_list()

    var emu = gme_new_emu( gme_nsf_type, 48000 ) // 48kHz
    gme_open_file("path-to-file-on-disk.nsf", &emu, 48000) // 48kHz

    let sampleCount: Int32 = 150 * 48000 * 2 // 150 = Lenght in sec, 48kHz
    var output = Array<Int16>.init(repeating: 0, count: sampleCount)

    gme_start_track(emu, 0)
    gme_play(emu, sampleCount, &output) // Generates *sampleCount* samples in Int16 format

    let samples = output.withUnsafeBufferPointer { buffer -> Array<Int16> in
        var result = [Int16]()
        for i in stride(from: buffer.startIndex, to: buffer.endIndex, by: 2) {
            result.append(buffer[i])
        }
        return result
    }

    // Calls a slightly modified version of example 2 above 
    // (to support own samples, in Int16 rather than Float).
    // Works! But "fillWave" method is soooo slow!
    play(samples: samples)
}

快速浏览了 SDL 库及其 audio capabilities。似乎你可以只提供你想要的任何缓冲区类型,它就可以工作:

var desiredSpec = SDL_AudioSpec()
desiredSpec.freq = 48000
desiredSpec.format = SDL_AudioFormat(AUDIO_S16) // Specify Int16 as format
desiredSpec.samples = 1024

var obtainedSpec = SDL_AudioSpec()

SDL_OpenAudio(&desiredSpec, &obtainedSpec)
SDL_QueueAudio(1, samples, Uint32(sampleCount)) // Samples and count from original post
SDL_PauseAudio(0) // Starts playing, virtually no lag!

仍将感谢对原始 post/question 的任何反馈,但就解决方案而言,我认为这与任何解决方案一样好(或更好)。