IOS Core Audio - MP3 в WAV работает только при 1 канале, как получить стерео? - PullRequest
1 голос
/ 28 мая 2020

В данный момент принимаю MP3-файл, выводящий WAV. Мой код какое-то время работал нормально, но теперь я хочу изменить его, где экспортированный WAV представляет собой двухканальный стерео файл.

Проблема где-то здесь. Здесь описывается желаемый формат вывода.

Этот код ниже - это то, что раньше работало нормально (моно):

AudioStreamBasicDescription outputFormat = new AudioStreamBasicDescription();
outputFormat.setFormat(AudioFormat.LinearPCM);
outputFormat.setFormatFlags(AudioFormatFlags.Canonical);
outputFormat.setBitsPerChannel(16);
outputFormat.setChannelsPerFrame(1);
outputFormat.setFramesPerPacket(1);
outputFormat.setBytesPerFrame(2);
outputFormat.setBytesPerPacket(2);
outputFormat.setSampleRate(pitch);

Изменение его на setChannelsPerFrame(2); не сработало . Не уверены, что еще нужно изменить?

Ошибка:

Launcher[318:12909] 224: SetDataFormat failed
Launcher[318:12909] 367: EXCEPTION (1718449215): "create audio file"

org.robovm.apple.corefoundation.OSStatusException: 1718449215
at org.robovm.apple.corefoundation.OSStatusException.throwIfNecessary(OSStatusException.java:53)
at org.robovm.apple.audiotoolbox.ExtAudioFile.create(ExtAudioFile.java:80)
at package.Launcher.mp3ToPCM(Launcher.java:1108)
...

Где строка, о которой идет речь,

outputFileExtAudio = ExtAudioFile.create(outputFileURL, AudioFileType.WAVE, outputFormat, null, AudioFileFlags.EraseFile);

Но проблема должно происходить из моего AudioStreamBasicDescription для outputFormat, поскольку это единственное, что меняется на «2 канала», и внезапно оно больше не работает.

(Это код Java, использующий RoboVM для преобразования в собственный IOS код.)

1 Ответ

2 голосов
/ 28 мая 2020

Также необходимо обновить размеры.

В Core Audio сэмпл - это одно единственное значение, а кадр - это один сэмпл по всем каналам. Для звука PCM один кадр также является одним пакетом.

Для 16-битного монофонического звука кадр и образец являются синонимами и занимают 2 байта. Для 16-битного стереозвука кадр состоит из двух отсчетов (левого и правого), каждый из которых занимает 2 байта, а каждый кадр занимает 4 байта.

Значения AudioStreamBasicDescription незначительно различаются в зависимости от того, описываемый формат чередуется или нет.

Обычно вы можете думать о без чередования PCM AudioStreamBasicDescription s следующим образом:

asbd.mBytesPerFrame     = asbd.mBitsPerChannel / 8;

и с чередованием вот так:

asbd.mBytesPerFrame     = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;

с оба с

asbd.mFramesPerPacket   = 1;
asbd.mBytesPerPacket    = asbd.mBytesPerFrame;

AudioFormatFlags.Canonical устарело, но я предполагаю, что здесь это приравнивается к чередующемуся упакованному native- endian целые числа со знаком.

Итак, для вашего случая 16-битное стерео с чередованием:

AudioStreamBasicDescription outputFormat = new AudioStreamBasicDescription();
outputFormat.setFormat(AudioFormat.LinearPCM);
outputFormat.setFormatFlags(AudioFormatFlags.Canonical);

outputFormat.setSampleRate(pitch);
outputFormat.setChannelsPerFrame(2);
outputFormat.setBitsPerChannel(16);

outputFormat.setBytesPerFrame(4);
outputFormat.setFramesPerPacket(1);
outputFormat.setBytesPerPacket(4);

Вот две вспомогательные функции (в C ++), показывающие отношения:

static AudioFormatFlags CalculateLPCMFlags(UInt32 validBitsPerChannel, UInt32 totalBitsPerChannel, bool isFloat, bool isBigEndian, bool isNonInterleaved)
{
    return (isFloat ? kAudioFormatFlagIsFloat : kAudioFormatFlagIsSignedInteger) | (isBigEndian ? ((UInt32)kAudioFormatFlagIsBigEndian) : 0) | ((validBitsPerChannel == totalBitsPerChannel) ? kAudioFormatFlagIsPacked : kAudioFormatFlagIsAlignedHigh) | (isNonInterleaved ? ((UInt32)kAudioFormatFlagIsNonInterleaved) : 0);
}

static void FillOutASBDForLPCM(AudioStreamBasicDescription *asbd, Float64 sampleRate, UInt32 channelsPerFrame, UInt32 validBitsPerChannel, UInt32 totalBitsPerChannel, bool isFloat, bool isBigEndian, bool isNonInterleaved)
{
    asbd->mFormatID = kAudioFormatLinearPCM;
    asbd->mFormatFlags = CalculateLPCMFlags(validBitsPerChannel, totalBitsPerChannel, isFloat, isBigEndian, isNonInterleaved);

    asbd->mSampleRate = sampleRate;
    asbd->mChannelsPerFrame = channelsPerFrame;
    asbd->mBitsPerChannel = validBitsPerChannel;

    asbd->mBytesPerPacket = (isNonInterleaved ? 1 : channelsPerFrame) * (totalBitsPerChannel / 8);
    asbd->mFramesPerPacket = 1;
    asbd->mBytesPerFrame = (isNonInterleaved ? 1 : channelsPerFrame) * (totalBitsPerChannel / 8);
}
...