Деинтерлейв аудиоданных с различными битрейтами

#c

#c

Вопрос:

Я пытаюсь написать одну функцию, которая может деинтерлеевывать 8/16/24/32-битные аудиоданные, учитывая, что аудиоданные естественным образом поступают в 8-битный буфер.

У меня это работает для 8 бит, и оно работает для 16/24/32, но только для первого канала (канал 0). Я перепробовал так много и * и других операторов, что на данный момент я просто предполагаю. Я не могу найти волшебную формулу. Я использую C , но также принял бы a memcpy в вектор, если это проще всего.

Ознакомьтесь с кодом. Если вы измените demux вызов на другой битрейт, вы увидите проблему. Я уверен, что здесь есть простое математическое решение, я просто не могу его получить.

 #include <vector>
#include <map>
#include <iostream>
#include <iomanip>
#include <string>
#include <string.h>

const int bitrate = 8;
const int channel_count = 5;
const int audio_size = bitrate * channel_count * 4;
uint8_t audio_ptr[audio_size];
const int bytes_per_channel = audio_size / channel_count;

void Demux(int bitrate){
    int byterate = bitrate/8;
    std::map<int, std::vector<uint8_t> > channel_audio;
    for(int i = 0; i < channel_count; i  ){
        std::vector<uint8_t> audio;
        audio.reserve(bytes_per_channel);
        for(int x = 0; x < bytes_per_channel; x  = byterate){
            for(int z = 0; z < byterate; z  ){
                // What is the magic formula!
                audio.push_back(audio_ptr[(x * channel_count)   i   z]);
            }
        }
        channel_audio.insert(std::make_pair(i, audio));
    }
    
    int remapsize = 0;
    std::cout << "nRemapped  Audio";
    std::map<int, std::vector<uint8_t> >::iterator it;
    for(it = channel_audio.begin(); it != channel_audio.end();   it){
        std::cout << "nChannel" << it->first << " ";
        std::vector<uint8_t> v = it->second;
        remapsize  = v.size();
        for(size_t i = 0; i < v.size(); i  ){
            std::cout << "0x" << std::hex << std::setfill('0') << std::setw(2) <<  v[i] << " ";
            if(i amp;amp; (i   1) % 32 == 0){
                std::cout << std::endl;   
            }
        }
    }
    std::cout << "Total remapped audio size is " << std::dec << remapsize << std::endl;
}

int main()
{
    
    // External data
    std::cout << "Raw Audion";
    for(int i = 0; i < audio_size; i  ){
        audio_ptr[i] = i;   
        std::cout << "0x" << std::hex << std::setfill('0') << std::setw(2) <<  audio_ptr[i] << " ";
        if(i amp;amp; (i   1) % 32 == 0){
            std::cout << std::endl;   
        }
    }
    std::cout << "Total raw audio size is " << std::dec << audio_size << std::endl;
    
    Demux(8);
    //Demux(16);
    //Demux(24);
    //Demux(32);
}
 

Ответ №1:

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

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

 [channel0][channel1][channel2][channel3][channel4]
 

Ширина выборки в одном канале вызывается byterate в вашем коде, но мне не нравится это имя. Я собираюсь вызвать его bytes_per_sample вместо этого. Вы можете легко увидеть ширину всего кадра, это:

 int bytes_per_frame = bytes_per_sample * channel_count;
 

Должно быть столь же очевидно, что для нахождения начального смещения для канала c в пределах одного кадра вы умножаете следующим образом:

 int sample_offset_in_frame = bytes_per_sample * c;
 

Это практически все, что вам нужно! Последний бит — это ваш z цикл, который охватывает каждый байт в одной выборке для одного канала. Я не знаю, что z должно представлять, кроме случайного однобуквенного идентификатора, который вы выбрали, но давайте просто оставим его.

Собрав все это вместе, вы получаете абсолютное смещение выборки s в канале c , а затем копируете из него отдельные байты:

 int sample_offset = bytes_per_frame * s   bytes_per_sample * c;
for (int z = 0; z < bytes_per_sample;   z) {
    audio.push_back(audio_ptr[sample_offset   z]);
}
 

На самом деле предполагается, что вы перебираете количество выборок, а не количество байтов в вашем канале. Итак, давайте покажем все циклы для завершения:

 const int bytes_per_sample = bitrate / 8;
const int bytes_per_frame = bytes_per_sample * channel_count;
const int num_samples = audio_size / bytes_per_frame;

for (int c = 0; c < channel_count;   c)
{
    int sample_offset = bytes_per_sample * c;

    for (int s = 0; s < num_samples;   s)
    {
        for (int z = 0; z < bytes_per_sample;   z)
        {
            audio.push_back(audio_ptr[sample_offset   z]);
        }

        // Skip to next frame
        sample_offset  = bytes_per_frame;
    }
}
 

Здесь вы увидите, что я разделил математику так, чтобы она выполняла меньше умножений в циклах. Это в основном для удобства чтения, но также может помочь компилятору понять, что происходит, когда он пытается оптимизировать. Опасения по поводу оптимизации являются второстепенными (и в вашем случае с этими векторами и картой возникают гораздо более дорогостоящие проблемы)..

Самое главное, что у вас есть читаемый код с разумными именами переменных, который имеет логический смысл.

Комментарии:

1. Спасибо ..! Я ценю полное пошаговое руководство.