Создание стерео WAV-файла с использованием C

Я смотрю на создание WAV file на C и видел здесь пример.

Это выглядит хорошо, но я заинтересован в добавлении двух буферов для создания аудио-стерео (возможность иметь различный звук в каждом ухе) . Если я устанавливаю количество каналов на два, звук воспроизводится только из левого канала (что, по-видимому, правильно, так как левый канал является первым каналом). Я прочитал, я должен чередовать его с правильным каналом.

К сожалению, я не нашел много онлайн, чтобы помочь создать стерео WAV.

 write_little_endian((unsigned int)(data[i]),bytes_per_sample, wav_file); 

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

 for (j=0; i<BUF_SIZE; i++) { phase +=freq_radians_per_sample; buffertwo[i] = (int)((amplitude/2) * sin(phase));; } write_wav("test.wav", BUF_SIZE, buffer, buffertwo, S_RATE); 

(изменение функции для приема двух коротких целочисленных буферов)

И просто делать

  write_little_endian((unsigned int)(data[i]),bytes_per_sample, wav_file); write_little_endian((unsigned int)(datatwo[i]),bytes_per_sample, wav_file); 

Но это не работает. Это теоретически следует чередовать.

Поэтому я решил сделать это для удовольствия, и вот альтернативный способ написать WAV-файл. Он генерирует файл с именем sawtooth_test.wav . Когда вы воспроизводите его, вы должны услышать две разные частоты слева и справа. (Не играйте слишком громко, его раздражает.)

 /*Compiles with gcc -Wall -O2 -o wavwrite wavwrite.c*/ #include  #include  #include  #include  /* The header of a wav file Based on: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */ typedef struct wavfile_header_s { char ChunkID[4]; /* 4 */ int32_t ChunkSize; /* 4 */ char Format[4]; /* 4 */ char Subchunk1ID[4]; /* 4 */ int32_t Subchunk1Size; /* 4 */ int16_t AudioFormat; /* 2 */ int16_t NumChannels; /* 2 */ int32_t SampleRate; /* 4 */ int32_t ByteRate; /* 4 */ int16_t BlockAlign; /* 2 */ int16_t BitsPerSample; /* 2 */ char Subchunk2ID[4]; int32_t Subchunk2Size; } wavfile_header_t; /*Standard values for CD-quality audio*/ #define SUBCHUNK1SIZE (16) #define AUDIO_FORMAT (1) /*For PCM*/ #define NUM_CHANNELS (2) #define SAMPLE_RATE (44100) #define BITS_PER_SAMPLE (16) #define BYTE_RATE (SAMPLE_RATE * NUM_CHANNELS * BITS_PER_SAMPLE/8) #define BLOCK_ALIGN (NUM_CHANNELS * BITS_PER_SAMPLE/8) /*Return 0 on success and -1 on failure*/ int write_PCM16_stereo_header( FILE* file_p, int32_t SampleRate, int32_t FrameCount) { int ret; wavfile_header_t wav_header; int32_t subchunk2_size; int32_t chunk_size; size_t write_count; subchunk2_size = FrameCount * NUM_CHANNELS * BITS_PER_SAMPLE/8; chunk_size = 4 + (8 + SUBCHUNK1SIZE) + (8 + subchunk2_size); wav_header.ChunkID[0] = 'R'; wav_header.ChunkID[1] = 'I'; wav_header.ChunkID[2] = 'F'; wav_header.ChunkID[3] = 'F'; wav_header.ChunkSize = chunk_size; wav_header.Format[0] = 'W'; wav_header.Format[1] = 'A'; wav_header.Format[2] = 'V'; wav_header.Format[3] = 'E'; wav_header.Subchunk1ID[0] = 'f'; wav_header.Subchunk1ID[1] = 'm'; wav_header.Subchunk1ID[2] = 't'; wav_header.Subchunk1ID[3] = ' '; wav_header.Subchunk1Size = SUBCHUNK1SIZE; wav_header.AudioFormat = AUDIO_FORMAT; wav_header.NumChannels = NUM_CHANNELS; wav_header.SampleRate = SampleRate; wav_header.ByteRate = BYTE_RATE; wav_header.BlockAlign = BLOCK_ALIGN; wav_header.BitsPerSample = BITS_PER_SAMPLE; wav_header.Subchunk2ID[0] = 'd'; wav_header.Subchunk2ID[1] = 'a'; wav_header.Subchunk2ID[2] = 't'; wav_header.Subchunk2ID[3] = 'a'; wav_header.Subchunk2Size = subchunk2_size; write_count = fwrite( &wav_header, sizeof(wavfile_header_t), 1, file_p); ret = (1 != write_count)? -1 : 0; return ret; } /*Data structure to hold a single frame with two channels*/ typedef struct PCM16_stereo_s { int16_t left; int16_t right; } PCM16_stereo_t; PCM16_stereo_t *allocate_PCM16_stereo_buffer( int32_t FrameCount) { return (PCM16_stereo_t *)malloc(sizeof(PCM16_stereo_t) * FrameCount); } /*Return the number of audio frames sucessfully written*/ size_t write_PCM16wav_data(FILE* file_p, int32_t FrameCount, PCM16_stereo_t *buffer_p) { size_t ret; ret = fwrite( buffer_p, sizeof(PCM16_stereo_t), FrameCount, file_p); return ret; } /*Generate two saw-tooth signals at two frequencies and amplitudes*/ int generate_dual_sawtooth( double frequency1, double amplitude1, double frequency2, double amplitude2, int32_t SampleRate, int32_t FrameCount, PCM16_stereo_t *buffer_p) { int ret = 0; double SampleRate_d = (double)SampleRate; double SamplePeriod = 1.0/SampleRate_d; double Period1, Period2; double phase1, phase2; double Slope1, Slope2; int32_t k; /*Check for the violation of the Nyquist limit*/ if( (frequency1*2 >= SampleRate_d) || (frequency2*2 >= SampleRate_d) ) { ret = -1; goto error0; } /*Compute the period*/ Period1 = 1.0/frequency1; Period2 = 1.0/frequency2; /*Compute the slope*/ Slope1 = amplitude1/Period1; Slope2 = amplitude2/Period2; for(k = 0, phase1 = 0.0, phase2 = 0.0; k < FrameCount; k++) { phase1 += SamplePeriod; phase1 = (phase1 > Period1)? (phase1 - Period1) : phase1; phase2 += SamplePeriod; phase2 = (phase2 > Period2)? (phase2 - Period2) : phase2; buffer_p[k].left = (int16_t)(phase1 * Slope1); buffer_p[k].right = (int16_t)(phase2 * Slope2); } error0: return ret; } int main(void) { int ret; FILE* file_p; double frequency1 = 493.9; /*B4*/ double amplitude1 = 0.65 * (double)SHRT_MAX; double frequency2 = 392.0; /*G4*/ double amplitude2 = 0.75 * (double)SHRT_MAX; double duration = 10; /*seconds*/ int32_t FrameCount = duration * SAMPLE_RATE; PCM16_stereo_t *buffer_p = NULL; size_t written; /*Open the wav file*/ file_p = fopen("./sawtooth_test.wav", "w"); if(NULL == file_p) { perror("fopen failed in main"); ret = -1; goto error0; } /*Allocate the data buffer*/ buffer_p = allocate_PCM16_stereo_buffer(FrameCount); if(NULL == buffer_p) { perror("fopen failed in main"); ret = -1; goto error1; } /*Fill the buffer*/ ret = generate_dual_sawtooth( frequency1, amplitude1, frequency2, amplitude2, SAMPLE_RATE, FrameCount, buffer_p); if(ret < 0) { fprintf(stderr, "generate_dual_sawtooth failed in main\n"); ret = -1; goto error2; } /*Write the wav file header*/ ret = write_PCM16_stereo_header(file_p, SAMPLE_RATE, FrameCount); if(ret < 0) { perror("write_PCM16_stereo_header failed in main"); ret = -1; goto error2; } /*Write the data out to file*/ written = write_PCM16wav_data( file_p, FrameCount, buffer_p); if(written < FrameCount) { perror("write_PCM16wav_data failed in main"); ret = -1; goto error2; } /*Free and close everything*/ error2: free(buffer_p); error1: fclose(file_p); error0: return ret; } 

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

Конкретность – это специфическая архитектура. Первоначальный пример, вероятно, был для платы микроcontrollerов Arduino. Платы Arduino используют микроcontrollerы Atmel, которые являются широкоформатными. Вот почему код, который вы указали, явно должен преобразовать 16-разрядные целые числа в формат little-endian.

С другой стороны, ваш ноутбук использует процессоры x86, которые уже немного ориентированы, поэтому преобразование не требуется. Если вам нужен надежный переносимый код для преобразования endianness, вы можете использовать функцию htole16 в Linux. Просмотрите страницы руководства, чтобы узнать больше об этой функции.

Для быстрого, но не портативного исправления, я бы сказал, просто написать все 16-битное значение.

Кроме того, я не думаю, что вам нужно вдвое увеличить амплитуды от mono до стерео.