使用C创建立体声WAV文件

使用C创建立体声WAV文件,c,file,audio,wav,C,File,Audio,Wav,我正在考虑用C语言创建一个WAV文件,并且已经看到了一个示例 这看起来不错,但我有兴趣添加两个缓冲区,使音频立体声(每个耳朵可能有不同的声音)。如果我将频道数设置为2,则音频仅从左声道播放(显然是右声道,因为左声道是第一个声道)。我已经读过了,我必须把它和正确的频道交错 不幸的是,我还没有在网上找到太多帮助创建立体声WAV的东西 write_little_endian((unsigned int)(data[i]),bytes_per_sample, wav_file); 我试着创建第二个缓

我正在考虑用C语言创建一个
WAV文件
,并且已经看到了一个示例

这看起来不错,但我有兴趣添加两个缓冲区,使音频立体声(每个耳朵可能有不同的声音)。如果我将频道数设置为2,则音频仅从左声道播放(显然是右声道,因为左声道是第一个声道)。我已经读过了,我必须把它和正确的频道交错

不幸的是,我还没有在网上找到太多帮助创建立体声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\u little\u endian”。你不需要在笔记本电脑上使用它

Endianness是特定于架构的。最初的例子可能是Arduino微控制器板。Arduino板使用的Atmel微控制器是big-endian。这就是为什么您所引用的代码需要显式地将16位整数转换为little endian格式

另一方面,您的笔记本电脑使用的x86处理器已经是小端,因此不需要转换。如果希望使用健壮的可移植代码来转换endianness,可以在Linux中使用函数
htole16
。查阅手册页以了解有关此功能的更多信息

对于一个快速但不可移植的修复,我会说只需写出整个16位值


此外,我认为从单声道到立体声不需要将振幅减半。

因此我决定尝试一下,以获得乐趣,这里有一种编写.wav文件的替代方法。它生成一个名为
sawtooth\u test.wav
的文件。当你播放时,你应该听到左右两种不同的频率。(不要放得太大声,太烦人了。)

/*使用gcc-Wall-O2-o wavwrite.c编译*/
#包括
#包括
#包括
#包括
/*
wav文件的标头基于:
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
*/
类型定义结构文件头
{
char ChunkID[4];/*4*/
int32\u t ChunkSize;/*4*/
字符格式[4];/*4*/
char Subchunk1ID[4];/*4*/
int32_t Subchunk1Size;/*4*/
int16_t AudioFormat;/*2*/
int16\u t数字通道;/*2*/
int32_t采样器;/*4*/
int32_t ByteRate;/*4*/
int16\u t BlockAlign;/*2*/
int16_t BitsPerSample;/*2*/
char Subchunk2ID[4];
int32_t次chunk2尺寸;
}wavfile_头文件;
/*CD质量音频的标准值*/
#定义子chunk1大小(16)
#为PCM定义音频格式(1)/**/
#定义NUM_通道(2)
#定义采样率(44100)
#定义每个样本的位(16)
#定义字节速率(采样速率*通道数*每采样位数/8)
#定义块对齐(每个采样的通道数*位/8)
/*成功时返回0,失败时返回-1*/
int write_PCM16_立体声_头(文件*文件),
int32_t采样器,
int32(帧计数)
{
int ret;
wavfile_头\u t wav_头;
int32_t subchunk2_尺寸;
int32块大小;
大小\不写入\计数;
subchunk2_size=帧数*通道数*每采样位数/8;
块大小=4+(8+次块大小)+(8+次块大小);
wav_header.ChunkID[0]=“R”;
wav_header.ChunkID[1]=“I”;
wav_header.ChunkID[2]=“F”;
wav_header.ChunkID[3]=“F”;
wav_header.ChunkSize=块大小;
wav_头。格式[0]=“W”;
wav_header.Format[1]=“A”;
wav_header.Format[2]=“V”;
wav_header.Format[3]=“E”;
wav_头.Subchunk1ID[0]=“f”;
wav_header.Subchunk1ID[1]=“m”;
wav_header.Subchunk1ID[2]=“t”;
wav_header.Subchunk1ID[3]='';
wav_header.Subchunk1Size=Subchunk1Size;
wav_header.AudioFormat=音频_格式;
wav_header.NumChannels=NUM_通道;
wav_header.SampleRate=采样器;
wav_header.ByteRate=字节率;
wav_header.BlockAlign=块对齐;
wav_header.BitsPerSample=每个样本的位;
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;
写入计数=写入(&wav)头,
sizeof(wavfile\u头文件),1,
文件(p),;
ret=(1!=写入计数)?-1:0;
返回ret;
}
/*使用两个通道保存单个帧的数据结构*/
typedef结构PCM16_立体声
{
int16_t左;
国际权利;
}PCM16_立体声;
PCM16立体声*分配PCM16立体声缓冲区(int32帧计数)
{
返回(PCM16_立体声*)malloc(sizeof(PCM16_立体声)*帧数);
}
/*返回成功写入的音频帧数*/
大小写入PCM16wav数据(文件*文件),
int32_t帧计数,
PCM16(立体声*缓冲区)
{
尺寸(单位);;
ret=fwrite(缓冲区),
sizeof(PCM16立体声),帧数,
文件(p),;
返回ret;
}
/*以两个频率和振幅生成两个锯齿信号*/
int生成双锯齿(双频1,
双振幅1,
双频2,
双振幅2,
int32_t采样器,
int32_t帧计数,
PCM16(立体声*缓冲区)
{
int-ret=0;
双采样器d=(双)采样器;
双采样周期=1.0/SampleRate\u d;
双周期1,周期2;
双相1,相2;
双斜率1,斜率2;
int32_t k;
/*检查是否违反奈奎斯特极限*/
如果((频率1*2>=采样器d)| |(频率2*2>=采样器d))
{
ret=-1;
    write_little_endian((unsigned int)(data[i]),bytes_per_sample, wav_file);
    write_little_endian((unsigned int)(datatwo[i]),bytes_per_sample, wav_file);
/*Compiles with gcc -Wall -O2 -o wavwrite wavwrite.c*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>

/*
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;    
}