Search code examples
caudiowav

How can I make stereo wave sound in C?


While I want to make stereo wave sound in C, I could found mono sound code, and here it is

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream> 

#pragma once 
#define WAVE_FORMAT_UNKNOWN 0X0000; 
#define WAVE_FORMAT_PCM 0X0001;
#define WAVE_FORMAT_MS_ADPCM 0X0002; 
#define WAVE_FORMAT_IEEE_FLOAT 0X0003; 
#define WAVE_FORMAT_ALAW 0X0006; 
#define WAVE_FORMAT_MULAW 0X0007; 
#define WAVE_FORMAT_IMA_ADPCM 0X0011; 
#define WAVE_FORMAT_YAMAHA_ADPCM 0X0016;
#define WAVE_FORMAT_GSM 0X0031; 
#define WAVE_FORMAT_ITU_ADPCM 0X0040; 
#define WAVE_FORMAT_MPEG 0X0050;
#define WAVE_FORMAT_EXTENSIBLE 0XFFFE;


#define DURATION 8 
#define SAMPLE_RATE 48000
#define CHANNEL 2
#define BIT_RATE 16

typedef struct {
    unsigned char ChunkID[4]; // Contains the letters "RIFF" in ASCII form 
    unsigned int ChunkSize; // This is the size of the rest of the chunk following this number 
    unsigned char Format[4]; // Contains the letters "WAVE" in ASCII form 
} RIFF;

typedef struct {
    unsigned char ChunkID[4]; // Contains the letters "fmt " in ASCII form 
    unsigned int ChunkSize; // 16 for PCM. This is the size of the rest of the Subchunk which follows this number. 
    unsigned short AudioFormat; // PCM = 1 
    unsigned short NumChannels; // Mono = 1, Stereo = 2, etc. 
    unsigned int SampleRate; // 8000, 44100, etc. 
    unsigned int AvgByteRate; // SampleRate * NumChannels * BitsPerSample/8 
    unsigned short BlockAlign; // NumChannels * BitsPerSample/8 
    unsigned short BitPerSample; // 8 bits = 8, 16 bits = 16, etc 
} FMT;

typedef struct {
    char ChunkID[4]; // Contains the letters "data" in ASCII form 
    unsigned int ChunkSize; // NumSamples * NumChannels * BitsPerSample/8 
    } DATA; 

    typedef struct { 
        RIFF Riff; 
        FMT Fmt; 
        DATA Data; 
    } WAVE_HEADER;



int main() {
        FILE * f_out; 
    f_out = fopen("D:\\test.wav", "wb"); 

    WAVE_HEADER header; 
    memcpy(header.Riff.ChunkID, "RIFF", 4); 
    header.Riff.ChunkSize = DURATION * SAMPLE_RATE * CHANNEL * BIT_RATE / 8 + 36; 
    memcpy(header.Riff.Format, "WAVE", 4); 

    memcpy(header.Fmt.ChunkID, "fmt ", 4); 
    header.Fmt.ChunkSize = 0x10; 
    header.Fmt.AudioFormat = WAVE_FORMAT_PCM; 
    header.Fmt.NumChannels = CHANNEL; 
    header.Fmt.SampleRate = SAMPLE_RATE; 
    header.Fmt.AvgByteRate = SAMPLE_RATE * CHANNEL * BIT_RATE / 8; 
    header.Fmt.BlockAlign = CHANNEL * BIT_RATE / 8; 
    header.Fmt.BitPerSample = BIT_RATE; 

    memcpy(header.Data.ChunkID, "data", 4);
    header.Data.ChunkSize = DURATION * SAMPLE_RATE * CHANNEL * BIT_RATE / 8; 

    fwrite(&header, sizeof(header), 1, f_out); 

    short y[2]; double freq = 1000; 
    for (int i = 0; i < SAMPLE_RATE * DURATION * CHANNEL * BIT_RATE / 8; i++) {
        y[0] = (short)30000 * sin(2 * 3.141592 * i * freq / SAMPLE_RATE); 
        fwrite(&y[0], sizeof(short), 1, f_out);
        }


    fclose(f_out); 
        return 0; 
}

but I want to change this into stereo(Channel 2). In detail, I wish I could make control of left sound function and right sound function (ex. Left_sound[0] = ~~; right_sound[0] = ~~) and my final goal is making siren which is coming to me and go far away from me. How can I add a function of left and right sound??


Solution

  • The point is the 16bit stereo data are packed as follows in the data chunk:

    +-----------------+-----------------+-----------------+-----------------+
    |    Left ch      |    Left ch      |    Right ch     |    Right ch     |
    | low-order byte  | high-order byte | low-order byte  | high-order byte |
    +-----------------+-----------------+-----------------+-----------------+
    

    Then the difference from your code will be:

        short y[2]; double freq = 1000;
        for (int i = 0; i < SAMPLE_RATE * DURATION; i++) {
            double level_r = (double)i / (SAMPLE_RATE * DURATION);
            double level_l = 1.0 - level_r;
    
            y[0] = (short)(30000 * sin(2 * 3.141592 * i * freq / SAMPLE_RATE) * level_l);
            y[1] = (short)(30000 * sin(2 * 3.141592 * i * freq / SAMPLE_RATE) * level_r);
            fwrite(&y[0], sizeof(short), 1, f_out);
            fwrite(&y[1], sizeof(short), 1, f_out);
        }
    

    The sound starts from the left side, comes close to the center, then passes away to the right.
    Please note the range of index i in your code is incorrect, because the unit of i is sample, not byte.