Skip to content

Commit 306096e

Browse files
authored
Merge branch 'librespot-org:dev' into arg_parse_fixup
2 parents 368bee1 + 24e2c6d commit 306096e

3 files changed

Lines changed: 202 additions & 23 deletions

File tree

contrib/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#
1010
# If only one architecture is desired, cargo can be invoked directly with the appropriate options :
1111
# $ docker run -v /tmp/librespot-build:/build librespot-cross cargo build --release --no-default-features --features "alsa-backend"
12-
# $ docker run -v /tmp/librespot-build:/build librespot-cross cargo build --release --target arm-unknown-linux-gnueabihf --no-default-features --features "alsa-backend"
13-
# $ docker run -v /tmp/librespot-build:/build librespot-cross cargo build --release --target arm-unknown-linux-gnueabi --no-default-features --features "alsa-backend"
12+
# $ docker run -v /tmp/librespot-build:/build librespot-cross cargo build --release --target arm-unknown-linux-gnueabihf --no-default-features --features alsa-backend
13+
# $ docker run -v /tmp/librespot-build:/build librespot-cross cargo build --release --target arm-unknown-linux-gnueabi --no-default-features --features alsa-backend
14+
# $ docker run -v /tmp/librespot-build:/build librespot-cross cargo build --release --target aarch64-unknown-linux-gnu --no-default-features --features alsa-backend
15+
1416
# $ docker run -v /tmp/librespot-build:/build librespot-cross contrib/docker-build-pi-armv6hf.sh
1517

1618
FROM debian:stretch

playback/src/audio_backend/alsa.rs

Lines changed: 174 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@ use crate::convert::Converter;
44
use crate::decoder::AudioPacket;
55
use crate::{NUM_CHANNELS, SAMPLE_RATE};
66
use alsa::device_name::HintIter;
7-
use alsa::pcm::{Access, Format, HwParams, PCM};
7+
use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
88
use alsa::{Direction, ValueOr};
99
use std::cmp::min;
1010
use std::process::exit;
11-
use std::time::Duration;
1211
use thiserror::Error;
1312

14-
// 0.5 sec buffer.
15-
const PERIOD_TIME: Duration = Duration::from_millis(100);
16-
const BUFFER_TIME: Duration = Duration::from_millis(500);
13+
const MAX_BUFFER: Frames = (SAMPLE_RATE / 2) as Frames;
14+
const MIN_BUFFER: Frames = (SAMPLE_RATE / 10) as Frames;
15+
const ZERO_FRAMES: Frames = 0;
16+
17+
const MAX_PERIOD_DIVISOR: Frames = 4;
18+
const MIN_PERIOD_DIVISOR: Frames = 10;
1719

1820
#[derive(Debug, Error)]
1921
enum AlsaError {
@@ -195,28 +197,187 @@ fn open_device(dev_name: &str, format: AudioFormat) -> SinkResult<(PCM, usize)>
195197
e,
196198
})?;
197199

198-
hwp.set_buffer_time_near(BUFFER_TIME.as_micros() as u32, ValueOr::Nearest)
199-
.map_err(AlsaError::HwParams)?;
200+
// Clone the hwp while it's in
201+
// a good working state so that
202+
// in the event of an error setting
203+
// the buffer and period sizes
204+
// we can use the good working clone
205+
// instead of the hwp that's in an
206+
// error state.
207+
let hwp_clone = hwp.clone();
208+
209+
// At a sampling rate of 44100:
210+
// The largest buffer is 22050 Frames (500ms) with 5512 Frame periods (125ms).
211+
// The smallest buffer is 4410 Frames (100ms) with 441 Frame periods (10ms).
212+
// Actual values may vary.
213+
//
214+
// Larger buffer and period sizes are preferred as extremely small values
215+
// will cause high CPU useage.
216+
//
217+
// If no buffer or period size is in those ranges or an error happens
218+
// trying to set the buffer or period size use the device's defaults
219+
// which may not be ideal but are *hopefully* serviceable.
220+
221+
let buffer_size = {
222+
let max = match hwp.get_buffer_size_max() {
223+
Err(e) => {
224+
trace!("Error getting the device's max Buffer size: {}", e);
225+
ZERO_FRAMES
226+
}
227+
Ok(s) => s,
228+
};
200229

201-
hwp.set_period_time_near(PERIOD_TIME.as_micros() as u32, ValueOr::Nearest)
202-
.map_err(AlsaError::HwParams)?;
230+
let min = match hwp.get_buffer_size_min() {
231+
Err(e) => {
232+
trace!("Error getting the device's min Buffer size: {}", e);
233+
ZERO_FRAMES
234+
}
235+
Ok(s) => s,
236+
};
237+
238+
let buffer_size = if min < max {
239+
match (MIN_BUFFER..=MAX_BUFFER)
240+
.rev()
241+
.find(|f| (min..=max).contains(f))
242+
{
243+
Some(size) => {
244+
trace!("Desired Frames per Buffer: {:?}", size);
245+
246+
match hwp.set_buffer_size_near(size) {
247+
Err(e) => {
248+
trace!("Error setting the device's Buffer size: {}", e);
249+
ZERO_FRAMES
250+
}
251+
Ok(s) => s,
252+
}
253+
}
254+
None => {
255+
trace!("No Desired Buffer size in range reported by the device.");
256+
ZERO_FRAMES
257+
}
258+
}
259+
} else {
260+
trace!("The device's min reported Buffer size was greater than or equal to it's max reported Buffer size.");
261+
ZERO_FRAMES
262+
};
263+
264+
if buffer_size == ZERO_FRAMES {
265+
trace!(
266+
"Desired Buffer Frame range: {:?} - {:?}",
267+
MIN_BUFFER,
268+
MAX_BUFFER
269+
);
270+
271+
trace!(
272+
"Actual Buffer Frame range as reported by the device: {:?} - {:?}",
273+
min,
274+
max
275+
);
276+
}
203277

204-
pcm.hw_params(&hwp).map_err(AlsaError::Pcm)?;
278+
buffer_size
279+
};
280+
281+
let period_size = {
282+
if buffer_size == ZERO_FRAMES {
283+
ZERO_FRAMES
284+
} else {
285+
let max = match hwp.get_period_size_max() {
286+
Err(e) => {
287+
trace!("Error getting the device's max Period size: {}", e);
288+
ZERO_FRAMES
289+
}
290+
Ok(s) => s,
291+
};
205292

206-
let swp = pcm.sw_params_current().map_err(AlsaError::Pcm)?;
293+
let min = match hwp.get_period_size_min() {
294+
Err(e) => {
295+
trace!("Error getting the device's min Period size: {}", e);
296+
ZERO_FRAMES
297+
}
298+
Ok(s) => s,
299+
};
300+
301+
let max_period = buffer_size / MAX_PERIOD_DIVISOR;
302+
let min_period = buffer_size / MIN_PERIOD_DIVISOR;
303+
304+
let period_size = if min < max && min_period < max_period {
305+
match (min_period..=max_period)
306+
.rev()
307+
.find(|f| (min..=max).contains(f))
308+
{
309+
Some(size) => {
310+
trace!("Desired Frames per Period: {:?}", size);
311+
312+
match hwp.set_period_size_near(size, ValueOr::Nearest) {
313+
Err(e) => {
314+
trace!("Error setting the device's Period size: {}", e);
315+
ZERO_FRAMES
316+
}
317+
Ok(s) => s,
318+
}
319+
}
320+
None => {
321+
trace!("No Desired Period size in range reported by the device.");
322+
ZERO_FRAMES
323+
}
324+
}
325+
} else {
326+
trace!("The device's min reported Period size was greater than or equal to it's max reported Period size,");
327+
trace!("or the desired min Period size was greater than or equal to the desired max Period size.");
328+
ZERO_FRAMES
329+
};
330+
331+
if period_size == ZERO_FRAMES {
332+
trace!("Buffer size: {:?}", buffer_size);
333+
334+
trace!(
335+
"Desired Period Frame range: {:?} (Buffer size / {:?}) - {:?} (Buffer size / {:?})",
336+
min_period,
337+
MIN_PERIOD_DIVISOR,
338+
max_period,
339+
MAX_PERIOD_DIVISOR,
340+
);
341+
342+
trace!(
343+
"Actual Period Frame range as reported by the device: {:?} - {:?}",
344+
min,
345+
max
346+
);
347+
}
348+
349+
period_size
350+
}
351+
};
352+
353+
if buffer_size == ZERO_FRAMES || period_size == ZERO_FRAMES {
354+
trace!(
355+
"Failed to set Buffer and/or Period size, falling back to the device's defaults."
356+
);
357+
358+
trace!("You may experience higher than normal CPU usage and/or audio issues.");
359+
360+
pcm.hw_params(&hwp_clone).map_err(AlsaError::Pcm)?;
361+
} else {
362+
pcm.hw_params(&hwp).map_err(AlsaError::Pcm)?;
363+
}
364+
365+
let hwp = pcm.hw_params_current().map_err(AlsaError::Pcm)?;
207366

208367
// Don't assume we got what we wanted. Ask to make sure.
209368
let frames_per_period = hwp.get_period_size().map_err(AlsaError::HwParams)?;
210369

211370
let frames_per_buffer = hwp.get_buffer_size().map_err(AlsaError::HwParams)?;
212371

372+
let swp = pcm.sw_params_current().map_err(AlsaError::Pcm)?;
373+
213374
swp.set_start_threshold(frames_per_buffer - frames_per_period)
214375
.map_err(AlsaError::SwParams)?;
215376

216377
pcm.sw_params(&swp).map_err(AlsaError::Pcm)?;
217378

218-
trace!("Frames per Buffer: {:?}", frames_per_buffer);
219-
trace!("Frames per Period: {:?}", frames_per_period);
379+
trace!("Actual Frames per Buffer: {:?}", frames_per_buffer);
380+
trace!("Actual Frames per Period: {:?}", frames_per_period);
220381

221382
// Let ALSA do the math for us.
222383
pcm.frames_to_bytes(frames_per_period) as usize

playback/src/player.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,14 @@ impl PlayerTrackLoader {
737737
}
738738
};
739739

740-
assert!(audio.duration >= 0);
740+
if audio.duration < 0 {
741+
error!(
742+
"Track duration for <{}> cannot be {}",
743+
spotify_id.to_uri(),
744+
audio.duration
745+
);
746+
return None;
747+
}
741748
let duration_ms = audio.duration as u32;
742749

743750
// (Most) podcasts seem to support only 96 bit Vorbis, so fall back to it
@@ -943,9 +950,12 @@ impl Future for PlayerInternal {
943950
exit(1);
944951
}
945952
}
946-
Poll::Ready(Err(_)) => {
947-
warn!("Unable to load <{:?}>\nSkipping to next track", track_id);
948-
assert!(self.state.is_loading());
953+
Poll::Ready(Err(e)) => {
954+
warn!(
955+
"Skipping to next track, unable to load track <{:?}>: {:?}",
956+
track_id, e
957+
);
958+
debug_assert!(self.state.is_loading());
949959
self.send_event(PlayerEvent::EndOfTrack {
950960
track_id,
951961
play_request_id,
@@ -1045,8 +1055,11 @@ impl Future for PlayerInternal {
10451055
}
10461056
}
10471057
Err(e) => {
1048-
error!("PlayerInternal poll: {}", e);
1049-
exit(1);
1058+
warn!("Skipping to next track, unable to decode samples for track <{:?}>: {:?}", track_id, e);
1059+
self.send_event(PlayerEvent::EndOfTrack {
1060+
track_id,
1061+
play_request_id,
1062+
})
10501063
}
10511064
}
10521065
}
@@ -1058,8 +1071,11 @@ impl Future for PlayerInternal {
10581071
self.handle_packet(packet, normalisation_factor);
10591072
}
10601073
Err(e) => {
1061-
error!("PlayerInternal poll: {}", e);
1062-
exit(1);
1074+
warn!("Skipping to next track, unable to get next packet for track <{:?}>: {:?}", track_id, e);
1075+
self.send_event(PlayerEvent::EndOfTrack {
1076+
track_id,
1077+
play_request_id,
1078+
})
10631079
}
10641080
}
10651081
} else {

0 commit comments

Comments
 (0)