- Ensure there is enough disk space for the write file
- Switch streaming mode only if necessary
- Return `Err` on seeking errors, instead of exiting
- Use the actual position after seeking
This change fixes two issues with the error handling of the
Symphonia decode loop.
1) `Error::ResetRequired` should always be propagated to jump
to the next Spotify track.
2) On a decode error, get a new packet and try again instead of
propagating the error and jumping to the next track.
- Configure the decoder according to Spotify's metadata, don't probe
- Return from `AudioFile::open` as soon as possible, with the smallest
possible block size suitable for opening the decoder, so the UI
transitions from loading to playing/paused state. From there,
the regular prefetching will take over.
- Set ideal sample buffer size after decoding first full packet
- Prevent audio glitches after seeking
- Reset decoder when the format reader requires it
Credits: @pdeljanov
- Switch from `lewton` to `Symphonia`. This is a pure Rust demuxer
and decoder in active development that supports a wide range of
formats, including Ogg Vorbis, MP3, AAC and FLAC for future HiFi
support. At the moment only Ogg Vorbis and MP3 are enabled; all
AAC files are DRM-protected.
- Bump MSRV to 1.51, required for `Symphonia`.
- Filter out all files whose format is not specified.
- Not all episodes seem to be encrypted. If we can't get an audio
key, try and see if we can play the file without decryption.
- After seeking, report the actual position instead of the target.
- Remove the 0xa7 bytes offset from `Subfile`, `Symphonia` does
not balk at Spotify's custom Ogg packet before it. This also
simplifies handling of formats other than Ogg Vorbis.
- When there is no next track to load, signal the UI that the
player has stopped. Before, the player would get stuck in an
infinite reloading loop when there was only one track in the
queue and that track could not be loaded.
- Don't load explicit tracks when the client setting forbids them
- When a client switches explicit filtering on *while* playing
an explicit track, immediately skip to the next track
This should fix remaining cases of a client connecting, and failing
to start playback from *beyond* the beginning when `librespot` is
still loading that track.
This undoes the `suppress_loading_status` workaround from #430,
under the assumption that the race condition reported there has
since been fixed on Spotify's end.
Further changes:
- Improve some debug and trace messages
- Default to streaming download strategy
- Synchronize mixer volume on loading play
- Use default normalisation values when the file position isn't
exactly what we need it to be
- Update track position only when the decoder reports a
successful seek
Dynamically set the alsa buffer and period based on the device's reported min/max buffer and period sizes. In the event of failure use the device's defaults.
This should have no effect on devices that allow for reasonable buffer and period sizes but would allow us to be more forgiving with less reasonable devices or configurations.
Closes: https://github.com/librespot-org/librespot/issues/895
* Expose all fields of recent protobufs
* Add support for user-scoped playlists, user root playlists and
playlist annotations
* Convert messages with the Rust type system
* Attempt to adhere to embargos (tracks and episodes scheduled for
future release)
* Return `Result`s with meaningful errors instead of panicking on
`unwrap`s
* Add foundation for future playlist editing
* Up version in connection handshake to get all version-gated features
This makes `--device ?` only show compatible devices (ones that support 2 ch 44.1 Interleaved) and it shows what `librespot` format(s) they support.
This should be more useful to users as the info maps directly to `librespot`'s `--device` and `--format` options.
* Don't panic when parsing options. Instead list valid values and exit.
* Get rid of needless .expect in playback/src/audio_backend/mod.rs.
* Enforce reasonable ranges for option values (breaking).
* Don't evaluate options that would otherwise have no effect.
* Add pub const MIXERS to mixer/mod.rs very similar to the audio_backend's implementation. (non-breaking though)
* Use different option descriptions and error messages based on what backends are enabled at build time.
* Add a -q, --quiet option that changed the logging level to warn.
* Add a short name for every flag and option.
* Note removed options.
* Other misc cleanups.
While `Xoshiro256+` is faster on 64-bit, it has low linear complexity in the
lower three bits, which *are* used when generating dither.
Also, while `Xoshiro128StarStar` access one less variable from the heap,
multiplication is generally slower than addition in hardware.
* Make error messages more consistent and concise.
* `impl From<AlsaError> for io::Error` so `AlsaErrors` can be thrown to player as `io::Errors`. This little bit of boilerplate goes a long way to simplifying things further down in the code. And will make any needed future changes easier.
* Bonus: handle ALSA backend buffer sizing a little better.
* Improve error handling
* Harmonize `Seek`: Make the decoders and player use the same math for converting between samples and milliseconds
* Reduce duplicate calls: Make decoder seek in PCM, not ms
* Simplify decoder errors with `thiserror`
* Reuse the buffer for the life of the Alsa sink
* Don't depend on capacity being exact when sizing the buffer
* Always give the PCM a period's worth of audio even when draining the buffer
* Refactoring and code cleanup
* More meaningful error messages
* Use F32 if a user requests F64 (F64 is not supported by PulseAudio)
* Move all code that can fail to `start` where errors can be returned to prevent panics
* Use drain in `stop`
Better error handling in Alsa backend
* More consistent error messages
* Bail on fatal errors in player
* Capture and log the original error as a warning when trying to write to PCM before trying to recover
* Go back to 4 periods at 125ms.
* Deal strictly in period time and periods to set ALSA buffer.
* Rename `buffer` to `period_buffer`.
* Add comments and change some other var names to add clarity.
* Let ALSA calculate the size of `period_buffer`.
This caused quite a bump in CPU usage, which be acceptable if this
actually improved sound quality. However, it turns out that this
function only has one decimal precision, i.e. it would consider
all values from `0.50..0.60` (exclusive) as `0.5` which is in
error for our purposes.
* Remove deprecated use of std::u16::MAX
* Use `FromStr` for fallible `&str` conversions
* DRY up strings into constants
* Change `as_ref().map()` into `as_deref()`
* Use `Duration` for time constants and functions
* Optimize `Vec` with response times
* Move comments for `rustdoc` to parse
Dithering lowers digital-to-analog conversion ("requantization") error, linearizing output, lowering distortion and replacing it with a constant, fixed noise level, which is more pleasant to the ear than the distortion.
Guidance:
- On S24, S24_3 and S24, the default is to use triangular dithering. Depending on personal preference you may use Gaussian dithering instead; it's not as good objectively, but it may be preferred subjectively if you are looking for a more "analog" sound akin to tape hiss.
- Advanced users who know that they have a DAC without noise shaping have a third option: high-passed dithering, which is like triangular dithering except that it moves dithering noise up in frequency where it is less audible. Note: 99% of DACs are of delta-sigma design with noise shaping, so unless you have a multibit / R2R DAC, or otherwise know what you are doing, this is not for you.
- Don't dither or shape noise on S32 or F32. On F32 it's not supported anyway (there are no integer conversions and so no rounding errors) and on S32 the noise level is so far down that it is simply inaudible even after volume normalisation and control.
New command line option:
--dither DITHER Specify the dither algorithm to use - [none, gpdf,
tpdf, tpdf_hp]. Defaults to 'tpdf' for formats S16
S24, S24_3 and 'none' for other formats.
Notes:
This PR also features some opportunistic improvements. Worthy of mention are:
- matching reference Vorbis sample conversion techniques for lower noise
- a cleanup of the convert API
This is a squashed commit featuring the following:
Connect:
- Synchronize player volume with mixer volume on playback
- Fix step size on volume up/down events
- Remove no-op mixer started/stopped logic
Playback:
- Move from `connect` to `playback` crate
- Make cubic volume control available to all mixers with `--volume-ctrl cubic`
- Normalize volumes to `[0.0..1.0]` instead of `[0..65535]` for greater precision and performance (breaking)
- Add `--volume-range` option to set dB range and control `log` and `cubic` volume control curves
- Fix `log` and `cubic` volume controls to be mute at zero volume
Alsa mixer:
- Complete rewrite (breaking)
- Query card dB range for the `log` volume control unless specified otherwise
- Query dB range from Alsa softvol (previously only from hardware)
- Use `--device` name for `--mixer-card` unless specified otherwise
- Fix consistency for `cubic` between cards that report minimum volume as mute, and cards that report some dB value
- Fix `--volume-ctrl {linear|log}` to work as expected
- Removed `--mixer-linear-volume` option; use `--volume-ctrl linear` instead
fe37186 added the restriction that `Sink`s must be `Send`. It turned
out later that this restrictions was unnecessary, and since some
`Sink`s aren't `Send` yet, this restriction is lifted again.
librespot-org/librespot#601 refactored the `RodioSink` in order to make
it `Send`. These changes are partly reverted in favour of the initial
simpler design.
Furthermore, there were some compile errors in the gstreamer backend
which are hereby fixed.
- DRY-ups
- Remove incorrect optimization attempt in the libvorbis decoder,
that skewed 0.0 samples non-linear
- PortAudio and SDL backends do not support S24 output. The PortAudio
bindings could, but not through this API.
Usage: `--format {F32|S16}`. Default is F32.
- Implemented for all backends, except for JACK audio which itself
only supports 32-bit output at this time. Setting JACK audio to S16
will panic and instruct the user to set output to F32.
- The F32 default works fine for Rodio on macOS, but not on Raspian 10
with Alsa as host. Therefore users on Linux systems are warned to set
output to S16 in case of garbled sound with Rodio. This seems an issue
with cpal incorrectly detecting the output stream format.
- While at it, DRY up lots of code in the backends and by that virtue,
also enable OggData passthrough on the subprocess backend.
- I tested Rodio, ALSA, pipe and subprocess quite a bit, and call on
others to join in and test the other backends.
- Store and output samples as 32-bit floats instead of 16-bit integers.
This provides 24-25 bits of transparency, allowing for 42-48 dB of
headroom to do volume control and normalisation without throwing
away bits or dropping dynamic range below 96 dB CD quality.
- Perform volume control and normalisation in 64-bit arithmetic.
- Add a dynamic limiter with configurable threshold, attack time,
release or decay time, and steepness for the sigmoid transfer
function. This mimics the native Spotify limiter, offering greater
dynamic range than the old limiter, that just reduced overall gain
to prevent clipping.
- Make the configurable threshold also apply to the old limiter, which
is still available.
Resolves: librespot-org/librespot#608
Probably more simple than the previous approach which
doubles the code: Instead of implementing the `Open` trait,
we simply use custom SinkBuilder, one for the default host,
and one for the "jack" host.