rodio icon indicating copy to clipboard operation
rodio copied to clipboard

Get current output frequency and volume from sink/decoder

Open GhostFire90 opened this issue 2 years ago • 4 comments

Is there a way in the lib to get the frequency and volume that is currently being outputted?

GhostFire90 avatar Feb 14 '23 23:02 GhostFire90

I am also trying to get something like this to work. So far, the closest I could get was this:

let s =  Decoder::new(file).unwrap()
            .periodic_access(Duration::from_millis(40), move |s| {
                for amp in s.take(s.channels() as usize) {
                    tx.try_send(amp);
                }
            });

Where tx is a SyncSender created like this:

let (tx, rx) = mpsc::sync_channel(8);

This "works" in that I can get some amplitude values out of the stream every 40ms, but since .take consumes the elements in the Source Iterator, it causes audio glitches because I am effectively deleting some samples out of the stream as I go.

I would love to use .peekable() to access some elements of the Iterator without consuming anything, but since the closure in .periodic_access is run repeatedly and .peekable() always calls .next() the first time it runs, it doesn't really help.

anselanza avatar Sep 18 '23 09:09 anselanza

Cross reference https://github.com/RustAudio/rodio/issues/238

anselanza avatar Sep 18 '23 09:09 anselanza

Same problem here. I created a SpyDecoder POC, encapsulating the decoder and iterator, keeping a bunch of samples from time to time. Seems to work.


pub struct SpyDecoder<R> where R: Read + Seek
{
    inner: Decoder<R>,
    stats: [i16; 512],
    stats_index: usize,
    stats_wait: usize,
    stats_wait_index: usize,
    stats_collect: bool,
}

impl<R> SpyDecoder<R>
    where
        R: Read + Seek + Send + Sync + 'static,
{
    pub fn new(data: R) -> Result<SpyDecoder<R>, DecoderError> {
        let inner = Decoder::new(data)?;
        Ok(Self {
            inner,
            stats: [0; 512],
            stats_index: 0,
            stats_wait: 44000 / 10,
            stats_wait_index: 0,
            stats_collect: false,
        })
    }
}

impl<R> Iterator for SpyDecoder<R>
    where R: Read + Seek {
    type Item = i16;
    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        let sample = self.inner.next();
        if self.stats_collect {
            if self.stats_index < 512 {
                if sample.is_some() {
                    self.stats[self.stats_index] = sample.unwrap();
                    self.stats_index += 1;
                }
            } else {
                self.stats_collect = false;
                self.stats_index = 0;
                // Use the stats (try_send to a channel for stats computation, etc)
            }
        } else {
            if self.stats_wait_index < self.stats_wait {
                self.stats_wait_index += 1;
            } else {
                self.stats_collect = true;
                self.stats_wait_index = 0;
            }
        }
        sample
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.inner.size_hint()
    }
}

impl<R> Source for SpyDecoder<R>
    where R: Read + Seek {
    fn current_frame_len(&self) -> Option<usize> {
        self.inner.current_frame_len()
    }

    fn channels(&self) -> u16 {
        self.inner.channels()
    }

    fn sample_rate(&self) -> u32 {
        self.inner.sample_rate()
    }

    fn total_duration(&self) -> Option<Duration> {
        self.inner.total_duration()
    }
}

jmbarbier avatar Oct 08 '23 19:10 jmbarbier

Same problem here. I created a SpyDecoder POC, encapsulating the decoder and iterator, keeping a bunch of samples from time to time. Seems to work.

...

Thank you! This code helped me a lot with my recent project. It really works. With periodic access and a message channel.

YDS67 avatar Jan 13 '24 02:01 YDS67

As the question seems to have been answered I am gonna close this. Feel free to reopen if you have something related.

yara-blue avatar Mar 27 '24 10:03 yara-blue