mackron / miniaudio

Audio playback and capture library written in C, in a single source file.
https://miniaud.io
Other
4.07k stars 361 forks source link

Fix SSE2 sample swapping in mono expansion. #722

Closed nmlgc closed 1 year ago

nmlgc commented 1 year ago

The SSE2 code paths for mono expansion introduced in Version 0.11.15 mixed up the parameters of _mm_shuffle_ps(), which in turn caused adjacent PCM frames to be swapped in the channel-expanded output. The resulting audio corruption is particularly noticeable on low sampling rates (32,000 Hz and below), but can also be generally observed in the frequency spectrum.

A minimal example:

#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"

#include <stdio.h>

ma_engine engine;
FILE* engineOutput;

void data_callback(ma_device* device, void* out, const void* in, ma_uint32 frames)
{
    ma_uint32 bytes_per_frame = ma_get_bytes_per_frame(device->playback.format, device->playback.channels);
    ma_engine_read_pcm_frames(&engine, out, frames, NULL);
    if(engineOutput) {
        fwrite(out, (frames * bytes_per_frame), 1, engineOutput);
    }
    (void)in;
}

int main(int argc, char** argv)
{
    ma_result result;
    ma_device device;

    engineOutput = fopen("engine_output.snd", "wb");

    ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
    deviceConfig.dataCallback = data_callback;
    /* deviceConfig.playback.channels = 6; */
    result = ma_device_init(NULL, &deviceConfig, &device);
    if(result != MA_SUCCESS) {
        return 0;
    }

    ma_engine_config engineConfig = ma_engine_config_init();
    engineConfig.pDevice = &device;

    result = ma_engine_init(&engineConfig, &engine);
    if (result != MA_SUCCESS) {
        return -1;
    }

    ma_engine_play_sound(&engine, "sine.intro.flac", NULL);

    printf("Press Enter to quit...");
    getchar();

    ma_engine_uninit(&engine);

    if(engineOutput) {
        fclose(engineOutput);
    }

    return 0;
}

Here is a sample input file, and the engine output without and with this fix. I reproduced both the swapped and the fixed behavior under MSVC, Clang, and GCC.

The frequency spectrum without the fix: _

With the fix: _

mackron commented 1 year ago

Thanks for this, and good catch.