аудио — Экспорт буфера в WAV в переполнении стека

У меня есть простая программа, которая создает синусоидальную волну одного цикла и помещает числа с плавающей точкой в ​​буфер. Затем это экспортируется в текстовый файл.
Но я хочу иметь возможность экспортировать его в файл WAV (24 бит). Есть ли простой способ сделать это, как в текстовом файле?

Вот код, который у меня есть:

#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;

int main ()
{
long double pi = 3.14159265359; // Declaration of PI

ofstream textfile; // Text object
textfile.open("sine.txt"); // Creating the txt

double samplerate = 44100.00; // Sample rate

double frequency = 200.00; // Frequency

int bufferSize = (1/frequency)*samplerate; // Buffer size

double buffer[bufferSize]; // Buffer

for (int i = 0; i <= (1/frequency)*samplerate; ++i) // Single cycle
{
buffer[i] = sin(frequency * (2 * pi) * i / samplerate); // Putting into buffer the float values
textfile << buffer[i] << endl; // Exporting to txt
}

textfile.close(); // Closing the txt
return 0; // Success
}

1

Решение

Сначала нужно открыть поток для двоичного файла.

ofstream stream;
stream.open("sine.wav", ios::out | ios::binary);

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

int bufferSize = (1/frequency)*samplerate;
stream.write("RIFF", 4);                    // RIFF chunk
write<int>(stream, 36 + bufferSize*sizeof(int)); // RIFF chunk size in bytes
stream.write("WAVE", 4);                    // WAVE chunk
stream.write("fmt ", 4);                    // fmt chunk
write32(stream, 16);                     // size of fmt chunk
write16(stream, 1);                       // Format = PCM
write16(stream, 1);                       // # of Channels
write32(stream, samplerate);                // Sample Rate
write32(stream, samplerate*sizeof(int));    // Byte rate
write16(stream, sizeof(int));             // Frame size
write16(stream, 24);                      // Bits per sample
stream.write("data", 4);                   // data chunk
write32(stream, bufferSize*sizeof(int));   // data chunk size in bytes

Теперь, когда заголовок отсутствует, вам просто нужно изменить цикл, чтобы сначала преобразовать двойные (-1.0,1.0) сэмплы в 32-битный со знаком int. Обрежьте нижние 8 бит, так как вам нужно только 24 бита, а затем запишите данные. Как вы знаете, обычной практикой является хранение 24-битных сэмплов внутри 32-битного слова, потому что намного проще переходить от нативных типов.

for (int i = 0; i < bufferSize; ++i) // Single cycle
{
double tmp = sin(frequency * (2 * pi) * i / samplerate);
int intVal = (int)(tmp * 2147483647.0) & 0xffffff00;
stream << intVal;
}

Пара других вещей:

1) Я не знаю, как вы не переполняли buffer используя <= в вашей петле. Я изменил это на <,

2) Опять же по поводу размера буфера. Я не уверен, что вы знаете, но вы не можете иметь повторную форму волны, представленную одним циклом для всех частот. Я имею в виду, что для большинства частот, если вы используете этот код и ожидаете повторения сигнала, вы будете слышать сбой на каждом цикле. Он будет работать на хороших синхронных частотах, таких как 1 кГц, потому что в каждом такте будет ровно 48 выборок, и он придет примерно к той же фазе. 999,9 Гц будет другой историей.

1

Другие решения


По вопросам рекламы [email protected]