Как написать несколько звуковых дорожек в wav. файл с++

Я хочу использовать С++ для записи образцов сигналов в wav. файл, но я запутался, когда у меня более одной звуковой дорожки, я должен записать их все в wav. файл один за другим, или я должен рассчитать смешанный сигнал и сэмплировать его, а затем записать его как одну звуковую дорожку?

Извините, если я что-то не так понял, я не знаком с wav. файловая структура.

Вот мой код:

//Creat headers here

const int sample_rate = 44100;
int tri_wave(int fre, int sample_rate){
    //calculation of sample
    return sample;
}

int square_wave(int fre, int sample_rate){
    //calculation of sample
    return sample;
}

int16_t make_wave(int fre, int sample_rate){
    //do something
    return sample;
}

int main(){
    std::ofstream out("test.wav", std::ios::binary);
    out.write(reinterpret_cast<const char *>(&wav), sizeof(wav));//write header of wav. file

    int16_t sample_track1 = make_wave(fre1, sample_rate);
    int16_t sample_track2 = make_wave(fre2, sample_rate);
    for (int i = 0; i < fsize; ++i) {
      // write in blocks
      out.write(reinterpret_cast<char *>(&sample), sizeof(int16_t));
    }

    return 0;
}

Вы получаете много других файлов из вашего .bash_profile. Вы их тоже проверяли?

user1934428 11.04.2023 14:16
stackoverflow.com/questions/35825617/…
Hans Passant 09.05.2023 15:23
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
84
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Запись каждой дорожки в отдельный канал или передискретизация всех дорожек в один канал действительно зависит от ожидаемого результата.

  • Если вы хотите монофонический звук или если вы иным образом ограничены одним каналом, передискретизируйте все дорожки в один канал.

  • В противном случае запишите каждую дорожку в отдельный канал.

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

Некоторые хорошие источники в формате файлов RIFF/WAVE, которые я использовал для чтения и записи файлов wav:

http://web.archive.org/web/20160530121622/http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/wave.htm

http://web.archive.org/web/20140821040519/http://www.sonicspot.com/guide/wavefiles.html

Часть, которая вас, скорее всего, заинтересует:

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

Время Канал Ценить 0 1 (слева) 0x0053 2 (справа) 0x0024 1 1 (слева) 0x0057 2 (справа) 0x0029 2 1 (слева) 0x0063 2 (справа) 0x003C

Обратите внимание, что ваш цикл записи определенно не будет записывать ваши образцы, как указано в вашем вопросе:

for (int i = 0; i < fsize; ++i) {
  // write in blocks
  out.write(reinterpret_cast<char *>(&sample), sizeof(int16_t));
}

Во время этого цикла адрес sample остается прежним, поэтому каждая итерация записывает одни и те же 2 байта.

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

void write_sample(std::ofstream& out, uint16_t sample)
{
    // little endian (least significant byte first)
    out.put( static_cast<char>(    (sample)&0xFF) ); // first byte
    out.put( static_cast<char>( (sample>>8)&0xFF) ); // second byte
}

// example of how to use with 4 channels
uint16_t* track_C1 = make_wave( 523, sample_rate, fsize);
uint16_t* track_E1 = make_wave( 660, sample_rate, fsize);
uint16_t* track_G1 = make_wave( 784, sample_rate, fsize);
uint16_t* track_C2 = make_wave(1047, sample_rate, fsize);

for (int i=0; i<fsize; i++) {
    write_sample(out, track_C1[i]);
    write_sample(out, track_E1[i]);
    write_sample(out, track_G1[i]);
    write_sample(out, track_C2[i]);
}

Спасибо за ваш ответ! Я читал, что если у меня есть 2 канала, мне нужно написать как 0x13 0x64 0x24 0x34, где представляют для t = 0 слева, t = 0 справа, t = 1 слева, t = 1 справа (если я ошибаюсь, поправьте меня), что, если я есть 4 звуковые дорожки? Должен ли я объявить номер канала =4 и написать их отдельно? (предположим, у меня есть четыре инструмента)

user17780852 09.05.2023 15:31

Это верно для 2 каналов. Если у вас 4 канала, вы пишете: t=0 channel=0,t=0 channel=1,t=0 channel=2,t=0 channel=3,t=1 channel=0,t=1 channel=1,t=1 channel=2,t=1 channel=3 и т.д...

KompjoeFriek 09.05.2023 16:07

Спасибо. И для проблемы приведения между int16_t и char, как я могу избежать этой проблемы? Я видел, что ofstream::write() требует ввода char*, есть ли другие функции, которые я могу использовать для записи int16_t или int8_t?

user17780852 10.05.2023 09:16

Я добавил пример того, как написать образец переносимым способом.

KompjoeFriek 10.05.2023 13:01

Другие вопросы по теме