mjbrusso / AudioPlayer

audioplayer is a cross platform Python 3 package for playing sounds (mp3, wav, ...). It provides the key features of an audio player, such as opening a media file, playing (loop/block), pausing, resuming, stopping, and setting the playback volume.
MIT License
39 stars 11 forks source link

adapted console player #15

Open zoldaten opened 2 years ago

zoldaten commented 2 years ago

hello!

i`ve adapted player to use it from keyboard on windows. keys to operate: 'space' - pause, resume 'esc' - out up, down - volume left, right - forward, backward seek.

but i small issue i cant handle: loop play of file. a.play(mode=PlayMode.ONCE_ASYNC) - not working in my code. give an advice please.

from abstractaudioplayer import AbstractAudioPlayer, AudioPlayerError, State, PlayMode
from ctypes import windll, create_unicode_buffer
import threading
import time
from pynput import keyboard
from pprint import pprint
from pydub import AudioSegment #https://pythonrepo.com/repo/jiaaro-pydub-python-audio

pprint ('клавиши управления:')
pprint ('escape: выход, space: пауза + снятие с паузы')
pprint ('громкость: вверх, вниз, перемотка: влево, вправо')

class AudioPlayerWindows(AbstractAudioPlayer):
    def __init__(self, filename, callback=None):
        super().__init__(filename, callback)
        self._alias = "A{}".format(id(self))
        self._buffer = create_unicode_buffer(255)

    def _mciSendString(self, command):
        result = windll.winmm.mciSendStringW(command, self._buffer, 255, 0)
        return result, self._buffer.value or ""

    def _do_load_player(self):
        ret, _a = self._mciSendString('open "{}" type mpegvideo alias {}'.format(self._filename, self._alias))
        ret = int(ret)
        if ret > 0:
            raise AudioPlayerError( 'Failed to play "{}"'.format(self.fullfilename))
        self._mciSendString('set {} time format ms'.format(self._alias))
        return ret

    def _get_duration(self):
        _, length = self._mciSendString('status {} length'.format(self._alias))
        return float(length or 0) / 1000

    def _get_position(self):
        _, position = self._mciSendString('status {} position'.format(self._alias))
        return float(position or 0) / 1000

    def _set_position(self, pos):
        pos = int(float(pos) * 1000)
        if True or self.state == State.PLAYING:
            self._mciSendString('play {} from {}'.format(self._alias, pos))
            if self.state == State.PAUSED:
                self._dopause()
        else:
            self._mciSendString('seek {} to {}'.format(self._alias, pos))

    def _set_volume(self, value):
        value = int(value * 10)  # MCI volume: 0...1000
        self._mciSendString(
            'setaudio {} volume to {}'.format(self._alias, value))

    def _doplay(self, loop=False, block=False):
        sloop = 'repeat' if loop else ''
        swait = 'wait' if block else ''
        self._mciSendString('play {} from 0 {} {}'.format(
            self._alias, sloop, swait))

    def _dopause(self):
        self._mciSendString('pause {}'.format(self._alias))

    def _doresume(self):
        self._mciSendString('resume {}'.format(self._alias))

    def _dostop(self):
        self._mciSendString('stop {}'.format(self._alias))
        self._mciSendString('seek {} to 0'.format(self._alias))

    def _doclose(self):
        self._mciSendString('close {}'.format(self._alias))

def background_audio():    
    a.play(mode=PlayMode.ONCE_ASYNC) # not working
    #if a.position == song.duration_seconds:
        #print('конец')        

    while True:
        if pausing.is_set():
            pausing.clear()
            resuming.clear()
            a.pause()            

        if resuming.is_set():            
            pausing.clear()
            resuming.clear()
            a.resume()

        if stopping.is_set():            
            a.stop()
            a.close()
            return False

        if volume_up.is_set():            
            volume_down.clear()
            volume_up.clear()
            a.volume=a.volume+10

        if volume_down.is_set():            
            volume_down.clear()
            volume_up.clear()
            a.volume=a.volume-10

        if forward.is_set():
            forward.clear()
            backward.clear()
            a.position=a.position+3.0                        

        if backward.is_set():
            forward.clear()
            backward.clear()
            a.position=a.position-3.0            

def on_press(key):    
    if key == keyboard.Key.esc: # escape pressed - stop listener
        print("Stopping")
        stopping.set()        
        return False  
    try:
        k = key.char  # single-char keys
    except:
        k = key.name  # other keys       

    if k == 'space' and a.state == State.PLAYING:  # space - pause        
        print('Pause')
        #print(a.state)
        pausing.set()

    if k == 'space' and a.state == State.PAUSED: #space again - resume
        print('Resume')
        resuming.set()

    if k == 'up': #volume up
        print('volume up')
        volume_up.set()

    if k == 'down': #volume down 
        print('volume down')
        volume_down.set()

    if k == 'right': #перемотка вперед
        print('вперед +3 сек')
        forward.set()

    if k == 'left': #перемотка назад
        print('назад -3 сек')
        backward.set()

if __name__ == "__main__":
    filename='english.wav' #аудиофайл 
    a = AudioPlayerWindows(filename) 
    song = AudioSegment.from_wav(filename) #это нужно только, чтобы узнать длину аудиофайла
    print('Длина аудио: {} секунд'.format(int(song.duration_seconds)))
    #print(dir(a))
    a.volume=30 #громкость при старте 
    #print(a.duration)    
    #print(a.position)
    #print(PlayMode(0))

    pausing = threading.Event()
    resuming = threading.Event()
    volume_up = threading.Event()
    volume_down = threading.Event()    
    stopping = threading.Event()
    forward = threading.Event()
    backward = threading.Event()

    audio_thread = threading.Thread(target=background_audio) #PlayMode.ONCE_ASYNC - проиграть один раз
    audio_thread.start()

    listener = keyboard.Listener(on_press=on_press)
    listener.start()  
    listener.join()    
    audio_thread.join()
zoldaten commented 2 years ago

the problem only exists on Windows, on Linux everything works OK.