/* FAudio - XAudio Reimplementation for FNA * * Copyright (c) 2011-2020 Ethan Lee, Luigi Auriemma, and the MonoGame Team * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * * Ethan "flibitijibibo" Lee * */ #ifndef DISABLE_XNASONG #include "FAudio_internal.h" /* stb_vorbis */ #define malloc FAudio_malloc #define realloc FAudio_realloc #define free FAudio_free #ifdef memset /* Thanks, Apple! */ #undef memset #endif #define memset FAudio_memset #ifdef memcpy /* Thanks, Apple! */ #undef memcpy #endif #define memcpy FAudio_memcpy #define memcmp FAudio_memcmp #define pow FAudio_pow #define log(x) FAudio_log(x) #define sin(x) FAudio_sin(x) #define cos(x) FAudio_cos(x) #define floor FAudio_floor #define abs(x) FAudio_abs(x) #define ldexp(v, e) FAudio_ldexp((v), (e)) #define exp(x) FAudio_exp(x) #define qsort FAudio_qsort #define assert FAudio_assert #define FILE FAudioIOStream #ifdef SEEK_SET #undef SEEK_SET #endif #ifdef SEEK_END #undef SEEK_END #endif #ifdef EOF #undef EOF #endif #define SEEK_SET FAUDIO_SEEK_SET #define SEEK_END FAUDIO_SEEK_END #define EOF FAUDIO_EOF #define fopen(path, mode) FAudio_fopen(path) #define fclose(io) FAudio_close(io) #define fread(dst, size, count, io) io->read(io->data, dst, size, count) #define fseek(io, offset, whence) io->seek(io->data, offset, whence) #define ftell(io) io->seek(io->data, 0, FAUDIO_SEEK_CUR) #define STB_VORBIS_NO_PUSHDATA_API 1 #define STB_VORBIS_NO_INTEGER_CONVERSION 1 #include "stb_vorbis.h" /* Globals */ static float songVolume = 1.0f; static FAudio *songAudio = NULL; static FAudioMasteringVoice *songMaster = NULL; static FAudioSourceVoice *songVoice = NULL; static FAudioVoiceCallback callbacks; static stb_vorbis *activeSong = NULL; static stb_vorbis_info activeSongInfo; static uint8_t *songCache; /* Internal Functions */ static void XNA_SongSubmitBuffer(FAudioVoiceCallback *callback, void *pBufferContext) { FAudioBuffer buffer; uint32_t decoded = stb_vorbis_get_samples_float_interleaved( activeSong, activeSongInfo.channels, (float*) songCache, activeSongInfo.sample_rate * activeSongInfo.channels ); if (decoded == 0) { return; } buffer.Flags = (decoded < activeSongInfo.sample_rate) ? FAUDIO_END_OF_STREAM : 0; buffer.AudioBytes = decoded * activeSongInfo.channels * sizeof(float); buffer.pAudioData = songCache; buffer.PlayBegin = 0; buffer.PlayLength = decoded; buffer.LoopBegin = 0; buffer.LoopLength = 0; buffer.LoopCount = 0; buffer.pContext = NULL; FAudioSourceVoice_SubmitSourceBuffer( songVoice, &buffer, NULL ); } static void XNA_SongKill() { if (songVoice != NULL) { FAudioSourceVoice_Stop(songVoice, 0, 0); FAudioVoice_DestroyVoice(songVoice); songVoice = NULL; } if (songCache != NULL) { FAudio_free(songCache); songCache = NULL; } if (activeSong != NULL) { stb_vorbis_close(activeSong); activeSong = NULL; } } /* "Public" API */ FAUDIOAPI void XNA_SongInit() { FAudioCreate(&songAudio, 0, FAUDIO_DEFAULT_PROCESSOR); FAudio_CreateMasteringVoice( songAudio, &songMaster, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, 0, NULL ); } FAUDIOAPI void XNA_SongQuit() { XNA_SongKill(); FAudioVoice_DestroyVoice(songMaster); FAudio_Release(songAudio); } FAUDIOAPI float XNA_PlaySong(const char *name) { FAudioWaveFormatEx format; XNA_SongKill(); activeSong = stb_vorbis_open_filename(name, NULL, NULL); /* Set format info */ activeSongInfo = stb_vorbis_get_info(activeSong); format.wFormatTag = FAUDIO_FORMAT_IEEE_FLOAT; format.nChannels = activeSongInfo.channels; format.nSamplesPerSec = activeSongInfo.sample_rate; format.wBitsPerSample = sizeof(float) * 8; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; format.cbSize = 0; /* Allocate decode cache */ songCache = (uint8_t*) FAudio_malloc(format.nAvgBytesPerSec); /* Init voice */ FAudio_zero(&callbacks, sizeof(FAudioVoiceCallback)); callbacks.OnBufferEnd = XNA_SongSubmitBuffer; FAudio_CreateSourceVoice( songAudio, &songVoice, &format, 0, 1.0f, /* No pitch shifting here! */ &callbacks, NULL, NULL ); FAudioVoice_SetVolume(songVoice, songVolume, 0); /* Okay, this song is decoding now */ stb_vorbis_seek_start(activeSong); XNA_SongSubmitBuffer(NULL, NULL); /* Finally. */ FAudioSourceVoice_Start(songVoice, 0, 0); return stb_vorbis_stream_length_in_seconds(activeSong); } FAUDIOAPI void XNA_PauseSong() { if (songVoice == NULL) { return; } FAudioSourceVoice_Stop(songVoice, 0, 0); } FAUDIOAPI void XNA_ResumeSong() { if (songVoice == NULL) { return; } FAudioSourceVoice_Start(songVoice, 0, 0); } FAUDIOAPI void XNA_StopSong() { XNA_SongKill(); } FAUDIOAPI void XNA_SetSongVolume(float volume) { songVolume = volume; if (songVoice != NULL) { FAudioVoice_SetVolume(songVoice, songVolume, 0); } } FAUDIOAPI uint32_t XNA_GetSongEnded() { FAudioVoiceState state; if (songVoice == NULL || activeSong == NULL) { return 1; } FAudioSourceVoice_GetState(songVoice, &state, 0); return state.BuffersQueued == 0; } FAUDIOAPI void XNA_EnableVisualization(uint32_t enable) { /* TODO: Enable/Disable FAPO effect */ } FAUDIOAPI uint32_t XNA_VisualizationEnabled() { /* TODO: Query FAPO effect enabled */ return 0; } FAUDIOAPI void XNA_GetSongVisualizationData( float *frequencies, float *samples, uint32_t count ) { /* TODO: Visualization FAPO that reads in Song samples, FFT analysis */ } #endif /* DISABLE_XNASONG */ /* vim: set noexpandtab shiftwidth=8 tabstop=8: */