How To Mix Two Audio Streams Dynamically Using Free Software

One of my partners is a huge Supernatural fan, so I was pretty stoked when I discovered ambient-mixer.com had a whole section of soundscapes where people recreated the sound of driving in the Impala with the Winchesters, that was a cool discovery.

But that wasn’t quite enough.

So I created a personal internet radio station playing the Supernatural soundtrack with the driving sound mixed in. Not only is it music reminding her of her favorite (and "comfort") show, but the added ambiance increases that feeling while providing a little bit of extra masking of outside noise while she’s wearing her headphones.

The instructions below are how I did this using free software on a Linux box; you should also be able to do the same on OSX using Homebrew, and Windows using WSL.

Sourcing The Media

First, I had to find a good listing of the music used in the show, and what-song.com delivered. I double-checked it against this Spotify playlist as well.

Luckily, there was already a large overlap between my personal music library and the music used in the show, and plugging the few holes wasn’t a problem. I also omitted a few tracks either because she specifically didn’t care for them or (in the case of The Beatles’ "Revolution") because the beginning of the song was entirely too jarring. I additionally found a bunch of sound clips of the Winchesters at 101soundboards.com to mix in and use.

All in all, I ended up with somewhere around twelve hours (!) of music and sound clips in a giant playlist.

I also customized the ambient sounds from ambient-mixer to match what I wanted, and got an hour-long mixed MP3 of the track. There’s also versions on YouTube, but I wasn’t quite as happy with those.

Glue And Duct Tape

The important things for me were that the background was dynamically mixed, the playlist could be random, lightweight, and that it could be accessed by pretty much any device with at least a browser. I had recently installed a small server at her place, but any solution still had to be relatively lightweight.

The solution: Two parallel installations of the Music Player Daemon (due to its built in HTTP streams), Icecast, and FFMPEG. Figuring it out was hard; but implementing was surprisingly easy. Instructions are for Debian-like systems (Ubuntu, Debian, MXLinux) with SystemD running.

  1. Install FFMPEG: sudo apt install ffmpeg

  2. Install the Music Player Daemon for your operating system (sudo apt install mpd). If you aren’t using it for anything else, disable autostarting at the system level (sudo systemctl stop mpd.service; systemctl disable mpd.service). Any program to control MPD will work fine here; Cantata and Sonata work well enough for our purposes.

  3. Install Icecast2 and likewise disable autostarting: (sudo apt install icecast2; sudo systemctl stop icecast2; sudo systemctl disable icecast2.service)

  4. Create a separate directories in the user home folder for MPD to write to. For example:

    mkdir -p $HOME/mpd1/playlists
    touch $HOME/mpd1/tag_cache
    sudo cp /etc/mpd.conf $HOME/mpd1/mpd.conf
    sudo chown $USER:$USER $HOME/mpd1/mpd.conf

    Substitute your username for $USER. Then use your favorite text editor to edit mpd.conf, changing music_directory to whatever is appropriate, and then setting these:

    db_file "$HOME/mpd1/tag_cache"
    pid_file "$HOME/mpd1/pidfile"
    sticker_file "$HOME/mpd1/sticker.sql"
    state_file "$HOME/mpd1/state"
    bind_to_address "localhost"
    port "6500"
    
    # For audio output, enable the built-in HTTP stream:
    audio_output {
    type        "httpd"
    encoder     "vorbis"        # optional, vorbis or lame
    port        "7998"
    bind_to_address "0.0.0.0"       # optional, IPv4 or IPv6
    bitrate     "128"           # do not define if quality is defined
    format      "44100:16:1"
    max_clients "0"         # optional 0=no limit
    }
  5. Repeat for your second instance of MPD. Substitute mpd2 for mpd1 throughout the instructions for the prior step, and change the first port to 6501 and the output port to 7999.

  6. Create a separate directory for Icecast2 and its configuration:

    mkdir -p $HOME/icecast2
    sudo cp /etc/icecast2/icecast.xml $HOME/icecast2/icecast.xml
    sudo chown $USER:$USER $HOME/icecast2/icecast.xml

    Use your favorite text editor to set the source and relay passwords for icecast. That’s it.

  7. Create this bash script as combine_sources.sh, changing ICECAST_PASSWORD to the password you used in the previous step:

    #!/bin/sh
    echo "initial sleep of 10 seconds"
    sleep 10
    echo "finished sleep"
    while true; do
           ffmpeg \
                   -i http://localhost:7998 \
                   -i http://localhost:7999 \
                   -filter_complex \
                           "[0:a]volume=1[music]; \
                           [1:a]volume=0.4[ambience]; \
                           [music][ambience]amix=inputs=2:duration=shortest" \
                   -c:a mp3 -b:a 64k -f mp3 -threads 4 -b 32k \
                   -ice_name "Our Mixed Stream" \
                   -ice_genre Rock \
                   -ice_description "A description because reasons" \
                   -ice_public 0 \
                   icecast://source:ICECAST_PASSWORD@localhost:8000/stream
    done 

    Set the script as executable (sudo chmod +x ./combine_sources.sh)

  8. Start the programs:

    mpd $HOME/mpd1/mpd.conf &
    mpd $HOME/mpd2/mpd.conf &
    icecast2 -c $HOME/icecast2/icecast.conf & 

    I personally load each of them up in a pane in tmux; you could also use SystemD services. Whatever works here for you.

  9. Use your program (Sonata or Cantata) to connect to each of your MPD instances (remember to use the non-standard ports!). MPD1 is our "music" one; load your playlist, set it to play random tracks and on repeat and start playback. MPD2 is our "ambiance" stream; load your ambient track and set it to repeat and start playback.

  10. In a separate pane, launch combine_sources.sh

  11. Test by pointing a browser at locahost:8000/stream. You should hear everything mixed together!

References:

Featured Image by Peter Fischer from Pixabay