Как воспроизвести массив аудиосэмплов [Int16] из памяти в Swift

Пытаюсь создать игровой музыкальный проигрыватель (NSF, SPC и т. д.) для Mac, используя библиотека GME.

Я провел часы за часами, тестируя так много решений и советов здесь, на SO, но, похоже, ни одно решение не работает хорошо. Я перепробовал много вариантов маршрута AVAudioEngine/AVAudioPlayerNode/scheduleBuffer, но поскольку ни один из них не работал, я просто переключился на преобразование семплов в данные Wav и воспроизведение по памяти. Однако это ДЕЙСТВИТЕЛЬНО работает, преобразование из [Int16] в [UInt8] (чтобы создать данные для волнового массива) происходит очень медленно. По крайней мере, для более высоких частот дискретизации и песен длиннее нескольких секунд. Пример чистого кода ниже. Отзывы и предложения ОЧЕНЬ приветствуются.

Протестировано:

  1. пример AVAudioPlayerNode (не удается приступить к работе, например, устройство ввода не найдено, нет звук и тд и тп)
  2. Пример буферизации в wav (работает, но медленно)

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

person Joneth    schedule 12.07.2020    source источник


Ответы (1)


Быстро ознакомился с библиотекой SDL и ее звуковыми возможностями. Кажется, вы можете просто передать любой тип буфера, который вы хотите, и он просто работает:

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!

По-прежнему был бы признателен за любые отзывы по исходному сообщению/вопросу, но с точки зрения решения я думаю, что это так же хорошо (или лучше), чем любое другое.

person Joneth    schedule 13.07.2020