You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
3.4 KiB

import time
import gevent
from holster.enum import Enum
from holster.emitter import Emitter
from disco.voice.client import VoiceState
from disco.voice.queue import PlayableQueue
MAX_TIMESTAMP = 4294967295
class Player(object):
Events = Enum(
'START_PLAY',
'STOP_PLAY',
'PAUSE_PLAY',
'RESUME_PLAY',
'DISCONNECT',
)
def __init__(self, client, queue=None):
self.client = client
# Queue contains playable items
self.queue = queue or PlayableQueue()
# Whether we're playing music (true for lifetime)
self.playing = True
# Set to an event when playback is paused
self.paused = None
# Current playing item
self.now_playing = None
# Current play task
self.play_task = None
# Core task
self.run_task = gevent.spawn(self.run)
# Event triggered when playback is complete
self.complete = gevent.event.Event()
# Event emitter for metadata
self.events = Emitter(spawn_each=True)
def disconnect(self):
self.client.disconnect()
self.events.emit(self.Events.DISCONNECT)
def skip(self):
self.play_task.kill()
def pause(self):
if self.paused:
return
self.paused = gevent.event.Event()
self.events.emit(self.Events.PAUSE_PLAY)
def resume(self):
if self.paused:
self.paused.set()
self.paused = None
self.events.emit(self.Events.RESUME_PLAY)
def play(self, item):
# Grab the first frame before we start anything else, sometimes playables
# can do some lengthy async tasks here to setup the playable and we
# don't want that lerp the first N frames of the playable into playing
# faster
frame = item.next_frame()
if frame is None:
return
start = time.time()
loops = 0
while True:
loops += 1
if self.paused:
self.client.set_speaking(False)
self.paused.wait()
gevent.sleep(2)
self.client.set_speaking(True)
start = time.time()
loops = 0
if self.client.state == VoiceState.DISCONNECTED:
return
if self.client.state != VoiceState.CONNECTED:
self.client.state_emitter.wait(VoiceState.CONNECTED)
self.client.send_frame(frame)
self.client.timestamp += item.samples_per_frame
if self.client.timestamp > MAX_TIMESTAMP:
self.client.timestamp = 0
frame = item.next_frame()
if frame is None:
return
next_time = start + 0.02 * loops
delay = max(0, 0.02 + (next_time - time.time()))
gevent.sleep(delay)
def run(self):
self.client.set_speaking(True)
while self.playing:
self.now_playing = self.queue.get()
self.events.emit(self.Events.START_PLAY, self.now_playing)
self.play_task = gevent.spawn(self.play, self.now_playing)
self.play_task.join()
self.events.emit(self.Events.STOP_PLAY, self.now_playing)
if self.client.state == VoiceState.DISCONNECTED:
self.playing = False
self.complete.set()
return
self.client.set_speaking(False)
self.disconnect()