Я хочу выполнить некоторую обработку аудиофайлов без их воспроизведения, просто математика.Я сомневаюсь, что я делаю правильно, и у меня есть несколько вопросов.Я прочитал несколько примеров, но большинство из них касается потокового видео и вообще не работает с необработанными данными.
Я подготовил mp3-файл, который имеет 2 одинаковых канала, т.е. он является стереофоническимно левый и правый одинаковы.После декодирования я ожидал получить буфер с парами равных чисел, потому что PCM-16 поочередно хранит выборки каналов, например, {L
R
L
R
L
R
...}, верно?Например:
{105
105
601
601
-243
-243
-484
-484
...}.
Но я получаю пары близких чисел, но не равных:
{-308
-264
-1628
-1667
-2568
-2550
-4396
-4389
}
Алгоритмы mp3 по-разному кодируют одни и те же значения или почему?
Я хочу обрабатывать данные в пакетах по 1024 семпла.Если для другого пакета будет недостаточно образцов, я хочу сохранить остальные до следующей партии необработанных данных (см. mExcess
в коде).Есть ли гарантия, что порядок будет сохранен?
Я привык понимать «выборку» как каждое значение аудиоданных.Здесь я вижу MediaExtractor::readSampleData
и MediaExtractor::advance
методы.Первое возвращает ~ 2000 значений, в описании второго сказано «Переход к следующему образцу».Это просто совпадение имен?Я видел пару примеров, где эти методы вызываются попарно в цикле.Является ли мое использование правильным?
Вот мой код:
public static void foo(String filepath) throws IOException {
final int SAMPLES_PER_CHUNK = 1024;
MediaExtractor mediaExtractor = new MediaExtractor();
mediaExtractor.setDataSource(filepath);
MediaFormat mediaFormat = mediaExtractor.getTrackFormat(0);
mediaExtractor.release();
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
mediaFormat.setString(MediaFormat.KEY_FRAME_RATE, null);
String codecName = mediaCodecList.findDecoderForFormat(mediaFormat);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 0); // MediaCodec crashes with JNI
// error if FRAME_RATE is null
MediaCodec mediaCodec = MediaCodec.createByCodecName(codecName);
mediaCodec.setCallback(new MediaCodec.Callback() {
private MediaExtractor mExtractor;
private short[] mExcess;
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
if (mExtractor == null) {
mExtractor = new MediaExtractor();
try {
mExtractor.setDataSource(filepath);
mExtractor.selectTrack(0);
} catch (IOException e) {
e.printStackTrace();
}
mExcess = new short[0];
}
ByteBuffer in = codec.getInputBuffer(index);
in.clear();
int sampleSize = mExtractor.readSampleData(in, 0);
if (sampleSize > 0) {
boolean isOver = !mExtractor.advance();
codec.queueInputBuffer(
index,
0,
sampleSize,
mExtractor.getSampleTime(),
isOver ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
} else {
int helloAmaBreakpoint = 1;
}
}
@Override
public void onOutputBufferAvailable(
MediaCodec codec,
int index,
MediaCodec.BufferInfo info) {
ByteBuffer tmp = codec.getOutputBuffer(index);
if (tmp.limit() == 0) return;
ShortBuffer out = tmp.order(ByteOrder.nativeOrder()).asShortBuffer();
// Prepend the remainder from previous batch to the new data
short[] buf = new short[mExcess.length + out.limit()];
System.arraycopy(mExcess, 0, buf, 0, mExcess.length);
out.get(buf, mExcess.length, out.limit());
final int channelCount
= codec.getOutputFormat().getInteger(MediaFormat.KEY_CHANNEL_COUNT);
for (
int offset = 0;
offset + SAMPLES_PER_CHUNK * channelCount < buf.length;
offset += SAMPLES_PER_CHUNK * channelCount) {
double[] x = new double[SAMPLES_PER_CHUNK]; // left channel
double[] y = new double[SAMPLES_PER_CHUNK]; // right channel
switch (channelCount) {
case 1: // if 1 channel then make 2 identical arrays
for (int i = 0; i < SAMPLES_PER_CHUNK; ++i) {
x[i] = (double) buf[offset + i];
y[i] = (double) buf[offset + i];
}
break;
case 2: // if 2 channels then read values alternately
for (int i = 0; i < SAMPLES_PER_CHUNK; ++i) {
x[i] = (double) buf[offset + i * 2];
y[i] = (double) buf[offset + i * 2 + 1];
}
break;
default:
throw new IllegalStateException("No algorithm for " + channelCount + " channels");
}
/// ... some processing ... ///
}
// Save the rest until next batch of raw data
int samplesLeft = buf.length % (SAMPLES_PER_CHUNK * channelCount);
mExcess = new short[samplesLeft];
System.arraycopy(
buf,
buf.length - samplesLeft,
mExcess,
0,
samplesLeft);
codec.releaseOutputBuffer(index, false);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) > 0) {
codec.stop();
codec.release();
mExtractor.release();
}
}
@Override
public void onError(MediaCodec codec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
}
});
mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT);
mediaCodec.configure(mediaFormat, null, null, 0);
mediaCodec.start();
}
Быстрый просмотр кода также приветствуется.