mackron / dr_libs

Audio decoding libraries for C/C++, each in a single source file.
Other
1.24k stars 205 forks source link

Not able to save .wav #129

Closed Nowhk closed 4 years ago

Nowhk commented 4 years ago

I'm trying to save a buffer of samples on a wav file using your fancy library. Unfortunately, it outputs weird samples on the waveform, not sure why.

Here's the code:

bool Wave::saveWave(std::string &pathFile) {
    drwav wav;
    drwav_data_format format;

    format.container = drwav_container_riff;
    format.format = DR_WAVE_FORMAT_PCM;

    format.channels = mNumChannels;
    format.sampleRate = mSampleRateWave;
    format.bitsPerSample = 16;

    if (drwav_init_file_write(&wav, pathFile.c_str(), &format, NULL)) {
        std::vector<float> samples;
        for (int i = 0; i < mNumSamples; i++) {
            samples.push_back(mBuffer[0][i]);

            if (mNumChannels == 2) {
                samples.push_back(mBuffer[1][i]);
            }
        }

        drwav_uint64 framesWritten = drwav_write_pcm_frames(&wav, mNumSamples, samples.data());

        if (framesWritten == mNumSamples) {
            return true;
        }
    }

    return false;
}

It seems it write the correct number of samples, but when I open the wav, it sounds crap. Where am I wrong? Or is it a bug?

Note: It seems it also ignore the num channels and the bit depth I set. It always save a 32bit/2 channels wave file, also if I set 1 channel and 16 bit.

mackron commented 4 years ago

It's because you're writing 32-bit floats:

std::vector<float> samples;

but are specifying an output format of 16-bit integers:

format.format = DR_WAVE_FORMAT_PCM;
format.bitsPerSample = 16;

You need to either change your output format to 32-bit floats:

format.format = DR_WAVE_FORMAT_IEEE_FLOAT;
format.bitsPerSample = 32;

or convert your samples to 16 bit integers:

std::vector<drwav_int16> samples;
samples.push_back(f32_to_s16(mBuffer[0][i]));
samples.push_back(f32_to_s16(mBuffer[1][i]));

...

drwav_int16 f32_to_s16(float x)
{
    x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    /* clip */
    return (drwav_int16)(x * 32767);
}
Nowhk commented 4 years ago

You are right, now it sounds ok! Thanks. But there are still one problem: drwav_write_pcm_frames return 173436 as written samples, but when I open an audio editor, the length is 173050. It seems it cut last samples, any reason?

Nowhk commented 4 years ago

Also: if I store with:

format.format = DR_WAVE_FORMAT_IEEE_FLOAT;
format.bitsPerSample = 32;

I'm not able later to open the sample with drwav_open_file_and_read_pcm_frames_f32 :O

mackron commented 4 years ago

It's probably because you aren't uninitializing the drwav object. As a result, the internal file isn't being closed with fclose() which means the last bit is probably never flushed to disk. Do this:

if (framesWritten == mNumSamples) {
    drwav_uninit(&wav); /* <-- Add this line. */
    return true;
}

I expect that should fix your drwav_open_file_and_read_pcm_frames_f32().

Nowhk commented 4 years ago

Perfect, it was that!!!! :) Thanks dude, you make my day!

Anyway, experimenting this, I've maybe found a "real" but ahah.

Id I do this:

unsigned int channels;
unsigned int sampleRateWave;
drwav_uint64 numTotalSamples;
float *pSampleData = drwav_open_file_and_read_pcm_frames_f32(mPathFile.c_str(), &channels, &sampleRateWave, &numTotalSamples, NULL);

if (pSampleData) {
    // ok, process samples
}

drwav_free(pSampleData, NULL);

When I load correctly a sample, it works perfect. If, for example, I create a txt file, and I rename it .wav, and I try to open, obviously it doesn't process and skip if (pSampleData), going directly to drwav_free(pSampleData, NULL);

But at that time, if I go to file system and I try to rename/cancel the fake sample already loaded, it can't. It seems it "blocked" by the drwav process. i.e. its doesn't free the file.

Is it normal? Bug? Or am I freeing wrongly?

mackron commented 4 years ago

That's definitely not intended. Good catch! I've pushed a fix to the dev branch. Are you able to try that? https://github.com/mackron/dr_libs/blob/dev/dr_wav.h

Commit: https://github.com/mackron/dr_libs/commit/aab128563678fd622ab063c13a596cad57718dec

Nowhk commented 4 years ago

Perfect, it seems fixed. Thanks. I wait the official release to be downloaded so ;)

Bonus tip: on documentation, you know forgot to specify to set NULL in most of the function. For example, this doesn't work as "copy/paste":

If you just want to quickly open and read the audio data in a single operation you can do something like this:

    ```c
    unsigned int channels;
    unsigned int sampleRate;
    drwav_uint64 totalPCMFrameCount;
    float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount);
    if (pSampleData == NULL) {
        // Error opening and reading WAV file.
    }

because lacks , NULL in the end of drwav_open_file_and_read_pcm_frames_f32 ;)

mackron commented 4 years ago

Thanks for testing that. I've fixed those documentation errors and have released version 0.12.2 to the master branch.

I'll go ahead and close this one now. Feel free to open a new issue if you find anything new. Thanks for the report!