dsward2 / LocalRadio

📻 LocalRadio is "Radio for Cord-Cutters" – a Software-Defined Radio (SDR) app for your Mac and mobile devices. With an inexpensive RTL-SDR USB device, LocalRadio provides a casual, home-based radio listening experience for your favorite local frequencies - FM broadcasts/free music/news/sports/weather/public safety & aviation scanner/etc.
GNU General Public License v2.0
365 stars 30 forks source link

LocalRadio Pre-release Test Version v1.0 #2

Open dsward2 opened 6 years ago

dsward2 commented 6 years ago

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.

dsward2 commented 6 years ago

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 5 years ago

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
dsward2 commented 5 years ago

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 5 years ago

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.

dsward2 commented 5 years ago

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 5 years ago

"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.

dsward2 commented 5 years ago

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 5 years ago

This looks promising if you decide to use web sockets:

https://github.com/joewalnes/websocketd https://github.com/joewalnes/websocketd

Most libs are web clients but this one creates a server via the shell. Not sure if you can have a persistent shell via NSTask, though (it's been a while since I've done Cocoa programming). Like I said, I can assist fleshing out the code if you want to integrate it.

On Feb 15, 2019, at 4:28 PM, Douglas Ward notifications@github.com wrote:

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.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/dsward2/LocalRadio/issues/2#issuecomment-464207161, or mute the thread https://github.com/notifications/unsubscribe-auth/AD_P989VojvcS2sOuMXz6D8NWXNP8jZpks5vNyaPgaJpZM4Pcizp.

dsward2 commented 5 years ago

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 5 years ago

Sorry, just realized I never told you my name. It's Steve.

Regarding the cleanup script: to make sure I'm understanding you, are you saying you have a bunch of independent NSTasks floating around in process space when the app quits and that's why you need the cleanup script? What about having an additional NSTask "watchdog" keeping an eye on the app and when the main app disappears it signals the tasks to quit then quits itself? I'm just saying that would be more user-friendly for a final release. Is that even possible or am I just rambling about things I know nothing about. Lol.

BTW, you made me think of something when you mention the possibility of custom tasks. Check this out: https://github.com/projectstorm/react-diagrams https://github.com/projectstorm/react-diagrams

On Feb 16, 2019, at 4:48 AM, Douglas Ward notifications@github.com wrote:

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 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.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/dsward2/LocalRadio/issues/2#issuecomment-464326760, or mute the thread https://github.com/notifications/unsubscribe-auth/AD_P9wqWLqwka-TRupuUjFZyHZ99ZFXyks5vN9P5gaJpZM4Pcizp.

wx007 commented 5 years ago

More feedback:

So I'm thinking a lot of the issues I was experiencing are do to the delay in acquiring the new station. I thought there was something wrong with the app so I'd keep clicking the buttons. Perhaps it would be a good idea to display a message in the browser that says something like, "Switching stations...." and then if you know it typically takes about 10 seconds then maybe show a progress bar for 10 seconds (It doesn't actually have to link to anything... unless you're using web sockets then it actually could provide realtime status messages).

Also, I notice the volume button in the browser only mutes the volume rather than allowing you to change the volume. It that an IceCast thing?

On Feb 16, 2019, at 11:17 AM, s teschker steschker@gmail.com wrote:

Sorry, just realized I never told you my name. It's Steve.

Regarding the cleanup script: to make sure I'm understanding you, are you saying you have a bunch of independent NSTasks floating around in process space when the app quits and that's why you need the cleanup script? What about having an additional NSTask "watchdog" keeping an eye on the app and when the main app disappears it signals the tasks to quit then quits itself? I'm just saying that would be more user-friendly for a final release. Is that even possible or am I just rambling about things I know nothing about. Lol.

BTW, you made me think of something when you mention the possibility of custom tasks. Check this out: https://github.com/projectstorm/react-diagrams https://github.com/projectstorm/react-diagrams

On Feb 16, 2019, at 4:48 AM, Douglas Ward <notifications@github.com mailto:notifications@github.com> wrote:

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 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.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/dsward2/LocalRadio/issues/2#issuecomment-464326760, or mute the thread https://github.com/notifications/unsubscribe-auth/AD_P9wqWLqwka-TRupuUjFZyHZ99ZFXyks5vN9P5gaJpZM4Pcizp.

dsward2 commented 5 years ago

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.