121 lines
3.3 KiB
C
121 lines
3.3 KiB
C
////////////////////////////////////////////////////////////
|
|
//~ 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 = ArenaNext(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;
|
|
}
|