cmnybo / nrsc5-gui

A graphical interface for nrsc5
56 stars 21 forks source link

Error leading to choppy Audio (May be caused by PyAudio) #17

Closed andrewfer000 closed 3 years ago

andrewfer000 commented 3 years ago

Hi Devs,

I gave this project a try because it looks neat. It works okay but I get a slight problem on my Ubuntu 20.04 Laptop that causes choppy audio. Here is the output to console.

ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred.

Not sure what that is, but it causes audio to be choppy. I tried the solution posted in the Raspberry Pi Solution but it did not work. I'm not sure if this has to do with my signal quality but when I run programs like hdfm I have absolutely no issue. I know Nrsc5 itself has no problems either. Like what does those programs do right that this one does not?

andrewfer000 commented 3 years ago

I was able to slightly isolate the problem. The semi-consistent choppy audio only happens for about the first minute of running and when there is "Error" (even when the signal is at it's strongest) Currently I have 0.00 - 0.10% error and it's chopping like CRAZY!!!!!

Edit: Also programs like hdfm take the audio directly from nrsc-5, In my opinion that is better then sending the audio through another program.

Edit2: PyAudio keeps disappearing and re-appearing when I look at my audio mixer which is what seems to be causing the choppy audio. So this is defiantly PyAudio's fault. I'm going to look around for alternatives and try to implement them in the code. If Devs ever see this message please give me some information on where/how audio is processed and output.

andrewfer000 commented 3 years ago

So I kind of found a solution. I lset the frames_per_buffer=512, in the def audio_worker(self): function in nrsc5_gui.py with much better results.

markjfine commented 3 years ago

I've noticed this happens in some other applications when the sample rate for the device is set too high for the level of processing being done. What rate does nrsc5 hit your device at? Mine says: Exact sample rate is: 1488375.071248 Hz

andrewfer000 commented 3 years ago

I've noticed this happens in some other applications when the sample rate for the device is set too high for the level of processing being done. What rate does nrsc5 hit your device at? Mine says: Exact sample rate is: 1488375.071248 Hz

Exact sample rate is: 1488375.071248 Hz (I believe that is default for R820T/2)

The fix I mentioned above works very well though.

markjfine commented 3 years ago

cool. whatever works.

andrewfer000 commented 3 years ago

PyAudio is causing this program to work like complete shit again. Tested the same station with hdfm with no issue. What in the actual hell is wrong with PyAudio? I never been so frustrated with a SDR program before in my life WTF.

New finding: When my laptop is unplugged, I need to lower frames_per_buffer=512, to 256. Like fr what the hell is wrong with PyAudio?

Like Fr. WTF is this ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred

ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred

Don't take this post as me hating on this program, I know the devs worked hard on this and it is a FOSS project and likely a hobby project. Things are working better than the beginning and very well at most times. I'm mostly pissed off at PyAudio's performance since that is backed by the Python Foundation.

andrewfer000 commented 3 years ago

After some continued modification and testing, some of the error was on my part with my SDR's antenna setup. I also modified the program's PyAudio stream code very slightly. It would be nice if a developer can leave some info on how the audio processing happens in this program (like a flowchart or something) so I can make real improvements to the sound processing system if possible.

andrewfer000 commented 3 years ago

I'm starting to give up on this, the sample rate can be lowered to 43900 without noticing the changes which helps the issue a little. What I need to be successful is to know how/where this program gets the audio data. I modified and screwed around with the audio worker for hours with no avail. This program has a killer GUI but if it's not used in near-perfect conditions it struggles where the other (not so nice looking) programs work. Seriously! what makes those programs work so well in bad conditions that is missing here?

What I got so far that seems to "improve" conditions in nrsc5_gui.py AUDIO_SAMPLE_RATE = 43900

in __init__(self): self.audio_queue = queue.Queue(maxsize=192)

in nrsc5_gui.py , indef audio_worker(self) under channels=2 frames_per_buffer=384

in nrsc5.py in def pipe_samples_cs16(self, samples): result = NRSC5.libnrsc5.nrsc5_pipe_samples_cs16(self.radio, samples, len(samples) // 1)

Edit: For anyone new to this issue, I am using the code from PR #20 repository

markjfine commented 3 years ago

Just thinking holistically out of the blue. To me, an underrun means the audio buffer isn't filling fast/complete enough. Perhaps what's happening is the thread that does this doesn't have enough of a priority and gets waylaid...

andrewfer000 commented 3 years ago

Didn't mean to sound that way. It's just I've been working on this issue for a few weeks yet I can't find a solid root cause or solution. I figured out that the underrun happens when there is not enough correct data which is a good start. For awhile I wanted to find a way to "fill" the data with empty white noise but I cannot figure out how to do it.

markjfine commented 3 years ago

I get it. I may see how this runs in F33 once I fix it, because kernel 5.11.7 just broke it. I run Fedora in a Parallels VM on a Mac. The VM has some basic I/O code that breaks every time there's a major kernel update. There's a small team of us that debug the errors and make code changes that Parallels $hould be doing. If you want to see what frustration looks like, see some of my comments over there. I'm about to get real ugly with them.

Was also thinking it could be something with nrsc5, but apparently hdfm also uses it. Almost certainly has to be something interrupting the sound processing here. I'll try to take a look at it today and see if I can find what's going on.

markjfine commented 3 years ago

Got Fedora unscrewed, got nrsc5 built, and nrsc5-gui installed. Note: When using LoadLibrary(lib_name) in nrsc5.py it couldn't find the library in /usr/local/lib/ until I added that prefix. Shouldn't have to have it hard-wired, but that's another problem for another day.

Got it started and viola: ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ...

I'm omitting a whole lot of other ALSA-related status messages that were generated on the app's startup. Didn't see anything like that under Darwin.

So another clue is that this could be Linux-specific, and quite possibly with PyAudio's interface with faad2 and ALSA. I still have PTSD from getting Digital Radio Mondiale (DReaM) to work with a modified faad2 and ALSA in 2004 and this is starting to look a lot like that. Think I ended up saying 'screw it' and just using straight pulseaudio instead. Will have to review my notes on how I got past it.

markjfine commented 3 years ago

Put some debugging into the processing loop that pulls the samples off the queue and writes it to the default output stream. The queue seems to be filling up appropriately at 8k, so the underrun isn't coming from that.

Almost seems as if there's an occasional delay in writing to the stream. ALSA may be busy (for whatever reason) and that's where the underrun problem is. It's not getting what it's supposed to be getting because it was distracted by other shiny things. On startup there's a similar massive delay/lockup when trying to determine the default output device. You basically can't press play or do anything until it gets past it... not something I see natively on Mac with a different sound interface.

Bottom line: I may be biased, but ALSA isn't playing nice with PyAudio here at all. Looking for alternative sound interfaces for PyAudio to leverage here.

andrewfer000 commented 3 years ago

Interesting find. While I still get the ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred and a little delay that does not matter much to me. My minor modifications mentioned above work well enough for now but if you have the time and knowledge to improve the program even more I will be glad to help/test.

markjfine commented 3 years ago

Found a possible solution keeping the audio_worker settings as originally coded (pulseaudio to the rescue):

  1. Quit nrsc5-gui if it's running.
  2. Edit /etc/pulse/daemon.conf (with sudo).
  3. All the way at the end, uncomment and change default-fragment-size-msec from 25 to 12.
  4. You can also uncomment and change default-fragments from 4 to 5, but I'm not sure it's as effective.
  5. You can optionally restart, but I think the .conf takes effect the next time pulseaudio is invoked.

You'll still get underruns, but not nearly as many, and the audio doesn't chop a lot for relatively error-free signals.

You can play around with the number in step 3: The number is inversely proportional to the essential priority that the audio system's given. For example, I initially changed it to 2 based on something I read, and it basically locked out the python gui. So, too small is not a good thing. I mean, you'll get zero chopping, but all the priority is going to the stream thread and you lose control of the app, having to kill it. lol

I still don't like the delay and status messages it starts with as it searches for an appropriate device/configuration. Might look at how to streamline that nightmare next.

BTW: Creating a shell script to run python3 nrsc5_gui.py and a .desktop to call it will 'out of sight, out of mind' any remaining status messages... but that's a cop out. Does make it easier to run tho.

andrewfer000 commented 3 years ago

Set default-fragment-size-msec to 8 and default-fragmentsto 5 and that seemed to help quite a bit.

markjfine commented 3 years ago

Ok, so today I checked and now it's not working as well as it did yesterday. This is so bizarre...

Edit: May be related to the average bitrate showing for the stream (in the status bar).

Edit 2: Just changed 12 to 8 as you did, then also changed default-sample-rate to 48000, just in case. This time, making sure it changed by restarting the service: systemctl --user restart pulseaudio.service. Seems to be ok now.

Perhaps it's touchy about what kinds of errors, e.g. where they are in the stream that makes a difference.

While I'm at it, I noticed that the status bar is using the average bit error rate instead of the currently reported one. I would think the current one would be best to report, especially if you're using it for antenna positioning, etc. Average is always going to be a latent measure of reality and not real helpful when trying to diagnose things like this, either.

Edit 3: Also thinking it might have something to do with the automatic gain sometimes going too high (or too low) creating spurs and other erratic reception phenomena. Almost impossible to achieve consistent results for anything. Decided to leave it set at 30 and see what that does for a while.

andrewfer000 commented 3 years ago

Maybe this is a possible solution? https://github.com/tynn/PyAO

andrewfer000 commented 3 years ago

I also found this very incomplete fork of this program that has no audio issues at all. Maybe we should try to work with it? https://github.com/zefie/nrsc5-gui

markjfine commented 3 years ago

Is it possible that all the extra display calls I added is causing a processing problem?

Edit: There's still room to slim it down some if that's the case. Don't need the extra station call on the info tab. Also, not sure how helpful the slogan/message is, but many stations use them so differently.

I've already changed the buttons and audio/data info to come from SIG instead of SIS. SIG seems to only comes up once, whereas SIS data comes up a lot. So that shouldn't be an issue.

andrewfer000 commented 3 years ago

The major difference I saw between this version of the program and the older fork is that the older fork plays the audio from the nrsc5 program directly (look in your sound mixer)

The older fork- image

This Program (it was going in and out so much I had to stop the playback)- image

Do you have any idea on how the code can be changed to use the audio directly from the nrsc-5 executable? (that is also how hdfm works)

markjfine commented 3 years ago

hmm. Let me take a look. Shouldn't be too hard to figure out how to pull that in.

markjfine commented 3 years ago

Take that back... Looks like he's completely rewritten the way nrsc5 interfaces to de-abstract from Python and let the OS do most of it. It's doable, but it's major surgery and will take some time. Architecturally, it's basically a different app.

andrewfer000 commented 3 years ago

I see. If you can do it somehow that would be great I was thinking maybe we can have both audio systems running at once but the pyaudio is muted by default and only used for data processing and the audio is output from OS. You seem to be experienced with this kinda stuff so I have some hope.

andrewfer000 commented 3 years ago

If you are good at GUIs, maybe we can try to work on a different program like hdfm and build a real GUI on top of that. Tbh I feel like the owner of this project abandoned it and we are just it's life support...

If the US used DAB(+) like the rest of the world, then we wouldn't be dealing with this xD. I wish this had the same level of support as the welle.io project. (I already asked them for HD radio support but they said they couldn't do it)

markjfine commented 3 years ago

I mentioned that I contributed a little to the Dream project with recevier interfacing very early on in it's life. At the time it was mostly DRM for shortwave. But now, DRM is attempting to make worldwide inroads on domestic broadcast bands, particularly as an Ibiquity/HD Radio alternative here in the US. The FCC has been stiff-arming DRM for a while, but hopefully Pai and his dumb toothy smile will soon be replaced so this can be considered. nrsc5 is the only thing that's been able to successfully get around the proprietary parts of HD Radio that I've seen. No one really wants to touch it. I'm just interested as a radio geek.

Incidentally, a lot of these kinds of projects have gone dormant lately. The last of the Dream contributors have seemingly abandoned it on Sourceforge. In fact their wiki was badly hacked some time ago and no one's paying attention. A lot of this could be due to the pandemic and you wonder / hope that the devs are ok. when you consider this nightmare has killed about 5% of the population over the past year and re-surging now, the odds are not all that great.

andrewfer000 commented 3 years ago

The reason why HD Radio sucks is because it's proprietary and while nrsc-5 does a really good job at being an open source implantation nothing is going to change the fact that it's proprietary to install in cars and broadcast which basically made it DOA in 2002. If the world just used open standards for everything the world would be a better place. But in any case let's end the rant here and let's focus on making this program work.

I also tried to put the old and new program pieces together to no avail. I'm no python pro so idk what to do at this point. If the nrsc5 API had better documentation and if I was good at programming I would of tried to build a new program in C++ using GTK. But then I was thinking, what if we wrapped the code in a JIT compiler like PyPy or that other python JIT compile that I can't remember the name of? Maybe of the code runs faster or more efficient, the problem may be solved! But that's just a theory that I don't know how to implement.

andrewfer000 commented 3 years ago

PyPy does not work because of missing modules I do not know how to resolve.

andrewfer000 commented 3 years ago

I am starting to believe my original conclusion that whatever is processing the signal now is very sensitive and causing issues. It's likely a part of nrsc5.py

andrewfer000 commented 3 years ago

I hope the developer is okay and they will come back soon. When I asked for this issue to be fixed I thought it would of been simple because this program wasn't so big. But BBOOYYY was I wrong. Today I decided to dig deep in this program since I had nothing else to do and it only brought me frustration. Today I learned that python and pyaudio are slow and complicated. I just want a program like this with stable audio. I know I sound like i'm complimenting like crazy but I never put so much into a project like this before and it's painful to see it all be for nothing. Like I just want an easy, single window GUI to listen to my favorite HD-2 station and get a weather radar when I'm outside. But because Linux audio and pyaudio are stupidly difficult for a program and I can't find the real absolute true cause of the issue here and it's taking a mental toll on me. All I want to know is the root cause and how to fix it!

This also seems to be a problem in the nrsc5's python code Download and run the cli.py with the nrsc5.py and you'll see.

markjfine commented 3 years ago

Python is a scripting language that likely runs on an interpreter engine, which doesn't lend itself to anything that might be process-intense. The same is true for perl, php, javascript, etc. What you gain to a certain extent is platform independence if the interfaces are built properly. What you lose is processing speed as compared to something optimized and compiled for the platform you're running on. You can gain some apparent speed through asynchronous threading, but that only takes you so far.

Java was supposed to get you the best of both worlds but it changes too fast. Code can become deprecated in a few short months. I built a Java/web-based CADRG GIS and map server in the 90's. The maintenance required was frustrating. ESRI and Google ended up blowing me out of the water... but that's another story.

What you may be onto is possibly applying the current gui to zefie's code instead of trying to re-architect this project. I'll look at doing that over the weekend and see what I come up with. That might be the best viable approach for now. As it is, I think zefie's threading model is more current than this one, which always generates deprecation warnings.

markjfine commented 3 years ago

Pulled it down to take an initial look and my first impression he's apparently done this for Python 2.7 for Windows. Going to have to update the Gtk/GObject/Gdk on this so it runs on Py 3.9 for Mac/*nix. Py 2.7 is going away.

markjfine commented 3 years ago

Got it running, and things play, but it's not displaying anything. lol

andrewfer000 commented 3 years ago

I tested it on Linux already and yeah it doesn't show anything but the audio works great. I suggest checking out the hdfm source code for ways on how to get the data from nrsc5 since I am sure it's different than this. Thank you for your time. I really appreciate it.

Unrelated, but by any chance were you involved in the Free Software Foundation in the 1990s? If so I'd like to ask you some questions regarding recent events. (a.k.a RMS's return to the FSF)

markjfine commented 3 years ago

He's using a pipe and regex's, but did it for Windows and didn't strip the newlines. So the regexes never match. lol

markjfine commented 3 years ago

added a .strip() to line in parseFeedback and changed the regexes to start differently: e.g., "^[0-9\:]{8,8} Station name: (.*)$" and now the feedback displays.

markjfine commented 3 years ago

Just saw the second part of that. No history with those guys. Been mostly with defense as a civilian and a contractor.

andrewfer000 commented 3 years ago

So how's the other program working for ya? Its good to hear that you were able to get the text to work. I just hope you can get the rest of it to work without having too much trouble. I'm happy you are doing this and if you need anything just let me know. (even though my knowledge is limited, I'll try my best)

markjfine commented 3 years ago

Working fairly well actually, just a couple of different problems to work out and it's basically ready for testing.

  1. Right now changing the stream via the buttons restarts nrsc5, need to pipe the stream number to the existing thread like you do when running it stand alone so it's not as clunky.
  2. The separate map display apparently wasn't completed as the radio buttons don't work. The images are being created and properly displayed in the main display (and are all stretchy) after a few tweaks of the resizing mechanism.

Other than that I started plugging away yesterday and it seems to work fairly well. Bottom line it's pretty much done. Then it's just a matter of how to distro for testing. I may try to make a fork of a fork... if that's possible. Don't think it's fair to the project owners if I just drop files here. That said, if I create another fork, not sure what that does to the version I already have pull listed. I just don't want to be in a position to screw a whole lot of people up.

There is one interesting thing that I need to investigate, tho: When running stand alone nrsc5 refuses to synch with 100.3 WHTZ in NY. In the earlier version of gui it played fine and was able to get traffic/weather maps from it pretty easily, so there was some magic occurring there. Now that the new gui is just spawning nrsc5 and communicating via pipes, I can't get WHTZ to play in it, and it's a strong iHeart station. In order to generate maps, I had to work with 106.1 The Breeze and deal with a much more erratic signal (not in the best of receiving condx where I am).

So, I have no idea what's going on with that, but it seems like a nrsc5 problem. The whole synchronization thing is "FM" to me, as are most of these detailed digital protocols. I would think that if I ever got nrsc5 working with my AirSpy or some other device that isn't as spur and image crazy, and whole lot more sensitive, those kinds of problems would go away.

andrewfer000 commented 3 years ago

Ah that's great! As for the distribution of the program and source, I suggest you make a whole new repo and just upload thr files to it. No need to make forks of forks, I'm sure you added so much and changed so much that it's almost a different program.

Also I live closer to some of the Philly iHeart stations (106.1 The Breeze, 104.5 ALT-FM, etc...) so I would be happy to test and deliver results. I think now would be an appropriate time to upload your new code to a new repo and we should get out of here and start a new issue to discuss testing there. Just drop a link here when you are ready.

markjfine commented 3 years ago

Done. Changed the name to nrsc5-dui (play on words) to avoid confusion.

andrewfer000 commented 3 years ago

New project above fixes the issue. Closing this issue.