AVAudioFile.write (from :) завершается ошибкой, когда буфер содержит чередующийся звук - PullRequest
2 голосов
/ 22 марта 2020

Я пытаюсь записать аудиофайл после некоторой обработки, и получаю ошибку. Я сократил ошибку до этого простого автономного случая:

import Foundation
import AVFoundation

do {
    let inputFileURL = URL(fileURLWithPath: "/Users/andrewmadsen/Desktop/test.m4a")
    let file = try AVAudioFile(forReading: inputFileURL, commonFormat: .pcmFormatFloat32, interleaved: true)
    guard let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: AVAudioFrameCount(file.length)) else {
        throw NSError()
    }
    buffer.frameLength = buffer.frameCapacity
    try file.read(into: buffer)

    let tempURL =
    URL(fileURLWithPath: NSTemporaryDirectory())
        .appendingPathComponent("com.openreelsoftware.AudioWriteTest")
        .appendingPathComponent(UUID().uuidString)
        .appendingPathExtension("caf")
    let fm = FileManager.default
    let dirURL = tempURL.deletingLastPathComponent()
    if !fm.fileExists(atPath: dirURL.path, isDirectory: nil) {
        try fm.createDirectory(at: dirURL, withIntermediateDirectories: true, attributes: nil)
    }

    var settings = buffer.format.settings
    settings[AVAudioFileTypeKey] = kAudioFileCAFType
    let tempFile = try AVAudioFile(forWriting: tempURL, settings: settings)
    try tempFile.write(from: buffer)

} catch {
    print(error)
}

Когда этот код выполняется, вызов tempFile.write(from: buffer) выдает ошибку:

Error Domain=com.apple.coreaudio.avfaudio Code=-50 "(null)" UserInfo={failed call=ExtAudioFileWrite(_imp->_extAudioFile, buffer.frameLength, buffer.audioBufferList)}

test.m4a - стерео , 44,1 кГц AA C (из магазина iTunes), хотя сбой происходит и с другими стереофайлами в других форматах (AIFF и WAV).

Код не произойдет сбой и вместо этого правильно сохранит исходное аудио в новый файл, если я изменю параметр interleaved на false при создании исходного ввода AVAudioFile (file). Однако в этом случае на консоль записывается следующее сообщение:

Audio files cannot be non-interleaved. Ignoring setting AVLinearPCMIsNonInterleaved YES.

Кажется странным и сбивающим с толку, что запись в буфер без чередования работает нормально, несмотря на сообщение о том, что файлы должны чередоваться, а запись чередуемого буфера не удалась. Это противоположно тому, что я ожидал.

Мне известно, что при чтении файла с использованием обычного инициализатора AVAudioFile(forReading:) без указания формата по умолчанию используется не чередующийся (ie. "Стандарт" AVAudioFormat при фактической частоте дискретизации файла и количестве каналов). Означает ли это, что мне действительно нужно преобразовать чересстрочное аудио в не чередующееся, прежде чем пытаться записать его?

Примечательно, что в реальной программе, где возникла эта проблема, я делаю нечто гораздо более сложное, чем просто чтение файла и запись его снова, и мне нужно обрабатывать чередующийся звук. Однако я подтвердил, что этот оригинальный, более сложный код также не работает только для чересстрочного стереозвука.

Есть ли что-то хитрое, что мне нужно сделать, чтобы заставить AVAudioFile написать из буфера, содержащего чересстрочный звук PCM?

Ответы [ 2 ]

1 голос
/ 22 марта 2020

Смешение здесь состоит в том, что в игре два формата: формат выходного файла и формат буферов, которые вы будете записывать (формат обработки). Инициализатор AVAudioFile(forWriting: settings:) не позволяет вам выбрать формат обработки и по умолчанию принимает чередование, следовательно, ваша ошибка.

Это открывает файл для записи в стандартном формате (с плавающей точкой без чередования).

Вам необходимо использовать другой инициализатор: AVAudioFile(forWriting:settings: commonFormat:interleaved:), последние два аргумента которого определяют формат обработки (имена аргументов могли бы быть более понятными в этом случае).

var settings: [String : Any] = [:]

settings[AVFormatIDKey] = kAudioFormatMPEG4AAC
settings[AVAudioFileTypeKey] = kAudioFileCAFType
settings[AVSampleRateKey] = buffer.format.sampleRate
settings[AVNumberOfChannelsKey] = 2
settings[AVLinearPCMIsFloatKey] = (buffer.format.commonFormat == .pcmFormatInt32)

let tempFile = try AVAudioFile(forWriting: tempURL, settings: settings, commonFormat: buffer.format.commonFormat, interleaved: buffer.format.isInterleaved)
try tempFile.write(from: buffer)

пс Передав настройку формата буфера непосредственно в AVAudioFile, вы получите caf-файл LPCM, который вам может не понадобиться, поэтому я воссоздаю настройки файла.

1 голос
/ 22 марта 2020

Здесь нет ничего положительного, но, возможно, поскольку настройки выходного файла совпадают с форматом обработки, возможно, формат обработки имеет негибкую политику в отношении чередования, тогда как формат параметров файла подойдет для этого - или наоборот наоборот.

Вот что я попробую в первую очередь. Неполный пример, но должно быть достаточно, чтобы проиллюстрировать тестируемые области.

let sourceFile: AVAudioFile
let format: AVAudioFormat

do {
    // for the moment, try this without any specific format and see what it gives you
    let sourceFile = try AVAudioFile(forReading: inputFileURL)
    format = sourceFile.processingFormat
    print(format) // let's see what we're getting so far, maybe some clues
} catch {
    fatalError("Unable to load the source audio file: \(error.localizedDescription).")
}

let sourceSettings = sourceFile.fileFormat.settings
var outputSettings = sourceSettings // start with the settings of the original file rather than the buffer format settings
outputSettings[AVAudioFileTypeKey] = kAudioFileCAFType

// etc...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...