Hello, I really need help figuring out Rodio audio playback for a rust project. I need to play a short audio clip of a casino wheel turning a certain amount of times in a loop. When I run the main function, the program plays the audio clip once and then stops completely. If anybody who has used Rodio can give me help it would be greatly appreciated. Also, I have tried using a longer duration in the play_sound function but it doesn’t change anything. Thank you

  • boring_bohr@feddit.de
    link
    fedilink
    arrow-up
    5
    ·
    edit-2
    10 months ago

    You say you’ve tried longer durations, but you’re only sleeping for microseconds here… I’d assume you’re simply playing the sound 30 times in 30 microseconds and as such only hear the last iteration. If you want the whole file to play every time, you could replace play_raw with play_once and use the returned Sink to simply sleep_until_end() instead of your own delay.

    Edit: I just saw that you wrote that the clip played once and then quit, that doesn’t make a lot of sense (with that sleep duration, it should quit pretty much immediately). It’s been a while since I played around with Rodio and things seem to have changed a little. From quickly looking into it, I’d probably only call your play_sound function once and edit it to create a Sink in there (see the example in the documentation). You can then append an infinitely repeating source to that sink and sleep for you total desired duration.

  • TehPers@beehaw.org
    link
    fedilink
    English
    arrow-up
    2
    ·
    edit-2
    10 months ago

    If you want the source to repeat indefinitely, you can try calling repeat_infinite on it. Combine that with pausable/stoppable, and use periodic_access to occasionally check whether the audio source should be paused/stopped probably by using an Arc[AtomicBool] (using square brackets because Lemmy hates angle ones).

    It could look something like this:

    let src = ...;
    let stop = Arc::new(AtomicBool::default());
    let stop2 = stop.clone();
    let src = src
        .repeat_infinite()
        .stoppable()
        .periodic_access(Duration::from_millis(50), move |src| {
            if stop2.load(Ordering::Relaxed) {
                src.stop();
            }
        });
    
    // later on...
    stop.store(true, Ordering::Relaxed);
    

    periodic_access is also how Sink controls the source when you want to pause/stop/etc it. You could probably use Sink directly if you want more control over the source.

    • uncle_agrey@lemmy.mlOP
      link
      fedilink
      English
      arrow-up
      1
      ·
      10 months ago

      I dont want to pause/play the source. I simply want to replay it over and over again but with a delay in between. The audio is a click of a game wheel spinning, its duration is somewhere in the low milliseconds. I have an exponential function which tells the program how much time to sleep until the next click on the wheel. I just want to play the sound each time the wheel clicks. Do you think appending the sound to a sink and removing it once Sink.sleep_until_end() finishes would produce that outcome.

      • TehPers@beehaw.org
        link
        fedilink
        English
        arrow-up
        3
        ·
        10 months ago

        In this case, I don’t think Sink will let you selectively remove sources (although you can clear the sink if you want), but whenever you want to play a click you could clear the sink and append the clicking source to it. Alternatively, you could create a source that chains the clicking sound with something like Zero and have it repeat indefinitely, but have the Zero source play until you receive a new signal to play the click audio (and stop the source once you’re done clicking).

        I think how you should approach this depends on your architecture, so I’ll give a couple approaches I would consider if I were trying to do this myself:

        1. For a blocking approach: I’d play the click sound once (using .append on the sink, for example), then use Instant::now() - last_instant and pass whatever duration is left to wait off to thread::sleep. This would look something like this (pseudo-ish code):

          let mut audio_started = Instant::now();
          for _click_idx in 0..num_clicks {
              sink.append(click_sound.clone()); // you can buffer the click_sound source and clone the buffer using .buffered() if needed
              let remaining = Instant::now() - audio_started;
              if remaining > Duration::ZERO {
                  std::thread::sleep(remaining);
              }
          }
          
        2. For a non-blocking approach where you have a separate thread managing the audio, I’d use a channel or similar as a signal for when to play the click. Your thread could then wait until that signal is received and append the click sound to the sink. You’d basically have a thread dedicated to managing the audio in this case. If you want a more complicated version this as an example, here’s a project where we used rodio with tauri (like you) to queue up audio sources to be played on demand whenever the user clicks certain buttons in the UI. The general architecture is the same - just a for loop listening for events from a channel, and then using those events to add sources to our output stream (though you can just use a Sink I believe to keep things simple).

        • uncle_agrey@lemmy.mlOP
          link
          fedilink
          arrow-up
          3
          ·
          10 months ago

          Man I implemented something like the Sink option and it is playing the audio clips at inputted intervals just as I wanted. The only thing missing for me now is just the intervals, which means i just need to tinker with my exponential time functions. Thank you so much for your help.

  • Knusper@feddit.de
    link
    fedilink
    arrow-up
    2
    ·
    10 months ago

    I don’t know Rodio, but in line 75 of your code, you’re hiding errors. You need to unwrap/expect the result or do something with it to see what errors are being thrown.