diff --git a/game/overlord/jak2/srpc.cpp b/game/overlord/jak2/srpc.cpp
index 8b9a05cec96..c50b14913cb 100644
--- a/game/overlord/jak2/srpc.cpp
+++ b/game/overlord/jak2/srpc.cpp
@@ -148,7 +148,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
           }
           // lg::warn("RPC: PLAY {} v:{}, p:{}", sound->name, GetVolume(sound), GetPan(sound));
 
-          s32 handle = snd_PlaySoundByNameVolPanPMPB(0, nullptr, sound->name, GetVolume(sound),
+          s32 handle = snd_PlaySoundByNameVolPanPMPB(nullptr, nullptr, sound->name, GetVolume(sound),
                                                      GetPan(sound), sound->params.pitch_mod,
                                                      sound->params.bend);
           sound->sound_handle = handle;
diff --git a/game/sound/989snd/ame_handler.cpp b/game/sound/989snd/ame_handler.cpp
index 4bad545f74e..7f66f53370c 100644
--- a/game/sound/989snd/ame_handler.cpp
+++ b/game/sound/989snd/ame_handler.cpp
@@ -12,12 +12,13 @@ namespace snd {
 u64 SoundFlavaHack = 0;
 u8 GlobalExcite = 0;
 
-AmeHandler::AmeHandler(MultiMidi* block,
+AmeHandler::AmeHandler(SoundHandle oid,
+                       MultiMidi* block,
                        MusicBank::MIDISound& sound,
                        s32 vol,
                        s32 pan,
                        SoundBank& bank)
-    : m_sound(sound), m_bank(bank), m_header(block), m_repeats(sound.Repeats) {
+    : SoundHandler(oid), m_sound(sound), m_bank(bank), m_header(block), m_repeats(sound.Repeats) {
   if (vol == VOLUME_DONT_CHANGE) {
     vol = 1024;
   }
@@ -57,8 +58,8 @@ void AmeHandler::StartSegment(u32 id) {
     // Skip adding if not midi type
     u32 type = (midi.SoundHandle >> 24) & 0xf;
     if (type == 1 || type == 3) {
-      m_midis.emplace(id, std::make_unique<MidiHandler>(static_cast<Midi*>(&midi), m_sound, m_vol,
-                                                        m_pan, m_bank, this));
+      m_midis.emplace(id, std::make_unique<MidiHandler>(0, static_cast<Midi*>(&midi), m_sound,
+                                                        m_vol, m_pan, m_bank, this));
     }
   }
 }
diff --git a/game/sound/989snd/ame_handler.h b/game/sound/989snd/ame_handler.h
index bad3aedc6e0..543ae543cb7 100644
--- a/game/sound/989snd/ame_handler.h
+++ b/game/sound/989snd/ame_handler.h
@@ -23,7 +23,12 @@ class AmeHandler : public SoundHandler {
   friend class MidiHandler;
 
  public:
-  AmeHandler(MultiMidi* block, MusicBank::MIDISound& sound, s32 vol, s32 pan, SoundBank& bank);
+  AmeHandler(SoundHandle oid,
+             MultiMidi* block,
+             MusicBank::MIDISound& sound,
+             s32 vol,
+             s32 pan,
+             SoundBank& bank);
   bool Tick() override;
   SoundBank& Bank() override { return m_bank; };
 
@@ -71,4 +76,10 @@ class AmeHandler : public SoundHandler {
   std::array<u8, 16> m_register{};
   std::array<u8*, 16> m_macro{};
 };
+
+AmeHandler* AllocAmeSound(MultiMidi* block,
+                          MusicBank::MIDISound& sound,
+                          s32 vol,
+                          s32 pan,
+                          SoundBank& bank);
 }  // namespace snd
diff --git a/game/sound/989snd/blocksound_handler.cpp b/game/sound/989snd/blocksound_handler.cpp
index 7834abe841a..f9df448fbb0 100644
--- a/game/sound/989snd/blocksound_handler.cpp
+++ b/game/sound/989snd/blocksound_handler.cpp
@@ -10,12 +10,11 @@
 namespace snd {
 std::array<s8, 32> g_block_reg{};
 
-BlockSoundHandler::BlockSoundHandler(SoundBank& bank,
-                                     SFXBlock::SFX& sfx,
-                                     s32 sfx_vol,
-                                     s32 sfx_pan,
-                                     SndPlayParams& params)
-    : m_group(sfx.VolGroup), m_sfx(sfx), m_bank(bank) {
+BlockSoundHandler* BlockSoundHandler::MakeBlockSound(SoundBank& bank,
+                                                     SFXBlock::SFX& sfx,
+                                                     s32 sfx_vol,
+                                                     s32 sfx_pan,
+                                                     SndPlayParams& params) {
   s32 vol, pan, pitch_mod, pitch_bend;
   if (sfx_vol == -1) {
     sfx_vol = sfx.Vol;
@@ -60,42 +59,49 @@ BlockSoundHandler::BlockSoundHandler(SoundBank& bank,
     pan = sfx_pan;
   }
 
-  m_orig_volume = sfx_vol;
-  m_orig_pan = sfx_pan;
+  if (sfx.Flags.solo()) {
+    lg::warn("989snd: Unsupported solo sound flag");
+  }
+
+  auto* hnd = AllocBlockSound(bank, sfx, vol);
+  if (hnd == nullptr) {
+    return nullptr;
+  }
 
-  m_cur_volume = play_vol;
-  m_cur_pan = pan;
-  m_cur_pb = pitch_bend;
-  m_cur_pm = pitch_mod;
+  hnd->m_orig_volume = sfx_vol;
+  hnd->m_orig_pan = sfx_pan;
 
-  m_app_volume = vol;
-  m_app_pan = pan;
-  m_app_pb = pitch_bend;
-  m_app_pm = pitch_mod;
+  hnd->m_cur_volume = play_vol;
+  hnd->m_cur_pan = pan;
+  hnd->m_cur_pb = pitch_bend;
+  hnd->m_cur_pm = pitch_mod;
 
-  m_lfo_volume = 0;
-  m_lfo_pan = 0;
-  m_lfo_pb = 0;
-  m_lfo_pm = 0;
+  hnd->m_app_volume = vol;
+  hnd->m_app_pan = pan;
+  hnd->m_app_pb = pitch_bend;
+  hnd->m_app_pm = pitch_mod;
+
+  hnd->m_lfo_volume = 0;
+  hnd->m_lfo_pan = 0;
+  hnd->m_lfo_pb = 0;
+  hnd->m_lfo_pm = 0;
 
   if (params.registers.has_value()) {
-    m_registers = params.registers.value();
+    hnd->m_registers = params.registers.value();
   }
 
-  // Figure this stuff out properly someday
-  // if (m_sfx.d.Flags & 2) {
-  //   fmt::print("solo flag\n");
-  //   m_done = true;
-  //   return;
-  // }
-
-  m_next_grain = 0;
-  m_countdown = m_sfx.Grains[0].Delay;
-  while (m_countdown <= 0 && !m_done) {
-    DoGrain();
+  hnd->m_next_grain = 0;
+  hnd->m_countdown = hnd->m_sfx.Grains[0].Delay;
+  while (hnd->m_countdown <= 0 && !hnd->m_done) {
+    hnd->DoGrain();
   }
+
+  return hnd;
 }
 
+BlockSoundHandler::BlockSoundHandler(SoundHandle oid, SoundBank& bank, SFXBlock::SFX& sfx)
+    : SoundHandler(oid), m_group(sfx.VolGroup), m_sfx(sfx), m_bank(bank) {}
+
 BlockSoundHandler::~BlockSoundHandler() {
   for (auto& p : m_voices) {
     auto v = p.lock();
@@ -113,8 +119,9 @@ bool BlockSoundHandler::Tick() {
   }
 
   for (auto it = m_children.begin(); it != m_children.end();) {
-    bool done = it->get()->Tick();
+    bool done = (*it)->Tick();
     if (done) {
+      FreeSound(*it);
       it = m_children.erase(it);
     } else {
       ++it;
diff --git a/game/sound/989snd/blocksound_handler.h b/game/sound/989snd/blocksound_handler.h
index cdd0cf58769..d0c3833d12c 100644
--- a/game/sound/989snd/blocksound_handler.h
+++ b/game/sound/989snd/blocksound_handler.h
@@ -20,11 +20,13 @@ class BlockSoundVoice : public VagVoice {
 
 class BlockSoundHandler : public SoundHandler {
  public:
-  BlockSoundHandler(SoundBank& bank,
-                    SFXBlock::SFX& sfx,
-                    s32 sfx_vol,
-                    s32 sfx_pan,
-                    SndPlayParams& params);
+  static BlockSoundHandler* MakeBlockSound(SoundBank& bank,
+                                           SFXBlock::SFX& sfx,
+                                           s32 sfx_vol,
+                                           s32 sfx_pan,
+                                           SndPlayParams& params);
+
+  BlockSoundHandler(SoundHandle oid, SoundBank& bank, SFXBlock::SFX& sfx);
 
   ~BlockSoundHandler() override;
   bool Tick() override;
@@ -55,8 +57,7 @@ class BlockSoundHandler : public SoundHandler {
   SFXBlock::SFX& m_sfx;
 
   std::list<std::weak_ptr<BlockSoundVoice>> m_voices;
-
-  std::list<std::unique_ptr<SoundHandler>> m_children;
+  std::vector<SoundHandler*> m_children;
 
   s32 m_orig_volume{0};
   s32 m_orig_pan{0};
@@ -85,4 +86,7 @@ class BlockSoundHandler : public SoundHandler {
   s32 m_countdown{0};
   u32 m_next_grain{0};
 };
+
+BlockSoundHandler* AllocBlockSound(SoundBank& bank, SFXBlock::SFX& sfx, s32 sfx_vol);
+
 }  // namespace snd
diff --git a/game/sound/989snd/midi_handler.cpp b/game/sound/989snd/midi_handler.cpp
index c8d8e3931b4..a9c008179d8 100644
--- a/game/sound/989snd/midi_handler.cpp
+++ b/game/sound/989snd/midi_handler.cpp
@@ -22,12 +22,13 @@ namespace snd {
 **
 */
 
-MidiHandler::MidiHandler(Midi* block,
+MidiHandler::MidiHandler(SoundHandle oid,
+                         Midi* block,
                          MusicBank::MIDISound& sound,
                          s32 vol,
                          s32 pan,
                          SoundBank& bank)
-    : m_sound(sound), m_repeats(sound.Repeats), m_bank(bank), m_header(block) {
+    : SoundHandler(oid), m_sound(sound), m_repeats(sound.Repeats), m_bank(bank), m_header(block) {
   if (vol == VOLUME_DONT_CHANGE) {
     vol = 1024;
   }
@@ -46,13 +47,15 @@ MidiHandler::MidiHandler(Midi* block,
   InitMidi();
 }
 
-MidiHandler::MidiHandler(Midi* block,
+MidiHandler::MidiHandler(SoundHandle oid,
+                         Midi* block,
                          MusicBank::MIDISound& sound,
                          s32 vol,
                          s32 pan,
                          SoundBank& bank,
                          std::optional<AmeHandler*> parent)
-    : m_parent(parent),
+    : SoundHandler(oid),
+      m_parent(parent),
       m_sound(sound),
       m_vol(vol),
       m_pan(pan),
diff --git a/game/sound/989snd/midi_handler.h b/game/sound/989snd/midi_handler.h
index de3e4dbc80e..992ba6a460d 100644
--- a/game/sound/989snd/midi_handler.h
+++ b/game/sound/989snd/midi_handler.h
@@ -29,9 +29,15 @@ class midi_voice : public VagVoice {
 class AmeHandler;
 class MidiHandler : public SoundHandler {
  public:
-  MidiHandler(Midi* block, MusicBank::MIDISound& sound, s32 vol, s32 pan, SoundBank& bank);
+  MidiHandler(SoundHandle oid,
+              Midi* block,
+              MusicBank::MIDISound& sound,
+              s32 vol,
+              s32 pan,
+              SoundBank& bank);
 
-  MidiHandler(Midi* block,
+  MidiHandler(SoundHandle oid,
+              Midi* block,
               MusicBank::MIDISound& sound,
               s32 vol,
               s32 pan,
@@ -123,4 +129,11 @@ class MidiHandler : public SoundHandler {
 
   static std::pair<size_t, u32> ReadVLQ(u8* value);
 };
+
+MidiHandler* AllocMidiSound(Midi* block,
+                            MusicBank::MIDISound& sound,
+                            s32 vol,
+                            s32 pan,
+                            SoundBank& bank);
+
 }  // namespace snd
diff --git a/game/sound/989snd/musicbank.cpp b/game/sound/989snd/musicbank.cpp
index 8a8eb25b66d..ec29d43833d 100644
--- a/game/sound/989snd/musicbank.cpp
+++ b/game/sound/989snd/musicbank.cpp
@@ -7,11 +7,7 @@
 
 namespace snd {
 
-std::optional<std::unique_ptr<SoundHandler>> MusicBank::MakeHandler(u32 sound_id,
-                                                                    s32 vol,
-                                                                    s32 pan,
-                                                                    s32 pm,
-                                                                    s32 pb) {
+SoundHandler* MusicBank::MakeHandler(u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) {
   auto& sound = Sounds[sound_id];
 
   // FIXME: global midi list
@@ -21,27 +17,24 @@ std::optional<std::unique_ptr<SoundHandler>> MusicBank::MakeHandler(u32 sound_id
   if (sound.Type == 4) {
     auto& midi = std::get<Midi>(MidiData);
     if (sound.MIDIID == midi.ID) {
-      return std::make_unique<MidiHandler>(&midi, sound, vol, pan, *this);
+      return AllocMidiSound(&midi, sound, vol, pan, *this);
     }
-    return std::nullopt;
+    return nullptr;
   } else if (sound.Type == 5) {
     auto& midi = std::get<MultiMidi>(MidiData);
     if (sound.MIDIID == midi.ID) {
-      return std::make_unique<AmeHandler>(&midi, sound, vol, pan, *this);
+      return AllocAmeSound(&midi, sound, vol, pan, *this);
     }
-    return std::nullopt;
+    return nullptr;
   } else {
     lg::error("Invalid music sound type");
-    return std::nullopt;
+    return nullptr;
     // error
   }
 }
 
-std::optional<std::unique_ptr<SoundHandler>> MusicBank::MakeHandler(u32 sound_id,
-                                                                    s32 vol,
-                                                                    s32 pan,
-                                                                    SndPlayParams& params) {
-  return std::nullopt;
+SoundHandler* MusicBank::MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) {
+  return nullptr;
 }
 
 }  // namespace snd
diff --git a/game/sound/989snd/musicbank.h b/game/sound/989snd/musicbank.h
index 40d6e8b3b39..177ac29ff06 100644
--- a/game/sound/989snd/musicbank.h
+++ b/game/sound/989snd/musicbank.h
@@ -67,15 +67,7 @@ class MusicBank : public SoundBank {
                              nonstd::span<u8> samples,
                              nonstd::span<u8> midi_data);
 
-  std::optional<std::unique_ptr<SoundHandler>> MakeHandler(u32 sound_id,
-                                                           s32 vol,
-                                                           s32 pan,
-                                                           s32 pm,
-                                                           s32 pb) override;
-
-  std::optional<std::unique_ptr<SoundHandler>> MakeHandler(u32 sound_id,
-                                                           s32 vol,
-                                                           s32 pan,
-                                                           SndPlayParams& params) override;
+  SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) override;
+  SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) override;
 };
 }  // namespace snd
diff --git a/game/sound/989snd/player.cpp b/game/sound/989snd/player.cpp
index f05d0bdfe6b..f2b01ea2ee9 100644
--- a/game/sound/989snd/player.cpp
+++ b/game/sound/989snd/player.cpp
@@ -6,11 +6,13 @@
 
 #include "loader.h"
 #include "sfxblock.h"
+#include "sound_handler.h"
 #include "vagvoice.h"
 
 #include "third-party/fmt/core.h"
 
 #ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
 #include <combaseapi.h>
 #include <windows.h>
 #endif
@@ -20,8 +22,7 @@ namespace snd {
 
 u8 g_global_excite = 0;
 std::recursive_mutex gTickLock;  // TODO does not need to recursive with some light restructuring
-IdAllocator gHandleAllocator;
-std::unordered_map<u32, std::unique_ptr<SoundHandler>> gHandlers;
+std::vector<SoundHandler*> gHandlers;
 Synth gSynth;
 s32 gTick{0};
 
@@ -134,10 +135,10 @@ void Tick(s16Output* stream, int samples) {
       gTick++;
 
       for (auto it = gHandlers.begin(); it != gHandlers.end();) {
-        bool done = it->second->Tick();
+        bool done = (*it)->Tick();
         if (done) {
           // fmt::print("erasing handler\n");
-          gHandleAllocator.FreeId(it->first);
+          FreeSound(*it);
           it = gHandlers.erase(it);
         } else {
           ++it;
@@ -167,15 +168,12 @@ u32 PlaySound(BankHandle bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb
   }
 
   auto handler = bank->MakeHandler(sound_id, vol, pan, pm, pb);
-  if (!handler.has_value()) {
+  if (!handler) {
     return 0;
   }
 
-  u32 handle = gHandleAllocator.GetId();
-  gHandlers.emplace(handle, std::move(handler.value()));
-  // fmt::print("play_sound {}:{} - {}\n", bank_id, sound_id, handle);
-
-  return handle;
+  gHandlers.push_back(handler);
+  return handler->Handle();
 }
 
 u32 PlaySoundByName(BankHandle bank_id,
@@ -202,6 +200,7 @@ u32 PlaySoundByName(BankHandle bank_id,
 
   auto sound = bank->GetSoundByName(sound_name);
   if (sound.has_value()) {
+    // lg::error("play_sound_by_name: playing {}", sound_name);
     return PlaySound(bank, sound.value(), vol, pan, pm, pb);
   }
 
@@ -212,35 +211,32 @@ u32 PlaySoundByName(BankHandle bank_id,
 
 void StopSound(u32 sound_id) {
   std::scoped_lock lock(gTickLock);
-  auto handler = gHandlers.find(sound_id);
-  if (handler == gHandlers.end())
+  auto s = GetSound(sound_id);
+  if (s == nullptr)
     return;
 
-  handler->second->Stop();
-
-  // m_handle_allocator.free_id(sound_id);
-  // m_handlers.erase(sound_id);
+  s->Stop();
 }
 
 void SetSoundReg(u32 sound_id, u8 reg, u8 value) {
   std::scoped_lock lock(gTickLock);
-  if (gHandlers.find(sound_id) == gHandlers.end()) {
+  auto s = GetSound(sound_id);
+  if (s == nullptr) {
     // fmt::print("set_midi_reg: Handler {} does not exist\n", sound_id);
     return;
   }
 
-  auto* handler = gHandlers.at(sound_id).get();
-  handler->SetRegister(reg, value);
+  s->SetRegister(reg, value);
 }
 
-bool SoundStillActive(u32 sound_id) {
+u32 SoundStillActive(u32 sound_id) {
   std::scoped_lock lock(gTickLock);
-  auto handler = gHandlers.find(sound_id);
-  if (handler == gHandlers.end())
-    return false;
+  auto s = GetSound(sound_id);
+  if (s == nullptr) {
+    return 0;
+  }
 
-  // fmt::print("sound_still_active {}\n", sound_id);
-  return true;
+  return s->Handle();
 }
 
 void SetMasterVolume(u32 group, s32 volume) {
@@ -274,8 +270,8 @@ void UnloadBank(BankHandle bank_handle) {
     return;
 
   for (auto it = gHandlers.begin(); it != gHandlers.end();) {
-    if (&it->second->Bank() == bank_handle) {
-      gHandleAllocator.FreeId(it->first);
+    if (&((*it)->Bank()) == bank) {
+      FreeSound(*it);
       it = gHandlers.erase(it);
     } else {
       ++it;
@@ -287,28 +283,28 @@ void UnloadBank(BankHandle bank_handle) {
 
 void PauseSound(s32 sound_id) {
   std::scoped_lock lock(gTickLock);
-  auto handler = gHandlers.find(sound_id);
-  if (handler == gHandlers.end())
+  auto s = GetSound(sound_id);
+  if (s == nullptr)
     return;
 
-  handler->second->Pause();
+  s->Pause();
 }
 
 void ContinueSound(s32 sound_id) {
   std::scoped_lock lock(gTickLock);
-  auto handler = gHandlers.find(sound_id);
-  if (handler == gHandlers.end())
+  auto s = GetSound(sound_id);
+  if (s == nullptr)
     return;
 
-  handler->second->Unpause();
+  s->Unpause();
 }
 
 void PauseAllSoundsInGroup(u8 group) {
   std::scoped_lock lock(gTickLock);
 
   for (auto& h : gHandlers) {
-    if ((1 << h.second->Group()) & group) {
-      h.second->Pause();
+    if ((1 << h->Group()) & group) {
+      h->Pause();
     }
   }
 }
@@ -317,34 +313,34 @@ void ContinueAllSoundsInGroup(u8 group) {
   std::scoped_lock lock(gTickLock);
 
   for (auto& h : gHandlers) {
-    if ((1 << h.second->Group()) & group) {
-      h.second->Unpause();
+    if ((1 << h->Group()) & group) {
+      h->Unpause();
     }
   }
 }
 
 void SetSoundVolPan(s32 sound_id, s32 vol, s32 pan) {
   std::scoped_lock lock(gTickLock);
-  auto handler = gHandlers.find(sound_id);
-  if (handler == gHandlers.end())
+  auto s = GetSound(sound_id);
+  if (s == nullptr)
     return;
 
-  handler->second->SetVolPan(vol, pan);
+  s->SetVolPan(vol, pan);
 }
 
 void SetSoundPmod(s32 sound_handle, s32 mod) {
   std::scoped_lock lock(gTickLock);
-  auto handler = gHandlers.find(sound_handle);
-  if (handler == gHandlers.end())
+  auto s = GetSound(sound_handle);
+  if (s == nullptr)
     return;
 
-  handler->second->SetPMod(mod);
+  s->SetPMod(mod);
 }
 
 void StopAllSounds() {
   std::scoped_lock lock(gTickLock);
   for (auto it = gHandlers.begin(); it != gHandlers.end();) {
-    gHandleAllocator.FreeId(it->first);
+    FreeSound(*it);
     it = gHandlers.erase(it);
   }
 }
diff --git a/game/sound/989snd/player.h b/game/sound/989snd/player.h
index 77ac42bdf6c..1a307180489 100644
--- a/game/sound/989snd/player.h
+++ b/game/sound/989snd/player.h
@@ -34,7 +34,7 @@ u32 PlaySoundByName(BankHandle bank,
                     s32 pb);
 void SetSoundReg(u32 sound_id, u8 reg, u8 value);
 void SetGlobalExcite(u8 value);
-bool SoundStillActive(u32 sound_id);
+u32 SoundStillActive(u32 sound_id);
 void SetMasterVolume(u32 group, s32 volume);
 void UnloadBank(BankHandle bank_handle);
 void StopSound(u32 sound_handle);
diff --git a/game/sound/989snd/sfxblock.cpp b/game/sound/989snd/sfxblock.cpp
index 8b957bf40f1..053d95a0894 100644
--- a/game/sound/989snd/sfxblock.cpp
+++ b/game/sound/989snd/sfxblock.cpp
@@ -7,17 +7,14 @@
 
 namespace snd {
 
-std::optional<std::unique_ptr<SoundHandler>> SFXBlock::MakeHandler(u32 sound_id,
-                                                                   s32 vol,
-                                                                   s32 pan,
-                                                                   SndPlayParams& params) {
+SoundHandler* SFXBlock::MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) {
   auto& SFX = Sounds[sound_id];
 
   if (SFX.Grains.empty()) {
-    return std::nullopt;
+    return nullptr;
   }
 
-  auto handler = std::make_unique<BlockSoundHandler>(*this, SFX, vol, pan, params);
+  auto handler = BlockSoundHandler::MakeBlockSound(*this, SFX, vol, pan, params);
   return handler;
 }
 
diff --git a/game/sound/989snd/sfxblock.h b/game/sound/989snd/sfxblock.h
index 13b5cf7a565..3dba02d3f5a 100644
--- a/game/sound/989snd/sfxblock.h
+++ b/game/sound/989snd/sfxblock.h
@@ -48,10 +48,7 @@ class SFXBlock : public SoundBank {
 
   static SFXBlock* ReadBlock(nonstd::span<u8> bank_data, nonstd::span<u8> samples);
 
-  std::optional<std::unique_ptr<SoundHandler>> MakeHandler(u32 sound_id,
-                                                           s32 vol,
-                                                           s32 pan,
-                                                           SndPlayParams& params) override;
+  SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) override;
 
   std::optional<std::string_view> GetName() override { return Name; };
   std::optional<u32> GetSoundByName(const char* name) override;
diff --git a/game/sound/989snd/sfxgrain.cpp b/game/sound/989snd/sfxgrain.cpp
index da5b49344b0..e68b538e7fe 100644
--- a/game/sound/989snd/sfxgrain.cpp
+++ b/game/sound/989snd/sfxgrain.cpp
@@ -158,8 +158,8 @@ s32 Grain::snd_SFX_GRAIN_TYPE_STARTCHILDSOUND(BlockSoundHandler& handler) {
 
   if (index >= 0) {
     auto child_handler = block.MakeHandler(index, vol, pan, params);
-    if (child_handler.has_value()) {
-      handler.m_children.emplace_front(std::move(child_handler.value()));
+    if (child_handler) {
+      handler.m_children.push_back(child_handler);
     }
 
     return 0;
@@ -176,9 +176,10 @@ s32 Grain::snd_SFX_GRAIN_TYPE_STOPCHILDSOUND(BlockSoundHandler& handler) {
 
   if (psp.sound_id >= 0) {
     for (auto it = handler.m_children.begin(); it != handler.m_children.end();) {
-      auto* sound = static_cast<BlockSoundHandler*>(it->get());
+      auto* sound = static_cast<BlockSoundHandler*>(*it);
       // TODO VERIFY that this works
       if (&sound->m_sfx == &block.Sounds[psp.sound_id]) {
+        FreeSound(sound);
         it = handler.m_children.erase(it);
       } else {
         ++it;
diff --git a/game/sound/989snd/sound_handler.cpp b/game/sound/989snd/sound_handler.cpp
new file mode 100644
index 00000000000..b05f9ae8abe
--- /dev/null
+++ b/game/sound/989snd/sound_handler.cpp
@@ -0,0 +1,251 @@
+#include "sound_handler.h"
+
+#include "ame_handler.h"
+#include "blocksound_handler.h"
+#include "midi_handler.h"
+
+namespace snd {
+
+enum {
+  SOUND_BLOCK = 1,
+  SOUND_MIDI,
+  SOUND_AME,
+};
+
+static constexpr SoundHandle MakeHandle(u8 tag, u8 idx, u16 sequence) {
+  return ((tag & 0x1f) << 24) | (idx << 16) | sequence;
+}
+
+static constexpr u8 HandleType(SoundHandle handle) {
+  return (handle >> 24) & 0x1f;
+}
+
+static constexpr u8 HandleIndex(SoundHandle handle) {
+  return (handle >> 16) & 0xff;
+}
+
+static constexpr u16 HandleSequence(SoundHandle handle) {
+  return handle & 0xffff;
+}
+
+template <typename T, u32 size, u8 tag>
+class HandlerPool {
+ public:
+  HandlerPool() {
+    int i = 0;
+    for (auto& h : mHandlers) {
+      h.ID = MakeHandle(tag, i, 0);
+      h.Free = true;
+      i++;
+    }
+  }
+
+  T* GetPtrFromHandle(SoundHandle handle) {
+    auto idx = HandleIndex(handle);
+    if (idx >= size) {
+      return nullptr;
+    }
+
+    auto& h = mHandlers[idx];
+    if (h.Free == false && h.ID == handle) {
+      return reinterpret_cast<T*>(&h.hnd);
+    }
+
+    return nullptr;
+  }
+
+  template <typename... Args>
+  T* AllocateHandler(Args&&... args) {
+    for (auto& h : mHandlers) {
+      if (h.Free) {
+        auto idx = HandleIndex(h.ID);
+        auto sq = HandleSequence(h.ID);
+
+        h.ID = MakeHandle(tag, idx, sq + 1);
+        h.Free = false;
+
+        auto p = new (&h.hnd) T(h.ID, args...);
+
+        return p;
+      }
+    }
+
+    return nullptr;
+  }
+
+  void FreeHandler(SoundHandle handle) {
+    auto idx = HandleIndex(handle);
+    if (idx >= size) {
+      return;
+    }
+
+    auto& h = mHandlers[idx];
+    if (h.Free == false && h.ID == handle) {
+      auto* p = reinterpret_cast<T*>(&h.hnd);
+      std::destroy_at(p);
+      h.Free = true;
+    }
+  }
+
+  T* Idx(std::size_t idx) {
+    if (idx >= size) {
+      return nullptr;
+    }
+
+    auto& h = mHandlers[idx];
+    if (h.Free == false) {
+      return reinterpret_cast<T*>(&h.hnd);
+    }
+  }
+
+ private:
+  struct HandlerEntry {
+    SoundHandle ID{0};
+    bool Free{true};
+    typename std::aligned_storage<sizeof(T), alignof(T)>::type hnd;
+  };
+
+  std::array<HandlerEntry, size> mHandlers;
+};
+
+HandlerPool<BlockSoundHandler, 64, SOUND_BLOCK> gBlockSounds;
+HandlerPool<MidiHandler, 32, SOUND_MIDI> gMidiSounds;
+HandlerPool<AmeHandler, 4, SOUND_AME> gAmeSounds;
+
+void SoundInit() {
+  // TODO reset?
+}
+
+bool CheckInstanceLimit(SFXBlock::SFX& sfx, s32 sfx_vol, BlockSoundHandler** weakest_out) {
+  s32 instances = 0;
+  BlockSoundHandler* weakest = nullptr;
+
+  if (!sfx.InstanceLimit) {
+    return false;
+  }
+
+  if (sfx.Flags.instlimit_tick()) {
+    lg::warn("unhandled tick instlimit");
+  }
+
+  for (int i = 0; i < 64; i++) {
+    auto* s = gBlockSounds.Idx(i);
+    if (s == nullptr) {
+      continue;
+    }
+
+    if (&s->m_sfx == &sfx) {
+      instances++;
+      if (!weakest) {
+        weakest = s;
+      }
+
+      if (sfx.Flags.instlimit_vol() && s->m_app_volume < weakest->m_app_volume) {
+        weakest = s;
+      }
+
+      // if (sfx.Flags.instlimit_tick() && s->m_start_tick < weakest->m_start_tick) {
+      //   weakest = s;
+      // }
+    }
+  }
+
+  if (instances > sfx.InstanceLimit) {
+    lg::warn("instance limit exceeded {}", sfx.InstanceLimit);
+    if (!weakest) {
+      lg::warn("no weakest");
+      return false;
+    }
+
+    if (sfx.Flags.instlimit_vol() && weakest->m_app_volume < sfx_vol) {
+      *weakest_out = weakest;
+      lg::warn("found weaker handler {} < {}", weakest->m_app_volume, sfx_vol);
+      return true;
+    }
+
+    if (sfx.Flags.instlimit_vol()) {
+      lg::warn("failed to find weaker handler {} < {}", weakest->m_app_volume, sfx_vol);
+    }
+
+    return false;
+  }
+
+  return true;
+}
+
+BlockSoundHandler* AllocBlockSound(SoundBank& bank, SFXBlock::SFX& sfx, s32 sfx_vol) {
+  BlockSoundHandler* weakest = nullptr;
+
+  if (sfx.Flags.has_instlimit()) {
+    if (!CheckInstanceLimit(sfx, sfx_vol, &weakest)) {
+      return nullptr;
+    }
+
+    if (weakest) {
+      weakest->Stop();
+    }
+  }
+
+  return gBlockSounds.AllocateHandler(bank, sfx);
+}
+
+MidiHandler* AllocMidiSound(Midi* block,
+                            MusicBank::MIDISound& sound,
+                            s32 vol,
+                            s32 pan,
+                            SoundBank& bank) {
+  return gMidiSounds.AllocateHandler(block, sound, vol, pan, bank);
+}
+
+AmeHandler* AllocAmeSound(MultiMidi* block,
+                          MusicBank::MIDISound& sound,
+                          s32 vol,
+                          s32 pan,
+                          SoundBank& bank) {
+  return gAmeSounds.AllocateHandler(block, sound, vol, pan, bank);
+}
+
+void FreeSound(SoundHandler* handler) {
+  auto handle = handler->Handle();
+  u8 type = HandleType(handle);
+  switch (type) {
+    case SOUND_BLOCK:
+      gBlockSounds.FreeHandler(handle);
+      break;
+    case SOUND_MIDI:
+      gMidiSounds.FreeHandler(handle);
+      break;
+    case SOUND_AME:
+      gAmeSounds.FreeHandler(handle);
+      break;
+    default:
+      lg::die("Unknown sound type (messed up sound handle) {:x}", handle);
+      break;
+  }
+}
+
+SoundHandler* GetSound(SoundHandle handle) {
+  if (handle == 0) {
+    return nullptr;
+  }
+
+  u8 type = HandleType(handle);
+  switch (type) {
+    case SOUND_BLOCK:
+      return gBlockSounds.GetPtrFromHandle(handle);
+      break;
+    case SOUND_MIDI:
+      return gMidiSounds.GetPtrFromHandle(handle);
+      break;
+    case SOUND_AME:
+      return gAmeSounds.GetPtrFromHandle(handle);
+      break;
+    default:
+      lg::die("Unknown sound type (messed up sound handle) {:x}", handle);
+      break;
+  }
+
+  return nullptr;
+}
+
+}  // namespace snd
diff --git a/game/sound/989snd/sound_handler.h b/game/sound/989snd/sound_handler.h
index 9fd3b6f1dd2..e82525d0b4a 100644
--- a/game/sound/989snd/sound_handler.h
+++ b/game/sound/989snd/sound_handler.h
@@ -10,9 +10,11 @@ static constexpr int PAN_DONT_CHANGE = -2;
 static constexpr int VOLUME_DONT_CHANGE = 0x7fffffff;
 
 class SoundBank;
+using SoundHandle = u32;
 
 class SoundHandler {
  public:
+  SoundHandler(SoundHandle OwnerID) : mOwnerId(OwnerID){};
   virtual ~SoundHandler() = default;
   virtual bool Tick() = 0;
   virtual SoundBank& Bank() = 0;
@@ -24,5 +26,13 @@ class SoundHandler {
   virtual void SetPMod(s32 mod) = 0;
   virtual void SetPBend(s32 /*mod*/){};
   virtual void SetRegister(u8 /*reg*/, u8 /*value*/) {}
+
+  SoundHandle Handle() { return mOwnerId; }
+
+  SoundHandle mOwnerId;
 };
+
+SoundHandler* GetSound(SoundHandle handle);
+void FreeSound(SoundHandler* handler);
+
 }  // namespace snd
diff --git a/game/sound/989snd/soundbank.h b/game/sound/989snd/soundbank.h
index 1ad9e6b494f..5e90f16817e 100644
--- a/game/sound/989snd/soundbank.h
+++ b/game/sound/989snd/soundbank.h
@@ -53,11 +53,7 @@ class SoundBank {
   u32 BankID;
   s8 BankNum;
 
-  virtual std::optional<std::unique_ptr<SoundHandler>> MakeHandler(u32 sound_id,
-                                                                   s32 vol,
-                                                                   s32 pan,
-                                                                   s32 pm,
-                                                                   s32 pb) {
+  virtual SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) {
     SndPlayParams params{};
     params.vol = vol;
     params.pan = pan;
@@ -67,10 +63,7 @@ class SoundBank {
     return MakeHandler(sound_id, -1, -1, params);
   };
 
-  virtual std::optional<std::unique_ptr<SoundHandler>> MakeHandler(u32 sound_id,
-                                                                   s32 vol,
-                                                                   s32 pan,
-                                                                   SndPlayParams& params) = 0;
+  virtual SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) = 0;
 
   virtual std::optional<std::string_view> GetName() { return std::nullopt; };
   virtual std::optional<u32> GetSoundByName(const char* /*name*/) { return std::nullopt; };
diff --git a/game/sound/CMakeLists.txt b/game/sound/CMakeLists.txt
index 4804d844df0..4abc1a940b0 100644
--- a/game/sound/CMakeLists.txt
+++ b/game/sound/CMakeLists.txt
@@ -5,6 +5,7 @@ set(SOUND_SOURCES
   989snd/midi_handler.cpp
   989snd/ame_handler.cpp
   989snd/blocksound_handler.cpp
+  989snd/sound_handler.cpp
   989snd/musicbank.cpp
   989snd/sfxblock.cpp
   989snd/sfxgrain.cpp
diff --git a/game/sound/sndshim.cpp b/game/sound/sndshim.cpp
index 0212054d3a8..e2887df2632 100644
--- a/game/sound/sndshim.cpp
+++ b/game/sound/sndshim.cpp
@@ -78,11 +78,7 @@ void snd_SetPlayBackMode(s32 mode) {
 }
 
 s32 snd_SoundIsStillPlaying(s32 sound_handle) {
-  if (snd::SoundStillActive(sound_handle)) {
-    return sound_handle;
-  }
-
-  return 0;
+  return snd::SoundStillActive(sound_handle);
 }
 
 void snd_StopSound(s32 sound_handle) {
@@ -141,7 +137,7 @@ void snd_SetSoundPitchModifier(s32 sound_handle, s32 pitch_mod) {
 }
 
 void snd_SetSoundPitchBend(s32 sound_handle, s32 bend) {
-  lg::warn("unimplemented snd_SetSoundPitchBend");
+  //lg::warn("unimplemented snd_SetSoundPitchBend({:x}, {})", sound_handle, bend);
 }
 
 void snd_PauseSound(s32 sound_handle) {