#include #include #include float argPlayBegin = 0.0f; float argPlayLength = 0.0f; float argLoopBegin = 0.0f; float argLoopLength = 0.0f; uint32_t argLoopCount = 0; FAudio *faudio = NULL; FAudioMasteringVoice *mastering_voice = NULL; FAudioSourceVoice *source_voice = NULL; FAudioWaveFormatExtensible *wfx = NULL; FAudioBuffer buffer = {0}; FAudioBufferWMA buffer_wma = {0}; /* based on https://docs.microsoft.com/en-us/windows/desktop/xaudio2/how-to--load-audio-data-files-in-xaudio2 */ #define fourccRIFF *((uint32_t *) "RIFF") #define fourccDATA *((uint32_t *) "data") #define fourccFMT *((uint32_t *) "fmt ") #define fourccWAVE *((uint32_t *) "WAVE") #define fourccXWMA *((uint32_t *) "XWMA") #define fourccDPDS *((uint32_t *) "dpds") uint32_t FindChunk(FILE *hFile, uint32_t fourcc, uint32_t *dwChunkSize, uint32_t *dwChunkDataPosition) { uint32_t hr = 0; if (fseek(hFile, 0, SEEK_SET) != 0) { return -1; } uint32_t dwChunkType; uint32_t dwChunkDataSize; uint32_t dwRIFFDataSize = 0; uint32_t dwFileType; uint32_t bytesRead = 0; uint32_t dwOffset = 0; while (hr == 0) { if (fread(&dwChunkType, sizeof(uint32_t), 1, hFile) < 1) hr = 1; if (fread(&dwChunkDataSize, sizeof(uint32_t), 1, hFile) < 1) hr = 1; if (dwChunkType == fourccRIFF) { dwRIFFDataSize = dwChunkDataSize; dwChunkDataSize = 4; if (fread(&dwFileType, sizeof(uint32_t), 1, hFile) < 1) hr = 1; } else { if (fseek(hFile, dwChunkDataSize, SEEK_CUR) != 0) return 1; } dwOffset += sizeof(uint32_t) * 2; if (dwChunkType == fourcc) { *dwChunkSize = dwChunkDataSize; *dwChunkDataPosition = dwOffset; return 0; } dwOffset += dwChunkDataSize; if (bytesRead >= dwRIFFDataSize) return 1; } return 1; } uint32_t ReadChunkData(FILE *hFile, void * buffer, uint32_t buffersize, uint32_t bufferoffset) { uint32_t hr = 0; if (fseek(hFile, bufferoffset, SEEK_SET) != 0) return 1; if (fread(buffer, buffersize, 1, hFile) < 1) hr = 1; return hr; } uint32_t load_data(const char *filename) { /* open the audio file */ FILE *hFile = fopen(filename, "rb"); if (!hFile) return 1; fseek(hFile, 0, SEEK_SET); /* Locate the 'RIFF' chunk in the audio file, and check the file type. */ uint32_t dwChunkSize; uint32_t dwChunkPosition; uint32_t filetype; FindChunk(hFile,fourccRIFF, &dwChunkSize, &dwChunkPosition); ReadChunkData(hFile, &filetype, sizeof(uint32_t), dwChunkPosition); if (filetype != fourccWAVE && filetype != fourccXWMA) return 1; /* Locate the 'fmt ' chunk, and copy its contents into a WAVEFORMATEXTENSIBLE structure. */ FindChunk(hFile,fourccFMT, &dwChunkSize, &dwChunkPosition ); if (dwChunkSize > sizeof(FAudioWaveFormatExtensible)) { wfx = (FAudioWaveFormatExtensible *) malloc(dwChunkSize); printf("chunk-size exceeds wfx size, allocating more: %u > %u\n", dwChunkSize, sizeof(FAudioWaveFormatExtensible)); } else { wfx = (FAudioWaveFormatExtensible *) malloc(sizeof(FAudioWaveFormatExtensible)); printf("chunk-size equal or less than wfx size, capping: %u <= %u\n", dwChunkSize, sizeof(FAudioWaveFormatExtensible)); } ReadChunkData(hFile, wfx, dwChunkSize, dwChunkPosition ); /* Locate the 'data' chunk, and read its contents into a buffer. */ FindChunk(hFile, fourccDATA, &dwChunkSize, &dwChunkPosition); uint8_t *pDataBuffer = (uint8_t *) malloc(dwChunkSize); ReadChunkData(hFile, pDataBuffer, dwChunkSize, dwChunkPosition); printf("data chunk size: %u\n", dwChunkSize); buffer.AudioBytes = dwChunkSize; //buffer containing audio data buffer.pAudioData = pDataBuffer; //size of the audio buffer in bytes buffer.Flags = FAUDIO_END_OF_STREAM; // tell the source voice not to expect any data after this buffer /* Locate the 'dpds' chunk, and read its contents into a buffer. */ if (FindChunk(hFile, fourccDPDS, &dwChunkSize, &dwChunkPosition) == 0) { uint32_t *cumulBytes = (uint32_t *) malloc(dwChunkSize); ReadChunkData(hFile, cumulBytes, dwChunkSize, dwChunkPosition); buffer_wma.pDecodedPacketCumulativeBytes = cumulBytes; buffer_wma.PacketCount = dwChunkSize / sizeof(uint32_t); } fclose(hFile); return 0; } void faudio_setup() { uint32_t hr = FAudioCreate(&faudio, 0, FAUDIO_DEFAULT_PROCESSOR); if (hr != 0) { return; } hr = FAudio_CreateMasteringVoice(faudio, &mastering_voice, 2, 44100, 0, 0, NULL); if (hr != 0) { return; } hr = FAudio_CreateSourceVoice( faudio, &source_voice, (FAudioWaveFormatEx *) wfx, FAUDIO_VOICE_USEFILTER, FAUDIO_MAX_FREQ_RATIO, NULL, NULL, NULL ); } void play(void) { buffer.PlayBegin = argPlayBegin * wfx->Format.nSamplesPerSec; buffer.PlayLength = argPlayLength * wfx->Format.nSamplesPerSec; buffer.LoopBegin = argLoopBegin * wfx->Format.nSamplesPerSec; buffer.LoopLength = argLoopLength * wfx->Format.nSamplesPerSec; buffer.LoopCount = argLoopCount; if (buffer_wma.pDecodedPacketCumulativeBytes != NULL) FAudioSourceVoice_SubmitSourceBuffer(source_voice, &buffer, &buffer_wma); else FAudioSourceVoice_SubmitSourceBuffer(source_voice, &buffer, NULL); uint32_t hr = FAudioSourceVoice_Start(source_voice, 0, FAUDIO_COMMIT_NOW); int is_running = 1; while (hr == 0 && is_running) { FAudioVoiceState state; FAudioSourceVoice_GetState(source_voice, &state, 0); is_running = (state.BuffersQueued > 0) != 0; SDL_Delay(10); } FAudioVoice_DestroyVoice(source_voice); /* free allocated space for FAudioWafeFormatExtensible */ free(wfx); } int main(int argc, char *argv[]) { /* process command line arguments. This is just a test program, didn't go too nuts with validation. */ if (argc < 2 || (argc > 4 && argc != 7)) { printf("Usage: %s filename [PlayBegin] [PlayLength] [LoopBegin LoopLength LoopCount]\n", argv[0]); printf(" - filename (required): can be either a WAV or xWMA audio file.\n"); printf(" - PlayBegin (optional): start playing at this offset. (in seconds)\n"); printf(" - PlayLength (optional): duration of the region to be played. (in seconds)\n"); printf(" - LoopBegin (optional): start looping at this offset. (in seconds)\n"); printf(" - LoopLength (optional): duration of the loop (in seconds)\n"); printf(" - LoopCount (optional): number of times to loop\n"); return -1; } switch (argc) { case 7: sscanf(argv[6], "%d", &argLoopCount); sscanf(argv[5], "%f", &argLoopLength); sscanf(argv[4], "%f", &argLoopBegin); case 4: sscanf(argv[3], "%f", &argPlayLength); case 3: sscanf(argv[2], "%f", &argPlayBegin); } if (load_data(argv[1]) != 0) { printf("Error loading data\n"); return -1; } faudio_setup(); play(); return 0; }