Last active
August 29, 2015 14:13
-
-
Save milannankov/e7c5130c469f02334868 to your computer and use it in GitHub Desktop.
Universal Audio Component Blog Snippets - http://www.newventuresoftware.com
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct AudioData | |
{ | |
byte * bytes; | |
unsigned int numberOfBytes; | |
WAVEFORMATEX* waveFormat; | |
// loop information | |
unsigned int loopStart; | |
unsigned int loopLength; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private async Task<IBuffer> GetBuffer(string sampleName) | |
{ | |
IBuffer buffer = null; | |
if (!this.buffers.ContainsKey(sampleName)) | |
{ | |
var path = String.Format("ms-appx:///Assets/{0}.wav", sampleName); | |
var audioFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(path)); | |
buffer = await FileIO.ReadBufferAsync(audioFile); | |
this.buffers[sampleName] = buffer; | |
} | |
return this.buffers[sampleName]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AudioData RiffReader::Read(IBuffer^ buffer) | |
{ | |
auto formatChunk = FindChunk(buffer, fourccFMT); | |
auto dataChunk = FindChunk(buffer, fourccDATA); | |
auto waveFormat = reinterpret_cast<WAVEFORMATEX*>(formatChunk.data); | |
AudioData data; | |
data.bytes = dataChunk.data; | |
data.numberOfBytes = dataChunk.size; | |
data.waveFormat = waveFormat; | |
// read loop data | |
this->SetLoopData(buffer, data); | |
return data; | |
} | |
// new method that reads the 'smpl' chunk | |
void RiffReader::SetLoopData(IBuffer^ buffer, AudioData& data) | |
{ | |
auto sampleChunk = FindChunk(buffer, fourccSAMPLE); | |
data.loopLength = 0; | |
data.loopStart = 0; | |
if (sampleChunk.id != fourccSAMPLE) | |
{ | |
return; | |
} | |
if (sampleChunk.size < sizeof(RIFFMIDISample)) | |
{ | |
return; | |
} | |
auto midiSample = reinterpret_cast<const RIFFMIDISample*>(sampleChunk.data); | |
auto loops = reinterpret_cast<const MIDILoop*>(sampleChunk.data + sizeof(RIFFMIDISample)); | |
for (unsigned int i = 0; i < midiSample->loopCount; i++) | |
{ | |
if (loops[i].type == MIDILoop::LOOP_TYPE_FORWARD) | |
{ | |
// Return 'forward' loop | |
data.loopStart = loops[i].start; | |
data.loopLength = loops[i].end + loops[i].start + 1; | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
XAUDIO2_BUFFER UniversalAudioPlayer::CreateAudioBuffer(AudioData data) | |
{ | |
XAUDIO2_BUFFER buffer = { 0 }; | |
buffer.AudioBytes = data.numberOfBytes; | |
buffer.pAudioData = data.bytes; | |
buffer.Flags = XAUDIO2_END_OF_STREAM; | |
buffer.LoopCount = XAUDIO2_LOOP_INFINITE; | |
// set the loop information | |
if (data.loopLength > 0) | |
{ | |
buffer.LoopLength = data.loopLength; | |
buffer.LoopBegin = data.loopStart; | |
} | |
return buffer; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// some code has been omitted for brevity | |
Array<byte>^ AudioDecoder::Decode(IRandomAccessStream^ audioStream) | |
{ | |
ComPtr<IMFByteStream> byteStream = nullptr; | |
ComPtr<IMFSourceReader> reader = nullptr; | |
// Microsoft Media Foundation byte stream that wraps an IRandomAccessStream | |
HRESULT hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)audioStream, &byteStream); | |
// Get IMFSourceReader | |
hr = MFCreateSourceReaderFromByteStream(byteStream.Get(), NULL, &reader); | |
auto data = DecodeAsWav(reader); | |
return data; | |
} | |
Array<byte>^ AudioDecoder::DecodeAsWav(ComPtr<IMFSourceReader> reader) | |
{ | |
ComPtr<IMFMediaType> audioType = ConfigureAudioStream(reader); | |
// decode the audio data as WAVE | |
Vector<byte>^ decodedData = GetDecodedAudioData(reader); | |
// construct WAVE using the decompressed audio data | |
Array<byte>^ wav = GetWav(decodedData, audioType); | |
return wav; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Vector<byte>^ AudioDecoder::GetDecodedAudioData(ComPtr<IMFSourceReader> reader) | |
{ | |
Vector<byte>^ dataBytes = ref new Vector<byte>(); | |
DWORD cbBuffer = 0; | |
DWORD streamIndex = (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM; | |
ComPtr<IMFSample> sample = nullptr; | |
ComPtr<IMFMediaBuffer> buffer = nullptr; | |
BYTE *pAudioData = NULL; | |
while (true) | |
{ | |
DWORD dwFlags = 0; | |
HRESULT hr = reader->ReadSample(streamIndex, 0, 0, &dwFlags, NULL, &sample); | |
if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM) | |
{ | |
// End of input file | |
break; | |
} | |
if (sample == nullptr) | |
{ | |
continue; | |
} | |
hr = sample->ConvertToContiguousBuffer(&buffer); | |
hr = buffer->Lock(&pAudioData, NULL, &cbBuffer); | |
WriteDataToBuffer(dataBytes, pAudioData, cbBuffer); | |
hr = buffer->Unlock(); | |
pAudioData = NULL; | |
} | |
return dataBytes; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// some code has been omitted for brevity | |
Array<byte>^ AudioDecoder::GetWav(Vector<byte>^ audioBytes, ComPtr<IMFMediaType> audioType) | |
{ | |
WAVEFORMATEX * wavFormat = NULL; | |
UINT32 wavFormatBytes = 0; | |
HRESULT hr = MFCreateWaveFormatExFromMFMediaType(audioType.Get(), &wavFormat, &wavFormatBytes); | |
int dataChunkBytes = 8 + audioBytes->Size; | |
int formatChunkBytes = 8 + wavFormatBytes; | |
int totalBytes = 12 + dataChunkBytes + formatChunkBytes; | |
auto buffer = ref new Array<byte>(totalBytes); | |
byte * bufferPtr = buffer->begin(); | |
DWORD header[] = { | |
FCC('RIFF'), // Riff chunk | |
totalBytes - 8, | |
FCC('WAVE'), | |
FCC('fmt '), // 'fmt ' chunk | |
wavFormatBytes | |
}; | |
DWORD dataHeader[] = { | |
FCC('data'), // data chunk | |
audioBytes->Size | |
}; | |
int headerBytes = sizeof(header); | |
int dataHeaderBytes = sizeof(dataHeader); | |
// write header - RIFF chunk header + 'fmt ' chunk header | |
WriteDataToBuffer(bufferPtr, reinterpret_cast<byte*>(header), headerBytes); | |
bufferPtr += headerBytes; | |
// write 'fmt ' chunk format | |
WriteDataToBuffer(bufferPtr, reinterpret_cast<byte*>(wavFormat), wavFormatBytes); | |
bufferPtr += wavFormatBytes; | |
// write 'data' chunk header | |
WriteDataToBuffer(bufferPtr, reinterpret_cast<byte*>(dataHeader), dataHeaderBytes); | |
bufferPtr += dataHeaderBytes; | |
// write 'data' chunk data | |
WriteDataToBuffer(bufferPtr, audioBytes); | |
return buffer; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace UniversalAudioComponent | |
{ | |
public ref class AudioDecoder sealed | |
{ | |
public: | |
AudioDecoder(); | |
Array<byte>^ Decode(IRandomAccessStream^ audioStream); | |
private: | |
ComPtr<IMFMediaType> ConfigureAudioStream(ComPtr<IMFSourceReader> reader); | |
Vector<byte>^ GetDecodedAudioData(ComPtr<IMFSourceReader> reader); | |
Array<byte>^ DecodeAsWav(ComPtr<IMFSourceReader> reader); | |
Array<byte>^ GetWav(Vector<byte>^ audioBytes, ComPtr<IMFMediaType> audioType); | |
unsigned int WriteHeader(byte * bufferPtr, ComPtr<IMFMediaType> audioType); | |
unsigned int WriteFormat(byte * bufferPtr, ComPtr<IMFMediaType> audioType); | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public sealed partial class MainPage : Page | |
{ | |
// create decoder | |
private AudioDecoder decoder = new AudioDecoder(); | |
private async Task<IBuffer> GetBuffer(string sampleName) | |
{ | |
if (!this.buffers.ContainsKey(sampleName)) | |
{ | |
var path = String.Format("ms-appx:///Assets/{0}.mp3", sampleName); | |
var audioFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(path)); | |
// decode MP3 | |
var audioFileStream = await audioFile.OpenReadAsync(); | |
var wavBytes = this.decoder.Decode(audioFileStream); | |
var buffer = wavBytes.AsBuffer(); | |
this.buffers[sampleName] = buffer; | |
} | |
return this.buffers[sampleName]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// some code has been omitted for brevity | |
public sealed partial class MainPage : Page | |
{ | |
private UniversalAudioPlayer player = new UniversalAudioPlayer(); | |
private Dictionary<string, IBuffer> buffers = new Dictionary<string, IBuffer>(); | |
private async Task ToggleSample(string name, bool isPlaying) | |
{ | |
// name = someFile.wav | |
var buffer = await this.GetBuffer(name); | |
var sample = new AudioSample(name, buffer); | |
if (isPlaying) | |
{ | |
this.player.Play(sample); | |
} | |
else | |
{ | |
this.player.Stop(sample); | |
} | |
} | |
private async Task<IBuffer> GetBuffer(string sampleName) | |
{ | |
IBuffer buffer = null; | |
if (!this.buffers.ContainsKey(sampleName)) | |
{ | |
var path = String.Format("ms-appx:///Assets/{0}.wav", sampleName); | |
var audioFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(path)); | |
buffer = await FileIO.ReadBufferAsync(audioFile); | |
this.buffers[sampleName] = buffer; | |
} | |
return this.buffers[sampleName]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Little-Endian | |
const uint32 fourccDATA = 'atad'; // "data" chunk FOURCC | |
const uint32 fourccFMT = ' tmf'; // "fmt" chunk FOURCC | |
AudioData RiffReader::Read(IBuffer^ buffer) | |
{ | |
auto formatChunk = FindChunk(buffer, fourccFMT); | |
auto dataChunk = FindChunk(buffer, fourccDATA); | |
auto waveFormat = reinterpret_cast<WAVEFORMATEX*>(formatChunk.data); | |
AudioData data; | |
data.bytes = dataChunk.data; | |
data.numberOfBytes = dataChunk.size; | |
data.waveFormat = waveFormat; | |
return data; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace UniversalAudioComponent | |
{ | |
class RiffReader sealed | |
{ | |
private: | |
ChunkInfo FindChunk(IBuffer^ buffer, uint32 fourcc); | |
byte* GetBufferByteAccess(IBuffer^ buffer); | |
public: | |
RiffReader(); | |
AudioData Read(IBuffer^ buffer); | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// some code has been omitted for brevity | |
UniversalAudioPlayer::UniversalAudioPlayer() | |
{ | |
HRESULT hr = XAudio2Create(&xAudio); | |
hr = xAudio->CreateMasteringVoice(&masteringVoice); | |
xAudio->StartEngine(); | |
} | |
void UniversalAudioPlayer::Play(AudioSample^ sample) | |
{ | |
if (this->IsPlaying(sample)) | |
{ | |
return; | |
} | |
auto reader = new RiffReader(); | |
auto data = reader->Read(sample->Buffer); | |
IXAudio2SourceVoice * voice = this->CreateVoice(data.waveFormat); | |
XAUDIO2_BUFFER buffer = this->CreateAudioBuffer(data); | |
HRESULT hr = voice->SubmitSourceBuffer(&buffer); | |
voice->Start(0); | |
this->runningVoices[sample->Name] = voice; | |
} | |
XAUDIO2_BUFFER UniversalAudioPlayer::CreateAudioBuffer(AudioData data) | |
{ | |
XAUDIO2_BUFFER buffer = { 0 }; | |
buffer.AudioBytes = data.numberOfBytes; | |
buffer.pAudioData = data.bytes; | |
buffer.Flags = XAUDIO2_END_OF_STREAM; | |
buffer.LoopCount = XAUDIO2_LOOP_INFINITE; | |
return buffer; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace UniversalAudioComponent | |
{ | |
public ref class UniversalAudioPlayer sealed | |
{ | |
private: | |
ComPtr<IXAudio2> xAudio; | |
IXAudio2MasteringVoice * masteringVoice; | |
std::map<String^, IXAudio2SourceVoice*> runningVoices; | |
bool IsPlaying(AudioSample^); | |
IXAudio2SourceVoice* CreateVoice(WAVEFORMATEX* wavFormat); | |
XAUDIO2_BUFFER CreateAudioBuffer(AudioData data); | |
public: | |
UniversalAudioPlayer(); | |
void Play(AudioSample^ sample); | |
void Stop(AudioSample^ sample); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment