Proof of concept player
This commit is contained in:
parent
8177e2bd07
commit
0a3bcd0668
221
audplayer.py
Executable file
221
audplayer.py
Executable file
@ -0,0 +1,221 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from threading import Timer
|
||||
from pydub import AudioSegment
|
||||
from time import sleep
|
||||
from timeloop import Timeloop
|
||||
import vlc
|
||||
|
||||
|
||||
class RepeatedTimer(object):
|
||||
def __init__(self, interval, function, *args, **kwargs):
|
||||
self._timer = None
|
||||
self.interval = interval
|
||||
self.function = function
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.is_running = False
|
||||
self.start()
|
||||
|
||||
def _run(self):
|
||||
self.is_running = False
|
||||
self.start()
|
||||
self.function(*self.args, **self.kwargs)
|
||||
|
||||
def start(self):
|
||||
if not self.is_running:
|
||||
self._timer = Timer(self.interval, self._run)
|
||||
self._timer.start()
|
||||
self.is_running = True
|
||||
|
||||
def stop(self):
|
||||
self._timer.cancel()
|
||||
self.is_running = False
|
||||
|
||||
|
||||
tl = Timeloop()
|
||||
|
||||
|
||||
def leading_silence(audio_segment, silence_threshold=-50.0, chunk_size=10):
|
||||
"""
|
||||
Returns the millisecond/index that the leading silence ends.
|
||||
audio_segment - the segment to find silence in
|
||||
silence_threshold - the upper bound for how quiet is silent in dFBS
|
||||
chunk_size - chunk size for interating over the segment in ms
|
||||
|
||||
https://github.com/jiaaro/pydub/blob/master/pydub/silence.py
|
||||
"""
|
||||
|
||||
trim_ms = 0 # ms
|
||||
assert chunk_size > 0 # to avoid infinite loop
|
||||
while (
|
||||
audio_segment[trim_ms:trim_ms + chunk_size].dBFS < silence_threshold
|
||||
and trim_ms < len(audio_segment)):
|
||||
trim_ms += chunk_size
|
||||
|
||||
# if there is no end it should return the length of the segment
|
||||
return min(trim_ms, len(audio_segment))
|
||||
|
||||
|
||||
def significant_fade(audio_segment, fade_threshold=-20.0, chunk_size=10):
|
||||
"""
|
||||
Returns the millisecond/index of the point where the fade is down to
|
||||
fade_threshold and doesn't get louder again.
|
||||
audio_segment - the segment to find silence in
|
||||
fade_threshold - the upper bound for how quiet is silent in dFBS
|
||||
chunk_size - chunk size for interating over the segment in ms
|
||||
"""
|
||||
|
||||
assert chunk_size > 0 # to avoid infinite loop
|
||||
|
||||
segment_length = audio_segment.duration_seconds * 1000 # ms
|
||||
trim_ms = segment_length - chunk_size
|
||||
while (
|
||||
audio_segment[trim_ms:trim_ms + chunk_size].dBFS < fade_threshold
|
||||
and trim_ms > 0):
|
||||
trim_ms -= chunk_size
|
||||
|
||||
# if there is no trailing silence, return lenght of track (it's less
|
||||
# the chunk_size, but for chunk_size = 10ms, this may be ignored)
|
||||
return trim_ms
|
||||
|
||||
|
||||
def trailing_silence(audio_segment, silence_threshold=-50.0, chunk_size=10):
|
||||
"""
|
||||
Returns the millisecond/index that the trailing silence starts.
|
||||
audio_segment - the segment to find silence in
|
||||
silence_threshold - the upper bound for how quiet is silent in dFBS
|
||||
chunk_size - chunk size for interating over the segment in ms
|
||||
"""
|
||||
|
||||
assert chunk_size > 0 # to avoid infinite loop
|
||||
|
||||
segment_length = audio_segment.duration_seconds * 1000 # ms
|
||||
trim_ms = segment_length - chunk_size
|
||||
while (
|
||||
audio_segment[trim_ms:trim_ms + chunk_size].dBFS < silence_threshold
|
||||
and trim_ms > 0):
|
||||
trim_ms -= chunk_size
|
||||
|
||||
# if there is no trailing silence, return lenght of track (it's less
|
||||
# the chunk_size, but for chunk_size = 10ms, this may be ignored)
|
||||
return trim_ms
|
||||
|
||||
|
||||
def ms_to_mmss(ms, decimals=0):
|
||||
if not ms:
|
||||
return "-"
|
||||
if ms < 0:
|
||||
sign = "-"
|
||||
else:
|
||||
sign = ""
|
||||
|
||||
minutes, remainder = divmod(ms, 60 * 1000)
|
||||
seconds = remainder / 1000
|
||||
|
||||
return f"{sign}{minutes:.0f}:{seconds:02.{decimals}f}"
|
||||
|
||||
|
||||
# @tl.job(interval=timedelta(seconds=1))
|
||||
def update_progress(player, talk_at, silent_at):
|
||||
elapsed_time = player.get_time()
|
||||
total_time = player.get_length()
|
||||
remaining_time = total_time - elapsed_time
|
||||
talk_time = remaining_time - (total_time - talk_at)
|
||||
silent_time = remaining_time - (total_time - silent_at)
|
||||
end_time = (datetime.now() + timedelta(
|
||||
milliseconds=remaining_time)).strftime("%H:%M:%S")
|
||||
print(
|
||||
f"\t{ms_to_mmss(elapsed_time)}/"
|
||||
f"{ms_to_mmss(total_time)}\t\t"
|
||||
f"Talk in: {ms_to_mmss(talk_time)} "
|
||||
f"Silent in: {ms_to_mmss(silent_time)} "
|
||||
f"Ends at: {end_time} [{ms_to_mmss(remaining_time)}]"
|
||||
, end="\r")
|
||||
|
||||
|
||||
# Print name of current song, print name of next song. Play current when
|
||||
# return pressed, Pri--current-song-output-lengthnt remaining time every
|
||||
# second. When it ends, print name of new current and next song.
|
||||
|
||||
|
||||
def test():
|
||||
track = "wibg.mp3"
|
||||
segment = AudioSegment.from_mp3(track)
|
||||
print(f"Track: {track}")
|
||||
print(f"Leading silence: {ms_to_mmss(leading_silence(segment), decimals=1)}")
|
||||
talk_at = significant_fade(segment)
|
||||
silent_at = trailing_silence(segment)
|
||||
print(f"Talkover fade: {ms_to_mmss(talk_at)}")
|
||||
print(f"Track silent from: {ms_to_mmss(silent_at)}")
|
||||
p = vlc.MediaPlayer("wibg.mp3")
|
||||
_ = input("")
|
||||
p.play()
|
||||
print()
|
||||
rt = RepeatedTimer(0.5, update_progress, p, talk_at, silent_at)
|
||||
sleep(1)
|
||||
while p.is_playing():
|
||||
sleep(1)
|
||||
rt.stop() # better in a try/finally block to make sure the program ends!
|
||||
print("End")
|
||||
|
||||
|
||||
test()
|
||||
# next_song = get_next_song
|
||||
#
|
||||
# def play_track():
|
||||
# r = run_aud_cmd("--current-song-length
|
||||
#
|
||||
#
|
||||
#
|
||||
# def play():
|
||||
# play_track()
|
||||
# songtimer_start()
|
||||
#
|
||||
#
|
||||
# print("Start playing in 3 seconds")
|
||||
#
|
||||
# sleep(3)
|
||||
#
|
||||
# play()
|
||||
|
||||
|
||||
# print("starting...")
|
||||
# This auto-starts, no need of rt.start()
|
||||
# rt = RepeatedTimer(0.3, hello, "World")
|
||||
#
|
||||
# count = 5
|
||||
# while count > 0:
|
||||
# print("main job here")
|
||||
# sleep(2)
|
||||
# rt.stop() # better in a try/finally block to make sure the program ends!
|
||||
|
||||
|
||||
# #!/usr/bin/python3
|
||||
#
|
||||
# import time
|
||||
# from timeloop import Timeloop
|
||||
# from datetime import timedelta
|
||||
#
|
||||
# tl = Timeloop()
|
||||
#
|
||||
# @tl.job(interval=timedelta(seconds=2))
|
||||
# def sample_job_every_2s():
|
||||
# print(f"2s job current time: {time.ctime()}")
|
||||
#
|
||||
# @tl.job(interval=timedelta(seconds=5))
|
||||
# def sample_job_every_5s():
|
||||
# print(f"5s job current time: {time.ctime()}")
|
||||
#
|
||||
# @tl.job(interval=timedelta(seconds=10))
|
||||
# def sample_job_every_10s():
|
||||
# print(f"10s job current time: {time.ctime()}")
|
||||
#
|
||||
# @tl.job(interval=timedelta(seconds=0.2))
|
||||
# def sample_job_every_10s():
|
||||
# print(f"200ms job current time: {time.ctime()}")
|
||||
#
|
||||
# if __name__ == "__main__":
|
||||
# tl.start(block=True)
|
||||
#
|
||||
Loading…
Reference in New Issue
Block a user