diff --git a/audplayer.py b/audplayer.py new file mode 100755 index 0000000..338d608 --- /dev/null +++ b/audplayer.py @@ -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) +#