6

Short

I would like to stream multiple overlapping audio files (some sound effects that play at certain random times). So kind of a generated audio stream that will NEVER repeat exactly the same way. Some audio files are looping, some are at specific times. Probably kind of realtime stream insertion would be good I guess.

What is the best way to write such a server software? What protocols should be used for streaming that (I prefer over HTTP). I would probably want to expose an url for each configuration (tracks & timing of sound effects).

Any pointers to code/libraries? Best if in any language like java/kotlin/go/rust/ruby/python/node/...

Example

Url: https://server.org/audio?file1=loop&file2=every30s&file2_volume=0.5

Response: Audio stream (that plays on cast devices)

Stream loops the file1. At every 30s it plays file2 with 50% volume (overlayed over file1 which plays at 100%). File 1 is like 10m9s long. So the the combination never repeats really. So we can not just provide a pregenerated mp3 file.

Some background

I currently have an android application that plays different audio files at random. Some are looping, some play every x seconds. Sometimes as many as 10 at the same time.

Now I would like to add support for chromecast/chromecast audio/google home/... . I guess best would be to have a server that streams that. Every user would have his/her own stream when playing. No need for having multiple users listen to the same stream (even though it probably would be supported as well).

The server would basically read the url, get the configuration and then respond with a audio stream. The server opens one (or multiple audio files) that it then combines/overlays into a single stream. At certain times those audio files are looped. Some other audio files are opened at specific times and added/overlayed to the stream. Each audio file played is played at a different volume level (some are louder, some are quieter). The question is how to make such an audio stream and how to add the different files in in realtime.

Patrick Boos
  • 6,789
  • 3
  • 35
  • 36
  • 1
    Why not use the Web Audio API on the Chromecast itself? – Brad Mar 26 '18 at 17:16
  • @Brad that could work. Will probably go that way for now. But it would be nice to as well be able to just create a stream on the server. Gives me more flexibility. – Patrick Boos Apr 18 '18 at 10:49
  • @PatrickBoos, why not using `ffmpeg` and create the file ? https://superuser.com/questions/1123480/overlay-two-mp3-and-repeat-smallest-mp3-using-ffmpeg – Tarun Lalwani Apr 18 '18 at 11:35
  • So use a combination of previous comment and https://github.com/fluent-ffmpeg/node-fluent-ffmpeg#pipestream-options-pipe-the-output-to-a-writable-stream – Tarun Lalwani Apr 18 '18 at 12:13
  • @Tarun I can not create a file, because i want to combine multiple files (as well some playing just at random times). And that results in an endless file (because it will NEVER repeat exactly the same. even if some content repeats). – Patrick Boos Apr 18 '18 at 17:02
  • @PatrickBoos, that logic you can always control in the server and then use the `pipe` method mentioned in the `fluent-ffmpeg` to stream the data in return. This will be dynamic stream and not a saved one. And if you want to save time, you can combine the static ones together and have ready files and then mix with random ones. – Tarun Lalwani Apr 18 '18 at 17:12
  • @Tarun that does sound like it could work. But I am not 100% sure yet how to stream the data with pipe over the web. And as well how to play a sound asdf.ogg after 10seconds for example. Without having to specify that right at the beginning. Or how to loop a sound file. – Patrick Boos Apr 18 '18 at 17:41
  • I will work out an example and get back to you over the weekend – Tarun Lalwani Apr 18 '18 at 17:42

1 Answers1

1

So there are two parts to your problem

  • Mixing the audios using different options
  • Stream that mixed response from a webserver

I can help you with the later part and you need to figure out the first part yourself

Below is a sample nodejs script. Run it create a directory and run

npm init
npm install fluent-ffmpeg express

and then save the below file

server.js

var ff = require('fluent-ffmpeg');
var express = require('express')
var app = express()

app.get('/merged', (req, res) => {
    res.contentType('mp3');
    // res.header("Transfer-Encoding", "chunked")
    command = ff()
        .input("1.mp3")
        .input("2.mp3")
        .input("3.mp3")
        .input("4.mp3")
        .complexFilter(`[1]adelay=2|5[b];
        [2]adelay=10|12[c];
        [3]adelay=4|6[d];
        [0][b][c][d]amix=4`)
        .outputOptions(["-f", "flv"])
    ;

    command.on('end', () => {
        console.log('Processed finished')
        // res.end()
    })
    command.pipe(res, {end: true});
    command.on('error', function(err, stdout, stderr) {
        console.log('ffmpeg stdout: ' + stdout);
        console.log('ffmpeg stderr: ' + stderr);
    });

})

app.listen(9090)

Run it using below command

node server.js

Now in VLC open http://localhost:9090/merged

Open in VLC

Now for your requirement the below part will change

        .complexFilter(`[1]adelay=2|5[b];
        [2]adelay=10|12[c];
        [3]adelay=4|6[d];
        [0][b][c][d]amix=4`)

But I am no ffmpeg expert to guide you around that area. Perhaps that calls for another question or taking lead from lot of existing SO threads

ffmpeg - how to merge multiple audio with time offset into a video?

How to merge two audio files while retaining correct timings with ffmpeg

ffmpeg mix audio at specific time

https://superuser.com/questions/850527/combine-three-videos-between-specific-time-using-ffmpeg

Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
  • Almost, but not totally what I am searching for. 1. The stream needs to be endless. 2. The stream needs to be changable. For example. After 10 minutes decide to drop sound x, and add sound y. – Patrick Boos May 01 '18 at 06:10
  • See if this is of any help https://pedromtavares.wordpress.com/2012/12/28/streaming-audio-on-the-web-with-nodejs/ – Tarun Lalwani May 01 '18 at 06:16