From 5db8249ac998a7518e6af9f8d040fb8464ee09f7 Mon Sep 17 00:00:00 2001 From: Tom Wallroth Date: Sat, 8 Jul 2023 11:58:21 +0200 Subject: [PATCH] first release of guitar-songs app --- apps/guitarsongs/ChangeLog | 1 + apps/guitarsongs/README.md | 22 +++ apps/guitarsongs/app-icon.js | 1 + apps/guitarsongs/app.js | 157 ++++++++++++++++++++ apps/guitarsongs/app.png | Bin 0 -> 3468 bytes apps/guitarsongs/manage_songs.html | 223 +++++++++++++++++++++++++++++ apps/guitarsongs/metadata.json | 17 +++ apps/guitarsongs/screenshot.png | Bin 0 -> 2285 bytes 8 files changed, 421 insertions(+) create mode 100644 apps/guitarsongs/ChangeLog create mode 100644 apps/guitarsongs/README.md create mode 100644 apps/guitarsongs/app-icon.js create mode 100644 apps/guitarsongs/app.js create mode 100644 apps/guitarsongs/app.png create mode 100644 apps/guitarsongs/manage_songs.html create mode 100644 apps/guitarsongs/metadata.json create mode 100644 apps/guitarsongs/screenshot.png diff --git a/apps/guitarsongs/ChangeLog b/apps/guitarsongs/ChangeLog new file mode 100644 index 000000000..5453557bc --- /dev/null +++ b/apps/guitarsongs/ChangeLog @@ -0,0 +1 @@ +0.01: First Release diff --git a/apps/guitarsongs/README.md b/apps/guitarsongs/README.md new file mode 100644 index 000000000..12af8a9f4 --- /dev/null +++ b/apps/guitarsongs/README.md @@ -0,0 +1,22 @@ +# Guitar Songs + +Upload lyrics and chords to your BangleJS2. Play songs at the camp fire. + +![screenshot](screenshot.png) + +## Usage + +Install the app. Use the App Loader to add songs to the watch. + +You can scroll through the chords by dragging your finger left and right. + +You can scroll the lyrics by dragging it up and down. + +## Attribution + +[Fire icon created by Freepik - Flaticon](https://www.flaticon.com/free-icons/fire) + +## Credits + +Created by: devsnd +Inspired by: NovaDawn999 diff --git a/apps/guitarsongs/app-icon.js b/apps/guitarsongs/app-icon.js new file mode 100644 index 000000000..554372bb0 --- /dev/null +++ b/apps/guitarsongs/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwg967oABCaHQAYUNDCISBC4wfDC5gQCC4YYPC5BJOC65MFJCQXIGCIXGGAY0MC5K2EC54TCMhoWGC5YHEC5RBIIxREKC4gWHC5QLDFwXu9oXSCAXuDAoXILIYDD7wYBeJhxHC4QwEC6QwEaB4WCGAgXYDIxGKVQwXJLAQXESJYXGCYPjmYXPFYPtFwQXTAAczmc+C6fj+YXNa4QXEn//C43QC5kz/4XBMAPtVIQXM8YWBC4bBDC4xgCC4RFBC5AWGC4guDC55IBC4IuDC4xGHC5peJPAfdCQIAEVAdACglEABVDDAM97vUqgGBkUAggXLGAQXBotUCwM0oAWLC4cz6tUokyGANCIxwABooGBmkzmQuMoQXDDwQXBmgXMI4gGCmRFBoR3PFIYECVYJgOAwtEbQ4AoA")) diff --git a/apps/guitarsongs/app.js b/apps/guitarsongs/app.js new file mode 100644 index 000000000..9a3004374 --- /dev/null +++ b/apps/guitarsongs/app.js @@ -0,0 +1,157 @@ +const chords = { + // name: [name, ...finger_placement, fret], + c: ["C", "0X", "33", "22", "x", "11", "x", 0], + d: ["D", "0X", "0X", "x", "21", "33", "22", 0], + dm: ["Dm", "0x", "0x", "x", "22", "33", "11", 0], + e: ["E", "x", "22", "23", "11", "x", "x", 0], + em: ["Em", "x", "22", "23", "x", "x", "x", 0], + em7: ["Em7", "x", "11", "x", "x", "x", "x", 0], + f: ["F", "0x", "0x", "33", "22", "11", "11", 0], + g: ["G", "32", "21", "x", "x", "x", "33", 0], + am: ["Am", "0X", "x", "21", "22", "23", "x", 0], + a: ["A", "0x", "x", "23", "22", "11", "x", 0], + b7: ["B7", "0x", "22", "11", "23", "x", "24", 0], + cadd9: ["Cadd9", "0x", "32", "21", "x", "33", "34", 0], + dadd11: ["Dadd11", "0x", "33", "22", "x", "11", "x", 3], + csus2: ["Csus2", "0x", "33", "x", "x", "11", "0x", 0], + gadd9: ["Gadd9", "32", "0x", "x", "21", "x", "33", 0], + aadd9: ["Aadd9", "11", "33", "34", "22", "x", "x", 5], + fsharp7add11: ["F#7add11", "21", "43", "44", "32", "x", "x", 0], + d9: ["D9", "0x", "22", "11", "23", "23", "0x", 4], + g7: ["G7", "33", "22", "x", "x", "34", "11", 0], + bflatd: ["Bb/D", "0x", "33", "11", "11", "11", "0x", 3], + e7sharp9: ["E7#9", "0x", "22", "11", "23", "34", "0x", 6], + a11: ["A11", "33", "0x", "34", "22", "11", "0x", 0], + a9: ["A9", "32", "0x", "33", "21", "34", "0x", 3], +} + +const chordCache = {}; +function drawChordCached(chord, x, y, options) { + let image; + if (chordCache[chord[0]]) { + image = chordCache[chord[0]] + } else { + arrbuff = Graphics.createArrayBuffer(60,65,1,{msb:true}); + drawChord(arrbuff, chord, 0, 0, options); + image = {width: arrbuff.getWidth(), height: arrbuff.getHeight(), bpp:arrbuff.getBPP(), buffer: arrbuff.buffer, transparent:0} + chordCache[chord[0]] = image; + } + g.drawImage(image, x, y); +} + + +function drawChord(buffer, chord, x, y, options) { + const stringWidths = options.stringWidths; + const fretHeight = options.fretHeight; + const circleSize = options.circleSize; + const drawFinger = options.drawFinger; + const drawCircleRim = options.drawCircleRim; + + const name = chord[0]; + chord = chord.slice(1); + x += 2; + buffer.setColor(0x1).setFontAlign(-1, -1).drawString(name, x, y); + y += 15; + for (let i = 0; i < 6; i++) { + buffer.drawLine(x + i * stringWidths, y, x + i * stringWidths, y + fretHeight*4); + } + for (let i = 0; i < 5; i++) { + buffer.fillRect(x - 1, y + i * fretHeight - 1, x + stringWidths * 5 + 1, y + i * fretHeight + 1); + } + + for (let i = 0; i < 6; i++) { + const xPos = x + i * stringWidths; + let yPos = y + fretHeight * parseInt(chord[i][0]) - fretHeight/2 + + if (chord[i] === "x") { + buffer.setColor(0x1).drawCircle(xPos, y - 5, 2); + continue; + } + if (chord[i] === "0x") { + buffer.setFontAlign(0, 0); + buffer.setColor(0x1).drawString('x', xPos, y - 5); + continue; + } + buffer.setFontAlign(0, 0); + if (drawFinger) { + buffer.setColor(0x0).fillCircle(xPos, yPos, circleSize); + if (drawCircleRim) { + buffer.setColor(0x1).drawCircle(xPos, yPos, circleSize); + } + buffer.setColor(0x1).drawString(chord[i][1], xPos, yPos); + } else { + buffer.setColor(0x1).fillCircle(xPos, yPos, circleSize); + buffer.setFontAlign(0, -1) + buffer.setColor(0x1).drawString(chord[i][1], xPos, y + fretHeight*4 + 2); + } + } + if (chord[6] !== 0) { + buffer.setFontAlign(-1, -1); + buffer.drawString(chord[6] + 'fr', x + 5 * stringWidths + 2, y); + } +} + +const chordOptions = { + stringWidths: 8, + fretHeight: 10, + circleSize: 2, + drawFinger: false, + drawCircleRim: false, +} + +function drawApp(lyricsLines, chordsDraw, scrollY, chordScrollX) { + const R = Bangle.appRect; + + g.setFont('6x8'); + if (scrollY < 60) { + for (let i=0; i R.y2) break; + g.setFontAlign(-1, -1).drawString(lyricsLines[i], R.x, y); + } +} + +let currentScrollY = 0; +let chordScrollX = 0; +let currentChordScroll = 0; +let lyricsHeight = 0; + +function main(song) { + const lyrics = song.lyrics; + const foundChords = song.chords; + const lyricsLines = lyrics.split('\n'); + const chordsDraw = Object.values(chords).filter(c=>foundChords.includes(c[0])); + + g.clear(); + drawApp(lyricsLines, chordsDraw, currentScrollY, chordScrollX); + lyricsHeight = g.stringMetrics(lyrics).height; + Bangle.on('drag', (event) => { + currentScrollY = Math.min(0, currentScrollY + event.dy); + chordScrollX = Math.max(Math.min(0, chordScrollX + event.dx), -(chordsDraw.length - 3)*60); + g.clear(); + drawApp(lyricsLines, chordsDraw, currentScrollY, chordScrollX); + }) +} + +function mainMenu () { + const songs = ( + require("Storage").readJSON("guitar_songs.json", true) || + [{'name': 'No songs', 'lyrics': 'Em\nPlease upload a song\nAm\nusing the Bangle App Loader', 'chords': ['Am', 'Em']}] + ); + const menu = { + "": {"title": "Guitar Songs"}, + }; + for (let i=0; iTA+aa!>*QK%<348(*pTzeNtY>gJ&1 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 + + + + + + + +
+

List of Songs

+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
NameLyricschordssize
{{song.name}}{{song.lyrics.slice(0, 30)}}{{song.chords.join(' ')}}{{song.lyrics.length + song.name.length}}
+ +
+
+ +
+
+
+ +
+
+ Size on Bangle Flash: {{watchSongsSize}} Bytes +
+ New size: {{localSongsSize}} Bytes +
+
+ +
+
+ +
+ + + + diff --git a/apps/guitarsongs/metadata.json b/apps/guitarsongs/metadata.json new file mode 100644 index 000000000..d0e34fd3a --- /dev/null +++ b/apps/guitarsongs/metadata.json @@ -0,0 +1,17 @@ +{ "id": "guitarsongs", + "name": "Guitar Songs", + "shortName":"Guitar Songs", + "version":"0.01", + "description": "Songs lyrics and guitar chords", + "icon": "app.png", + "screenshots": [{"url": "screenshot.png"}], + "tags": "guitar, song, lyrics, chords", + "supports" : ["BANGLEJS2"], + "interface": "manage_songs.html", + "readme": "README.md", + "storage": [ + {"name":"guitarsongs.app.js","url":"app.js"}, + {"name":"guitarsongs.img","url":"app-icon.js","evaluate":true} + ], + "data": [{"name": "guitar_songs.json"}] +} diff --git a/apps/guitarsongs/screenshot.png b/apps/guitarsongs/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d7fe2ea58faf7400fe1e0c471a499192756233 GIT binary patch literal 2285 zcmeHJdoMYRS~j~XM*F+>%i5yE=3l2$t;5(y@4m1#*v zZA(2G9h8bBwj^OA)3nuUF^p%>km%B=h_EIihty`~%pdc|{EK91Zu`Z7KN})fFrQ#ZA7w?tgFu`?`}YO_$qGV50S2Q2 zDa9ZVP=N?O6-7!=)y~~*aQRD!K(_NBN0{5|<7IRmNGzVH4aj;pb}*h(e14p&-_k3X zZ-*oji46H^b^pxXH8rX_ppng3Yzrve%rK#@41BTJL|omk)~Rniju!8R|q=opysarwoe7V*^021lR+*ynypS_J~j z{3kiXB0p}euYV91NM{3uUhmNFoSB;gvY2%P<-dJDCNC{B!_~wj-}AlsLLL} zNe?g@Ja`F?-NFa{_B}A{a#|HI^^)iW`QD$9(w@{V=h*sZe)ap@!Lm_lKs3zVFcJ|c!l%5@ZWLR& zZ4omm%#Hw$;AsR>^3O5|-VxU_he!XlE`S;y%#%T_u$Oob#0}b`> zy19ciw39>PH~74JBhxKZ+?rziI!b(ux!n zc&mu#_c6DG&}qcS8j7bX*%IG;BE>6VYUzpzp73%Js5N@F3~Xla&8|nJ`BQ_^BD$z8_rKI%ubMC$_cH+26=o^$HH6t!qrIe1PMbZ!3emT^=jFz?{^M zi_y1jz@}gf4bD8C+Q@2VR!_x?^j?K7nTsp4Ni1cegIk4}mi10`h_hUGu83#7ipooP zKG~wIY2OfI33_&srJ{;$Y@fPyQktvc#Y&M#VX{CByZU70Tz_W$75br{Asluhx|heV zU{aK=ucs(A(mlf>RV{lo#!2D0;u=i8%Y-*f01Itw3XoL{q-_N z+a(g`7$IkHz_wLo8QwJF`yR{lyQuN>jL-i1Pp8|a;FtED{4{j_OQ1Bw)?~XxEF!$B zlG@q@g)tFzx`TA+re|R%ng!jwpHsfm>;X~z*X%m3VcP#@|mH;oQ5vM!e_eG=bvNWw0lZIDk|IZ)uFKq7!LPX!t3-g*sW_ky$T^6Cb6>U;udXGKY6m{ zlavljx*6;M|6L1AZO5qTVTTfNeaS@XLdAQW)()l-RMJZ(V`?WhzKbr73}H13@v>n~ zbD7F3FG8QxA&f%S?nUlPfqWqKfxd$VVr|cX;TC~%pl|*U^oJ7v-xh3|`$U*G5~}Yf R8cr4v;>+-0P7wC$e*uDaBkBME literal 0 HcmV?d00001