Skip to content

Commit 4fcb100

Browse files
authored
Merge pull request espruino#3875 from 8bitbuddhist/simplemusic
New app: simplemusic v0.0.1
2 parents 274d694 + f500a9c commit 4fcb100

File tree

6 files changed

+248
-0
lines changed

6 files changed

+248
-0
lines changed

apps/simplemusic/ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.01: First release!

apps/simplemusic/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Simple Music Controls
2+
3+
A small app for viewing and controlling music via Gadgetbridge on Android. This is a remix of [rigrig's Gadgetbridge Music App](https://banglejs.com/apps/?id=gbmusic&readme), only it adds on-screen buttons and doesn't run in the background.
4+
5+
Requires [Gadgetbridge](https://www.espruino.com/Gadgetbridge).
6+
7+
## Usage
8+
9+
1. Connect your Bangle.js to Gadgetbridge.
10+
2. Open a music player on your Android phone.
11+
3. Open this app on your Bangle.js.
12+
13+
## Features
14+
15+
- Shows the current song title and album
16+
- Provides on-screen buttons for changing or pausing the current track.
17+
- Supports swiping
18+
19+
## Controls
20+
21+
### Button controls
22+
23+
Press the side button to toggle playing or pausing the current track.
24+
25+
### Touch controls
26+
27+
Use the on-screen buttons to go back to the previous track, play/pause the current track, or skip to the next track.
28+
29+
Swipe up/down to increase/decrease the volume, or swip left/right to navigate to the previous/next song.
30+
31+
## Creator
32+
33+
8bitbuddhist (https://github.com/8bitbuddhist)

apps/simplemusic/app-icon.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/simplemusic/app.js

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// For info on interfacing with Gadgetbridge, see https://www.espruino.com/Gadgetbridge
2+
const Debug = false; // Set to true to show debugging into
3+
const Layout = require("Layout");
4+
const PrimaryFont = "Vector:18";
5+
6+
const buttonPadding = 10;
7+
8+
const Command = {
9+
next: "next",
10+
pause: "pause",
11+
play: "play",
12+
previous: "previous",
13+
volumeup: "volumeup",
14+
volumedown: "volumedown",
15+
};
16+
17+
const PlaybackState = {
18+
paused: "pause",
19+
playing: "play"
20+
};
21+
22+
/**
23+
* Format elapsed time in minutes and seconds.
24+
* @param {*} time Elapsed time
25+
* @returns Time string
26+
*/
27+
function formatTime(time) {
28+
let minute = 0, second = 0;
29+
if (time) {
30+
minute = Math.floor(time / 60);
31+
second = time % 60;
32+
}
33+
let minuteStr = minute.toString(), secondStr = second.toString();
34+
35+
if (minute < 10) minuteStr = `0${minute}`;
36+
if (second < 10) secondStr = `0${second}`;
37+
38+
return `${minuteStr}:${secondStr}`;
39+
}
40+
41+
/**
42+
* Global playback state tracker.
43+
* Follows the syntax {t:"musicstate", state:"play/pause",position,shuffle,repeat}
44+
*/
45+
let appState = { t: "musicstate", state: PlaybackState.paused, position: 0, shuffle: 0, repeat: 0 };
46+
47+
/**
48+
* Define the screen layout.
49+
*/
50+
let layout = new Layout({
51+
type: "v", c: [
52+
{ type: "txt", id: "title", halign: -1, fillx: 0, col: g.fg, font: PrimaryFont, label: "Track N/A" },
53+
{ type: "txt", id: "artist", halign: -1, fillx: 0, col: g.fg, font: PrimaryFont, label: "Artist N/A" },
54+
{
55+
type: "h", c: [
56+
{ type: "txt", id: "elapsed", halign: -1, fillx: 1, col: g.fg, font: PrimaryFont, label: formatTime(0) },
57+
{ type: "txt", id: "timeSplitter", halign: 0, fillx: 1, col: g.fg, font: PrimaryFont, label: " - " },
58+
{ type: "txt", id: "duration", halign: 1, fillx: 1, col: g.fg, font: PrimaryFont, label: formatTime(0) }
59+
]
60+
},
61+
{
62+
type: "h", c: [
63+
{ type: "btn", id: Command.previous, font: PrimaryFont, col: g.fg2, bgCol: g.bg2, pad: buttonPadding, label: "|<<", cb: l => sendCommand(Command.previous, true) },
64+
{ type: "btn", id: "playpause", font: PrimaryFont, col: g.fg2, bgCol: g.bg2, pad: buttonPadding, label: " > ", cb: l => sendCommand(appState.state === PlaybackState.paused ? Command.play : Command.pause, true) },
65+
{ type: "btn", id: Command.next, font: PrimaryFont, col: g.fg2, bgCol: g.bg2, pad: buttonPadding, label: ">>|", cb: l => sendCommand(Command.next, true) }
66+
]
67+
},
68+
]
69+
}, { lazy: true });
70+
71+
/// Set up the app
72+
function initialize() {
73+
// Detect whether we're using an emulator.
74+
if (typeof Bluetooth === "undefined" || typeof Bluetooth.println === "undefined") { // emulator!
75+
Bluetooth = {
76+
println: (line) => { console.log("Bluetooth:", line); },
77+
};
78+
}
79+
80+
// Set up listeners for swiping
81+
Bangle.on('swipe', function (directionLR, directionUD) {
82+
switch (directionLR) {
83+
case -1: // Left
84+
sendCommand(Command.previous, true);
85+
break;
86+
case 1: // Right
87+
sendCommand(Command.next, true);
88+
break;
89+
}
90+
91+
switch (directionUD) {
92+
case -1: // Up
93+
sendCommand(Command.volumeup, true);
94+
break;
95+
case 1: // Down
96+
sendCommand(Command.volumedown, true);
97+
break;
98+
}
99+
});
100+
101+
// Eat music events (๑ᵔ⤙ᵔ๑)
102+
Bangle.on("message", (type, message)=>{
103+
if (type.includes("music") && !message.handled) {
104+
processMusicEvent(message);
105+
message.handled = true;
106+
}
107+
});
108+
109+
// Toggle play/pause if the button is pressed
110+
setWatch(function() {
111+
sendCommand(appState.state === PlaybackState.paused ? Command.play : Command.pause, true);
112+
}, BTN, {edge: "falling", debounce: 50, repeat: true});
113+
114+
// Goad Gadgetbridge into sending us the current track info
115+
sendCommand(Command.volumeup, false);
116+
sendCommand(Command.volumedown, false);
117+
118+
// Render the screen
119+
g.clear();
120+
draw();
121+
}
122+
123+
function draw() {
124+
layout.update();
125+
layout.render();
126+
}
127+
128+
// Track how long the current song has been running.
129+
let elapsedTimer;
130+
let position = 0;
131+
function updateTime() {
132+
position++;
133+
layout.elapsed.label = formatTime(position);
134+
draw();
135+
136+
if (Debug) console.log("Tick");
137+
}
138+
139+
function clearTimer() {
140+
position = 0;
141+
if (elapsedTimer) {
142+
clearInterval(elapsedTimer);
143+
elapsedTimer = undefined;
144+
}
145+
}
146+
147+
/**
148+
* Send a command via Bluetooth back to Gadgetbridge.
149+
* @param {Command} command Which command to execute
150+
* @param {true|false} buzz Whether to vibrate the motor
151+
*/
152+
function sendCommand(command, buzz) {
153+
if (buzz) Bangle.buzz(50);
154+
Bangle.musicControl(command);
155+
}
156+
157+
function processMusicEvent(event) {
158+
if (Debug) console.log("State: " + event.state);
159+
if (Debug) console.log("Position: " + event.position);
160+
161+
position = event.position;
162+
163+
switch(event.state) {
164+
case PlaybackState.playing:
165+
if (Debug) console.log("Playing");
166+
appState.state = event.state;
167+
elapsedTimer = setInterval(updateTime, 1000);
168+
layout.playpause.label = " || ";
169+
break;
170+
case PlaybackState.paused:
171+
if (Debug) console.log("Paused");
172+
appState.state = event.state;
173+
clearTimer();
174+
layout.playpause.label = " > ";
175+
break;
176+
case PlaybackState.previous:
177+
case PlaybackState.next:
178+
// Reset position
179+
position = 0;
180+
appState.state = PlaybackState.playing;
181+
break;
182+
}
183+
184+
// Re-render track info on song change
185+
if (event.track != layout.title.label) {
186+
position = 0;
187+
layout.title.label = event ? event.track : "Track N/A";
188+
layout.artist.label = event ? event.artist : "Artist N/A";
189+
layout.duration.label = formatTime(event.dur);
190+
}
191+
192+
draw();
193+
if (Debug) layout.debug();
194+
}
195+
196+
// Start the app and set up listeners
197+
initialize();

apps/simplemusic/app.png

2.24 KB
Loading

apps/simplemusic/metadata.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{ "id": "simplemusic",
2+
"name": "Simple Music Controls",
3+
"shortName":" Simple Music",
4+
"version":"0.01",
5+
"description": "Control the music on your Gadgetbridge-enabled phone using Bangle.js. This is a remixed version of rigrig's fantastic Gadgetbridge Music Controls app.",
6+
"icon": "app.png",
7+
"type": "app",
8+
"tags": "bluetooth,gadgetbridge,music,tools",
9+
"supports" : ["BANGLEJS2"],
10+
"readme": "README.md",
11+
"allow_emulator": true,
12+
"storage": [
13+
{"name":"simplemusic.app.js","url":"app.js"},
14+
{"name":"simplemusic.img","url":"app-icon.js","evaluate":true}
15+
]
16+
}

0 commit comments

Comments
 (0)