def pump(self): underrun = False if self._stop_alsatime is not None: return underrun # Check that ALSA's still playing if self._playing: state = asound.snd_pcm_state(self.pcm) if state not in (asound.SND_PCM_STATE_RUNNING, asound.SND_PCM_STATE_PREPARED): # Underrun! check(asound.snd_pcm_prepare(self.pcm)) underrun = True alsatime = self._get_asound_time() try: while self._timestamps[0][0] < alsatime: self._timestamps.pop(0) while self._timestamps[0][0] is None: self._eos_count += 1 self._timestamps.pop(0) except IndexError: pass return underrun
def _clear_ring_buffer(self): check(asound.snd_pcm_drop(self._device.pcm)) check(asound.snd_pcm_prepare(self._device.pcm)) self._current_buffer_time = 0. self._cumulative_buffer_time = 0. self._source_read_index = 0 self._queue_audio_data = None self._start_time = None
def play(self): if self._playing: return state = asound.snd_pcm_state(self.pcm) if self.can_pause and state == asound.SND_PCM_STATE_PAUSED: check(asound.snd_pcm_pause(self.pcm, 0)) elif state not in (asound.SND_PCM_STATE_RUNNING, asound.SND_PCM_STATE_PREPARED): check(asound.snd_pcm_prepare(self.pcm)) self._playing = True if self._stop_alsatime is not None: diff = self._get_asound_time() - self._stop_alsatime self._timestamps = [(a + diff, t) for a, t in self._timestamps] self._stop_alsatime = None
def write(self, audio_data): samples = audio_data.length // self.audio_format.bytes_per_sample samples_out = asound.snd_pcm_writei(self.pcm, audio_data.data, samples) if samples_out < 0: if samples_out == -11: # EAGAIN return elif samples_out == -32: # EPIPE (xrun) check(asound.snd_pcm_prepare(self.pcm)) return else: raise ALSAException(asound.snd_strerror(samples_out)) delay = asound.snd_pcm_sframes_t() check(asound.snd_pcm_delay(self.pcm, delay)) alsatime = self._get_asound_time() + \ delay.value / float(self.audio_format.sample_rate) self._timestamps.append((alsatime, audio_data.timestamp)) audio_data.consume(samples_out * self.audio_format.bytes_per_sample, self.audio_format)
def dispatch_events(self): if not self._sources: return if not self._playing: # If paused, just update the video texture. if self._texture: self._sources[0]._update_texture(self, self.time) return # Create a device if there isn't one. TODO only if source and source # has audio if not self._device: self._device = Device('plug:front') self._device.prepare(self._sources[0]) self_time = self.time # Passed EOS? source = self._sources[0] while source and source.duration < self_time: if self._eos_action == self.EOS_NEXT: self.next() elif self._eos_action == self.EOS_STOP: self.stop() self._sources = [] return self.dispatch_event('on_eos') self_time -= source.duration self._cumulative_buffer_time -= source.duration assert self._cumulative_buffer_time >= -0.001 # some float err ok try: source = self._sources[0] self._set_start_time(self._sources[0], self_time) except IndexError: source = None self._start_time = None # Ensure device buffer is full try: source = self._sources[self._source_read_index] except IndexError: source = None while (source and self._cumulative_buffer_time + self._current_buffer_time - self_time < self._min_buffer_time): if self._queue_audio_data: audio_data = self._queue_audio_data self._queue_audio_data = None else: max_bytes = int( self._min_buffer_time * source.audio_format.bytes_per_second) max_bytes = min(max_bytes, self._max_buffer_size) audio_data = source._get_audio_data(max_bytes) if audio_data: samples = \ audio_data.length // source.audio_format.bytes_per_sample samples_out = asound.snd_pcm_writei(self._device.pcm, audio_data.data, samples) if samples_out < 0: if samples_out == -11: # EAGAIN self._queue_audio_data = audio_data elif samples_out == -32: # EPIPE # xrun recovery check(asound.snd_pcm_prepare(self._device.pcm)) self._queue_audio_data = audio_data else: raise ALSAException(asound.snd_strerror(samples_out)) elif samples_out < samples: audio_data.consume( samples_out * source.audio_format.bytes_per_sample, source.audio_format) self._current_buffer_time = audio_data.timestamp self._queue_audio_data = audio_data else: self._current_buffer_time = \ audio_data.timestamp + audio_data.duration if self._start_time is None: # XXX start playback self._set_start_time(source, audio_data.timestamp) else: # EOS on read source self._cumulative_buffer_time += source.duration self._current_buffer_time = 0. if self._eos_action == self.EOS_NEXT: self._source_read_index += 1 try: # preroll source = self._sources[self._source_read_index] source._play() except IndexError: source = None elif self._eos_action == self.EOS_LOOP: source._seek(0) elif self._eos_action == self.EOS_PAUSE: source = None elif self._eos_action == self.EOS_STOP: source = None else: assert False, 'Invalid eos_action' source = None # Update video texture if self._texture: self._sources[0]._update_texture(self, self_time)