INTERACT FORUM

Please login or register.

Login with username, password and session length
Advanced search  
Pages: [1]   Go Down

Author Topic: Bit-perfect problem found, proposed fix  (Read 6406 times)

GreenMan

  • Recent member
  • *
  • Posts: 16
Bit-perfect problem found, proposed fix
« on: July 31, 2016, 05:11:30 pm »

Executive summary:
When I converted 24-bit and 16-bit WAV files to 64-bit floating-point in a Python script, MC playback sounds more accurate.  I propose MC uses the ldexp() function from the standard C++ library to convert PCM/integer to 64-bit floating-point in a bit-perfect way.

Details:
I found the same problem and confirmed the same fix in both MC v21.0.83 and MC v22.0.13.  I run 64-bit Windows 8.1.

For a 24-bit WAV file, I used the Maria 24/192 sample (Windows version) at http://www.soundkeeperrecordings.com/format.htm
For a 16-bit WAV file, I used the Maria 16/44 sample from the same link.

I use a Lynx Hilo external 24-bit DAC for audio playback, which has an analog volume control after the DAC.  Therefore, I disable the volume control in MC to minimize distortion.  I observe that the Lynx Hilo sounds better with WASAPI exclusive than ASIO in MC and other applications, so I used WASAPI exclusive.  The Lynx ASIO driver has a mixer for sharing audio from multiple apps, so this could be why WASAPI exclusive sounds better.  Here are the options I used in an attempt to make MC bit-perfect for 24-bit and 16-bit WAV playback:
Tools > Options > Audio:
- Audio Device = Lynx Hilo [WASAPI]
- Audio Device ... Device settings... check "Open device for exclusive access", Bitdepth = 24-bit integer, Buffering = 100 msec
- Settings ... DSP & output format... uncheck all checkboxes at left (no changes made by DSP Studio)
- Settings > Bitstreaming = none
- Track Change > Switch tracks = Gapless
- Track Change > check "Do not play silence (leading and trailing)"
- Track Change > check "Use gapless for sequential album tracks"
- Track Change > check "Use gapless for manual track changes"
- Stop, Seek & Skip > Seek = Gapless
- Stop, Seek & Skip > Stop = Immediate
- Stop, Seek & Skip > Pause = Immediate
- Volume > Volume mode = Disabled Volume
- Advanced > Dither Mode (not zone-specific) = No Dithering
- Advanced > Live playback latency = 50 msec (recommended)

As a side note, disabling dithering in MC turned out to be important to get the best sound quality.

Here is a Python script named wav_to_64b.py that I used to convert the 24-bit and 16-bit WAV files to 64-bit floating-point:
Code: [Select]
# -*- coding: utf-8 -*-
"""WAV file reformat to 64b
Supports mono and stereo.

Usage:
Install NumPy and SciPy packages in Python.  See www.scipy.org/install.html
Download wavio.py from https://gist.github.com/WarrenWeckesser/7461781
Put wavio.py in the same folder as this script.
In a Python console:
>>> os.chdir(r'path of folder containing this script')
>>> from wav_to_64b import wav_reformat
>>> in_file = r'D:\Music\input file.wav'
>>> out_file = r'D:\Music\output file.wav'
>>> wav_reformat(in_file, out_file)
"""

import numpy
import scipy.io.wavfile
import wavio

def wav_reformat(in_file, out_file):
    # wavio supports only PCM/integer WAV files, with 8, 16, 24 or 32 bits.
    # in_array has one column per channel.
    sample_rate, bytes_per_mono_sample, in_array = wavio.readwav(in_file)
    # Convert integer to float64 dtype
    out_array = numpy.float64(in_array)
    # Scale to fractional format.
    if bytes_per_mono_sample == 2:
        out_array = numpy.ldexp(out_array, -15)
    elif bytes_per_mono_sample == 3:
        out_array = numpy.ldexp(out_array, -23)
    elif bytes_per_mono_sample == 4:
        out_array = numpy.ldexp(out_array, -31)
    else:
        raise ValueError('8-bit WAV files are not supported.')
    # Write to floating-point WAV file
    scipy.io.wavfile.write(out_file, sample_rate, out_array)

My listening tests showed that the 64-bit floating-point versions:
- Had deeper bass
- Had better clarity
- Had better 3D imaging
- Had more impact
- Were more emotionally involving and fun to listen to
If MC was bit-perfect, the original 24-bit and 16-bit WAV files would sound identical or better.

To summarize, all I did in Python was recast the PCM/integer values to floating-point (double type in C++) and use NumPy's ldexp() function to scale the data to the standard [-1, 1] fractional range for floating-point audio.  The same improvement in sound quality and realization of bit-perfect playback is achievable in MC by using the ldexp() standard library function in C++:
http://en.cppreference.com/w/cpp/numeric/math/ldexp

Enjoy!
Logged

Hendrik

  • Administrator
  • Citizen of the Universe
  • *****
  • Posts: 10934
Re: Bit-perfect problem found, proposed fix
« Reply #1 on: July 31, 2016, 05:18:46 pm »

MCs audio processing is bit-perfect as it is today, and this is exactly the math it uses as well, just in a more optimized form, but with the same accuracy.
Logged
~ nevcairiel
~ Author of LAV Filters

GreenMan

  • Recent member
  • *
  • Posts: 16
Re: Bit-perfect problem found, proposed fix
« Reply #2 on: July 31, 2016, 05:27:40 pm »

I documented my test conditions so JRiver and other users could replicate.  I invite all to try my test.

Is it possible that MC was tested to be bit-perfect with 24-bit WAV file input and 24-bit WASAPI exclusive output at one point in time, and that a bug has crept in since then?  How do you capture 24-bit WASAPI exclusive output to a file for an objective comparison with the 24-bit WAV file input?
Logged

GreenMan

  • Recent member
  • *
  • Posts: 16
Re: Bit-perfect problem found, proposed fix
« Reply #3 on: July 31, 2016, 05:50:00 pm »

A lot of developers copy code from the open-source libsndfile project, which uses 32767 as a scaler for 16-bit PCM and 8388607 as a scaler for 24-bit PCM conversion.  See http://www.mega-nerd.com/libsndfile/FAQ.html#Q010
Note that the developer admits that bit-perfect conversion is not achieved.

Since the convention used by WAV files and the ADC/DAC industry is to interpret the binary data as two's complement, 32768 (2**15) and 8388608 (2**23) are the correct scalers to use for PCM-to-floating-point conversion for 16-bit and 24-bit, respectively.  This applies in both directions of conversion!

The ldexp() function is ideal because it only modifies the exponent, not the mantissa.  This guarantees bit-perfect.
Logged

Hendrik

  • Administrator
  • Citizen of the Universe
  • *****
  • Posts: 10934
Re: Bit-perfect problem found, proposed fix
« Reply #4 on: July 31, 2016, 06:06:34 pm »

Like I said, this is the exact math MC uses.

The Disk Writer output plugin can write audio data straight from the audio engine into a file (at the same point it would usually be handed of to WASAPI etc). The bitdepth it writes in is not configurable however, but it should match the input file in most cases, as far as I can tell.

A quick test I ran on a single 24-bit file confirmed that both the input 24-bit file (a FLAC in this case) and the output WAV written by Disk Writer contain perfectly identical audio.

Please note that for this test its advised to disable "Do not play silence (leading and trailing)", otherwise a bit of audio can be cut off, screwing with the result when comparing entire files.

Please note that listening tests, especially without double-blind conditions, are usually tainted by expectation bias, and as such any results from those shall be taken with a grain of salt.
Logged
~ nevcairiel
~ Author of LAV Filters

mwillems

  • MC Beta Team
  • Citizen of the Universe
  • *****
  • Posts: 5234
  • "Linux Merit Badge" Recipient
Re: Bit-perfect problem found, proposed fix
« Reply #5 on: July 31, 2016, 06:10:18 pm »

I documented my test conditions so JRiver and other users could replicate.  I invite all to try my test.

Is it possible that MC was tested to be bit-perfect with 24-bit WAV file input and 24-bit WASAPI exclusive output at one point in time, and that a bug has crept in since then?  How do you capture 24-bit WASAPI exclusive output to a file for an objective comparison with the 24-bit WAV file input?

This article may be of interest, as it shows an easy objective test for establishing bit perfection of an output, which you could use to determine if anything has changed (he even appears to be using the same sound device as you):

http://www.computeraudiophile.com/content/520-fun-digital-audio-%96-bit-perfect-audibility-testing/

When the article was written, the test showed bit perfect output from JRiver, if anything's changed the test described in the article would likely show it.
Logged

Hendrik

  • Administrator
  • Citizen of the Universe
  • *****
  • Posts: 10934
Re: Bit-perfect problem found, proposed fix
« Reply #6 on: July 31, 2016, 06:19:42 pm »

Thanks for the link mwillems, I remembered that there was such a thing from a few years back, but couldn't remember quite where..
Using some sort of loopback setup is probably even better for testing, as used in that article, as it gives you more control and includes even more components that ordinary playback would use.
Logged
~ nevcairiel
~ Author of LAV Filters

GreenMan

  • Recent member
  • *
  • Posts: 16
Re: Bit-perfect problem found, proposed fix
« Reply #7 on: July 31, 2016, 06:55:53 pm »

I have read that article by Mitchco before.  One problem is that he used the ASIO interface to the Lynx Hilo for loopback.  My experience with Lynx Hilo drivers after his article came out is that WASAPI exclusive sounds better than ASIO.  As I said, the ASIO driver supports shared audio from multiple apps, so it has a mixer within that sounds like it has some distortion.  The other problem is that Mitchco tested an older version of MC, while I tested the latest version.

When testing with a FLAC file, it is important to decode it to WAV outside of MC.  That way, we are doing an apples-to-apples comparison.  Also, please use the exact WAV file I suggested for subjective testing, as it could be higher in fidelity than the FLAC file you are using.

The chain I tested is from a two's complement WAV file, to one's complement floating-point representation, to two's complement WASAPI.  It would be easy for a software developer to not achieve bit-perfect all the way through.  Its all about the details.  Does JRiver use scaler multiplication and division for the conversions from floating-point?  On the surface, this may appear to be the same math.

Asking again, how do you capture 24-bit WASAPI exclusive output to a file for an objective comparison with the 24-bit WAV file input?

I suggest that other MC users try to replicate my test condition and report what they hear.
Logged

mwillems

  • MC Beta Team
  • Citizen of the Universe
  • *****
  • Posts: 5234
  • "Linux Merit Badge" Recipient
Re: Bit-perfect problem found, proposed fix
« Reply #8 on: July 31, 2016, 07:22:23 pm »

I have read that article by Mitchco before.  One problem is that he used the ASIO interface to the Lynx Hilo for loopback.  My experience with Lynx Hilo drivers after his article came out is that WASAPI exclusive sounds better than ASIO.  As I said, the ASIO driver supports shared audio from multiple apps, so it has a mixer within that sounds like it has some distortion.  The other problem is that Mitchco tested an older version of MC, while I tested the latest version.

When testing with a FLAC file, it is important to decode it to WAV outside of MC.  That way, we are doing an apples-to-apples comparison.  Also, please use the exact WAV file I suggested for subjective testing, as it could be higher in fidelity than the FLAC file you are using.

The chain I tested is from a two's complement WAV file, to one's complement floating-point representation, to two's complement WASAPI.  It would be easy for a software developer to not achieve bit-perfect all the way through.  Its all about the details.  Does JRiver use scaler multiplication and division for the conversions from floating-point?  On the surface, this may appear to be the same math.

Asking again, how do you capture 24-bit WASAPI exclusive output to a file for an objective comparison with the 24-bit WAV file input?

I suggest that other MC users try to replicate my test condition and report what they hear.

Have you considered that it might be more profitable for you to replicate Mitch's objective test (his methodology isn't in any way limited to ASIO) to see if there is actually a difference than to invite others to confirm your subjective one? Audacity readily accepts inputs from a variety of sources, and there are other loopback solutions as well.  If you're stumped on a software solution you could literally run an spdif cable from the output of your hilo to the input.  You have the tools at your disposal, and it would be trivial to test and satisfy yourself.

Why invite others to confirm your subjective results when there's an easy objective method available to you, and when the devs have already said they performed tests (in response to your query) and found that the output was bit perfect?
Logged

blgentry

  • Regular Member
  • Citizen of the Universe
  • *****
  • Posts: 8014
Re: Bit-perfect problem found, proposed fix
« Reply #9 on: July 31, 2016, 07:26:57 pm »

When testing with a FLAC file, it is important to decode it to WAV outside of MC.  That way, we are doing an apples-to-apples comparison.

That doesn't make sense to me.  You're implying that MC's code does not decode FLAC into PCM correctly.  Is that what you are saying?  Because MC eventually turns all non-bitstreamed music files into PCM:  WAV, FLAC, MP3, etc.  All end up as PCM.

Brian.
Logged

JimH

  • Administrator
  • Citizen of the Universe
  • *****
  • Posts: 72438
  • Where did I put my teeth?
Re: Bit-perfect problem found, proposed fix
« Reply #10 on: August 01, 2016, 12:41:57 am »

When I converted 24-bit and 16-bit WAV files to 64-bit floating-point in a Python script, MC playback sounds more accurate.  I propose MC uses the ldexp() function from the standard C++ library to convert PCM/integer to 64-bit floating-point in a bit-perfect way.

...

My listening tests showed that the 64-bit floating-point versions:
- Had deeper bass
- Had better clarity
- Had better 3D imaging
- Had more impact
- Were more emotionally involving and fun to listen to
Greenman,
You're making claims about what you hear and asking JRiver to make a change in our math.

Hendrik answered for JRiver:

Like I said, this is the exact math MC uses.

This type of discussion can be infinitely long, and we have finite time available.  I suggest you post your opinions on computeraudiophile.com and hydrogenaud.io if you want to bring them more attention.

Closing this thread now.
Logged
Pages: [1]   Go Up