ProffieOS v7.x feature discussions - freeing wavplayer

Topic: Freeing a wavplayer when we run out. (such as spamming blasts).
Ideally, this would be the oldest triggered effect that is not hum, swingl, or swingh.

I thought I’d take a crack at this, but I’m doing something wrong when getting a currently occupied player because I still get ‘nothin’ reported.
But I should be on the right track?
Shouldn’t be a big deal to do, right?

  RefPtr<BufferedWavPlayer> PlayPolyphonic(Effect* f)  {
    EnableAmplifier();
    if (!f->files_found()) return RefPtr<BufferedWavPlayer>(nullptr);
    RefPtr<BufferedWavPlayer> player = GetFreeWavPlayer();
    if (player) {
      player->set_volume_now(font_config.volEff / 16.0f);
      player->PlayOnce(f);
      current_effect_length_ = player->length();
    } else {
STDOUT.println("************************** Out of WAV players! Getting more...I think.");
      RefPtr<BufferedWavPlayer> effect = GetWavPlayerPlaying(f);
      effect->set_fade_time(0.003);
      effect->FadeAndStop();
      effect.Free();
STDOUT.println("************************** ok freed?");
      player = GetFreeWavPlayer();
      if (player) {
STDOUT.println("************************** Found freed player, about to 2nd attempt to player->PlayOnce().");
        player->set_volume_now(font_config.volEff / 16.0f);
        player->PlayOnce(f);
        current_effect_length_ = player->length();
      } else {
STDOUT.println("************************** I got nothin. returning nothin.");
      }
    }
    return player;
  }

The 2nd attempt to play is to prevent a dead click while we “reload” this time around.

1 Like

First of all, when this calls GetWavPlayerPlaying() it’s not necessarily finding the oldest one.
However, the reason it probably doesn’t work is because GetFreeWavPlayer() won’t return a wav player that is playing a sound. And since you set fade time to 3 milliseconds, the wav player you’re trying to free won’t actually be free until 3 milliseconds later.

There is also a question of which is worse. Aborting a sound can make it sound terrible, but if we’re just cutting off the very end of it, it might be fine. Maybe we would need a per-effect value that specifies how far into the sound we need to be before it’s ok to abort it?

This is not right. Non-pointer type and other problems, but is something like this a way to find the oldest playing non-hum/SS player?

    float oldest_ = 0;
    float kill_;
// check wavplayers and rule out hum and smoothswings
    for (size_t i = 0; i < NELEM(wav_players); i++) {
      if (wav_players[i].current_file_id().GetEffect() == f) {
        float pos = wav_players[i]->pos();
        if (pos > oldest_) {
          oldest_ = pos;
          kill_ = wav_players[i]
        }   
      }
    }

where kill would then be faded and stopped.
Oh yeah, is fade time .0000001 fast enough?

Something like that should work.

There is no fade time that is fast enough. We either have to call Stop() on it (which I don’t recommend) or wait for it (which can be problematic too).

So doesn’t FadeandStop() happen, then we come back and do Free() before we move on?
Wondering why it would make the 2nd attempt before Free() is done.

How about just hurrying up the wait? Could we skip to the end by setting position to length minus 2 ms, fade for 1ms then stop and free?

  RefPtr<BufferedWavPlayer> PlayPolyphonic(Effect* f)  {
    EnableAmplifier();
    if (!f->files_found()) return RefPtr<BufferedWavPlayer>(nullptr);
    RefPtr<BufferedWavPlayer> player = GetFreeWavPlayer();
    if (!player) {
      STDOUT.println("************************** Out of WAV players! Getting more...I think.");
      float oldest_ = 0;
      float kill_;
  // check wavplayers, rule out hum and smoothswings
  // then kill oldest one by skipping to eof
      for (size_t i = 0; i < NELEM(wav_players); i++) {
        if (wav_players[i].current_file_id().GetEffect() == f) {
          float pos = wav_players[i]->pos();
          if (pos > oldest_) {
            oldest_ = pos;
            kill_ = wav_players[i]
          }   
        }
      }
      if (kill_->pos() > .002) {
        kill_.pos() = kill_->length() - .002;
        kill_->set_fade_time(0.001);
        kill_->FadeAndStop();
        kill_.Free();
        player = GetFreeWavPlayer();
      }
    }

    player->set_volume_now(font_config.volEff / 16.0f);
    player->PlayOnce(f);
    current_effect_length_ = player->length();
    return player;
  }

Would you be willing to sort out the RefPtr and type errors in this “theoretical” flow?
I know it doesn’t like float pos = wav_players[i]->pos();

Setting the position doesn’t change anything, it would just create a “click” in the sound.
If we had a time machine we could go back and start the fade 3 milliseconds ago, which would be handy, but unfortunately I’m all out of flux capacitors.

Try replacingwav_players[i]->whatever with wav_players[i].wavever.
wav_players[i] is not a pointer.

So are you saying the best we could shoot for is fade and stop asap and just minimize the “lag” ?
I can’t fix the errors so I’m at a stopping point right now.

The simplest option is to set the ramp-down time to something short, then call FadeAndStop() then do something like:

   while(kill_->isPlaying()) {
#ifdef VERSION_MAJOR >= 4
     armv7m_core_yield();
#endif
  }

This will make PlayPolyphonic take more time, since we’re waiting for the player to finish playing, but that just might be worth it.

A more complex solution would be to trigger a callback/event when a player becomes available and do the playback then.

By Jove, this works! Thanks!

  RefPtr<BufferedWavPlayer> PlayPolyphonic(Effect* f)  {
    EnableAmplifier();
    if (!f->files_found()) return RefPtr<BufferedWavPlayer>(nullptr);
    RefPtr<BufferedWavPlayer> player = GetFreeWavPlayer();
    if (!player) {
      STDOUT.println("************************** Out of WAV players! Getting more...");
      float oldest_ = 0;
      uint32_t kill_ = 0;
  // check wavplayers, rule out hum and smoothswings then kill oldest one 
      for (size_t i = 0; i < NELEM(wav_players); i++) {
        if (wav_players[i].current_file_id().GetEffect() == f) {
          float pos = wav_players[i].pos();
          if (pos > oldest_) {
            oldest_ = pos;
            kill_ = i;
          }   
        }
      }
      if (wav_players[kill_].pos() > .002) {
        wav_players[kill_].set_fade_time(0.001);
        wav_players[kill_].FadeAndStop();
        STDOUT << "*************************** Killing off " << wav_players[kill_].filename() << "\n";
        while(wav_players[kill_].isPlaying()) {
#ifdef VERSION_MAJOR >= 4
          armv7m_core_yield();
#endif
        }
        player = GetFreeWavPlayer();
      }
    }
    player->set_volume_now(font_config.volEff / 16.0f);
    player->PlayOnce(f);
    current_effect_length_ = player->length();
    return player;
  }

Output showing what was played and that the first played gets killed:
(I removed unrelated messages like ‘Playing swingl’ )

21:39:57.449 -> unit = 0 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst07.wav
21:39:57.449 -> channels: 1 rate: 44100 bits: 16
21:39:57.903 -> unit = 4 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst05.wav
21:39:57.903 -> channels: 1 rate: 44100 bits: 16
21:39:58.256 -> unit = 5 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst07.wav
21:39:58.256 -> channels: 1 rate: 44100 bits: 16
21:39:58.535 -> unit = 6 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst01.wav
21:39:58.569 -> channels: 1 rate: 44100 bits: 16
21:39:58.842 -> ************************** Out of WAV players! Getting more...
21:39:58.842 -> *************************** Killing off aa_Juansith/Red_Five/blst/blst07.wav
21:39:58.842 -> unit = 0 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst05.wav
21:39:58.842 -> channels: 1 rate: 44100 bits: 16


21:40:01.849 -> unit = 0 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst02.wav
21:40:01.849 -> channels: 1 rate: 44100 bits: 16
21:40:02.137 -> unit = 4 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst03.wav
21:40:02.137 -> channels: 1 rate: 44100 bits: 16
21:40:02.391 -> unit = 5 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst08.wav
21:40:02.391 -> channels: 1 rate: 44100 bits: 16
21:40:02.607 -> unit = 6 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst01.wav
21:40:02.607 -> channels: 1 rate: 44100 bits: 16
21:40:02.890 -> ************************** Out of WAV players! Getting more...
21:40:02.890 -> *************************** Killing off aa_Juansith/Red_Five/blst/blst02.wav
21:40:02.890 -> unit = 0 vol = 0.50, Playing aa_Juansith/Red_Five/blst/blst05.wav
21:40:02.890 -> channels: 1 rate: 44100 bits: 16

21:44:48.148 -> EVENT: Clash ON millis=311740
21:44:48.148 -> unit = 6 vol = 0.50, Playing aa_Juansith/Red_Five/clsh/clsh09.wav
21:44:48.148 -> channels: 1 rate: 44100 bits: 16
21:44:48.402 -> EVENT: Clash ON millis=312007
21:44:48.402 -> unit = 4 vol = 0.50, Playing aa_Juansith/Red_Five/clsh/clsh06.wav
21:44:48.402 -> channels: 1 rate: 44100 bits: 16
21:44:48.684 -> EVENT: Clash ON millis=312277
21:44:48.684 -> unit = 5 vol = 0.50, Playing aa_Juansith/Red_Five/clsh/clsh09.wav
21:44:48.684 -> channels: 1 rate: 44100 bits: 16
21:44:48.958 -> EVENT: Clash ON millis=312551
21:44:48.958 -> unit = 0 vol = 0.50, Playing aa_Juansith/Red_Five/clsh/clsh19.wav
21:44:48.958 -> channels: 1 rate: 44100 bits: 16
21:44:49.246 -> EVENT: Clash ON millis=312837
21:44:49.246 -> ************************** Out of WAV players! Getting more...
21:44:49.246 -> *************************** Killing off aa_Juansith/Red_Five/clsh/clsh09.wav
21:44:49.246 -> unit = 6 vol = 0.50, Playing aa_Juansith/Red_Five/clsh/clsh12.wav
21:44:49.246 -> channels: 1 rate: 44100 bits: 16
21:44:49.493 -> EVENT: Clash ON millis=313109
21:44:49.493 -> ************************** Out of WAV players! Getting more...
21:44:49.493 -> *************************** Killing off aa_Juansith/Red_Five/clsh/clsh06.wav
21:44:49.529 -> unit = 4 vol = 0.50, Playing aa_Juansith/Red_Five/clsh/clsh09.wav
21:44:49.529 -> channels: 1 rate: 44100 bits: 16
21:44:49.773 -> EVENT: Clash ON millis=313376
21:44:49.773 -> ************************** Out of WAV players! Getting more...
21:44:49.773 -> *************************** Killing off aa_Juansith/Red_Five/clsh/clsh09.wav
21:44:49.773 -> unit = 5 vol = 0.50, Playing aa_Juansith/Red_Five/clsh/clsh10.wav
21:44:49.808 -> channels: 1 rate: 44100 bits: 16

Video. I don’t think buttons can even spam this fast, and it’s handled great!

1 Like

So this is the good case though.
What happens if you tried to do this with like force quotes or something long?
I’m not sure if we can just make this the default for everything all the time.

Works like a charm. I have a 7 second Death Star Destruction and “Use the force, Luke” as force01 and force02. I spammed the bejeezus out of it for 10 secs straight and it consistently played something on each command.

Ok, but did it sound good? Or could you hear stuff getting interrupted?

It does not sound…NOT ok. Ya know, it sounds like triggering the same 2 sounds million times.
Use the…use the.force force boom boom use use the force Luke.
It sounds like expected. Blaster block sounds seem “all there”, nothing unnatural about lost tail ends at all in perception.
In my opinion it’s right, and with the goal being to HAVE an event on button press, not “duds”, it’s working great here. No pops or clicks, no gaps, and crazy good in the responsive department.
Of course someone else should try it. Results may vary? But looking good to me.

Aaaaaaand as soon as I hit return on that post and used the actual saber buttons for more testing. I got a NULL POINTER and squee of death. :frowning:

Edit- one time occurance so far.

@Afrojedi @Revo @Driftrotor @astromech
You guys are usually down for testing things. Anyone feel like swapping out lines 233-243 in sound/hybrid_font.h with the last posted code block above and see how it does for you?

2 Likes

I would love to but my usb connector still needs to be fixed. so i won’t be able to flash my saber.

I thought you had more than one saber by now?

1 Like

yeah I should but my workbench was taken over. so i kind of stopped, i need to get back on it. but being a big woose doesn’t help.

1 Like