def _al_play(self): if not self._timestamps: return state = al.ALint() al.alGetSourcei(self._al_source, al.AL_SOURCE_STATE, state) if state.value != al.AL_PLAYING: al.alSourcePlay(self._al_source)
def play(self): if self._playing: return self._playing = True if not self._sources: return if self._sources[0].al_format: buffers = al.ALint() al.alGetSourcei(self._al_source, al.AL_BUFFERS_QUEUED, buffers) if buffers.value: al.alSourcePlay(self._al_source) self._al_playing = True self._last_known_system_time = time.time()
def pump(self): # Release spent buffers processed = al.ALint() al.alGetSourcei(self._al_source, al.AL_BUFFERS_PROCESSED, processed) processed = processed.value if processed: buffers = (al.ALuint * processed)() al.alSourceUnqueueBuffers(self._al_source, len(buffers), buffers) al.alDeleteBuffers(len(buffers), buffers) # Pop timestamps and check for eos markers try: while processed: if not _have_1_1: self._timestamp_system_time = time.time() _, duration = self._timestamps.pop(0) self._buffered_time -= duration while self._timestamps[0][0] is None: self._eos_count += 1 self._timestamps.pop(0) processed -= 1 except IndexError: pass if _have_1_1: samples = al.ALint() al.alGetSourcei(self._al_source, al.AL_SAMPLE_OFFSET, samples) self._current_buffer_time = samples.value / \ float(self.audio_format.sample_rate) else: # Interpolate system time past buffer timestamp self._current_buffer_time = time.time() - \ self._timestamp_system_time # Check for underrun if self._playing: state = al.ALint() al.alGetSourcei(self._al_source, al.AL_SOURCE_STATE, state) if state.value != al.AL_PLAYING: al.alSourcePlay(self._al_source) return True # underrun notification
def play(self): al.alSourcePlay(self.source)
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 # Calculate once only for this method. self_time = self.time # Update state of AL source state = al.ALint() al.alGetSourcei(self._al_source, al.AL_SOURCE_STATE, state) self._al_playing = state.value == al.AL_PLAYING if self._sources[0].al_format: # Find out how many buffers are done processed = al.ALint() al.alGetSourcei(self._al_source, al.AL_BUFFERS_PROCESSED, processed) processed = processed.value queued = al.ALint() al.alGetSourcei(self._al_source, al.AL_BUFFERS_QUEUED, queued) # Release spent buffers if processed: buffers = (al.ALuint * processed)() al.alSourceUnqueueBuffers(self._al_source, len(buffers), buffers) # If any buffers were EOS buffers, dispatch appropriate # event. for buffer in buffers: info = self._queued_buffers.pop(0) assert info is buffer_pool.info[buffer] if info.is_eos: if self._eos_action == self.EOS_NEXT: self.next() elif self._eos_action == self.EOS_STOP: # For ManagedSoundPlayer only. self.stop() self.dispatch_event('on_eos') buffer_pool.release(buffer) else: # Check for EOS on silent source if self_time > self._sources[0].duration: if self._eos_action == self.EOS_NEXT: self.next() self.dispatch_event('on_eos') # Determine minimum duration of audio already buffered (current buffer # is ignored, as this could be just about to be dequeued). buffer_time = sum([b.length for b in self._queued_buffers[1:]]) # Ensure audio buffers are full try: source = self._sources[self._source_read_index] except IndexError: source = None while source and buffer_time < self._min_buffer_time: # Read next packet of audio data if source.al_format: 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 there is audio data, create and queue a buffer if source.al_format and audio_data: buffer = buffer_pool.get(audio_data.timestamp, audio_data.duration, buffer_pool, audio_data.is_eos) al.alBufferData(buffer, source.al_format, audio_data.data, audio_data.length, source.audio_format.sample_rate) # TODO consolidate info and audio_data info = buffer_pool.info[buffer.value] self._queued_buffers.append(info) buffer_time += info.length # Queue this buffer onto the AL source. al.alSourceQueueBuffers(self._al_source, 1, ctypes.byref(buffer)) else: # No more data from source, check eos behaviour if self._eos_action == self.EOS_NEXT: self._source_read_index += 1 try: source = self._sources[self._source_read_index] source._play() # Preroll source ahead of buffering 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) # Ensure the AL source is playing (if there is a buffer underrun # this restarts the AL source). This needs to be at the end of the # function to ensure it catches newly queued sources without needing # a second iteration of dispatch_events. if (self._sources and self._sources[0].al_format and self._queued_buffers and self._playing and not self._al_playing): al.alSourcePlay(self._al_source) self._al_playing = True