Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error parsing status when playing DSD files #38

Open
zhelezov opened this issue May 11, 2019 · 4 comments
Open

Error parsing status when playing DSD files #38

zhelezov opened this issue May 11, 2019 · 4 comments

Comments

@zhelezov
Copy link

zhelezov commented May 11, 2019

Not really sure where this problem comes from but I'll try my best to explain.

I've got a basic loop that idles in blocking mode waiting for Subsystem::Player events, matches Status.state and sets the cover on song change, or unsets it if stopped. It all goes well with other types of files, but if I start playing a DSF file I have a surge of player events and Status.state is always State::Stop. Meanwhile CPU usage goes sky high while looping through those events. Is this possibly an issue with MPD itself? How can I provide more info? Any way to clear the idle events queue?

Meanwhile mpc correctly reports state as playing

Edit: I asked if I can clear the events queue because even if I change the song to a known working one, say FLAC, MP3, whatever, I continue getting the accumulated events and state continues being matched to State::Stop.

@zhelezov
Copy link
Author

zhelezov commented May 11, 2019

My bad, I was using unwrap_or_default to get the Status so I was masking the actual error:
Parse(BadRate(ParseIntError { kind: InvalidDigit }))

So you can ignore the part that concerns matching against Status.state

The result from probing MPD directly:

OK MPD 0.20.0
status
volume: 100
repeat: 0
random: 0
single: 0
consume: 0
playlist: 76
playlistlength: 6
mixrampdb: 0.000000
state: play
song: 0
songid: 51
time: 16:629
elapsed: 16.253
bitrate: 2822
duration: 628.993
audio: dsd64:2
nextsong: 1
nextsongid: 52
OK

I'll have a look at the status parser later, but the audio: dsd64:2 field seems to be the culprit.

And still I don't get it, why the next call to wait() doesn't block?

@zhelezov zhelezov changed the title Status::Stop when _playing_ DSF files Error getting status when playing DSD files May 11, 2019
@zhelezov zhelezov changed the title Error getting status when playing DSD files Error parsing status when playing DSD files May 11, 2019
@zhelezov
Copy link
Author

zhelezov commented May 11, 2019

Investigating why the call to wait() doesn't block when playing a DSD file I found out this.

The first two calls to client.wait(&[mpd::Subsystem::Player]) result in immediate Ok([]). Why is the list empty and not Ok([Player]) as expected?

After those two I start getting Err(Io(Os { code: 32, kind: BrokenPipe, message: "Broken pipe" })), so yeah, that's why no blocking. Still need to figure out why the connection breaks down... If I reinitialize the client at the beginning of each loop iteration instead of outside the loop wait() returns immediate Ok([]) at each call.

Can you test it yourself? Play some DSD and run the following:

use mpd::Idle;

fn main() {
    let mut mpc = mpd::Client::connect("127.0.0.1:6600").unwrap();

    for _ in 0..5 {
        if let Err(e) = mpc.status() {
            eprintln!("status error: {:?}", e);
        }

        // Do something
        std::thread::sleep(std::time::Duration::from_millis(100));

        let result = mpc.wait(&[mpd::Subsystem::Player]);
        eprintln!("idle result: {:?}", result);
    }
}

I get:

status error: Parse(BadRate(ParseIntError { kind: InvalidDigit }))
idle result: Ok([])
idle result: Ok([])
idle result: Err(Io(Os { code: 32, kind: BrokenPipe, message: "Broken pipe" }))
status error: Io(Os { code: 32, kind: BrokenPipe, message: "Broken pipe" })
idle result: Err(Io(Os { code: 32, kind: BrokenPipe, message: "Broken pipe" }))
status error: Io(Os { code: 32, kind: BrokenPipe, message: "Broken pipe" })
idle result: Err(Io(Os { code: 32, kind: BrokenPipe, message: "Broken pipe" }))

@zhelezov
Copy link
Author

zhelezov commented May 12, 2019

Hi, I had a look at the parser and came up with the following which works for me:

impl FromStr for AudioFormat {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<AudioFormat, ParseError> {
        let mut dsd = false;
        let mut it = s.split(':');
        let rate = it.next().ok_or(ParseError::NoRate).and_then(|v| {
            if v.starts_with("dsd") {
                dsd = true;
                v.trim_start_matches("dsd").parse::<u32>().map(|v| v * 44100 / 8)
            } else {
                v.parse()
            }
            .map_err(ParseError::BadRate)
        })?;
        let bits = if dsd {
            1
        } else {
            it.next().ok_or(ParseError::NoBits).and_then(|v| match v {
                "f" => Ok(0),
                "dsd" => Ok(1),
                _ => v.parse().map_err(ParseError::BadBits),
            })?
        };
        let chans = it
            .next()
            .ok_or(ParseError::NoChans)
            .and_then(|v| v.parse().map_err(ParseError::BadChans))?;
        Ok(AudioFormat { rate, bits, chans })
    }
}

I'm new to Rust so I've no idea if it's idiomatic or silly, or whatever, but that's the gist and it works. I found the info in the MPD user manual, it was missing from the protocol specs, here:
https://www.musicpd.org/doc/html/user.html#configuring-audio-outputs

Anyway, I've still no clue why the connection breaks down on parse error, haven't looked at it. Any idea?

@zhelezov
Copy link
Author

zhelezov commented May 13, 2019

I've found these relevant bits in the MPD code base and I've put them together in a gist so I stop cluttering this feed with overly long snippets: https://gist.github.com/zhelezov/585e0dd20cce521f16cd46da00fa3f68

Basically FromStr should be orthogonal to ToString in AudioFormat.cxx. To achieve this without loss of information I've introduced a small enum -- SampleFormat, have a look at the last file in the gist: test.rs. I've used it for testing. The implementation of FromStr there is still not completely orthogonal though. It returns an error in case of SampleFormat::UNDEFINED but doesn't check if rate or chans equal zero, which they shouldn't.

Note that floating point PCM signal is defined with 32 bit resolution, not zero. Zero is defined as SampleFormat::UNDEFINED, so an error. You can verify that in the definition of sample_format_size() in SampleFormat.hxx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant