Implement `pause` and `play` for ALSA backend (#176)

* Implement `pause` and `play` for ALSA backend

This commit also ensures that the Voice is initially paused when
returned to remain consistent with the rest of the CPAL backends.

Related to #175.

* Remove ineffective pause from end of build_voice method

* ALSA - Change `is_paused` flag from `AtomicBool` to `bool`

* Add pause and play ALSA addition to CHANGELOG
This commit is contained in:
mitchmindtree 2017-11-02 19:30:15 +10:00 committed by tomaka
parent 743e4e3526
commit 019b27350f
2 changed files with 42 additions and 19 deletions

View File

@ -2,6 +2,7 @@
- Changed the emscripten backend to consume less CPU.
- Added improvements to the crate documentation.
- Implement `pause` and `play` for ALSA backend.
# Version 0.5.1 (2017-10-21)

View File

@ -13,8 +13,8 @@ use SupportedFormat;
use UnknownTypeBuffer;
use std::{cmp, ffi, iter, mem, ptr};
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::vec::IntoIter as VecIntoIter;
pub type SupportedFormatsIterator = VecIntoIter<SupportedFormat>;
@ -276,6 +276,8 @@ unsafe impl Sync for EventLoop {
enum Command {
NewVoice(VoiceInner),
PlayVoice(VoiceId),
PauseVoice(VoiceId),
DestroyVoice(VoiceId),
}
@ -309,8 +311,11 @@ struct VoiceInner {
// Minimum number of samples to put in the buffer.
period_len: usize,
// Wherease the sample stream is paused
is_paused: AtomicBool,
// Whether or not the hardware supports pausing the stream.
can_pause: bool,
// Whether or not the sample stream is currently paused.
is_paused: bool,
// A file descriptor opened with `eventfd`.
// It is used to wait for resume signal.
@ -367,6 +372,22 @@ impl EventLoop {
Command::DestroyVoice(voice_id) => {
run_context.voices.retain(|v| v.id != voice_id);
},
Command::PlayVoice(voice_id) => {
if let Some(voice) = run_context.voices.iter_mut()
.find(|voice| voice.can_pause && voice.id == voice_id)
{
alsa::snd_pcm_pause(voice.channel, 0);
voice.is_paused = false;
}
},
Command::PauseVoice(voice_id) => {
if let Some(voice) = run_context.voices.iter_mut()
.find(|voice| voice.can_pause && voice.id == voice_id)
{
alsa::snd_pcm_pause(voice.channel, 1);
voice.is_paused = true;
}
},
Command::NewVoice(voice_inner) => {
run_context.voices.push(voice_inner);
},
@ -561,6 +582,8 @@ impl EventLoop {
check_errors(alsa::snd_pcm_hw_params(playback_handle, hw_params.0))
.expect("hardware params could not be set");
let can_pause = alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1;
let mut sw_params = mem::uninitialized(); // TODO: RAII
check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)).unwrap();
check_errors(alsa::snd_pcm_sw_params_current(playback_handle, sw_params)).unwrap();
@ -605,36 +628,35 @@ impl EventLoop {
num_channels: format.channels.len() as u16,
buffer_len: buffer_len,
period_len: period_len,
is_paused: AtomicBool::new(true),
can_pause: can_pause,
is_paused: false,
resume_trigger: Trigger::new(),
};
self.commands
.lock()
.unwrap()
.push(Command::NewVoice(voice_inner));
self.pending_trigger.wakeup();
self.push_command(Command::NewVoice(voice_inner));
Ok(new_voice_id)
}
}
#[inline]
pub fn destroy_voice(&self, voice_id: VoiceId) {
self.commands
.lock()
.unwrap()
.push(Command::DestroyVoice(voice_id));
fn push_command(&self, command: Command) {
self.commands.lock().unwrap().push(command);
self.pending_trigger.wakeup();
}
#[inline]
pub fn play(&self, _: VoiceId) {
//unimplemented!()
pub fn destroy_voice(&self, voice_id: VoiceId) {
self.push_command(Command::DestroyVoice(voice_id));
}
#[inline]
pub fn pause(&self, _: VoiceId) {
unimplemented!()
pub fn play(&self, voice_id: VoiceId) {
self.push_command(Command::PlayVoice(voice_id));
}
#[inline]
pub fn pause(&self, voice_id: VoiceId) {
self.push_command(Command::PauseVoice(voice_id));
}
}