Skip to content

Error parsing status when playing DSD files #38

Open

Description

@zhelezov

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.

Activity

  1. zhelezov commented on May 11, 2019

    @zhelezov
    Author

    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?

  2. changed the title [-]Status::Stop when _playing_ DSF files[/-] [+]Error getting status when playing DSD files[/+] on May 11, 2019
  3. changed the title [-]Error getting status when playing DSD files[/-] [+]Error parsing status when playing DSD files[/+] on May 11, 2019
  4. zhelezov commented on May 11, 2019

    @zhelezov
    Author

    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" }))
    
  5. zhelezov commented on May 12, 2019

    @zhelezov
    Author

    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?

  6. zhelezov commented on May 13, 2019

    @zhelezov
    Author

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions