Skip to content

Instantly share code, notes, and snippets.

@ggirou
Last active December 23, 2024 17:12
Show Gist options
  • Save ggirou/20b2ece3b1f7bad5e1408afb36122bec to your computer and use it in GitHub Desktop.
Save ggirou/20b2ece3b1f7bad5e1408afb36122bec to your computer and use it in GitHub Desktop.
Keyboard jukebox
#!/usr/bin/env python3
import colorsys
import rainbowhat as rh
from rainbowhat import display, lights, weather, rainbow, buzzer, touch
import time
import signal
from subprocess import Popen, PIPE
import asyncio
import glob
import threading
import re
class Player:
def __init__(self, path='music/'):
self.process = Popen(["echo", "Hello World!"])
self.playing = False
def findDir(path): return sorted(glob.glob(path + "**/", recursive=True))
def findMp3(path): return sorted(glob.glob(path + "*.mp3"))
self.albums = [album for album in [findMp3(d) for d in findDir(path)] if len(album) > 0]
self.currentAlbum = 0
self.currentTrack = 0
self.__display()
# print(self.albums)
# print(self.album)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
display.clear()
display.show()
self.stop()
self.process.__exit__(exc_type, exc_value, traceback)
def __display(self):
value = '%02d%02d' % (min(self.__currentAlbum + 1, 99), min(self.__currentTrack + 1, 99))
display.clear()
display.print_str(value)
display.set_decimal(1, True)
display.show()
@property
def currentAlbum(self):
return self.__currentAlbum
@currentAlbum.setter
def currentAlbum(self, value):
self.__currentAlbum = (value + len(self.albums)) % len(self.albums)
self.album = self.albums[self.__currentAlbum]
@property
def currentTrack(self):
return self.__currentTrack
@currentTrack.setter
def currentTrack(self, value):
if(value < 0):
self.currentAlbum -= 1
self.__currentTrack = (value + len(self.album)) % len(self.album)
elif(value >= len(self.album)):
self.currentAlbum += 1
self.__currentTrack = 0
else:
self.__currentTrack = value
def stop(self):
self.playing = False
# if self.process.poll() is not None:
self.process.send_signal(signal.SIGINT)
def play(self, file=None):
if(file is None):
file = self.album[self.currentTrack]
self.stop()
args = ['mpg123', '-a', 'bluealsa', file]
# args = ['sleep', '30000']
self.process = Popen(args, stdout=PIPE, stderr=PIPE)
self.playing = True
print("Playing %s" % file)
self.__display()
def waitProcessTermination(self):
_, stderr = self.process.communicate()
# print("returncode %s" % self.process.returncode)
if(self.process.returncode == 0):
self.next()
elif(self.process.returncode is not None):
error = re.sub('[^\t\r\n!-~]+', ' ', stderr.decode('utf-8')).strip()
print("Error %s: %s" % (self.process.returncode, error))
thread = threading.Thread(target=waitProcessTermination, args=[self])
thread.start()
def pause(self):
print("Pause")
self.process.send_signal(signal.SIGSTOP)
self.playing = False
def resume(self):
print("Resume")
self.process.send_signal(signal.SIGCONT)
self.playing = True
def togglePauseResume(self):
# print("%s" % self.process.poll())
if self.process.poll() is not None:
self.play()
elif self.playing:
self.pause()
else:
self.resume()
def previous(self):
print("Previous")
self.currentTrack -= 1
self.play()
def next(self):
print("Next")
self.currentTrack += 1
self.play()
def previousAlbum(self):
print("Previous album")
def nextAlbum(self):
print("Next album")
class Buttons:
def __init__(self, player):
self.player = player
touch.A.press(lambda button: self.press(button))
touch.B.press(lambda button: self.press(button))
touch.C.press(lambda button: self.press(button))
touch.A.release(lambda button: self.release(button))
touch.B.release(lambda button: self.release(button))
touch.C.release(lambda button: self.release(button))
def press(self, button):
lights.rgb(int(button == 0), int(button == 1), int(button == 2))
# print("A %s B %s C %s" % (touch.A.pressed, touch.B.pressed, touch.C.pressed))
def release(self, button):
lights.rgb(0, 0, 0)
if(button == touch.A._index):
self.player.previous()
elif(button == touch.B._index):
self.player.togglePauseResume()
elif(button == touch.C._index):
self.player.next()
with Player() as player:
Buttons(player)
signal.pause()
# import os
# os.spawnl(os.P_DETACH, 'some_long_running_command')
# from touch import Buttons as touch
# value = 0
# process = None
# print("A %s B %s C %s" % (touch.A.pressed, touch.B.pressed, touch.C.pressed))
# import RPi.GPIO as GPIO
# def my_callback_one(channel):
# print('Callback one %s' % channel)
# channel=21
# GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# GPIO.add_event_detect(channel, GPIO.BOTH)
# GPIO.add_event_callback(channel, my_callback_one)
# channel=20
# GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# GPIO.add_event_detect(channel, GPIO.BOTH)
# GPIO.add_event_callback(channel, my_callback_one)
# print("A %s\nB %s\nC %s" % (touch.A.__dict__, touch.B.__dict__, touch.C.__dict__))
# def runProcess(args):
# global process
# if process != None:
# # print("Killing " + str(process.pid))
# process.kill()
# print("Running " + str(args))
# process = Popen(args, stdout=PIPE, stderr=PIPE)
# # stdout, stderr = process.communicate()
# # returncode = process.returncode
# # if returncode != 0:
# # return "Error: " + stderr.decode('utf-8')
# # return stdout.decode('utf-8')
# async def pressAsync(button):
# # print("A %s\nB %s\nC %s" % (touch.A.__dict__, touch.B.__dict__, touch.C.__dict__))
# print("A %s B %s C %s" % (touch.A.pressed, touch.B.pressed, touch.C.pressed))
# def press(button):
# print("press %s" % button)
# lights.rgb(int(button == 0), int(button == 1), int(button == 2))
# asyncio.run(pressAsync(button))
# print("Over")
# # print("A %s\nB %s\nC %s" % (touch.A.__dict__, touch.B.__dict__, touch.C.__dict__))
# # print("lights %s %s %s" % (int(button == 0), int(button == 1), int(button == 2)))
# # print("info %s %s %s" % (touch.A, touch.A.noindex, touch.A.members))
# def release(button):
# print("release %s" % button)
# file = '/usr/share/sounds/alsa/Front_Center.wav'
# # file = "music/1 - La Reine des Neiges 2/CD1/01 - La berceuse d'Ahtohallan.flac"
# runProcess(['aplay', '-D', 'bluealsa', '-N', file])
# lights.rgb(0, 0, 0)
# async def main():
# print('hello')
# touch.A.press(press)
# touch.B.press(press)
# touch.C.press(press)
# touch.A.release(release)
# touch.B.release(release)
# touch.C.release(release)
# await asyncio.sleep(1)
# print('world')
# asyncio.run(main())
# while True:
# t = weather.temperature()
# p = weather.pressure()
# print(t, p)
# time.sleep(0.5)
# Pause the main thread so it doesn't exit
# signal.pause()
# exit()
# #######################
# rainbow.set_pixel(0, 255, 0, 0)
# rainbow.show()
# #######################
# while True:
# for pixel in range(7):
# rainbow.clear()
# rainbow.set_pixel(pixel, 255, 0, 0)
# rainbow.show()
# time.sleep(0.1)
# #######################
# rainbow.clear()
# rainbow.show()
# while True:
# for i in range(101):
# h = i / 100.0
# r, g, b = [int(c * 255) for c in colorsys.hsv_to_rgb(h, 1.0, 1.0)]
# rainbow.set_all(r, g, b)
# rainbow.show()
# time.sleep(0.02)
# #######################
# s = 100 / 7
# while True:
# for i in range(101):
# for pixel in range(7):
# h = ((i + s * pixel) % 100) / 100.0
# # rainbow.clear()
# r, g, b = [int(c * 255) for c in colorsys.hsv_to_rgb(h, 1.0, 0.1)]
# rainbow.set_pixel(pixel, r, g, b)
# rainbow.show()
# #######################
# display.print_str('AHOY')
# display.show()
# #######################
# i = 0.0
# while i < 999.9:
# display.clear()
# display.print_float(i)
# display.show()
# i += 0.01
# #######################
# display.clear()
# display.set_decimal(0, True)
# display.show()
# #######################
# @touch.A.press()
# def touch_a(channel):
# print('Button A pressed')
# lights.rgb(1, 0, 0)
# @touch.A.release()
# def release_a(channel):
# print('Button A released')
# lights.rgb(0, 0, 0)
# #######################
# value = 0
# def show_value(plus):
# global value
# value = value + plus
# display.clear()
# display.print_float(value)
# display.show()
# @touch.A.press()
# def touch_a(channel):
# lights.rgb(1, 0, 0)
# show_value(-1)
# @touch.A.release()
# def release_a(channel):
# lights.rgb(0, 0, 0)
# @touch.B.press()
# def touch_b(channel):
# lights.rgb(0, 1, 0)
# show_value(+1)
# @touch.B.release()
# def release_b(channel):
# lights.rgb(0, 0, 0)
# #######################
# while True:
# t = weather.temperature()
# p = weather.pressure()
# print(t, p)
# time.sleep(0.5)
# #######################
# while True:
# t = weather.temperature()
# display.clear()
# display.print_float(t)
# display.show()
# time.sleep(0.5)
# #######################
# buzzer.note(261, 1)
# buzzer.midi_note(60, 1)
# song = [68, 68, 68, 69, 70, 70, 69, 70, 71, 72]
# for note in song:
# buzzer.midi_note(note, 0.5)
# time.sleep(1)
# #######################
# while True:
# if int(time.time()) % 2 == 0:
# display.print_float(float(time.strftime('%H.%M')))
# else:
# display.print_str(time.strftime('%H%M'))
# display.show()
# time.sleep(1)
#!/bin/bash
cd songs/1
while true
do
clear
echo 'Running Keyboard Jukebox...'
echo 'Tape a key to play a song'
echo; ls -la; echo
echo 'Ctrl^c to exit'; echo
[[ $prevchar ]] && echo Playing `ls -la $prevchar`; echo
read -n 1 char
char=${char,}
if [ "$char" != "$prevchar" ] || ! pidof omxplayer.bin && [ -f $char ];
then
pkill omxplayer
prevchar=$char
omxplayer --no-keys $char &
fi
done
@ggirou
Copy link
Author

ggirou commented May 29, 2017

ln -s path/to/some.mp3 songs/a
alias yaudio='youtube-dl --format m4a --add-metadata'

function song-ln {
     ln -sr "$2" ~/songs/$1
}

Install MPD

sudo apt-get install mpd
sudo nano /etc/mpd.conf
sudo service mpd start

Install Kodi

sudo apt-get install kodi kodi-audiodecoder-*
# Tester sans ? sudo apt-get --no-install-recommends install xserver-xorg xserver-xorg-video-fbdev xinit pciutils xinput xfonts-100dpi xfonts-75dpi xfonts-scalable
nano  ~/.kodi/userdata/guisettings.xml
# Enable services > webserver and remove attribute default="true"
kodi # or kodi-standalone ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment