diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h
index d662c221c4..fd11555a93 100644
--- a/src/audio_core/hle/adts.h
+++ b/src/audio_core/hle/adts.h
@@ -16,7 +16,7 @@ struct ADTSData {
     u32 samplerate;
 };
 
-u32 ParseADTS(char* buffer, ADTSData* out);
+ADTSData ParseADTS(char* buffer);
 
 // last two bytes of MF AAC decoder user data
 // see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types
diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp
index 58201d1628..42ac4afa2a 100644
--- a/src/audio_core/hle/adts_reader.cpp
+++ b/src/audio_core/hle/adts_reader.cpp
@@ -8,36 +8,39 @@ constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 3
                                             16000, 12000, 11025, 8000,  7350,  0,     0,     0};
 constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8};
 
-u32 ParseADTS(char* buffer, ADTSData* out) {
+ADTSData ParseADTS(char* buffer) {
     u32 tmp = 0;
+    ADTSData out;
 
     // sync word 0xfff
     tmp = (buffer[0] << 8) | (buffer[1] & 0xf0);
-    if ((tmp & 0xffff) != 0xfff0)
-        return 0;
-    out->MPEG2 = (buffer[1] >> 3) & 0x1;
+    if ((tmp & 0xffff) != 0xfff0) {
+        out.length = 0;
+        return out;
+    }
+    out.MPEG2 = (buffer[1] >> 3) & 0x1;
     // bit 17 to 18
-    out->profile = (buffer[2] >> 6) + 1;
+    out.profile = (buffer[2] >> 6) + 1;
     // bit 19 to 22
     tmp = (buffer[2] >> 2) & 0xf;
-    out->samplerate_idx = tmp;
-    out->samplerate = (tmp > 15) ? 0 : freq_table[tmp];
+    out.samplerate_idx = tmp;
+    out.samplerate = (tmp > 15) ? 0 : freq_table[tmp];
     // bit 24 to 26
     tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3);
-    out->channel_idx = tmp;
-    out->channels = (tmp > 7) ? 0 : channel_table[tmp];
+    out.channel_idx = tmp;
+    out.channels = (tmp > 7) ? 0 : channel_table[tmp];
 
     // bit 55 to 56
-    out->framecount = (buffer[6] & 0x3) + 1;
+    out.framecount = (buffer[6] & 0x3) + 1;
 
     // bit 31 to 43
     tmp = (buffer[3] & 0x3) << 11;
     tmp |= (buffer[4] << 3) & 0x7f8;
     tmp |= (buffer[5] >> 5) & 0x7;
 
-    out->length = tmp;
+    out.length = tmp;
 
-    return tmp;
+    return out;
 }
 
 // last two bytes of MF AAC decoder user data
diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp
index 7440049f4e..2d448a216d 100644
--- a/src/audio_core/hle/wmf_decoder.cpp
+++ b/src/audio_core/hle/wmf_decoder.cpp
@@ -104,8 +104,7 @@ void WMFDecoder::Impl::Clear() {
 MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header,
                                              std::array<std::vector<u8>, 2>& out_streams) {
     MFOutputState output_status = MFOutputState::OK;
-    char* output_buffer = nullptr;
-    DWORD output_len = 0;
+    std::optional<std::vector<f32>> output_buffer;
     unique_mfptr<IMFSample> output;
 
     while (true) {
@@ -113,22 +112,18 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header,
 
         // 0 -> okay; 3 -> okay but more data available (buffer too small)
         if (output_status == MFOutputState::OK || output_status == MFOutputState::HaveMoreData) {
-            CopySampleToBuffer(output.get(), (void**)&output_buffer, &output_len);
+            output_buffer = CopySampleToBuffer(output.get());
 
             // the following was taken from ffmpeg version of the decoder
             f32 val_f32;
-            for (size_t i = 0; i < output_len;) {
+            for (size_t i = 0; i < output_buffer->size(); i++) {
                 for (std::size_t channel = 0; channel < adts_header.channels; channel++) {
-                    std::memcpy(&val_f32, output_buffer + i, sizeof(val_f32));
+                    val_f32 = output_buffer->at(i);
                     s16 val = static_cast<s16>(0x7FFF * val_f32);
                     out_streams[channel].push_back(val & 0xFF);
                     out_streams[channel].push_back(val >> 8);
-                    i += sizeof(val_f32);
                 }
             }
-
-            if (output_buffer)
-                free(output_buffer);
         }
 
         // in case of "ok" only, just return quickly
diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp
index 8ecfae1af0..8f0147a2c7 100644
--- a/src/audio_core/hle/wmf_decoder_utils.cpp
+++ b/src/audio_core/hle/wmf_decoder_utils.cpp
@@ -25,6 +25,12 @@ void ReportError(std::string msg, HRESULT hr) {
 
 bool MFCoInit() {
     HRESULT hr = S_OK;
+    hr = CoInitialize(NULL);
+    // S_FALSE will be returned when COM has already been initialized
+    if (hr != S_OK && hr != S_FALSE) {
+        ReportError("Failed to start COM components", hr);
+        return false;
+    }
 
     // lite startup is faster and all what we need is included
     hr = MFStartup(MF_VERSION, MFSTARTUP_LITE);
@@ -216,8 +222,8 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag)
     UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00};
     uint16_t tag = 0;
 
-    uint32_t result = ParseADTS(buffer, &tmp);
-    if (result == 0) {
+    tmp = ParseADTS(buffer);
+    if (tmp.length == 0) {
         return -1;
     }
 
@@ -325,33 +331,37 @@ std::tuple<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* t
     return std::make_tuple(MFOutputState::OK, std::move(sample));
 }
 
-int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) {
+std::optional<std::vector<f32>> CopySampleToBuffer(IMFSample* sample) {
     unique_mfptr<IMFMediaBuffer> buffer;
     HRESULT hr = S_OK;
+    std::optional<std::vector<f32>> output;
+    std::vector<f32> output_buffer;
     BYTE* data;
+    DWORD len = 0;
 
-    hr = sample->GetTotalLength(len);
+    hr = sample->GetTotalLength(&len);
     if (FAILED(hr)) {
         ReportError("Failed to get the length of sample buffer", hr);
-        return -1;
+        return std::nullopt;
     }
 
     hr = sample->ConvertToContiguousBuffer(Amp(buffer));
     if (FAILED(hr)) {
         ReportError("Failed to get sample buffer", hr);
-        return -1;
+        return std::nullopt;
     }
 
     hr = buffer->Lock(&data, nullptr, nullptr);
     if (FAILED(hr)) {
         ReportError("Failed to lock the buffer", hr);
-        return -1;
+        return std::nullopt;
     }
 
-    *output = malloc(*len);
-    std::memcpy(*output, data, *len);
+    output_buffer.resize(len / sizeof(f32));
+    std::memcpy(output_buffer.data(), data, len);
+    output = output_buffer;
 
     // if buffer unlock fails, then... whatever, we have already got data
     buffer->Unlock();
-    return 0;
+    return output;
 }
diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h
index 79514c19ce..187ec68d7f 100644
--- a/src/audio_core/hle/wmf_decoder_utils.h
+++ b/src/audio_core/hle/wmf_decoder_utils.h
@@ -6,8 +6,10 @@
 // AAC decoder related APIs are only available with WIN7+
 #define WINVER _WIN32_WINNT_WIN7
 
+#include <optional>
 #include <string>
 #include <tuple>
+#include <vector>
 #include <comdef.h>
 #include <mfapi.h>
 #include <mferror.h>
@@ -45,7 +47,7 @@ public:
 
 private:
     SmartPtr& smart_ptr;
-    RawPtr raw_ptr;
+    RawPtr raw_ptr = nullptr;
 };
 
 template <typename SmartPtr>
@@ -72,4 +74,4 @@ void MFFlush(IMFTransform* transform);
 MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample);
 std::tuple<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* transform,
                                                                  DWORD out_stream_id);
-int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len);
+std::optional<std::vector<f32>> CopySampleToBuffer(IMFSample* sample);