Я хочу использовать С++ для записи образцов сигналов в 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;
}
Запись каждой дорожки в отдельный канал или передискретизация всех дорожек в один канал действительно зависит от ожидаемого результата.
Если вы хотите монофонический звук или если вы иным образом ограничены одним каналом, передискретизируйте все дорожки в один канал.
В противном случае запишите каждую дорожку в отдельный канал.
Если вы выбираете несколько каналов, имейте в виду, что другое программное обеспечение может сделать вывод об использовании каждого канала на основе списка стандартных каналов динамиков.
Некоторые хорошие источники в формате файлов RIFF/WAVE, которые я использовал для чтения и записи файлов wav:
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 и написать их отдельно? (предположим, у меня есть четыре инструмента)
Это верно для 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
и т.д...
Спасибо. И для проблемы приведения между int16_t
и char
, как я могу избежать этой проблемы? Я видел, что ofstream::write() требует ввода char*, есть ли другие функции, которые я могу использовать для записи int16_t
или int8_t
?
Я добавил пример того, как написать образец переносимым способом.
Вы получаете много других файлов из вашего .bash_profile. Вы их тоже проверяли?