swharden / Spectrogram

.NET library for creating spectrograms (visual representations of frequency spectrum over time)
https://nuget.org/packages/Spectrogram
MIT License
315 stars 57 forks source link

IO error when reading 32-bit WAV file #21

Closed asehgal123 closed 4 years ago

asehgal123 commented 4 years ago

First of all great package, I am definately going to play around with it.

However I am gettting follwing error on line 105 in WavFile.cs

System.IO.EndOfStreamException: 'Unable to read beyond the end of the stream.'

By reading the source code it looks like the loop on line 103 goes through to sampleCount which is calculated based on blocksize. For some reason blocksize is being read as 1 (if I set it to 2 then everything works). I am new to this so I don't fully understand the file format.

Wav file that I am loading has channelCount of 1 and blockSize of 1 (Is that even correct? could it be that wav file is incorrect), you think you can take a look at this issue?

swharden commented 4 years ago

Hi @asehgal123, thanks for reporting this! Can you upload the WAV file you're trying to read? My guess is something is weird about the WAV file header.

An alternative solution is to use a different library (one designed just for reading audio files) to read your WAV file. I think NAudio has a module which can do this.

asehgal123 commented 4 years ago

Apparently github won't allow me to upload wav file, can I email that to you?

swharden commented 4 years ago

Interesting! Perhaps try zipping it, then uploading it?

You're welcome to email it to me too, though if it's too big for email it might not come through

asehgal123 commented 4 years ago

sound1.zip

These 2 files are a better example of the issue, original won't work but converted does. Used this online tool (https://audio.online-convert.com/convert-to-wav) to convert wav without making any changes to the parameters (size of the file was doubled after convertion).

swharden commented 4 years ago

It doubled in size because the original is mono but the converted one is stereo 🤔

Interestingly the source audio is a 32-bit WAV file, and I'm pretty sure that's the core problem here, but I'll work try to get get it supported anyhow. We're making progress here, but it might take another day or two... thanks for uploading these samples! 👍

Reading it outputs this:

First chunk 'RIFF' indicates 3,200,036 bytes
Format chunk 'fmt ' indicates 16 bytes
audio format: 1
channel count: 1
sample rate: 40000 Hz
byteRate: 80000
block size: 1 bytes per sample
resolution: 16-bit
Chunk at 36 ('data') indicates 3,200,000 bytes
PCM data starts at 44 and contains 3200000 bytes
swharden commented 4 years ago

The WAV header claims it's a 16-bit format... This is inaccurate. This byte is the length of this portion of the header.

image

but GoldWave somehow knows its a 32-bit image

I want to study spectrograms, not WAV file header byte structure - that problem is solved better elsewhere - so I think my solution for this will to flash a warning message whenever the user tries to use my WAV file reader encouraging them to use another library that's intended for decoding audio file formats.

An intermediate option is I can make a WAV file reading function where the user has to manually define the bit-depth, sample rate, byte format, etc... TBH my only gripe is about reading WAV headers, and the massive inconsistency in how different programs write them 😩

swharden commented 4 years ago

@asehgal123 install NAudio then use its AudioFileReader like this:

double[] audio;
int sampleRate;
using (var audioFileReader = new AudioFileReader("asehgal-original.wav"))
{
    sampleRate = audioFileReader.WaveFormat.SampleRate;
    var wholeFile = new List<float>((int)(audioFileReader.Length / 4));
    var readBuffer = new float[audioFileReader.WaveFormat.SampleRate * audioFileReader.WaveFormat.Channels];
    int samplesRead = 0;
    while ((samplesRead = audioFileReader.Read(readBuffer, 0, readBuffer.Length)) > 0)
        wholeFile.AddRange(readBuffer.Take(samplesRead));
    audio = Array.ConvertAll(wholeFile.ToArray(), x => (double)x);
}

int fftSize = 8192;
var spec = new Spectrogram(sampleRate, fftSize, stepSize: 4_000, maxFreq: 2_000);
spec.Add(audio);
spec.SaveImage("asehgal.png", intensity: 10_000, dB: true);

The audio file you provided is pretty boring - it's just silence - but the bottom 2kHz of its spectrogram looks like this when you run the code above:

image

Hope it helps!

asehgal123 commented 4 years ago

thanks for your time and help. I will try this out.