getmusicbee.com

MusicBee & Add-Ons => Customizations => Plugins => Topic started by: Mayibongwe on August 31, 2024, 09:51:19 PM

Title: AB Repeat Toggle (Hotkey)
Post by: Mayibongwe on August 31, 2024, 09:51:19 PM
Download it from the add-on link here (https://getmusicbee.com/addons/plugins/517/ab-repeat-toggle-hotkey/)

NB: I'm yet to address one aspect of the plugin that'll currently prevent you from setting up the AB Repeat for the last played song from the previous MB session.

I initially planned to add this function to the other hotkey plugin I shared a couple of weeks ago, but I've decided otherwise.
Splitting them up will help with discoverability and ensure users get to download only the features they desire.

The function was requested here and I've been enjoying it myself too:
It would be great if AB repeat functionality could be added to Musicbee.

(https://i.imgur.com/si9GsiD.png)
(https://i.imgur.com/R4wwmPn.png)

The entire code for anyone interested in taking a peak.
Code
using System;
using System.IO;
using System.Timers;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MusicBeePlugin
{
    public partial class Plugin
    {
MusicBeeApiInterface mbApi;
System.Timers.Timer myTimer = new Timer();
ToolTip status = new ToolTip();

string dataFolder;
string logPath;

bool timer_Active = false;
bool pauseOn = false;

int mode = 0;
int start_time;
int end_time;
int streamHandle = -1;
double position;

        public PluginInfo Initialise(IntPtr apiInterfacePtr)
        {
            mbApi = new MusicBeeApiInterface();
            PluginInfo info = new PluginInfo();

            mbApi.Initialise(apiInterfacePtr);
            info.PluginInfoVersion = PluginInfoVersion;
            info.Name = "AB_Repeat Hotkey Toggle";
            info.Description = "Triggers a repeat state between two points in a song";
            info.Author = "Mayibongwe (2024)";
            info.TargetApplication = "";
            info.Type = PluginType.General;
            info.VersionMajor = 1;
            info.VersionMinor = 3;
            info.Revision = 0;
            info.MinInterfaceVersion = MinInterfaceVersion;
            info.MinApiRevision = MinApiRevision;
            info.ReceiveNotifications = ReceiveNotificationFlags.PlayerEvents | ReceiveNotificationFlags.TagEvents;
            info.ConfigurationPanelHeight = -1;

            dataFolder = Path.Combine(mbApi.Setting_GetPersistentStoragePath(), "mb_RepeatAB");
            if (Directory.Exists(dataFolder) == false) Directory.CreateDirectory(dataFolder);
            logPath = Path.Combine(dataFolder, "Activity.log");

            return info;
        }

public bool Configure(IntPtr panelHandle)
        {
            return false;
        }

public void SaveSettings(){}

public void Close(PluginCloseReason reason)
{
            myTimer.Dispose();
            status.Dispose();
}

public void Uninstall()
{
            if (Directory.Exists(dataFolder)) Directory.Delete(dataFolder, true);
}

public void writeToLog(string data)
{
            File.AppendAllText(logPath, data + Environment.NewLine);
}

public void ReceiveNotification(string sourceFileUrl, NotificationType type)
        {
switch(type)
{
case NotificationType.PluginStartup:
File.WriteAllText(logPath, "Plugin in effect" + Environment.NewLine);
mbApi.MB_AddMenuItem(null, "Player: Toggle AB Repeat", repeatHandler);
break;

case NotificationType.TrackChanged:
if(end_time != 0)
{
resetPoints();
writeToLog("Repeat off / New playing track");
}
break;

case NotificationType.PlayStateChanged:
if(end_time != 0)
{
PlayState status = mbApi.Player_GetPlayState();

if(timer_Active == true)
{
mode = 0;
if(status == PlayState.Paused)
{
pauseOn = true;
writeToLog("Playback paused");
}

if(status == PlayState.Stopped) writeToLog("Playback stopped");
}
else
{
if(status == PlayState.Playing && pauseOn == true)
{
mode = 2;
pauseOn = false;
myTimer.Enabled = true;
writeToLog("Playback resumed");
}
}
}
break;
}
        }

public void repeatHandler(object sender, EventArgs e)
{
if(streamHandle != -1 && mbApi.Player_GetPlayState() != PlayState.Stopped)
{
mode += 1;
switch(mode)
{
case 1:
start_time = mbApi.Player_GetPosition();
writeToLog("Start Time: " + start_time);
break;

case 2:
end_time = mbApi.Player_GetPosition();

if(end_time < start_time)
{
writeToLog("End Time Invalid: " + end_time);
resetPoints();
writeToLog("AB points captured discarded: Retry");
}
else
{
writeToLog("End Time: " + end_time);
repeatTrack();
showTooltip("on");
}
break;

case 3:
resetPoints();
writeToLog("Repeat turned off");
showTooltip("off");
break;
}
}
else
{
writeToLog("Cannot proceed without stream handle");
showTooltip("handle");
}
}

private void showTooltip(string enabled)
{
if(enabled == "on") enabled = "AB Repeat On";
else if (enabled == "off") enabled = "AB Repeat Off";
else enabled = "Missing Stream Handle";

status.Show(enabled, Application.OpenForms[0], Cursor.Position.X-42, Cursor.Position.Y-25, 1750);
}

private void resetPoints()
{
mode = 0;
start_time = 0;
end_time = 0;
}

private void repeatTrack()
{
switch(mbApi.Player_GetPlayState())
{
case PlayState.Playing:
mbApi.Player_SetPosition(start_time);

myTimer = new System.Timers.Timer();
myTimer.Interval = 1;
myTimer.Enabled = true;
myTimer.Elapsed += timer_Tick;
writeToLog("AB Repeat Commenced: While Playing");
break;

case PlayState.Paused:
pauseOn = true;
mbApi.Player_SetPosition(start_time);
BASS_ChannelPause(streamHandle);
writeToLog("AB Repeat Commenced: While Paused");
break;
}
}

private void timer_Tick(object sender, ElapsedEventArgs e)
{
if(mode == 2)
{
timer_Active = true;
position = toSeconds(BASS_ChannelGetPosition(streamHandle, 0));

if(position > end_time || position < start_time)
{
writeToLog("Loop ended: " + position);
mbApi.Player_SetPosition(start_time);
}
}
else
{
timer_Active = false;
writeToLog("Timing disabled");
myTimer.Enabled = false; // stop timing if repeat is disabled
}
}

private double toSeconds(long byteData)
{
double seconds = BASS_ChannelBytes2Seconds(streamHandle, byteData);
return seconds*1000;
}

public void InitialiseStream(int handle)
{
streamHandle = handle;
writeToLog("Stream Initialised: " + handle);
}

[System.Security.SuppressUnmanagedCodeSecurity()]
        [DllImport("bass.dll", CharSet = CharSet.Auto)]
public static extern long BASS_ChannelGetPosition(int handle, int mode);

[System.Security.SuppressUnmanagedCodeSecurity()]
        [DllImport("bass.dll", CharSet = CharSet.Auto)]
public static extern bool BASS_ChannelPause(int handle);

[System.Security.SuppressUnmanagedCodeSecurity()]
        [DllImport("bass.dll", CharSet = CharSet.Auto)]
public static extern double BASS_ChannelBytes2Seconds(int handle, long byteData);
    }
}
Title: Re: AB Repeat Toggle (Hotkey)
Post by: Mayibongwe on September 01, 2024, 12:33:20 PM
After more testing, I've released an updated version with better optimization and a few error handlers here and there.
Title: Re: AB Repeat Toggle (Hotkey)
Post by: monkey on September 01, 2024, 04:21:40 PM
very good idea
it runs very well for me (with visual markers on the wave bar it would be top ... but even like this  it's really cool...)
thanks
Title: Re: AB Repeat Toggle (Hotkey)
Post by: Mayibongwe on September 14, 2024, 02:05:51 PM
I've added a tooltip as a visual indication of when this repeat mode is turned on/off.
Title: Re: AB Repeat Toggle (Hotkey)
Post by: monkey on September 14, 2024, 02:39:35 PM
i found the possibility to make a button in the toolbar and it's really cool
but i don't find the visual indication of when this repeat mode is turned on/off ?
could you explain please ?
Title: Re: AB Repeat Toggle (Hotkey)
Post by: Mayibongwe on September 14, 2024, 02:51:22 PM
The hotkey currently has 3 cycles where:

- the 1st call/click/press sets the start time
- the 2nd call sets the end time (at this point, a tooltip showing "AB Repeat On" should flash near your mouse pointer for nearly 2 seconds)
- the 3rd call turns off the AB repeat (a tooltip showing "AB Repeat Off" should appear for the same duration near your mouse pointer)

Edit: Something like this
(https://i.imgur.com/QRGPJiS.png)
Title: Re: AB Repeat Toggle (Hotkey)
Post by: monkey on September 14, 2024, 03:06:55 PM
thanks ! Now i see it but it isn't nearby the mouse pointer (i'm using a touchpad)
it is in the left top corner
is there something to do to have it, nearby the mouse pointer ?
(https://i.imgur.com/O7TrDIw.png)
Title: Re: AB Repeat Toggle (Hotkey)
Post by: Mayibongwe on September 14, 2024, 04:42:58 PM
Now i see it but it isn't nearby the mouse pointer (i'm using a touchpad)
I've now explicitly set it to appear wherever the cursor is (despite it oddly having behaved that way on my machine already).
Redownload the plugin and see if it works well for you too now.
Title: Re: AB Repeat Toggle (Hotkey)
Post by: monkey on September 14, 2024, 04:52:47 PM
now it's ok !  thanks !
just an idea, i don't know if it's possible and if it's a better solution
would it be possible to display rather on the wavebar nearby the player cursor ?
Title: Re: AB Repeat Toggle (Hotkey)
Post by: Mayibongwe on September 14, 2024, 05:29:19 PM
would it be possible to display rather on the wavebar nearby the player cursor?
A proper solution for this would be complicated, I fear (unless I asked Steven to provide access to the control that houses the player panel).
As it stands, I only have the means to position the tooltip relative to the main MusicBee window.

If I displayed the tooltip somewhere at the bottom of the MB window (working on the assumption that that's where the player panel normally is),
it would work against those who have their player panel positioned at the top.
 
Perhaps this suggestion by hiccup would be the best way to go: https://getmusicbee.com/forum/index.php?topic=1972.msg224518#msg224518
Let me see if I can bump that request - not suggesting you hold your breath though as this is very low priority on all ends.
Title: Re: AB Repeat Toggle (Hotkey)
Post by: monkey on September 14, 2024, 05:55:18 PM
i understand that it's not so easy, and anyway it's just an idea (and perhaps not so a good idea... the decision depends on you and Steven)
i think this plugin is a good idea, and if you have the possibility to improve it in one way or other, it would be great !
 :D  :D  :D