windytan / stereodemux

Command-line FM stereo decoder with deemphasis
MIT License
18 stars 2 forks source link

Wishlist #2

Closed windytan closed 4 months ago

windytan commented 2 years ago
Otuxam3 commented 2 years ago

Hello there!

First, thanks for making such a useful tool, I've been using it for years now.

About testing with known MPX files...

I made some using Thimeo Stereo Tool 8.00, which is a pretty known processor that can also output a standard MPX signal including RDS and polar/pilot-tone stereo (even LSB stereo sub-carrier which allows different test cases). It's actually inside a hardware product from Thimeo called STXtreme which makes it "commercially known" enough to me.

Source being inspired from your file, sawtooth at 400Hz, -18dBFS, with transitions to test most stereo configurations (L=R, -L=R mostly) every second. graphical representation here :

composition

As for Stereo Tool settings, it's the default ones:

Files have been generated from the VST version of the software, so no quality loss as it's bit-perfect (maybe too perfect for real use case)

All files are here : MPX.zip

About de-emphasis...

It was something weird I noticed when I first listened to demuxed content, audio was more muffled than from a regular analog receiver, and after checking in the constants set in your code (I got no knowledge of C++), pre-emphasis values look wrong compared to a lot of stuff I've read here and there on internet. I would've loved to share a standard document from ITU but BS.450 is the closest I could find about some settings, pre-emph methods aren't described. Thankfully this site gives a mathematical approach of the calculation of emphasis. To summarize, here are the calculations for commercial FM radios as documented :

Additionally, as it's theoretically correct to set the audio filter at 15kHz (BS.450 specifies 50-15000), many stations if not all where I am go beyond (Saw a video showing a Telos's Omnia 9 in action, cutoff seemed set at 16.5 kHz. And from a local transmitter broadcasting France Inter, I got a cutoff at 15.5 kHz based on measurements with an RTL-SDR dongle).

So I changed those hardcoded values in the header to better fit what I said above :

constexpr float kAudioFIRCutoffHz   = 16500.0f;     // tested with this value, no pilot or RDS artifacts in final audio

constexpr int   kDeEmphasisOrder    = 1;
constexpr float kDeEmphasisCutoffHz = 3183.1f;      // rounded

I still noticed after running my tests with Stereo Tool a little difference of 1.5 dB at 15 kHz, maybe due to some flaws in my setup. But aside this detail would it be relevant to add a de-emph time constant argument to better calculate the cutoff frequencies while allowing a single version of the tool to work in Europe as well as in USA/CA?

And there ends my looonnng comment, I'm open for more discussions about everything being said here. ^^

windytan commented 2 years ago

Hi, thanks a lot for the help! This will be immensely useful for the next version! I'll try and implement them as time permits :)

Yes, I agree that a time constant parameter would be better. I think it could be implemented together with the constant internal sample rate feature (input will be resampled) to make things a little more consistent.

windytan commented 2 years ago

I hope this works -- got a bit lazy and didn't write tests for the frequency response. But it looked good on a spectrum analyzer :)

Otuxam3 commented 2 years ago

Impressive, that was fast! I'll run this new version tomorrow on my setup. Without a doubt it'll simplify workflows if I plan to share my project including your work as dependency 👌🏻

Otuxam3 commented 2 years ago

So after more testing it gives really good results, possibly even better ones than what I got before. Under a dB of difference at 15 kHz, 99.99% accuracy which is enough to me.

Some question by the way: I had to add 6 dB through sox to actually have the right volume based on samplerate of rtl_fm (75 kHz max deviation from BS.450, which gives 150 kHz of bandwidth, And by default redsea recommends 171 kHz). Result being around -3 dBFS. That would give possibly a volume adjustment directly on the stereo demuxer along with output resampling? Then sox won't be necessary anymore.

But that's only my thoughts, aside this little detail the tool works very well and once again I must thank you for that.

windytan commented 2 years ago

I think that'd be a good addition. Do you think there's a way to deduce how loud the result should be from the MPX alone? Based on the samplerate? I wonder if it's off systematically. The redsea samplerate is kind of a historical relic, it's an integer multiple of 19k...

Otuxam3 commented 2 years ago

20*log(rate/ref) will help you to directly find the necessary gain from incoming sampling rate and a reference one. Since the sampling rate in rtl_fm DSP is associated to the bandwidth in reception, calculation becomes easy. In this case: 20*log(171000/150000) = 1.14 dB gain.

Tests in terminal confirms the -6 dB, which comes from rtl_fm for no reason, even if setting a low bandwidth it clips at -6 dBFS, maybe a compensation can be done through the demux as an extra gain of +6.02 dB?

Here is a result of both gains applied in my recordings, a very slight headroom of let's say 0.1 dB which is ideal!

Audacity

You also told that redsea will have to resample if it's any different from 171k, knowing I try to save a maximum of CPU time on my raspberry pi 3b+, it's valid to simplify calculation by having a multiple of the RDS center frequency (x3 actually). But maybe if I remove sox from my processing chain it'll be worth changing the rate...

windytan commented 2 years ago

Gain can now be specified with -g and output samplerate with -R.

By the way, the audio lowpass filter is quite CPU hungry. It becomes less so if you set constexpr float kAudioFIRLengthUsec = 300.f;, but of course more of the RDS and pilot will leak in. I've seen some home receivers just output unfiltered audio, so it could be an option. It's borderline ultrasonic afterall.

Otuxam3 commented 2 years ago

Been testing that new commit.

Again, awesome work in adding those features! Some testing on my side with my calculated gain gives as expected a slight headroom, avoiding clipping and at the same time keeping a good loudness (of course I don't expect improved SNR since it's a gain applied to 16 bits samples).

Honestly, having a less aggressive filter wouldn't free as much resources as removing sox in the processing chain. In average I've measured the CPU usage of each step of the chain, here are my observations considering it's based on a Raspberry 3B+ (Broadcom BCM2837B0, quad-core ARMv8 1.4GHz):

NB: htop gives CPU as percentage of a single core, the total is then 400%

htop Before htop After

The new version of the demuxer took the same CPU amount as before, so in my opinion, I'm more favorable to keep the filter as it is, to somehow show filtering is done way better than an analog receiver (my Sony STR-DE235's filter still lets a slight amount of the pilot and 38k sub-carrier bleeding in the spectrum). I'm also sensitive to high frequencies and I can hear the pilot tone as a pretty annoying whistle in headphones when not filtered at all.

Well I think my contribution to this repository is done. Unless you get other ideas or request more MPX files I don't think I'd be of any help sadly 😅

Or maybe I could provide compiled binaries for ARMv8 machines like Raspberry Pi to be included in future releases for easy distribution.

windytan commented 2 years ago

That's really good to see :) Probably helped with some task switching overhead, too. I'm not sure if precompiled binaries will be necessary - this should be possible to compile with minimal requirements, afterall.

Thanks for the help and valuable ideas once again!