Author Topic: Remote controlled MusicBee  (Read 17341 times)

Elberet

  • Full Member
  • ***
  • Posts: 167
As I mentioned in another thread, I'm using EventGhost, a fantastic little application to react to all kinds of events - most importantly the input events generated by an infrared receiver - with macros ranging in complexity from selecting a predefined action from a plugin, up to scripting something in Python, using Python's Win32 API bindings.

MusicBee has given me a bit trouble in this regard. Focus management within musicbee seems to be a bit off at times (for example, input focus is "lost" when the alt key is pressed twice. try it: select a track, use up/down to select another; press alt to active the menu, press alt again to deactivate the menu; up/down no longer do anything, input focus was not returned), some elements aren't reachable via keyboard (e.g. the Now Playing list), and tabbing through elements isn't terribly safe in either case, since the UI - and thus tab order - can change easily. Now, my primary concern here is not using MusicBee with a keyboard; I'm perfectly happy with my mouse plus a few select hotkeys, but when controlling MB with a remote - or rather, feeding it simulated keystrokes -, the mouse centric UI means I can't even tell MusicBee to 1) clear Now Playing, 2) open the Auto-DJ and 3) start playing.

Hence, my wish is a facility that lets me control MusicBee's playback functions and a few select other things programmatically. This could be a TCP socket (ZoomPlayer does this, for instance), plain old DDE, some form of RPC or RMI... you name it (but K.I.S., please :P). Through this interface, MusicBee may be given simple commands: stop, pause, play, play/pause, stop after current, restart current track, next, previous, skip, rewind,exit. Additionally, commands necessary to change what and how will be played: clear now playing, queue all listed tracks [last] (or start the Auto-DJ if it is shown), show Nth playlist, show library, set repeat on/off, set shuffle on/off, set shuffle settings. Finally, limited information may be queried through the interface: list available playlists.

This would make it relatively simple to make MusicBee start playing the Auto-DJ now with a single keystroke or button on a remote. Smart devices (WLAN-enabled phones) could even list the available playlists and let the user choose what music he wants to listen to (assuming he has set up appropriate playlists beforehand).

Steven

  • Administrator
  • Hero Member
  • *****
  • Posts: 34982
the mouse centric UI means I can't even tell MusicBee to 1) clear Now Playing, 2) open the Auto-DJ and 3) start playing.
I havent digested your entire message but just so you are aware there are assignable hotkeys for all of these

Elberet

  • Full Member
  • ***
  • Posts: 167
True enough, but sending simulated keystrokes isn't exactly reliable and, for instance, doesn't work with globally assigned hotkeys. For Start/Stop/Next/Prev, that's not too big of a problem since these exist twice ("Multimedia: Stop" and "Playback: Stop", for whatever reason), but if I wanted to bind "Start Auto-DJ" to a global hotkey, I'm out of luck.

By the way, while going over the available hotkeys (dammit, there really is a Start Auto-DJ hotkey! I never saw that one before now...), I found some weirdness going on. Clicking the assigned hotkeys list immediately moves the input focus to the "actions available" list; clicking the latter moves input focus to the hotkey textbox. Neither list can be scrolled with the mousewheel unless it has the focus.

Anyway, back to the issue at hand. I mentioned ZoomPlayer as an example. ZP has a very extensive communications API for other programs to find out what it's doing or to control it. See here for Inmatrix's ZP API example downloads or here for an excerpt from the API which documents the commands one may send to or receive from ZoomPlayer. Most parts won't apply to MusicBee, but you get the idea.
So, what I'd like to be able to do is something like this (in Perl syntax):
Code
socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp')); #get a TCP socket
connect(SOCK, 'localhost:12346');                          #connect to MusicBee
print SOCK "Stop\n";              #stop playback, if any
print SOCK "ClearNowPlaying\n";   #clear Now Playing list
print SOCK "AutoDJActive 1\n";    #enable Auto-DJ with current settings
print SOCK "Play\n";              #start playback
close(SOCK);
Depending on which commands the MB command API offers, many other applications are possible which certainly can not be reduced to simulating hotkeys:
Code
print SOCK "ListNowPlaying 4\n";    #dump playlist, starting with the currently playing song, in
                                    #shuffled order (e.g. the second song will be played next)
while($line = <SOCK>) { magically_parse_response($line); }
$i = divine_user_selection();
print SOCK "PlayNowPlaying $i\n";   #play the $i'th song in the playlist
The idea is that this could run on a mobile device (an iPhone App to control MusicBee, perhaps?) or another computer in my LAN, or even on the same computer in response to infrared-remote events...

By the way, I believe you metioned you were going to design a plug-in API for MusicBee...?

Steven

  • Administrator
  • Hero Member
  • *****
  • Posts: 34982
i dont have any previous experience programming with sockets but it looks straight-forward so i think i can do that.
I do anticpate having an api for plugins but thats been in more plans for over a year now! still too many things to be done for core functionality so its still some time off

Steven

  • Administrator
  • Hero Member
  • *****
  • Posts: 34982
i've done this now - just enable in the main preferences panel and choose a port.
http://www.mediafire.com/?oyke1zw3mzg

its working for me in my test environment so hopefully it will also work for you. These are the commands implemented so far:
Play
QueueNext
QueueLast
Stop
StopAfterCurrent
PlayLibraryShuffled
PlayPause
PlayNextTrack
PlayPreviousTrack
StartAutoDj
ClearNowPlaying
VolumeDown
VolumeUp

Elberet

  • Full Member
  • ***
  • Posts: 167
Wow :o
I never expected this to happen so fast, great job!
Now to write an EventGhots plugin... (yay, I get to learn more Python ;D)

QueueNext and QueueLast, do they expect a parameter? If so, a filename? Which encoding, UTF8? Should certain characters be escaped?
Also, now that this can of worms is open, there are tons of other useful commands to implement... :) Off the top of my hat: EnumPlaylists and SetAutoDjSource (L <library>|P <playlist>|F <folderlist>)...

Steven

  • Administrator
  • Hero Member
  • *****
  • Posts: 34982
the Play, QueueNext and QueueLast commands should take a filename as a parameter (surrounded by quotes if you want to pass multiple filenames) but its not processing it and currently only plays the selected song in MB. Everything should be done in UTF8.
I'll add that in but you can still test with the other commands.
Last Edit: June 27, 2010, 01:57:16 PM by Steven

Elberet

  • Full Member
  • ***
  • Posts: 167
Ok, first test results aren't looking stellar. I think you're not picking up data sent to MB until the socket is closed:
I'm opening the connection and send "50:6c:61:79:50:61:75:73:65:0a" ("PlayPause\n") -- nothing happens. Three seconds later, I repeat the command, and again, nothing happens. Another three seconds later, I close the connection and MusicBee immediately stops playing, if it was playing before; if it was stopped or paused before, it produces a short burst of sound, as would be expected.

Also, it would be nice to get a result message back for each separate command sent. A simple "OK" should do, or an "ERR" if the command was invalid. Better: a message that tells about MB's state after performing the command, such as playing, paused, stopped, ..., conveniently encoded as some defined number. ;)

Elberet

  • Full Member
  • ***
  • Posts: 167
Oh, I forgot: for security reasons, you should not listen on public IP addresses by default. Let the user specify a list of IPs to listen on and initialize it to 127.0.0.1...

butty

  • Sr. Member
  • ****
  • Posts: 435
Umm... I'm afraid that this feature is not so easy and should be postponed because a lot of other features should be impletemted.
I'm sorry Elberet.

FYI, I've also been remote controlling MB using IrDA controller device, driver app, and hot-keys. It's very simple.

Steven

  • Administrator
  • Hero Member
  • *****
  • Posts: 34982
elberet, I am not familar with Python scripting but i know with my own testing using a c# client i needed to flush the data in the data stream on the client side before it was actually received by MB. I didnt need to close the connection but closing the connection would also flush pending data.
Could you send the python script you are using including the command to open the connection and the command to send data.

And is the a flush() function on the socket object in python?

Steven

  • Administrator
  • Hero Member
  • *****
  • Posts: 34982
actually i see i am looping until the connection is closed so no need to look at this - i will change the code

Elberet

  • Full Member
  • ***
  • Posts: 167
Sheesh, have some faith in my networking code-fu, will you? ;D

Anyway, the little test script is quite close to what I posted earlier:
Code
#!/usr/bin/perl

use strict;
use warnings;

use IO::Socket;

my $sock = new IO::Socket::INET(
    Proto => 'tcp',
    PeerAddr => '192.168.99.2',
    PeerPort => '4072',
) or die $!;

{
    local $| = 1;
    print $sock "PlayPause\n";
    sleep 3;
    print $sock "PlayPause\n";
    sleep 3;
}

close($sock);

I'll start working on the EG plugin (in Python) a little later this week; gotta finish a Java project first that's been due a couple weeks ago...

(P.S.: This should run on Windows with either cygwin's Perl or ActivePerl.)
Last Edit: June 28, 2010, 10:36:12 PM by Elberet

Steven

  • Administrator
  • Hero Member
  • *****
  • Posts: 34982
i've changed the looping logic so lets see if this works better
http://www.mediafire.com/?wuhrjtxonbz

i havent yet put in support for setting specific ip addresses but will do

Elberet

  • Full Member
  • ***
  • Posts: 167
Uhm... now MB closes the connection immediately after parsing the first line.
Also, Stop behaves exactly like PlayPause.