//////////////////////////////// //~ Decode MP3_Result MP3_Decode(Arena *arena, String encoded, u32 sample_rate, MP3_DecodeFlag flags) { MP3_Result result = ZI; u64 bytes_per_sample = 2; u64 channel_count; u32 channel_mask; if (flags & MP3_DecodeFlag_Stereo) { channel_count = 2; channel_mask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; } else { channel_count = 1; channel_mask = SPEAKER_FRONT_CENTER; } //- Startup MFStartup(MF_VERSION, MFSTARTUP_LITE); /* Create IStream from encoded string */ IStream *i_stream = SHCreateMemStream(encoded.text, encoded.len); /* Create IMFByteStream from IStream */ IMFByteStream *byte_stream = 0; MFCreateMFByteStreamOnStream(i_stream, &byte_stream); /* Create reader from IMFByteStream */ IMFSourceReader *reader; MFCreateSourceReaderFromByteStream(byte_stream, 0, &reader); //- Get media type /* Read only first audio stream */ IMFSourceReader_SetStreamSelection(reader, (DWORD)MF_SOURCE_READER_ALL_STREAMS, 0); IMFSourceReader_SetStreamSelection(reader, (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 1); WAVEFORMATEXTENSIBLE format = { .Format = { .wFormatTag = WAVE_FORMAT_EXTENSIBLE, .nChannels = (WORD)channel_count, .nSamplesPerSec = (WORD)sample_rate, .nAvgBytesPerSec = (DWORD)(sample_rate * channel_count * bytes_per_sample), .nBlockAlign = (WORD)(channel_count * bytes_per_sample), .wBitsPerSample = (WORD)(8 * bytes_per_sample), .cbSize = sizeof(format) - sizeof(format.Format) }, .Samples.wValidBitsPerSample = 8 * bytes_per_sample, .dwChannelMask = channel_mask, .SubFormat = MEDIASUBTYPE_PCM }; /* Media Foundation in Windows 8+ allows reader to convert output to different format than native */ IMFMediaType *type; MFCreateMediaType(&type); MFInitMediaTypeFromWaveFormatEx(type, &format.Format, sizeof(format)); IMFSourceReader_SetCurrentMediaType(reader, (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, type); IMFMediaType_Release(type); //- Read result.samples = PushDry(arena, i16); u64 sample_bytes_read = 0; for (;;) { IMFSample *sample; DWORD sample_flags = 0; HRESULT hr = IMFSourceReader_ReadSample(reader, (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, 0, &sample_flags, 0, &sample); if (FAILED(hr)) { break; } /* Check if done */ if (sample_flags & MF_SOURCE_READERF_ENDOFSTREAM) { result.ok = 1; break; } Assert(sample_flags == 0); /* Read samples */ IMFMediaBuffer *buffer; IMFSample_ConvertToContiguousBuffer(sample, &buffer); BYTE *src; DWORD size_bytes; IMFMediaBuffer_Lock(buffer, &src, 0, &size_bytes); { i16 *dst = PushStructsNoZero(arena, i16, (size_bytes + 1) >> 1); CopyBytes(dst, src, size_bytes); sample_bytes_read += size_bytes; } IMFMediaBuffer_Unlock(buffer); IMediaBuffer_Release(buffer); IMFSample_Release(sample); } result.samples_count = sample_bytes_read / bytes_per_sample; //- Cleanup IMFSourceReader_Release(reader); IMFByteStream_Close(byte_stream); /* FIXME: Enable this */ //IStream_Release(i_stream); MFShutdown(); return result; }