From 1574c45251a9e62fd708d2a1c4d11ae7cb997c42 Mon Sep 17 00:00:00 2001 From: Philip Lundrigan Date: Thu, 24 Oct 2024 23:36:07 -0600 Subject: [PATCH 1/7] Add sound broadcaster --- broadcasters/__init__.py | 14 ++++++++++ broadcasters/broadcast_message.py | 16 +++++++++++ broadcasters/mqtt.py | 5 ++-- broadcasters/sound.py | 45 +++++++++++++++++++++++++++++++ requirements.txt | 1 + 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 broadcasters/broadcast_message.py create mode 100644 broadcasters/sound.py diff --git a/broadcasters/__init__.py b/broadcasters/__init__.py index f21e78f..7cd0024 100644 --- a/broadcasters/__init__.py +++ b/broadcasters/__init__.py @@ -20,6 +20,7 @@ def start_outputs(system_queue, demo_output_queue): mqtt_q = Queue() + # TODO: Why do we pass system_queue to start_processing_output? mqtt_runner = mqtt.start_processing_output(system_queue, mqtt_q) logger.info("...done") except ValueError as e: @@ -38,10 +39,23 @@ def start_outputs(system_queue, demo_output_queue): logger.warning("Unable to import modules necessary to run MQTT output.") logger.warning("Program will continue to run without this output.") + logger.info("Loading sound output...") + from . import sound + + sound_q = Queue() + + # TODO: Why do we pass system_queue to start_processing_output? + sound_runner = sound.start_processing_output(system_queue, sound_q) + logger.info("...done") + while True: for payload in utils.get_all_from_queue(demo_output_queue): if mqtt_runner: mqtt_q.put(payload) next(mqtt_runner) + if sound_runner: + sound_q.put(payload) + next(sound_runner) + yield diff --git a/broadcasters/broadcast_message.py b/broadcasters/broadcast_message.py new file mode 100644 index 0000000..c6d36e3 --- /dev/null +++ b/broadcasters/broadcast_message.py @@ -0,0 +1,16 @@ +from dataclasses import dataclass + + +class BroadcastType: + SOUND = "sound" + TEXT = "text" + + +@dataclass +class BroadcastMessage: + """ + A dataclass to hold a broadcast message. + """ + + type: BroadcastType + message: str diff --git a/broadcasters/mqtt.py b/broadcasters/mqtt.py index 1dba706..4cdf1f4 100644 --- a/broadcasters/mqtt.py +++ b/broadcasters/mqtt.py @@ -10,8 +10,8 @@ def start_processing_output(system_queue, mqtt_q): Called by the broadcaster module to initialize a connection to the desired MQTT broker. Args: - system_queue (Queue): The queue to put system output events in. - mqtt_q (Queue): The queue to put MQTT messages in. + system_queue (Queue): The queue to put system output events out. + mqtt_q (Queue): The queue to put MQTT messages out. """ def on_connect(client, userdata, flags, rc): @@ -67,6 +67,7 @@ def process(): client.loop(timeout=0.01) for item in utils.get_all_from_queue(mqtt_q): + # TODO: Only publish messages that this broadcaster cares about client.publish( "byu_sss/output", payload=str(item), diff --git a/broadcasters/sound.py b/broadcasters/sound.py new file mode 100644 index 0000000..cd995cf --- /dev/null +++ b/broadcasters/sound.py @@ -0,0 +1,45 @@ +from pathlib import Path +from threading import Thread + +import paho.mqtt.client as mqtt +from playsound import playsound +from loguru import logger +from yaml import safe_load + +from . import utils +from .broadcast_message import BroadcastType + + +def start_processing_output(system_queue, sound_q): + """ + Called by the broadcaster module to initialize a connection to the desired MQTT broker. + + Args: + system_queue (Queue): The queue to put system output events out. + sound_q (Queue): The queue to put sound messages out. + """ + + sound_path = Path("sounds") + + def play_sound_background(): + while True: + for item in utils.get_all_from_queue(sound_q): + # Filter out things that the sound broadcaster doesn't care about + if item.type != BroadcastType.SOUND: + continue + + sound = item.message + logger.debug("Playing sound: {}", sound) + + sound_file = sound_path / sound + if not sound_file.exists(): + logger.error("Sound file does not exist: {}", sound_file) + continue + + playsound(sound_file) + + # Create thread to play sounds + Thread(target=play_sound_background, daemon=True).start() + + while True: + yield diff --git a/requirements.txt b/requirements.txt index 513932e..2a4b756 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ PyYAML==6.0.2 spidev==3.5 urllib3==1.26.19 sysv-ipc==1.1.0 +playsound==1.3.0 From e3b33f7645e7b25f6f3f097459672e5fa43e1a7b Mon Sep 17 00:00:00 2001 From: Philip Lundrigan Date: Thu, 24 Oct 2024 23:51:59 -0600 Subject: [PATCH 2/7] Create sound demo --- demos/sound/main.py | 74 ++++++++++++++++++++++++++++++++++++ sounds/Menu_Navigate_00.wav | Bin 0 -> 10716 bytes sounds/Menu_Navigate_01.wav | Bin 0 -> 10188 bytes sounds/Menu_Navigate_02.wav | Bin 0 -> 10188 bytes sounds/Menu_Navigate_03.wav | Bin 0 -> 10532 bytes 5 files changed, 74 insertions(+) create mode 100644 demos/sound/main.py create mode 100644 sounds/Menu_Navigate_00.wav create mode 100644 sounds/Menu_Navigate_01.wav create mode 100644 sounds/Menu_Navigate_02.wav create mode 100644 sounds/Menu_Navigate_03.wav diff --git a/demos/sound/main.py b/demos/sound/main.py new file mode 100644 index 0000000..22e34cd --- /dev/null +++ b/demos/sound/main.py @@ -0,0 +1,74 @@ +from loguru import logger + +from demos.utils import get_all_from_queue +from display.segment_display import SegmentDisplay +from broadcasters.broadcast_message import BroadcastMessage, BroadcastType + + +class Sound: + """ + This demo shows developers how to use the sound broadcaster to play + sounds. This is not a very interesting demo and is not designed to be used + in kiosk mode. + """ + + demo_time = None + + def __init__(self, input_queue, output_queue, screen): + """ + Constructor + + Args: + input_queue (Queue): Queue to receive messages from the main thread + output_queue (Queue): Queue to send messages to the main thread + screen (Screen): Surface to draw on + """ + self.frame_rate = 10 + + self.input_queue = input_queue + self.output_queue = output_queue + self.screen = screen + + self.display = SegmentDisplay(self.screen) + + self.density = 15 + self.rain_length = 2 + + def run(self): + """Main loop""" + + self.screen.draw_text(5, 6, "Press a button to play sound".upper()) + self.display.draw() + yield + + while True: + for item in get_all_from_queue(self.input_queue): + logger.debug("Received: {}", item) + sound = None + + if item == "LEFT_P": + sound = "Menu_Navigate_00.wav" + elif item == "RIGHT_P": + sound = "Menu_Navigate_01.wav" + elif item == "UP_P": + sound = "Menu_Navigate_02.wav" + elif item == "DOWN_P": + sound = "Menu_Navigate_03.wav" + + if sound is None: + yield + continue + + self.display.undraw() + self.screen.draw_text(5, 6, "Playing sound {}".format(sound).upper()) + self.display.draw() + self.output_queue.put( + BroadcastMessage(type=BroadcastType.SOUND, message=sound) + ) + yield + + yield + + def stop(self): + """Stop the thread""" + pass diff --git a/sounds/Menu_Navigate_00.wav b/sounds/Menu_Navigate_00.wav new file mode 100644 index 0000000000000000000000000000000000000000..d36ee07863a296999ccbe407dde559eee816cc1e GIT binary patch literal 10716 zcmeI%c~I16p2zVfF;R>WkwX*_uu-n2e^;ABAqXmJ1>%`_5Op1m8k3kY;KYoY!_hb% z8)F=NB$=+n&jA%4OHAHQ~?ws8@p1Gd6DJZ}Yb%>gV>zL%6yok0|i6lnOs&i`n8&;Ep}Q9$^aF?d|pqOu)e0fw`-21_^3{`Z*qS zKIoi_;|SCOwdE**qAU74IEOeR&iD|Cfr){JVkkd_C-%wvUhR^-}q?f0Aao4?EbU2d=%tgb{2hC7ElKf*0c)23_dd2X~Ct^OJ} zG2J=cc>q3|kM;^&D_jrPSD^}z8jl+9;v8blSaYFcp~D>+N`^89jdJ5X&mu)i>39}L z#3LQgqMO+5o@Y^$Taz2F#;c#e&FSX+7aWDZ=C3V90R&yp*P$LUMvRe$OBiL2GSeOD zj>k}}6f3X74+rIgax!j9x22CU5{+V`_zoN>7s`c|h`>Mc{fC>w&51!1gJwBqIc_Vr zl`V)tm0TsiiGesNos<^fVeAw4iBr%fv29ewv;W+#sk`ZnZgs7-EZ{1se8K+=1tj01FAkvH^!7I(}j_r;(oK{XNZz34S<>T@kJcvvw zQ%b@W@rw99qERQ*3F{!Er`l6p1PxKnDCc@KB0)>gK0zC%>C^Pz;$h=qV;+ve*Yq`C z!V$+2$8@wRt;&aZ9A$EuybJ?yOgbjbg*$eOyTwVkA>0tQVGLSwTXLUKpHcq92BkPs9G=*#>{TY9S#Fj$V+cy55@|Vt za8f)eE<`VUC442!M4Fjq#;URERhU-Cg3dnh=kvf1?FIiI`aov}{=PHekClOC;xf?}t{Ku(P`nlIh3Z1J2yxE1jyaHP zF0cY;kf`_RYe({UfDHmW<$A)lI`nh)eXkT(zSEAJ~2_*(wDV>WVLIxnq6Ke+D0 zt~pVI&>*}8*G%G&{g8bsTB2H_(opNHbtYM!c~ zc-(m0*orP@mySDUu9<7v^Xz%=;tD3@Psq=LM}bGdOUS|C!oh{70!{^Zik{+5VW%(> z)%I%pGW5ZhQC~()#1-2W+a^fJi_D8mMIRiBI22(ovKP7TH?HN#bL72`R!q;Io_`2^ z3;Gr;MG=%jrEnw8BcUjv=wmciG*-;F&$r))tx;Q}qETimv%LmC9E>~|IUTnmZbfWG zJQ~6q!q-DWnrE74|NQ>>ucI2V1+fJm;Wm;BlM4^PyU4rfW#k~FIHdS>6oJx~tj1@y z&uk-67g-m%5>}juI1w=q?${N+D|`Z4!dk+9jd(PLHif>2p{OgUD~KFX#!cv*N^PDD<`3+RD8;d{a-!u46{7>v;MEXNI;eEeDtuG)O_8-W(CpATa7WhQtie;^hMj|U4w{VikoJ(Bn1cJU*Sgp0Tj^UFQW{da z0)>bwiz-_M*WxNyz8a;lRoE)tfC+1*wK4@KaI5xK?Xoh{VLb9#d*7@j-qXUl)d;xy=Cit7+B?v)5P(jctJc4t9=K|A_gl(1E zDz9Tq)tIVuTtG~9O!dE_61LN}(?7>|@Tu{snT=ieBjRi0Yd;A7Ab1p-gPMar#yGSE zwgv9SOnBhffMWwv5sLDF@_>yPi5vZI^p{V|r+++JZXlsPq5eHoBJ51qnOATaT^hPHB;r?ag)jJQ@Y(s0&&%f*`!DtnL2#En)*~mgWp1StbwVPdUcHP!vTaQs_e5moE^@xJjUF-f59zuqDhI>59x|MZ%8E)9v zbz@gQEN@-j8jo6Ry0PiT^qbRfCf!Q9m3ce!w(I|(&ed6g&I)u^ptAy<73i!$X9YSd X&{=`b3UpSWvjUwJ=&Zm$y8?d#KpXvM literal 0 HcmV?d00001 diff --git a/sounds/Menu_Navigate_01.wav b/sounds/Menu_Navigate_01.wav new file mode 100644 index 0000000000000000000000000000000000000000..d05a235511b7ad5c2292cea7c5f20a27f4b825d4 GIT binary patch literal 10188 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 literal 0 HcmV?d00001 diff --git a/sounds/Menu_Navigate_03.wav b/sounds/Menu_Navigate_03.wav new file mode 100644 index 0000000000000000000000000000000000000000..0b3d4aa6173582c4c3b43e1be8804369f5176084 GIT binary patch literal 10532 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 TdJ^bKpeKQz1bPzq|4!h)JJQf# literal 0 HcmV?d00001 From 9e7538c64aa413e0bdf9415f8a17daec80388002 Mon Sep 17 00:00:00 2001 From: Philip Lundrigan Date: Fri, 25 Oct 2024 00:06:24 -0600 Subject: [PATCH 3/7] Fix imports --- broadcasters/sound.py | 4 +--- demos/sound/main.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/broadcasters/sound.py b/broadcasters/sound.py index cd995cf..e5e910a 100644 --- a/broadcasters/sound.py +++ b/broadcasters/sound.py @@ -1,10 +1,8 @@ from pathlib import Path from threading import Thread -import paho.mqtt.client as mqtt -from playsound import playsound from loguru import logger -from yaml import safe_load +from playsound import playsound from . import utils from .broadcast_message import BroadcastType diff --git a/demos/sound/main.py b/demos/sound/main.py index 22e34cd..77fa9e9 100644 --- a/demos/sound/main.py +++ b/demos/sound/main.py @@ -1,8 +1,8 @@ from loguru import logger +from broadcasters.broadcast_message import BroadcastMessage, BroadcastType from demos.utils import get_all_from_queue from display.segment_display import SegmentDisplay -from broadcasters.broadcast_message import BroadcastMessage, BroadcastType class Sound: From b320868b6fc5fb3ba23eea3d5de24967605ba533 Mon Sep 17 00:00:00 2001 From: Philip Lundrigan Date: Fri, 25 Oct 2024 00:14:34 -0600 Subject: [PATCH 4/7] Switch to version of library that works on Linux https://github.com/TaylorSMarks/playsound/issues/160 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2a4b756..0065c0a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,4 @@ PyYAML==6.0.2 spidev==3.5 urllib3==1.26.19 sysv-ipc==1.1.0 -playsound==1.3.0 +playsound==1.2.2 From baaf09ced775ceddaad68fbdfcd5ae9f51fd187b Mon Sep 17 00:00:00 2001 From: Philip Lundrigan Date: Thu, 31 Oct 2024 21:06:02 +0100 Subject: [PATCH 5/7] Test out different class types --- broadcasters/broadcast_message.py | 16 ++++++++++------ broadcasters/sound.py | 9 ++++----- demos/sound/main.py | 6 ++---- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/broadcasters/broadcast_message.py b/broadcasters/broadcast_message.py index c6d36e3..9e528c9 100644 --- a/broadcasters/broadcast_message.py +++ b/broadcasters/broadcast_message.py @@ -1,16 +1,20 @@ from dataclasses import dataclass -class BroadcastType: - SOUND = "sound" - TEXT = "text" +@dataclass +class BroadcastSoundMessage: + """ + A dataclass to hold a broadcast message. + """ + + file: str + # channel: int @dataclass -class BroadcastMessage: +class BroadcastTextMessage: """ A dataclass to hold a broadcast message. """ - type: BroadcastType - message: str + text: str diff --git a/broadcasters/sound.py b/broadcasters/sound.py index e5e910a..a6b268d 100644 --- a/broadcasters/sound.py +++ b/broadcasters/sound.py @@ -5,7 +5,7 @@ from playsound import playsound from . import utils -from .broadcast_message import BroadcastType +from .broadcast_message import BroadcastSoundMessage def start_processing_output(system_queue, sound_q): @@ -23,13 +23,12 @@ def play_sound_background(): while True: for item in utils.get_all_from_queue(sound_q): # Filter out things that the sound broadcaster doesn't care about - if item.type != BroadcastType.SOUND: + if not isinstance(item, BroadcastSoundMessage): continue - sound = item.message - logger.debug("Playing sound: {}", sound) + logger.debug("Playing sound: {}", item.file) - sound_file = sound_path / sound + sound_file = sound_path / item.file if not sound_file.exists(): logger.error("Sound file does not exist: {}", sound_file) continue diff --git a/demos/sound/main.py b/demos/sound/main.py index 77fa9e9..b7e13dc 100644 --- a/demos/sound/main.py +++ b/demos/sound/main.py @@ -1,6 +1,6 @@ from loguru import logger -from broadcasters.broadcast_message import BroadcastMessage, BroadcastType +from broadcasters.broadcast_message import BroadcastSoundMessage from demos.utils import get_all_from_queue from display.segment_display import SegmentDisplay @@ -62,9 +62,7 @@ def run(self): self.display.undraw() self.screen.draw_text(5, 6, "Playing sound {}".format(sound).upper()) self.display.draw() - self.output_queue.put( - BroadcastMessage(type=BroadcastType.SOUND, message=sound) - ) + self.output_queue.put(BroadcastSoundMessage(file=sound)) yield yield From 07557fe832013ac971df537bcd543a0829d7ae6d Mon Sep 17 00:00:00 2001 From: Philip Lundrigan Date: Fri, 14 Nov 2025 20:51:09 -0700 Subject: [PATCH 6/7] Use pygame to play a sound --- broadcasters/sound.py | 31 +++++++++++++------------------ requirements.txt | 1 - 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/broadcasters/sound.py b/broadcasters/sound.py index a6b268d..0e4d7ae 100644 --- a/broadcasters/sound.py +++ b/broadcasters/sound.py @@ -1,8 +1,7 @@ from pathlib import Path -from threading import Thread from loguru import logger -from playsound import playsound +from pygame import mixer from . import utils from .broadcast_message import BroadcastSoundMessage @@ -18,25 +17,21 @@ def start_processing_output(system_queue, sound_q): """ sound_path = Path("sounds") + mixer.init() - def play_sound_background(): - while True: - for item in utils.get_all_from_queue(sound_q): - # Filter out things that the sound broadcaster doesn't care about - if not isinstance(item, BroadcastSoundMessage): - continue - - logger.debug("Playing sound: {}", item.file) + while True: + for item in utils.get_all_from_queue(sound_q): + # Filter out things that the sound broadcaster doesn't care about + if not isinstance(item, BroadcastSoundMessage): + continue - sound_file = sound_path / item.file - if not sound_file.exists(): - logger.error("Sound file does not exist: {}", sound_file) - continue + logger.debug("Playing sound: {}", item.file) - playsound(sound_file) + sound_file = sound_path / item.file + if not sound_file.exists(): + logger.error("Sound file does not exist: {}", sound_file) + continue - # Create thread to play sounds - Thread(target=play_sound_background, daemon=True).start() + mixer.Sound(str(sound_file)).play() - while True: yield diff --git a/requirements.txt b/requirements.txt index 0065c0a..513932e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,3 @@ PyYAML==6.0.2 spidev==3.5 urllib3==1.26.19 sysv-ipc==1.1.0 -playsound==1.2.2 From 80bcfa2bc572c7bca2f0984c12e93559242d1c43 Mon Sep 17 00:00:00 2001 From: Philip Lundrigan Date: Fri, 14 Nov 2025 20:57:01 -0700 Subject: [PATCH 7/7] Update sound demo refresh rate Helps with playing the sounds faster --- demos/sound/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/sound/main.py b/demos/sound/main.py index b7e13dc..02e92c2 100644 --- a/demos/sound/main.py +++ b/demos/sound/main.py @@ -23,7 +23,7 @@ def __init__(self, input_queue, output_queue, screen): output_queue (Queue): Queue to send messages to the main thread screen (Screen): Surface to draw on """ - self.frame_rate = 10 + self.frame_rate = 30 self.input_queue = input_queue self.output_queue = output_queue