A bash-script-based streaming audio system client controller for gstreamer

Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.
UPDATE:
The latest GSASysCon version is 1.01 and can be downloaded at the GSASysCon web page:
GSASysCon - A bash-script-based streaming audio system controller



Intro:
After being fortunate enough to find a few solid days of uninterrupted development time I have managed to create a system for controlling all my loudspeaker systems. At this point I have some code working and the concept seems feasible but I am still building everything out. I will update this thread with info on my progress, and the scripts, when they are working.

Concept:
A number of loudspeaker system receive streaming audio over WiFi. Audio sources are played by and streamed from a single computer, the "audio server". Loudspeaker systems are comprised of one or more "audio client" computers. The client computers receive the streaming audio, perform some processing (e.g. implement a DSP crossover) and then pass the audio to on-board amplifiers in the loudspeaker. On the audio server, audio is sourced by a player (MPD on my system). For each loudspeaker system a separate gstreamer pipeline is used to stream audio from the output of the player to audio clients. Launching or terminating the gstreamer pipeline for any loudspeaker system will cause it to turn on and begin playing (pipeline launched) or stop playing and then automatically turn off the amplifiers after a preset time (pipeline terminated).

A text-only, command line interface is used to allow the user to control the on/off behavior of each system and to see the current status of all available systems. This system will accommodate multiple concurrent users and remote clients - script can be run on multiple terminals simultaneously as there is not exclusive use of any resource.

Reducing Disk Usage:
To reduce actual disk usage, the contents of the system information sub-directories are copied to the tmp filesystem (that actually resides in memory) and all PID tracking and system control read/writes are done there. Only changes to the ip_list and system description files need to be written to the hard disk (e.g. when editing systems) and then copied over to the /tmp filesystem to show up in the control system interface.


Identification of System Clients:
Each loudspeaker system is controlled by 1 or more client computers (e.g. Raspberry Pi) that receive the streaming audio, implement the crossover, and control the on/off state of the amplifier(s) in the system. All client computers are always up (running) and have separate, fixed IP addresses and hostnames. The IP addresses are used by gstreamer to send audio from the server to the client over WiFi. Therefore, a list of system client IPs can be used to define and identify the clients that comprise each system. New systems can be created/added by specifying the client IP addresses in the system using the system editor.

Run Modes:
There are three run modes, single-shot, continuous, and autonomous.
Single Shot Mode: The system control is invoked and, after a system is toggled on or off, it exits.
Continuous Mode: The system control is invoked and the system status will update after a change (e.g. a system is turned on or off) or after a user-defined interval of N seconds even without user interaction. This continues until the user actively terminates the control interface. Using continuous mode, the user can be regularly appraised of system status no matter which terminal makes a change to the system.
Autonomous Mode: The desired change is made without displaying any system status info, and then the program exits. This allows GSASysCon to act as a back end for another program, or to be run by the operating system, eg. a cron job.



Motivation - Why Do I Want/Need This Control System?
In my home I want to control a number of audio (only) system. I use an MPD client to run MPD (which is actually running on the audio server) and control playback. The MPD client GUI interface is actually running on a 10" Android tablet that I can conveniently carry around with me. This lets me choose what I am hearing, but there isn't a great way to turn a system on or off without walking up to it and operating multiple physical switches - unfortunately MPD's output switching is not up to the task, and output switching is not available on my preferred MPD client anyway.

I found that I can easily open an SSH session on the audio server from my Android computer. This can be done using an SSH terminal app or even via an HTML browser using the Shellinabox software. Given that a text-based command-line control system would allow me to turn systems on and off from my tablet, in true DIY fashion I hacked one together. I think this approach might be useful to other DIYers even if they do not have multiple systems to control.


.​
 
Last edited:
Haha, from years on mailing lists I guess I've learned to not over quote the thread.

I've got some pi zero's and have been wanting to try setting up the following:

a pi zero, usb sound card, and two monoblocks at each speaker(irs2092 for the lf drivers, tda8932 for full range driver). The pi zeros handling the dsp/xover duties.

a media server on the local network that is managing the streams, linux/freebsd/mac, some *nix.

It could be wireless or wired, ultimately wired for some of the uses I have in my head(garage speakers, garage has multiple pulls of cat6 thanks to me :D).

My first use case is for a pair of open baffle speakers I'm building for my dad( http://www.diyaudio.com/forums/multi-way/287741-wild-burro-betsy-eminence-alpha-15a-open-baffle-build-2.html#post4751489 )


I haven't had time to dive in yet, but I should at some point this summer..hopefully!
 
Last edited:
Haha, from years on mailing lists I guess I've learned to not over quote the thread.

I've got some pi zero's and have been wanting to try setting up the following:

a pi zero, usb sound card, and two monoblocks at each speaker(irs2092 for the lf drivers, tda8932 for full range driver)

a media server on the local network that is managing the streams, linux/freebsd/mac, some *nix.

It could be wireless or wired, ultimately wired for some of the uses I have in my head(garage speakers, garage has multiple pulls of cat6 thanks to me :D).

My first use case is for a pair of open baffle speakers I'm building for my dad( http://www.diyaudio.com/forums/multi-way/287741-wild-burro-betsy-eminence-alpha-15a-open-baffle-build-2.html#post4751489 )


I haven't had time to dive in yet, but I should at some point this summer..hopefully!
I was able to buy a single Pi Zero a couple of months ago. After giving it a try I was a bit disappointed. First, the computing power is much lower than the Pi 2 for example, because the Pi Zero only has a single core. This will definitely limit what you can do with it vis a vis audio, although I have not done extensive experiments in that regard. Also, because the only real I/O is via the USB OTG port you will need an OTG adapter and a powered USB hub, the latter is not inexpensive. Add those into the total cost and you are approaching or exceeding the Pi 2 or Pi 3 at $35.

I put my Pi Zero on the shelf for awhile and am now using it as an NTP server in my system, for which it is perfectly suitable with only a single ethernet connection via an OTG-to-ethernet adapter dongle.

Not saying the Pi zero can't be used, but when you look at what you really need to make it work for a DSP crossover or streaming audio client it kind of comes up short.
 
Glad you are interested. I will be releasing the "code" (it's done exclusively with bash scripts) when it's working and some testing has been done on all the components.
ohh, i miss read, i thought it was working/tested etc. arr ok thank you muchly :) for working on it. yea I’m looking forward to it because it written in bash, just my kind of language :D and the only programming one I am familiar with.
 
New Feature!

I realized that it would be nice to be able to automate the system control so that it could be called during startup or as part of another bash script.

To do this I have added an "automated mode". In automated mode the intended action is supplied on the command line as a parameter, along with a flag to indicated that automated mode should be used.

For an example of this kind of usage, you could include the system control script program in your .profile file so that when you log in it automatically takes an action.

This was pretty easy to implement, since it is similar to "one shot" mode. I only had to add some error checking of the parameters to make sure that there is no unintended behavior.
 
I have a question for some Linux savvy people out there about automating SSH login from one machine (computer A)to another (computer B) so that a script running on A can execute processes on B.

I have seen some suggestions for a program called "sshpass". Installation and use is very simple:
Code:
sshpass -p your_password ssh user@hostname
Sshpass would work great for my needs, except the login password to B will need to be kept in an unprotected text file on A.

Another option I have seen uses OpenSSH to generate a pair of keys, one of which is put into a special directory on B while the other resides on A. Info on this process can be found here for example:
SSH login without password
This is more complicated and there would be more for the user to do to set it up.

If the password contained in the unprotected text file for sshpass is just for a Raspberry Pi type computer running on and accessed only on the LAN, is that kind of security risk really of concern?

I think I could do the following: supply the program so that it will use sshpass but leave a configuration variable that the user can switch over to another SSH implementation if they so desire, e.g. one that is more secure, however they would need to set it up themselves. Does that sound like a good compromise?
 
Last edited:
I would use ssh keys, it's really easy and OpenSSH has some built in tools to help, IIRC. 'ssh-copy-id'

https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys--2

Now to be clear, I'm a Linux Systems Engineer, so maybe some large salt grains should be taken with my opinion.

:D

Actually, thanks for the advice (and Phofman, too).

I think the most versatile path forward would for me to set up a CONFIG variable that holds the command that should be used for ssh. As supplied by me, this variable would initially be empty. I will give instructions on how to set up the system for sshpass, since that is brainless. But it is just as easy for the user to create keys and use ssh that way, however, I do not want to try and support that myself but will instead mention that possibility and direct the user to go and figure it out for themself. In that way, I can allow for multiple possibilities that will satisfy a variety of needs.
 
I've been thinking a little more about how far I want to expand the capabilities of this software. I think that I can do a bit more now that I have build most of the client side scripting.

The new capabilities including controlling the bit depth and sample rate of the streamed audio, and controlling the receiving gsteramer pipeline on the client and launching user-specified "other programs". For example, in my system I use gstreamer to send (from the server) and receive (on each client) the streaming audio. I then use ecasound to implement a crossover on each client and send the audio via DACs to onboard amplifiers. So, for a loudspeaker to begin playing sound all of these processes need to be launched and when the loudspeaker should stop playing all these processes should be killed. I have already build a system for tracking the PID of the gstreamer process on the server side - the gstreamer pipeline PID is written to a file and this is used to kill the process when that is needed. I can do the same on the client side - record the PIDs for gstreamer and for any "other" programs such as ecasound that the user would like to launch and then kill those PIDs when the system is to be shut down. Having this type of control, and the ability to specify arbitrary "other programs" will significantly expand the capabilities of the control system.

As part of this I realized that I could allow the user to specify the bit depth and sample rate for the stream. Perhaps you have a system where the DACs are only capable of up to 16bit, 48kHz while on other systems you have 24/96 or 24/192 DACs. Each system can have its own capabilities specified, and multiple instances of the same system but with different capabilities could be implemented. This would allow one to, for example, compare 16/48 playback quality with 24/96 using the same system.

I am currently figuring out how I can implement these features, but it seems to be doable at this time. I will post about my progress with these features as they are put in place.
 
UPDATE:
Got diverted away from this project for awhile but have now been able to pick it up again. Still making progress. Since my last post I decided on how the user can specify all sorts of server and client specific parameters, etc. Then I changed my mind and set it up a different way. Then I decided that still another approach would be better! Now I finally seem to have something that works well and is sufficiently flexible for my needs.

I hope to do some re-testing of the server-side stuff and then move on to implementing the client side. I will post more updates and test results here.
 
update: I'm still swimming in the coding/testing cycle and it's coming together nicely.

Learning for the first time about things like the "eval" command, packing command arguments into arrays, here-document processing. Cool stuff.

I'm pushing 1k lines now... just 'a bit' more than I originally expected. Luckily I like coding, and this has been a fun bash-shell-script learning experience.
 
OK, I'm finally having some success!

But at the same time I am having some very frustrating challenges that my weak BASH Fu is not able to overcome...

So I have managed to code up script that can launch and terminate gstreamer pipelines on both a "server" computer (where the audio source is found, e.g. a player such as mpd) and any number of "client" computers (for me these are raspberry pis) that comprise one or more loudspeaker or playback systems, or audio sinks if you prefer that term. This is the main functionality that I was hoping to achieve, so that is a great success!

But, to extend this and make it more flexible and powerful I was hoping that I could have the user specify multiple "other" commands that would be run on the client computer. For me, the main one is ecasound, the program that I use to implement the crossover on the R-Pi before spitting out the audio thru DACs to amps to the speakers. This parts is giving me a lot of problems.

The user specifies a sting of ";" separated commands in a text file on the server. Things like:
cd ~/somedir; ./my_script.sh
The dirty details is that I am logging into the clients using ssh and then running these commands. If they are simple things like echo "hello world" or even echo $! it works! But when the ssh session logs out the terminal is closed and any and all commands that are still running are SIGTERMinated and stop running.

Ah, you say. I know BASH and you just need nohup. Well this isn't working for me via the ssh session. It DOES work, however. I know this because I did a check by logging in manually using a different type of ssh program. Then I ran the shell script with the nohup preceding my ecasound commands and put it in the background. Then I logged out of that session and logged back in again. It was still running. When I call the exact same thing from my script, it always is killed off. So I am a bit perplexed.

If anyone knows BASH quite well and wants to help me out, feel free to drop me a line.

In the meantime I will keep working to see if I can find another solution to the problem.
 
After 12 hours of banging my head against my keyboard, I seem to have BASHed my way to success (pun intended).

It turned out that the ssh session on/to the client only gets a subset of the full complement of environmental variables. For instance the TERM and COLUMNS variables are left unset. For simple commands like echo or pwd this was not a problem. I was, however, wanting to run my ecasound command string. It's a multi-line thing, so I put it in a shell script. Every time I attempted to run it I got the error "TERM environmental variable not set". I first tried setting the TERM variable in the ecasound shell script, and later tried setting it in the HERE-DOCUMENT that was being piped to ssh, but neither of these worked. The fix I discovered was to set all (missing) environmental variables in the line where ssh is invoked so that these are adopted by the ssh session from the beginning, like this:
Code:
ssh user@host ARG1="value1" ARG2="value2" /bin/bash << ENDSSH
  # commands to run on remote host
  # ARG1 and ARG2 are now environmental variables within the ssh session on the client
  lines of code that are run on the client go here...
ENDSSH
These included the LADSPA_PATH and the COLUMNS variable. To make it possible for the user to set these environmental variables I will try to add another line to the system configuration file that the user can populate with the necessary text. Hopefully that will work, because these can't really be known in advance and hardcoded into the script.

One side benefit from my diagnostic work on the above problem was that I discovered that it is possible to run a script that is located on the SERVER on the CLIENT using ssh. One interesting application of this would be to store the ecasound script on the server instead of on the client. This might make it easier to implement changes to the ecasound string because the server probably has a head (a monitor) while the client is often headless (no monitor or input devices). The ecasound command runs in an identical fashion regardless of where the script is located, so I will think about how I might make both of these options possible (script located on client or on the server) and what would be easier to understand for the user. Locating scripts on the server is a way to centralize everything in one place.
 
Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.