dashboard: media bongo cat

This commit is contained in:
2 * r + 2 * t 2025-05-23 00:05:13 +08:00
parent 07954a4ed8
commit cf965934ac
3 changed files with 102 additions and 0 deletions

BIN
assets/bongocat.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

@ -0,0 +1,73 @@
#!/bin/python
import pyaudio
import numpy as np
import aubio
import signal
import sys
from typing import List, Tuple
class BeatDetector:
def __init__(self, buf_size: int):
self.buf_size: int = buf_size
# Set up pyaudio and aubio beat detector
self.audio: pyaudio.PyAudio = pyaudio.PyAudio()
samplerate: int = 44100
self.stream: pyaudio.Stream = self.audio.open(
format=pyaudio.paFloat32,
channels=1,
rate=samplerate,
input=True,
frames_per_buffer=self.buf_size,
stream_callback=self._pyaudio_callback
)
fft_size: int = self.buf_size * 2
# tempo detection
self.tempo: aubio.tempo = aubio.tempo("default", fft_size, self.buf_size, samplerate)
# this one is called every time enough audio data (buf_size) has been read by the stream
def _pyaudio_callback(self, in_data, frame_count, time_info, status):
# Interpret a buffer as a 1-dimensional array (aubio do not work with raw data)
audio_data = np.frombuffer(in_data, dtype=np.float32)
# true if beat present
beat = self.tempo(audio_data)
# if beat detected, calculate BPM and send to OSC
if beat[0]:
print(self.tempo.get_bpm(), flush=True)
return None, pyaudio.paContinue # Tell pyAudio to continue
def __del__(self):
self.stream.close()
self.audio.terminate()
print('--- Stopped ---')
# main
def main():
bd = BeatDetector(512)
# capture ctrl+c to stop gracefully process
def signal_handler(none, frame):
bd.stream.stop_stream()
bd.stream.close()
bd.audio.terminate()
print(' ===> Ctrl + C')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
# Audio processing happens in separate thread, so put this thread to sleep
signal.pause()
# main run
if __name__ == "__main__":
main()

View file

@ -1,6 +1,8 @@
import "root:/widgets"
import "root:/services"
import "root:/config"
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import QtQuick
import QtQuick.Shapes
@ -210,6 +212,33 @@ Item {
}
}
AnimatedImage {
id: bongocat
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottomMargin: Appearance.padding.large
anchors.margins: Appearance.padding.large * 2
playing: false
source: "root:/assets/bongocat.gif"
asynchronous: true
fillMode: AnimatedImage.PreserveAspectFit
z: -1
Process {
running: true
command: [`${Quickshell.shellRoot}/assets/realtime-beat-detector.py`]
stdout: SplitParser {
onRead: data => {
if (Players.active?.isPlaying)
bongocat.currentFrame++;
}
}
}
}
component Control: StyledRect {
id: control