dsward2/LocalRadio

LocalRadio Pre-release Test Version v1.0

dsward2 opened this issue · 12 comments

This is a thread for reporting issues for the first build of LocalRadio, tagged as v1.0.

The LocalRadio release are posted here: https://github.com/dsward2/LocalRadio/releases

The next version will be LocalRadio v1.1, based on reports received here.

I found a bug in the "Advanced Tuner" web interface. The "Listen" button does not work for trying out new frequencies.

The temporary workaround is to click the "Add New Favorite" button, then navigate to the Favorites section and the new frequency record, where the Listen button should work.

This bug will be fixed in LocalRadio 1.1, which should be available within a few days or sooner. The Listen button seems to be working for the other tuning modes.

wx007 commented

On OS X 10.12.6 the IceCast process crashes.
The app runs and I can open the web page, however I cannot see the RTL-SDR v3 dongle.
I verified the dongle is working with another app.
Here is the error:


Process:               icecast [4629]
Path:                  /Applications/LocalRadio.app/Contents/MacOS/icecast
Identifier:            icecast
Version:               0
Code Type:             X86-64 (Native)
Parent Process:        LocalRadio [3992]
Responsible:           icecast [4629]
User ID:               501

Date/Time:             2019-02-11 20:14:43.502 -0500
OS Version:            Mac OS X 10.12.6 (16G1815)
Report Version:        12
...
Crashed Thread:        0

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Reason:    DYLD, [0x3] Wrong version

Application Specific Information:
dyld: launch, loading dependent libraries

Dyld Error Message:
  Library not loaded: /usr/lib/libssl.dylib
  Referenced from: /Applications/LocalRadio.app/Contents/MacOS/icecast
  Reason: Incompatible library version: icecast requires version 1.0.0 or later, but libssl.0.9.8.dylib provides version 0.9.8

wx007 - Please try the "legacy" version of LocalRadio in the releases download. It should work for Mac OS X 10.12.

Currently, the latest version of the legacy app download is titled "LocalRadio-v1.18-alpha-legacy.zip". The legacy version of LocalRadio is built on an El Capitan system specifically to address the incompatible libssl problem. The main release version is built for Mojave only, where Apple's built-in libssl is version 1.0.0 or later.

Here is the more technical explanation of the problem, which you can skip, but might be helpful if you're trying to build LocalRadio from source -

The release versions of LocalRadio are built as sandboxed applications, which restricts applications from opening most files on the Mac system for security purposes. Icecast is a separate application in the LocalRadio bundle, and it needs libssl in order to support https connections, but due to Apple's sandboxing rules, Icecast must use Apple's version of libssl.

I tried to use the MacPorts version of OpenSSL, copied into LocalRadio app bundle, but it was incompatible for use in a sandboxed app due to libssl's requirement to read a file of trusted root certificates. Although LocalRadio generates a self-signed certificate which technically don't require the use of that file, the libssl library must still be able to read that file. I investigated the possibility of using a custom-built version of libssl, but it did not have a build option for accessing the trusted roots file in an alternate location for sandboxed apps.

wx007 commented

Thanks for the quick response!

The 1.18 alpha legacy version works but only if you put it in the /Applications folder.
The sound quality is way better than other apps I've tried, BTW.

FYI, some unexpected behavior I'm encountering:

• The "Now Playing" title does not consistently update in the browser (it will show the previous frequency).
• The browser and the app don't always sync information (like what is currently playing).
• Sometimes switching frequencies/favorites doesn't seem to work unless I quit and restart the app (after running the cleanup script). Sometimes toggling the play/pause button forces the station to play.
• Not sure if this is to be expected but there is about a 10 second delay when switching favorites.

Outside of that I'm having a lot of fun with this app. I really appreciate the work you're putting into it.

wx007 - Thanks again for your reports!

The current frequency display in the native Mac app is nearly instantaneous, but web browser interface does have some lag time on the current frequency display. The web page update intervals were chosen as tradeoffs between a reasonable update time vs. network traffic and energy consumption. It uses some JavaScript functions like periodicUpdate() and updateStatusDisplay() in localradio.js to update the current frequency status in the web page. The default time is 20000 ms (20 seconds), but if the app is running in scanner mode, the frequency will be updated 4 times per second.

The ideal way to provide instant frequency info to the web page would be to embed it as metadata in the AAC ADTS packets, but I'm not sure if is possible, or how a web browser could access that data while it is playing. Another possibility could be for the web page to open a listener socket to receive updates from the LocalRadio server for frequency change notifications. But for now, there is a delay on current frequency display in a web page.

On the ten-second delay in the audio after a frequency change, that is partially due to Icecast buffering, but there are other buffers before Icecast that can cause delays too. Another factor is the bandwidth of the input signal from the radio. The buffers before Icecast are usually set to a fixed size, and a high-bandwidth signal like FM broadcast can fill a buffer faster than a low-bandwidth signal (e.g. NOAA Weather Radio).

I've also noticed the issue you report about the web page audio player needing clicks on the pause/play button. I'm not sure why that is happening, but the server does try to keep an existing Icecast session alive while the radio frequency is changed. I've seen some versions of the rtl_fm tool that allow for instant re-tuning, but those work only if the bandwidth does not change when the new frequency is set. It might be possible to improve that for situations where the bandwidth does not change.

Finally, thanks for the report that LocalRadio legacy version must reside in /Applications. It was kind of hacked together to make an app that will run on old hardware, but that problem sounds fixable, so I'll try to check for that problem in future releases.

wx007 commented

"It uses some JavaScript functions like periodicUpdate() and updateStatusDisplay() in localradio.js to update the current frequency status in the web page."

If there were a websocket server library you could incorporate into your app you could have realtime, bi-directional message passing between the browser and your Cocoa app and eliminate any worry about timing.
I try to incorporate websockets into all my enterprise web apps now because everyone has gotten used to changes appearing simultaneously across the office without having to reload the web page. Most server libs have the ability to segregate messages into subscriber groups based on arbitrary criteria. Browser libs auto-reconnect in case of communication interference.
Just a thought. If you decide to go that route I can set you up with boilerplate for the browser (maybe the back-end also) and you won't have to make timed Ajax calls.

wx007 - I had considered web sockets as a faster way to update the display, but that idea got set aside for a while - at least until someone actually asked for it.

Currently, I'm exploring the possibility of an Apple TV client app for LocalRadio, so that might be a good opportunity to add a socket interface. I'm also considering a replacement for Icecast for streaming the AAC audio.

wx007 commented

wx007 - Thanks for that websocket link, I'll check it out.

Indeed, LocalRadio uses NSTasks extensively, and stdio to process the signal through several stages.

The first-stage pipeline of NSTasks for an FM broadcast station looks like this:

rtl_fm_localradio -> stereo demodulator -> Audio Monitor (resampled to 48000 Hz) -> UDP Sender

then a second chain of tasks, which usually remains persistent for the life of the app -

UDP Listener -> AAC Encoding -> Icecast Source (sends to Icecast)

Finally, the main Icecast server runs as a separate NSTask.

This arrangement allows the first stage to be terminated and restarted when a new frequency or bandwidth is selected, without killing the existing streaming audio connection from Icecast to the web client. All of those tasks get terminated when the main app quits normally, but if LocalRadio does not get a normal quit, the clean-up script will get rid of the remaining tasks.

The web socket interface you propose would be added to rtl_fm_localradio, which is a customized version of the standard rtl_fm tool in the standard Osmocom RTL-SDR suite to handle the tuning, demodulation, etc.

There is also an option in LocalRadio for "custom tasks", where the user can define a custom first-stage pipeline of NSTasks. This could include a software-defined radio demodulator besides rtl_fm_localradio - but if LocalRadio is built with Apple's sandbox option, the external tools must also be code-signed with the same signature as LocalRadio, or code-signing must be disabled for LocalRadio and the sub-projects. For example, there are plenty of experimental versions of rtl_fm on GitHub that can be used with the custom tasks option. The audio source could really be anything, as long as it sends the audio signal as PCM via stdout, and sets the sample rate. Sometimes, it is helpful to add a sox task too, with the output sample rate to 48000. Then the AudioMonitor task will receive the custom task data via stdin, and it eventually gets to the Icecast server.

wx007 commented
wx007 commented

Hi Steve - The NSTasks are running as separate processes, and they can be observed in ActivityMonitor.app. When the main LocalRadio app quits, those processes get terminated normally. There are also some situations where macOS will kill the processes itself, particularly when a process that is piped to other processes fails.

The clean-up script that runs in Automator.app is useful in situations where the main app crashes and therefore misses the routine to kill the other processes - ideally only rarely or never. I like your suggestion about a watchdog process, and I might also check for the zombie processes at LocalRadio launch time in a future release. I want to be cautious about killing processes, so for the alpha phase of this project, the Automator script seems like the most transparent way to clean-up.

Right now, I'm doing some research on AAC ADTS metadata. I think there is a way to send metadata at a rate of one byte per frame. If there is a way to intercept and parse the metadata in the web browser, I see a couple of possibilities for faster frequency display updates.