Stagelinq protocol API availability? (Part 1)

The new version will stream track titles, but requires Twitch auth for an upcoming Twitch extension. I’m actually going to adjust the auth on the next release so only Unbox Pro users that want the Twitch history extension will need to login.

The logs should stream into the app, although this may only be on Mac at the moment. I’ll make sure Windows has working logs in the next release.

1 Like

@erikrichardlarson

How does Ableton / Link fit into your current setup?

My ideal setup is the following:

The Prime 2 standalone unit outputs BPM and transport data into my computer via the ethernet cable (or USB cable, doesn’t matter). My computer is running Bitwig with Ableton Link, which has then synced to the BPM/Transport of the Prime 2 unit.

Within Bitwig, I have an assortment of synths, drum loops, etc. that I can trigger in time to the currently playing track, which is then output via my soundcard and routed back into the Prime 2’s AUX input. (This would be the same setup for the Prime 4, except you could route into any of the input channels).

1 Like

Hey, will there ever be a solution to get the tracks displayed on stream without using any DJ software, like just the Denon 6000 itself? :slight_smile: If there is already a solution for it - sry but please show me :smiley:

It’s entirely possible and much easier for Denon DJ to do it. I guess a login authentication to Twitch like we have on the streaming services and it will work like how Serato have done it. Also sent to a weblink where we can copy and paste it into OBS.

It needs a feature request if there isn’t one already.

Why stop there? Have a GoPro plugged into the USB port and stream from the unit, with lower thirds overlay. Depending on resolution it could be possible. Maybe not in the current models but a future model would be good.

I have the 5/6000

What’s the update on using Unbox with 6000 in standalone with non-prime mixers eg pioneer or rane ?

@Ste_barrow

What happens currently when you use the latest version of unbox?

A little preview for y’all, here’s the upcoming Twitch Panel Extension that will update in real-time as you play tracks. Your last 20 tracks will be listed so your viewers can see what you’ve played if they show up late to your sets or you aren’t live.

4 Likes

Haven’t tried it yet. Just researching before doing that.

So it should work yeah? The set up is SC6000M x 2 and DJM S11.

Ah yeah I actually think you might run into the issue where the volume levels aren’t logged, but I’d download unbox and just try it to see what happens. Are you on Mac or PC?

My stream computer is PC.

Running Win 10.

Works well with Serato but wanted to do a standalone set as well to mix things up

Hi, it was a friend of mine who had an analogue mixer that was having issues. I use the prime go so haven’t been able to test non Denon mixers.

hmmm … looks interesting. how far are you with that implemention ? how does the communication with twitch works ? If you have something to test, write me :wink:

I’m pretty far along with the implementation, I’ve submitted to Twitch for review so just waiting to hear back on any changes I made need to make. Once Twitch gives the go ahead to release I’ll definitely post here and you can check it out. Just a heads up there is a small cost for this feature / any other upcoming Pro features ($9/month).

The way this works is the Unbox desktop app sends out an update to your own realtime database that you can share with your Twitch viewers via a unique key in the config of the Twitch Extension. This way as each track is played your viewers see that track. Here’s a quick demo using Serato:

out

2 Likes

I’ve been playing with the main.go source code and used a bit of xor logic to work out if a deck was playing and if that decks fader was up - this now works regardless of 1st deck played / order of decks on the prime 4 - feel free to grab the source code from github (it’s in the function checkstatesums) - I’ve compiled this on my PC, renamed it and popped it into the denon unbox folder - once i figured out what the json query’s were in the js file i set the output.txt to match. i do have to open the exe before i open unbox as mines still only using one discover loop as i’m on a prime 4 go-stagelinq/cmd/stagelinq-discover at master · djswolf/go-stagelinq (github.com)

1 Like

Awesome work @swolf I can incorporate this in the next release. Can you walk me through the xor logic here? It looks like you cast the fader position to an int, how does go handle casting floats less than 1 to an int? Also it looks like the write to the output.txt file would happen until the last deck was found playing so if two decks were up it would grab the last of the four decks right?

Really like the use of xor here to cover the play states!

morning, sorry for my convoluted logic - my brain works in whole numbers (int) and strings (text) and i just about figured out how to do those in golang :slight_smile: I put my test test code on playgounds here: https://play.golang.org/p/Y6Vw5xGedRW this is the test code I used to write the xor part

in regards to casting the position to an int - golang rounds down accordingly - so for < 1 it rounds down to 0 and for > 1 it rounds down to 1. Round float to integer value · YourBasic Go " Convert to an int type

Note that when converting a floating-point number to an int type, the fraction is discarded (truncation towards zero)."

if stateplay1 == 1 { //deck1playing - taken from the play state of the button

    if ch1 == 1 { //channel 1 up 100% - fader is up?

        if channel1up == true {

            //if the only channel up to 100% - output to file - uses checksum to check

            fmt.Println("Deck 1 Now Playing: " + artistName1 + " - " + songName1 + " BPM: " + strconv.FormatFloat(BPM1, 'f', 2, 64))

            writeFile1(artistName1 + "-|-" + songName1 + "\n")

            nowplaying1()

        }

        if deck1playingon {

            //if the only deck playing output to file - uses checksum to check (e.g, you faded in another track it stops but you forgot to move the fader down on that deck - file will still get written as now the current deck is the only one playing)

            fmt.Println("Deck 1 Now Playing: " + artistName1 + " - " + songName1 + " BPM: " + strconv.FormatFloat(BPM1, 'f', 2, 64))

            writeFile1(artistName1 + "-|-" + songName1 + "\n")

            nowplaying1()

        }

    }

    fmt.Println("Deck 1 Coming Up: " + artistName1 + " - " + songName1 + " BPM: " + strconv.FormatFloat(BPM1, 'f', 2, 64))

} else {

    cleartextfiles1()

    fmt.Println("Deck 1 Now Playing: ")

}

this is for deck 1 - the other decks are in separate if conditions rather than nested using the same logic

the only bug I’ve found is that the output.txt gets updated a lot (admittedly with the same values) so the index.js doesn’t like that if you move the same fader a lot in a very short period)

probably due to fs.watchFile on line 333

also if it doesn’t complain it does overwrite the history with the same track for previous playing as it’s done a check of the output file and then updated the history text file.

not sure if it can watch and then check the contents into a buffer and see if they’ve changed? if they’re the same don’t do anything till changed - I spent hours pouring over documentation and got a bit crosseyed - not sure if that’s easy to do in golang and only output if the buffer changes from empty and then changes from it’s previous value? (or javascript).

also cos i’m “lazy” some of my songs aren’t tagged correctly so any with just a file name that gets output to just track name doesn’t get picked up by fs.readFileSync(path.join(__dirname, denonOutputPath)).toString().split("-|-"); as there’s no trackdata [0] to load into the array (which means the now playing doesn’t update)

Hope this helps and makes sense :slight_smile:

3 Likes

Haha no this is great!

Gotcha so maybe one thing we could do to improve this is to grab the volume floats for each deck then take the deck with the max volume and set that deck ch variable to 1. This would help in the case where someone doesn’t bring their faders up to / past 1.

Yeah we could do a check like below on the output file to see if we need to make the write or not, this should prevent the watchFile issues we’re seeing, something like this could work:

func writeFile(text string) {

	b, err := ioutil.ReadFile("./output.txt") 
if err != nil {
    fmt.Print(err)
}

currentTrack := string(b) 
	
	newTrack := currentTrack != text

	if (newTrack) {

		file, err := os.OpenFile(`./output.txt`, os.O_WRONLY|os.O_CREATE, 0666)
		file.Truncate(0)

		if err != nil {
			log.Printf("File does not exists or cannot be created")
			os.Exit(1)
		}

		defer file.Close()

		w := bufio.NewWriter(file)
		fmt.Fprintf(w, "%v", text)

		w.Flush()

	} else {
		log.Printf("No write needed")
	}
}

Here’s a sorting function that we could use:

func main() {
    
    m := make(map[int]float32)

    m[1] = .95
    m[2] = .2

   // Turning the map into this structure
    type deck struct {
        Key   int
        Value float32
    }

    var decks []deck
    for deckNum, volume := range m {
        decks = append(decks, deck{deckNum, volume})
    }

    sort.Slice(decks, func(i, j int) bool {
        return decks[i].Value > decks[j].Value
    })

    fmt.Printf("Set deck %d to 1\n", decks[0].Key)
    
}

The latest version of Unbox will take care of the empty artist field, just confirmed this, I’ll be sure it’s in the upcoming release. It’s basically handled by this bit of code here:

if ((trackData[0] === '\n') || (trackData[0] == undefined)) {
                trackData[0] = '';
            }

if ((trackData[1] === '\n') || (trackData[1] == undefined)) {
                    trackData[1] = '';
            }

@erikrichardlarson where is it you are decoding the Stagelinq protocol signal? I want to start looking into decoding BPM/Timecode information for use with Ableton Link, but I thought this was written in Go? Looking at the Unbox repository I see only .js files

EDIT: Ah apologies, I didn’t realize I wasn’t at the top of the thread. Read through everything now. In terms of just getting to a place where I’m reading out the data from my Prime 2 should I jump in with the Go library or adapt Unbox?

1 Like

Oh yeah I would just use the go library for this, if I integrated directly with Unbox I’d need to implement a websocket to make sure the BPM changes are reported in near real-time. You could try using this example actually to integrate link with the go library via a websocket: node-abletonlink-example/index.js at master · 2bbb/node-abletonlink-example · GitHub