c++audioieee-754wasapisine-wave

How can I adjust the volume of the left or right channel of a sine wave individually?


I am trying to adjust the volume of each individual channel in a sine wave for stereo output.

I am simply outputting this using WASAPI, with a 32 bit IEEE-float stereo format.

I have a sine wave in 32 bit IEEE-Float format, and I try to set the left and right channel volumes by multiplying by a value between 0f and 1f.

There are 4 bytes per float, so I am dividing the total count of bytes in the buffer by 4.

Alternating between floats should be left and right channels, or am I mistaken?

For example:

|-------------Frame-------------|
 (Left Channel)  (Right Channel)
[0] [0] [0] [0] | [0] [0] [0] [0]
|----Sample----| |----Sample----|

Each sample should be a float value, right?

I used DBOUT to print the values of two samples for testing to make sure it all made sense. nChannels is 2, and nSamplesPerSec is 48000.

static inline float sin01(float alpha) {
    return 0.5 * sin(alpha) + 0.5;
}

static inline float sin_minmax_Hz(float min, float max, float freq_Hz, float t) {
    return (max - min) / 2.0 * sin01(t * freq_Hz * TWO_PI);
}

Invoke() {
    BYTE* pData;

    hr = audioclient->GetCurrentPadding(&pad);

    int bytes_per_frame = wave_format.nChannels * 32 / 8;
    actual_size = (buffer_framecount - pad);
    count = actual_size * bytes_per_frame;

    hr = renderclient->GetBuffer(actual_size, &pData);

    float dt = 1.0 / (float)wave_format.nSamplesPerSec;

    float freq = (float)wave_format.nSamplesPerSec / ((float)count / 4);
    float* float_buffer = new float[count / 4];

    for (int i = 0; i < count / 4; i++) {
        static_assert(sizeof(float) == 4, "float size is expected to be 4 bytes");
        float t = (float)i * dt;
        float_buffer[i++] = sin_minmax_Hz(-1, 1, freq, t) * 0.5f; // should be half volume L channel
        float_buffer[i] = sin_minmax_Hz(-1, 1, freq, t) * 0.25f; // should be quarter volume R channel
    }

    if (SUCCEEDED(hr)) {
        std::memcpy(pData, float_buffer, count);
        //DBOUT(float_buffer[20] << " < float : byte > " << pData[80] << " " << pData[81] << " " << pData[82] << " " << pData[83] << "\n");
        //DBOUT(float_buffer[21] << " < float : byte > " << pData[84] << " " << pData[85] << " " << pData[86] << " " << pData[87] << "\n");

        hr = renderclient->ReleaseBuffer(actual_size, 0);
        EXIT_ON_ERROR(hr);
    }
}

No matter what I try, the left and right channel output are the same.

The volume will be adjusted if I decrease both multipliers, but I can't get the left and right channel volumes to be any different.

Does anyone know where I am going wrong?

The DBOUT results were this for a random frame, which makes sense to me:

alternating between *0.5 and *0.25
0.282632 < float : byte > 21 181 144 62
0.141316 < float : byte > 21 181 16 62
Converted to hex for comparison:
hex: 15 B5 90 3E // 0x3E90B515 = 0.282632
hex: 15 B5 10 3E // 0x3E10B515 = 0.141316

I have also tried this way:

float freq = (float)wave_format.nSamplesPerSec / (float)actual_size;
float* float_buffer = new float[count / 4];

for (int i = 0; i < count / 8; ++i) {
    float t = (float)i * dt;
    float_buffer[2 * i] = sin_minmax_Hz(-1, 1, freq, t) * 0.5f;
    float_buffer[2 * i + 1] = sin_minmax_Hz(-1, 1, freq, t) * 0.25f;
}

This is how I am getting the format:

    hr = pAudioClient->GetCurrentSharedModeEnginePeriod((WAVEFORMATEX**)&wavefmt, &current_period);

Which is the equivalent of this (I double checked to verify):

WAVEFORMATEX wave_format = {};
    wave_format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
    wave_format.nChannels = 2;
    wave_format.nSamplesPerSec = 48000;
    wave_format.nAvgBytesPerSec = 384000;
    wave_format.nBlockAlign = 8;
    wave_format.wBitsPerSample = 32;

Edit:

I just changed the frequency for the right channel only and it indeed played a different frequency on the right. If I then turn down the volume for the right, I can still hear the same frequency as the left channel, with a faint sound of the new frequency. So it appears that the right channel data is being mixed with the left channel somehow.


Solution

  • Well, I found the answer. Turns out this was not a programming issue, just a device settings issue.

    I simply needed to disable audio enhancements on the device settings. There is still a MINOR amount of bleed, which is totally acceptable.

    It's great that I spent three days trying to figure this out.. lol

    enter image description here