Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /******************************************************************************/
- /******************************************************************************/
- //"kit_xmp_sfx\main.cpp":
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <kit/all.hpp>
- #include <kit/xmp_sfx.hpp>
- using namespace kit;
- f32 randf(){
- u32 value = ((u32)rand())<<15 | rand();
- return (f32)value/(KIT_U32_MAX>>2);
- }
- #define winSize 600
- #define canvasSize 300
- #define in_bounds(_e_mouse) (\
- (_e_mouse.x >= 0) &&\
- (_e_mouse.y >= 0) &&\
- (_e_mouse.x < winSize) &&\
- (_e_mouse.y < winSize) \
- )
- //crashes sometimes
- int main(int argc, char** argv){ try {
- srand((u32)time::getTicks());
- Window win("left click to change the music's volume/pan settings, right click to play a sound effect",
- winSize, winSize, WINFLAG_REM_MAXIMIZE | WINFLAG_REM_MINIMIZE,
- WINPOS_CENTERED, WINPOS_CENTERED, canvasSize, canvasSize);
- BitmapFont _text("_font8x8.qoi", &win);
- FStr _fstr(512);
- #define textf(_x, _y, _fmt, ...) \
- _text.print(_x,_y, _fstr.fmt(_fmt, __VA_ARGS__), 0);
- SoundEngine* snd = new SoundEngine(64, false);
- snd->streamStartAndWait();
- //snd->musicLoadModule("2ND_PM.S3M");
- snd->musicLoadModule("tao tao.xm");
- snd->musicSetVolume(0.2f,0.2f);
- snd->musicStart();
- AudioData kameria("kameria-se.qoa", snd);
- snd->sfxSetVolumeAll(0.67f, 0.67f);
- kameria.volumeL = 0.1f; //test of AudioData master volume
- kameria.volumeR = 0.1f;
- bool run = true;
- _before_loop:
- while(run){
- WindowEvent e;
- while(pollWindowEvent(&e)){
- switch(e.type){
- case WINEVENT_WIN_CLOSE: run = false; goto _before_loop;
- case WINEVENT_MOUSE_MOVE: {
- if(e.mouse.button&MBUTTON_LEFT && in_bounds(e.mouse)){
- f32 pan = ((f32)e.mouse.x/winSize)*2.0f - 1.0f;
- f32 vol = 0.5f-((f32)e.mouse.y/winSize)*0.5f;
- snd->musicSetPan(pan);
- snd->musicSetVolume(vol,vol);
- //snd->musicSetVolumeForced(vol,vol);
- }
- } break;
- case WINEVENT_MOUSE_DOWN: {
- if(e.mouse.button&MBUTTON_RIGHT && in_bounds(e.mouse)){
- f64 audioSpeed = 2.0 - ((f64)e.mouse.y/winSize)*2.0;
- snd->sfxPlay(&kameria, 1,1, audioSpeed, 0);
- }
- } break;
- }
- }
- f64 cpu_load = snd->streamGetCPULoad();
- u32 active_tracks = snd->sfxGetActiveTracks();
- win.clear();
- textf(1,1, "audio's CPU load = %4.2f%%\nactive sfx tracks = %u/64", cpu_load*100, active_tracks);
- win.present();
- time::sleep(10);
- }
- snd->sfxStopAll();
- snd->musicStopAndWait();
- time::sleep(75);
- printf("%llu,",memory::getNumAllocations());
- MutexSimple* mutex = new MutexSimple;
- printf("%llu,",memory::getNumAllocations());
- delete mutex;
- printf("%llu\n",memory::getNumAllocations());
- AudioData* sndfx = new AudioData("kameria-se.qoa", snd);
- printf("%llu,",memory::getNumAllocations());
- delete sndfx;
- printf("%llu\n",memory::getNumAllocations());
- return 0;
- } catch(const char* errorText){
- #ifdef _DEBUG
- printf("FATAL EXCEPTION OCCERRED!: \"%s\"\n", errorText);
- #else
- showMessageBox(errorText, "FATAL EXCEPTION OCCURRED! COMPLAIN TO THE DEV! (lol)", MSGBOX_ICN_ERROR);
- #endif
- return -1;
- }}
- /******************************************************************************/
- /******************************************************************************/
- //"kit_xmp_sfx\src\main.cpp":
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <kit/all.hpp>
- #include <kit/xmp_sfx.hpp>
- using namespace kit;
- f32 randf(){
- u32 value = ((u32)rand())<<15 | rand();
- return (f32)value/(KIT_U32_MAX>>2);
- }
- #define winSize 600
- #define canvasSize 300
- #define in_bounds(_e_mouse) (\
- (_e_mouse.x >= 0) &&\
- (_e_mouse.y >= 0) &&\
- (_e_mouse.x < winSize) &&\
- (_e_mouse.y < winSize) \
- )
- //crashes sometimes
- int main(int argc, char** argv){ try {
- srand((u32)time::getTicks());
- Window win("left click to change the music's volume/pan settings, right click to play a sound effect",
- winSize, winSize, WINFLAG_REM_MAXIMIZE | WINFLAG_REM_MINIMIZE,
- WINPOS_CENTERED, WINPOS_CENTERED, canvasSize, canvasSize);
- BitmapFont _text("_font8x8.qoi", &win);
- FStr _fstr(512);
- #define textf(_x, _y, _fmt, ...) \
- _text.print(_x,_y, _fstr.fmt(_fmt, __VA_ARGS__), 0);
- SoundEngine* snd = new SoundEngine(64, false);
- snd->streamStartAndWait();
- //snd->musicLoadModule("2ND_PM.S3M");
- snd->musicLoadModule("tao tao.xm");
- snd->musicSetVolume(0.2f,0.2f);
- snd->musicStart();
- AudioData kameria("kameria-se.qoa", snd);
- snd->sfxSetVolumeAll(0.67f, 0.67f);
- kameria.volumeL = 0.1f; //test of AudioData master volume
- kameria.volumeR = 0.1f;
- bool run = true;
- _before_loop:
- while(run){
- WindowEvent e;
- while(pollWindowEvent(&e)){
- switch(e.type){
- case WINEVENT_WIN_CLOSE: run = false; goto _before_loop;
- case WINEVENT_MOUSE_MOVE: {
- if(e.mouse.button&MBUTTON_LEFT && in_bounds(e.mouse)){
- f32 pan = ((f32)e.mouse.x/winSize)*2.0f - 1.0f;
- f32 vol = 0.5f-((f32)e.mouse.y/winSize)*0.5f;
- snd->musicSetPan(pan);
- snd->musicSetVolume(vol,vol);
- //snd->musicSetVolumeForced(vol,vol);
- }
- } break;
- case WINEVENT_MOUSE_DOWN: {
- if(e.mouse.button&MBUTTON_RIGHT && in_bounds(e.mouse)){
- f64 audioSpeed = 2.0 - ((f64)e.mouse.y/winSize)*2.0;
- snd->sfxPlay(&kameria, 1,1, audioSpeed, 0);
- }
- } break;
- }
- }
- f64 cpu_load = snd->streamGetCPULoad();
- u32 active_tracks = snd->sfxGetActiveTracks();
- win.clear();
- textf(1,1, "audio's CPU load = %4.2f%%\nactive sfx tracks = %u/64", cpu_load*100, active_tracks);
- win.present();
- time::sleep(10);
- }
- snd->sfxStopAll();
- snd->musicStopAndWait();
- time::sleep(75);
- printf("%llu,",memory::getNumAllocations());
- MutexSimple* mutex = new MutexSimple;
- printf("%llu,",memory::getNumAllocations());
- delete mutex;
- printf("%llu\n",memory::getNumAllocations());
- AudioData* sndfx = new AudioData("kameria-se.qoa", snd);
- printf("%llu,",memory::getNumAllocations());
- delete sndfx;
- printf("%llu\n",memory::getNumAllocations());
- return 0;
- } catch(const char* errorText){
- #ifdef _DEBUG
- printf("FATAL EXCEPTION OCCERRED!: \"%s\"\n", errorText);
- #else
- showMessageBox(errorText, "FATAL EXCEPTION OCCURRED! COMPLAIN TO THE DEV! (lol)", MSGBOX_ICN_ERROR);
- #endif
- return -1;
- }}
- /******************************************************************************/
- /******************************************************************************/
- //"kit_xmp_sfx\src\kit_xmp_sfx\kit_AudioData.cpp":
- #include "_kit_shared.hpp"
- namespace kit {
- static inline void printAudioData(AudioDataHeader* data, size_t samplesToPrint){
- _printf("AudioDataHeader* = %p:\n", data);
- _printf(" magic = \"%.4s\" (0x%08X)\n", (char*)&data->magic, data->magic);
- char* format_name = nullptr;
- switch(data->format){
- case ASTREAM_FMT_U8 : format_name = "ASTREAM_FMT_U8"; break;
- case ASTREAM_FMT_S8 : format_name = "ASTREAM_FMT_S8"; break;
- case ASTREAM_FMT_S16: format_name = "ASTREAM_FMT_S16"; break;
- case ASTREAM_FMT_S24: format_name = "ASTREAM_FMT_S24"; break;
- case ASTREAM_FMT_S32: format_name = "ASTREAM_FMT_S32"; break;
- case ASTREAM_FMT_F32: format_name = "ASTREAM_FMT_F32"; break;
- default: format_name = "(unknown)";
- }
- _printf(" format = %s (0x%04X)\n", format_name, data->format);
- _printf(" headerSize = %u\n", data->headerSize);
- _printf(" dataSize = %llu\n", data->dataSize);
- _printf(" loopStart = %llu\n", data->loopStart);
- _printf(" loopEnd = %llu\n", data->loopEnd);
- _printf(" numSamples = %llu\n", data->numSamples);
- _printf(" sampleRate = %u\n", data->sampleRate);
- _printf(" bitRate = %u\n", data->bitRate);
- _printf(" loopCount = %u\n", data->loopCount);
- _printf(" channels = %u\n", data->channels);
- _printf(" bitRemainder = %u\n", data->bitRemainder);
- _printf(" userflags = 0x%02X\n", data->userflags);
- _printf(" mode = %u\n", data->mode);
- _printf(" samples = %p\n", data->samples);
- _printf(" AD_p = %p\n", data->AD_p);
- if(samplesToPrint > 0){
- if(data->format != ASTREAM_FMT_F32){
- _printf(" FORMAT != ASTREAM_FMT_F32\n");
- return;
- }
- u64 sampleRange = MIN(samplesToPrint, data->numSamples);
- if(data->channels == 1){
- f32* smpMono = (f32*)data->samples;
- for(u64 i=0; i<sampleRange; ++i)
- _printf("%4llu: %7.4f\n", i, smpMono[i]);
- } else if(data->channels == 2){
- smp_f32s* smpStereo = (smp_f32s*)data->samples;
- for(u64 i=0; i<sampleRange; ++i)
- _printf("%4llu: %7.4f, %7.4f\n", i, smpStereo[i].l, smpStereo[i].r);
- } else {
- _printf("CHANNELS IS NEITHER MONO NOR STEREO\n");
- }
- }
- }
- AudioDataHeader* AudioData::_parseKPM(BinaryData& fileData){
- //verify header data
- AudioDataHeader* hdr = (AudioDataHeader*)fileData.getData();
- size_t totalSize = fileData.getSize();
- if(hdr->magic != KIT_MAGIC_KPM) throw "magic != KIT_MAGIC_KPM";
- if(!isFormatValid(hdr->format)) throw "format is invalid";
- if(hdr->headerSize < sizeof(AudioDataHeader)) throw "headerSize < sizeof(AudioDataHeader)";
- if(hdr->dataSize != (totalSize-hdr->headerSize)) throw "dataSize is invalid";
- //(channels are checked before numSamples to prevent divide-by-zero exceptions)
- if(hdr->channels!=1 && hdr->channels!=2) throw "audio is neither mono nor stereo";
- //(numSamples is checked before loopStart/loopEnd, as their checks rely upon numSamples)
- if(hdr->numSamples != (hdr->dataSize/KIT_ASTREAM_FMT_BYTESIZE(hdr->format))/hdr->channels) throw "numSamples is invalid";
- if(hdr->loopStart >= hdr->numSamples) throw "loopStart >= numSamples";
- if(hdr->loopEnd > hdr->numSamples) throw "loopEnd > numSamples";
- if(hdr->sampleRate < 1000) throw "sampleRate < 1000";
- if(hdr->bitRate != hdr->sampleRate*hdr->channels*KIT_ASTREAM_FMT_BITSIZE(hdr->format)) throw "bitRate is invalid";
- if(hdr->bitRemainder != 0) throw "bitRemainder != 0";
- if(hdr->mode != 0) throw "only mode 0 kPCM files are currently supported";
- //allocate and copy buffers
- AudioDataHeader* data = (AudioDataHeader*)memory::alloc(totalSize);
- if(data == nullptr) throw "failed to allocate memory for intermediate buffer";
- //(header data is contiguous with sample data, so it's fine to do a straight copy here)
- memory::copy(data, hdr, totalSize);
- return data;
- }
- void AudioData::_construct(const char* filePath, u32 convertToSampleRate,
- bool convertToStereo)
- {
- _type = KIT_CLASSTYPE_AUDIODATA;
- BinaryData fileData(filePath);
- AudioDataHeader* audioIn = nullptr;
- switch(fileData.getMagic32()){
- case KIT_MAGIC_KPM: audioIn = _parseKPM(fileData); break;
- case KIT_MAGIC_QOA: audioIn = _parseQOA(fileData); break;
- default: throw "uknown audio format";
- }
- AudioDataHeader headerIn = *audioIn;
- //printAudioData(audioIn,0); //for debug
- //convert to stereo f32
- u64 numSamplesIn = headerIn.numSamples;
- //(the size of these does not include the header. this is just sample data)
- smp_ptr smpIn = ((u8*)audioIn) + audioIn->headerSize;
- smp_f32s* smpIn_f32s = (smp_f32s*)memory::alloc(numSamplesIn*sizeof(smp_f32s));
- if(smpIn_f32s == nullptr) throw "failed to allocate memory for 2nd intermediate buffer";
- u64 smpSize_s24 = numSamplesIn*3;
- u64 smpSize_s24s = numSamplesIn*6;
- switch(FORMAT_SWITCH(audioIn->channels, audioIn->format)){
- case fmtsw_u8 : for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._u8 [i]; break;
- case fmtsw_s8 : for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._s8 [i]; break;
- case fmtsw_s16 : for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._s16 [i]; break;
- case fmtsw_s32 : for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._s32 [i]; break;
- case fmtsw_f32 : for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._f32 [i]; break;
- case fmtsw_u8s : for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._u8s [i]; break;
- case fmtsw_s8s : for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._s8s [i]; break;
- case fmtsw_s16s: for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._s16s[i]; break;
- case fmtsw_s32s: for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._s32s[i]; break;
- case fmtsw_f32s: for(u64 i=0; i<numSamplesIn; ++i) smpIn_f32s[i] = smpIn._f32s[i]; break;
- //24-bit is considered a special case(s)
- case fmtsw_s24 :
- for(u64 i=KIT_U64_MAX, ii=0; ii<smpSize_s24; ii+=3){
- smpIn_f32s[++i] = *( (smp_s24*)(&smpIn._u8[ii]) );
- } break;
- case fmtsw_s24s:
- for(u64 i=KIT_U64_MAX, ii=0; ii<smpSize_s24s; ii+=6){
- smpIn_f32s[++i] = *( (smp_s24s*)(&smpIn._u8[ii]) );
- } break;
- default:
- memory::free(&audioIn);
- throw "channels/format is invalid (this exception should be impossible)";
- }
- memory::free(&audioIn); //free input buffer
- //sample rate conversion
- //first, calculate output header values
- f64 smpRateRatio = (f64)headerIn.sampleRate/convertToSampleRate;
- AudioDataHeader headerOut;
- headerOut.numSamples = (u64)( ((f64)headerIn.numSamples/smpRateRatio) + 0.5);
- headerOut.magic = headerIn.magic;
- headerOut.format = ASTREAM_FMT_F32;
- headerOut.headerSize = sizeof(AudioDataHeader);
- headerOut.dataSize = headerOut.numSamples * sizeof(smp_f32s);
- headerOut.loopStart = (u64)( ((f64)headerIn.loopStart /smpRateRatio) + 0.5);
- headerOut.loopEnd = (u64)( ((f64)headerIn.loopEnd /smpRateRatio) + 0.5);
- //(numSamples is already set)
- headerOut.sampleRate = convertToSampleRate;
- headerOut.bitRate = headerOut.sampleRate * sizeof(smp_f32s) * 8;
- headerOut.loopCount = headerIn.loopCount;
- headerOut.channels = 2;
- headerOut.bitRemainder = 0;
- headerOut.userflags = headerIn.userflags;
- headerOut.mode = 0;
- //headerOut.samples = <whatever the output audio data + headerSize is>
- headerOut.AD_p = this;
- if(headerOut.numSamples == 0){
- memory::free(&smpIn_f32s);
- throw "output's numSamples was equal to 0";
- }
- u64 totalSizeOut = headerOut.headerSize + headerOut.dataSize;
- AudioDataHeader* audioOut = (AudioDataHeader*)memory::alloc(totalSizeOut);
- if(audioOut == nullptr){
- memory::free(&smpIn_f32s);
- throw "failed to allocate memory for 3rd intermediate buffer";
- }
- headerOut.samples = ((u8*)audioOut) + headerOut.headerSize;
- *audioOut = headerOut;
- smp_f32s* smpOut = (smp_f32s*)audioOut->samples;
- //highest index of both the input and output sample buffers
- u64 hiIndexIn = headerIn.numSamples - 1;
- u64 hiIndexOut = headerOut.numSamples - 1;
- f64 inPosition = 0;
- f64 inSpeed = (f64)hiIndexIn/hiIndexOut;
- for(u64 i=0; i<hiIndexOut; ++i){
- smpOut[i] = linearSampleStereo(smpIn_f32s, inPosition);
- inPosition += inSpeed;
- }
- //do last sample separately to (hopefully) account for any prev. rounding errors
- smpOut[hiIndexOut] = smpIn_f32s[hiIndexIn];
- memory::free(&smpIn_f32s);
- //convert to mono if necessary
- if(!convertToStereo){
- //since we're *reducing* the number of channels here,
- //the mono sample assignments should always be outpaced
- //by the stereo sample accesses, thereby avoiding overlap
- f32* smpOutMono = (f32* )audioOut->samples;
- smp_f32s* smpOutStereo = (smp_f32s*)audioOut->samples;
- for(u64 i=0; i<headerOut.numSamples; ++i){
- smp_f32s smp = smpOutStereo[i];
- smpOutMono[i] = (smp.l + smp.r) / 2;
- }
- //update header to reflect the change from stereo to mono
- audioOut->dataSize /= 2;
- audioOut->bitRate /= 2;
- audioOut->channels = 1;
- //reallocate buffer *down* to reflect the change to dataSize
- if(!memory::realloc(&audioOut, audioOut->headerSize + audioOut->dataSize)){
- memory::free(&audioOut);
- throw "failed to reallocate memory for mono buffer";
- }
- audioOut->samples = ((u8*)audioOut) + headerOut.headerSize;
- }
- _data = audioOut;
- _valid = true;
- _constructing = false;
- }
- AudioData::AudioData(const char* filePath, const SoundEngine* se){
- if(se == nullptr)
- throw "se = nullptr";
- if(!KIT_IS_CLASS_TYPE(se,KIT_CLASSTYPE_SOUNDENGINE))
- throw "se is not a sound engine";
- if(!KIT_IS_CLASS_VALID(se))
- throw "se is invalid";
- _SoundEngineOpaque* se_opq = (_SoundEngineOpaque*)KIT_GET_CLASS_OPAQUE(se);
- AudioStreamInfo info = se_opq->stream->getInfo();
- u32 convertToSampleRate = (u32)info.sampleRate;
- bool convertToStereo = info.outputChannels == 2;
- _construct(filePath, convertToSampleRate, convertToStereo);
- //(^^_construct already throws if filePath is nullptr,
- //so a check here would be redundant)
- }
- AudioData::~AudioData(){
- if(!_valid) return;
- _valid = false;
- memory::free(&_data);
- }
- void AudioData::print(size_t samplesToPrint){
- #if defined(_DEBUG)
- printAudioData(_data, samplesToPrint);
- #endif
- }
- }; /* namespace kit */
- /******************************************************************************/
- /******************************************************************************/
- //"kit_xmp_sfx\src\kit_xmp_sfx\kit_AudioData_QOA.cpp":
- #include "_kit_shared.hpp"
- namespace kit {
- #include "kit_qoa.h"
- AudioDataHeader* AudioData::_parseQOA(BinaryData& fileData){
- s32 fileDataSize = (s32)fileData.getSize();
- u8* fileDataRaw = (u8*)fileData.getData();
- if(fileData.getSize() > KIT_S32_MAX) throw "qoa file data size >= 2GiB";
- //decode file data
- qoa_desc samples_info;
- s16* samples = qoa_decode(fileDataRaw, fileDataSize, &samples_info);
- if(samples == nullptr) "failed to decode qoa file data";
- u32 numSamples = samples_info.samples;
- u32 sampleRate = samples_info.samplerate;
- u32 channels = samples_info.channels;
- if(channels != 1 && channels != 2){
- memory::free(&samples);
- throw "qoa is neither mono nor stereo";
- }
- //copy sample data to intermediate buffer
- size_t dataSize = numSamples * sizeof(s16) * channels;
- AudioDataHeader* header = (AudioDataHeader*)memory::alloc(sizeof(AudioDataHeader) + dataSize);
- if(header == nullptr){
- memory::free(&samples);
- throw "failed to allocate space for intermediate buffer";
- }
- memory::set(header, 0, sizeof(AudioDataHeader) + dataSize);
- memory::copy((u8*)header + sizeof(AudioDataHeader), samples, dataSize);
- memory::free(&samples);
- //fill in header data
- header->magic = KIT_MAGIC_KPM;
- header->format = ASTREAM_FMT_S16;
- header->headerSize = sizeof(AudioDataHeader);
- header->dataSize = dataSize;
- header->loopStart = 0;
- header->loopEnd = numSamples;
- header->numSamples = numSamples;
- header->sampleRate = sampleRate;
- header->bitRate = sampleRate * sizeof(s16) * channels * 8;
- header->loopCount = 0;
- header->channels = channels;
- header->bitRemainder = 0;
- header->userflags = 0;
- header->mode = 0;
- //the AudioData ignores this section for input, so there's no need to set these
- //header->samples = (u8*)header + sizeof(AudioDataHeader);
- //header->AD_p = this;
- return header;
- }
- #define QOA_IMPLEMENTATION
- #include "kit_qoa.h"
- }; /* namespace kit */
- /******************************************************************************/
- /******************************************************************************/
- //"kit_xmp_sfx\src\kit_xmp_sfx\kit_SoundEngine.cpp":
- #include "_kit_shared.hpp"
- #define KIT_INVALID_SOUNDENGINE _kit_invalid_soundengine
- #define KIT_IS_INVALID_SOUNDENGINE (!_valid && !_constructing)
- namespace kit {
- const char _kit_invalid_soundengine[] = "invalid sound engine";
- extern s32 _SoundEngine_callback(const void* _input, void* _output,
- const AudioStreamInfo* info, void* userdata);
- SoundEngine::SoundEngine(s32 sfxNumTracks,
- bool musicUseNearestNeighbor,
- bool musicCheckLoop,
- bool musicNoFilter)
- {
- _type = KIT_CLASSTYPE_SOUNDENGINE;
- if(sfxNumTracks < 1) throw "sfxNumTracks < 1";
- _opq = (_SoundEngineOpaque*)memory::alloc(sizeof(_SoundEngineOpaque));
- if(_opq == nullptr) throw "ran out of memory creating _opq";
- memory::set(_opq, 0, sizeof(_SoundEngineOpaque));
- const char* errorText = nullptr;
- if(0){ //entered in the event of an exception
- //no need to delete critical section, since it's the last thing to be done,
- //and there is no failure condition for creating one anyway
- //_DeleteCritSect: DeleteCriticalSection(&_opq->lock);
- //(and by extension, this isn't necessary either!)
- //_FreeXMPBuffer : memory::free(&_opq->xmpBuffer);
- _FreeXMPContext: xmp_free_context(_opq->xmpCtx);
- _FreeTracks : memory::free(&_opq->tracks);
- _DelAudioStream: delete _opq->stream;
- _FreeOpaque : memory::free(&_opq);
- throw errorText;
- }
- //open audio stream
- s32 deviceID = audio::getDefOutputDevice();
- AudioDeviceInfo deviceInfo = audio::getDeviceInfo(deviceID);
- //libxmp only accepts sample rates between 8000 and 48000, so hopefully
- //no output device has a default that's lower or higher than that
- if(deviceInfo.defSampleRate < 8000.0) throw "default sample rate < 8000";
- if(deviceInfo.defSampleRate > 48000.0) throw "default sample rate > 48000";
- AudioStreamParams params;
- params.callback = _SoundEngine_callback;
- params.userdata = this;
- params.outputDeviceID = deviceID;
- params.outputChannels = MIN(2, deviceInfo.maxChannels.output);
- params.outputFormat = ASTREAM_FMT_F32;
- params.sampleFrames = (u32)( deviceInfo.defSampleRate*_soundEngineBufferSize );
- params.sampleRate = deviceInfo.defSampleRate;
- params.outputSuggestedLatency = deviceInfo.defHiLatency.output;
- if(params.outputChannels == 0) //idk how this could happen lol, but just in case
- throw "default output device's maximum channel count is 0";
- try {
- _opq->stream = new AudioStream(¶ms);
- } catch(const char* _errorText){
- errorText = _errorText;
- goto _FreeOpaque;
- }
- _opq->streamIsMono = params.outputChannels == 1;
- _opq->streamSampleRate = params.sampleRate;
- _opq->streamSampleFrames = params.sampleFrames;
- //create tracks
- _opq->tracks = (_SoundEngineTrack*)memory::alloc(sizeof(_SoundEngineTrack)*sfxNumTracks);
- if(_opq == nullptr){
- errorText = "failed to allocate memory for tracks";
- goto _DelAudioStream;
- }
- memory::set(_opq->tracks, 0, sizeof(_SoundEngineTrack)*sfxNumTracks);
- _opq->tracks_len = sfxNumTracks;
- //create xmp context, and set
- _opq->xmpCtx = xmp_create_context();
- if(_opq->xmpCtx == nullptr){
- errorText = "failed to create xmp context";
- goto _FreeTracks;
- }
- //create special buffer for libxmp, since it uses s16, not f32
- //(no need to cast from void*, since _data is already void*)
- _opq->xmpBuffer._data = memory::alloc(sizeof(s16)*params.sampleFrames*params.outputChannels);
- if(_opq->xmpBuffer._data == nullptr){
- errorText = "failed to allocate memory for intermediate libxmp buffer";
- goto _FreeXMPContext;
- }
- memory::set(_opq->xmpBuffer._data, 0, sizeof(s16)*params.sampleFrames*params.outputChannels);
- //create critical section
- InitializeCriticalSectionAndSpinCount(&_opq->lock, KIT_LOCK_SPINCOUNT);
- //initialize the rest of _opq's members
- _opq->volumeStream_old = 1.0f;
- _opq->volumeStream_new = 1.0f;
- _opq->panSfx_old = 0.0f;
- _opq->panSfx_new = 0.0f;
- _opq->volumeSfx_old = 1.0f;
- _opq->volumeSfx_new = 1.0f;
- _opq->panXmp_old = 0.0f;
- _opq->panXmp_new = 0.0f;
- _opq->volumeXmp_old = 1.0f;
- _opq->volumeXmp_new = 1.0f;
- _opq->fadeDelta = 1.0f / (f32)(_opq->streamSampleRate*_fadeDeltaSeconds);
- _opq->fadeInDelayStream = (u32)(_opq->streamSampleRate*_fadeInDelaySeconds);
- _opq->fadeVolumeStream = 0.0f;
- _opq->fadeVolumeXmp = 0.0f;
- _opq->fadeOutStream = false;
- _opq->fadeOutXmp = false;
- _opq->xmpUseNearest = musicUseNearestNeighbor;
- _opq->xmpCheckLoop = musicCheckLoop;
- _opq->xmpNoFilter = musicNoFilter;
- _opq->xmpModuleLoaded = false;
- _opq->xmpPlaying = false;
- _opq->streamPlaying = false;
- _valid = true;
- _constructing = false;
- }
- SoundEngine::~SoundEngine(){
- if(!_valid) return;
- _valid = false;
- if(_opq != nullptr){
- _opq->stream->stop();
- while(_opq->stream->isActive()) time::sleep(0);
- EnterCriticalSection(&_opq->lock);
- delete _opq->stream;
- //force module playback to stop before freeing any stuff relevant to libxmp
- if(_opq->xmpPlaying ){ xmp_end_player(_opq->xmpCtx); _opq->xmpPlaying=false; }
- if(_opq->xmpModuleLoaded){ xmp_release_module(_opq->xmpCtx); _opq->xmpModuleLoaded=false; }
- xmp_free_context(_opq->xmpCtx);
- memory::free(&_opq->xmpBuffer);
- //force all sound effects to stop playing, before freeing tracks
- _SoundEngineTrack* tracks = _opq->tracks;
- s32 tracks_len = _opq->tracks_len;
- for(s32 t=0; t<tracks_len; ++t) tracks[t].audio = nullptr;
- memory::free(&_opq->tracks);
- LeaveCriticalSection(&_opq->lock);
- DeleteCriticalSection(&_opq->lock);
- memory::free(&_opq);
- }
- }
- bool SoundEngine::streamIsMono(){
- if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
- return _opq->streamIsMono;
- }
- f64 SoundEngine::streamGetCPULoad(){
- if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
- return _opq->stream->getCPULoad();
- }
- void SoundEngine::streamSetVolume(f32 volumeL, f32 volumeR){
- if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
- smp_f32s volumeNew(volumeL, volumeR);
- lock(true);
- _opq->volumeStream_new = volumeNew;
- lock(false);
- }
- void SoundEngine::streamSetPlayback(bool playing){
- if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
- if(playing != _opq->streamPlaying){
- lock(true);
- _opq->fadeOutStream = !playing;
- if(playing){
- _opq->fadeInDelayStream = (u32)(_opq->streamSampleRate*_fadeInDelaySeconds);
- _opq->streamPlaying = true;
- _opq->stream->start();
- }
- //(fade out and actual pausing is done inside the callback)
- lock(false);
- }
- }
- void SoundEngine::streamSetPlaybackAndWait(bool playing){
- //(streamSetPlayback already does a validity check)
- //if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
- u64 timeStamp = time::getTicks();
- bool wasPlaying = _opq->streamPlaying;
- //mutex is locked inside streamSetPlayback,
- //and there's no need to lock in this function otherwise
- streamSetPlayback(playing);
- if(playing && !wasPlaying){
- time::sleep( (u32)(_totalFadeDelay*1000) );
- } else if(!playing && wasPlaying){
- u64 timeoutTicks = (u64)(_totalFadeDelay*3 * time::getTicksPerSecond());
- while(_opq->streamPlaying){
- time::sleep(10);
- if((time::getTicks()-timeStamp) > timeoutTicks) throw "stream pause timed out";
- }
- }
- }
- void SoundEngine::streamSetVolumeForced(f32 volumeL, f32 volumeR){
- if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
- smp_f32s volumeNew(volumeL, volumeR);
- lock(true);
- _opq->volumeStream_old = volumeNew;
- _opq->volumeStream_new = volumeNew;
- lock(false);
- }
- void SoundEngine::lock(bool locked){
- if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
- if(locked) EnterCriticalSection(&_opq->lock);
- else LeaveCriticalSection(&_opq->lock);
- }
- void SoundEngine::streamStopForced(){
- if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
- if(_opq->streamPlaying){
- lock(true);
- _opq->stream->abort();
- _opq->streamPlaying = false;
- lock(false);
- }
- }
- }; /* namespace kit */
- /******************************************************************************/
- /******************************************************************************/
- //"kit_xmp_sfx\src\kit_xmp_sfx\kit_SoundEngine_callback.cpp":
- //#include "_kit_shared.hpp" //included by kit_SoundEngine_callbackSfx.hpp
- #include "kit_SoundEngine_callbackSfx.hpp"
- namespace kit {
- static inline void applyFadeStream(_SoundEngineOpaque* opq,
- smp_ptr buffer, u32 buffer_len)
- {
- f32 fDelta = opq->fadeDelta;
- f32 fVolume = opq->fadeVolumeStream;
- u32 fInDelay = opq->fadeInDelayStream;
- u32 i = 0; //this index is shared, as the loops can jump to others at will
- //FADING OUT
- if(opq->fadeOutStream){
- _FadeOut:;
- if(opq->streamIsMono){ //mono fade out
- for(; i<buffer_len; ++i){
- if(!opq->fadeOutStream) goto _FadeIn;
- buffer._f32[i] *= fVolume;
- fVolume -= fDelta;
- if(fVolume < 0.0f) fVolume = 0.0f;
- }
- } else { //stereo fade out
- for(; i<buffer_len; ++i){
- if(!opq->fadeOutStream) goto _FadeIn;
- buffer._f32s[i] *= fVolume;
- fVolume -= fDelta;
- if(fVolume < 0.0f) fVolume = 0.0f;
- }
- }
- //pause stream if fade-out finishes
- if(fVolume <= 0.0f){
- opq->stream->stop();
- opq->streamPlaying = false;
- }
- //FADING IN
- } else if(fVolume < 1.0f){
- //let stream warm up before fading in
- //(this *was* a solution to an issue specific to SDL's audio subsystem,
- //but i'll include it here too, just in case)
- if(opq->streamIsMono){ //mono fade delay
- for(; (fInDelay)&&(i<buffer_len); ++i,--fInDelay) buffer._f32 [i] = 0.0f;
- } else { //stereo fade delay
- for(; (fInDelay)&&(i<buffer_len); ++i,--fInDelay) buffer._f32s[i] = 0.0f;
- }
- _FadeIn:;
- if(opq->streamIsMono){ //mono fade in
- for(; i<buffer_len; ++i){
- if(opq->fadeOutStream) goto _FadeOut;
- else if(fVolume >= 1.0f){ fVolume = 1.0f; break; }
- buffer._f32[i] *= fVolume;
- fVolume += fDelta;
- }
- } else { //stereo fade in
- for(; i<buffer_len; ++i){
- if(opq->fadeOutStream) goto _FadeOut;
- else if(fVolume >= 1.0f){ fVolume = 1.0f; break; }
- buffer._f32s[i] *= fVolume;
- fVolume += fDelta;
- }
- }
- }
- opq->fadeVolumeStream = fVolume; //update fade volume to new value
- opq->fadeInDelayStream = fInDelay; //update fade delay to new value
- }
- extern s32 _SoundEngine_callbackXmp(void* userdata);
- s32 _SoundEngine_callback(const void* _input, void* _output,
- const AudioStreamInfo* info, void* userdata)
- {
- s32 returnStatus = ASTREAM_RTN_CONTINUE;
- //userdata points to a SoundEngine, so to get its opaque we need to do this
- _SoundEngineOpaque* opq = (_SoundEngineOpaque*)KIT_GET_CLASS_OPAQUE(userdata);
- (void)_input; //input is left unused
- smp_ptr output = _output;
- u32 output_len = info->sampleFrames;
- bool output_mono = info->outputChannels == 1;
- if(!KIT_IS_CLASS_VALID(userdata)) return ASTREAM_RTN_ABORT;
- EnterCriticalSection(&opq->lock); //lock mutex
- if(!KIT_IS_CLASS_VALID(userdata)){ //just in case
- LeaveCriticalSection(&opq->lock);
- return ASTREAM_RTN_ABORT;
- }
- //start music thread
- Thread* musicThread = nullptr;
- try {
- musicThread = new Thread(_SoundEngine_callbackXmp, opq);
- } catch(const char* errorText){
- _printf("musicThread constructor error = \"%s\"\n", errorText);
- (void)errorText; //otherwise compiler warns of unreferenced variable #ifndef _DEBUG
- }
- //initialize output buffer to 0, as the stream buffer
- //has to be filled with *something* no matter what
- memory::set(_output, 0, info->outputBufferSize);
- //mix sfx
- _SoundEngineTrack* tracks = opq->tracks;
- s32 tracks_len = opq->tracks_len;
- f64 timestamp = info->timeOutput;
- f64 sampleRate = info->sampleRate;
- if(output_mono){
- for(s32 t=0; t<tracks_len; ++t){
- if(tracks[t].audio != nullptr)
- mixTracksMono(tracks[t], timestamp, output._f32, output_len, sampleRate);
- }
- } else { //stereo
- for(s32 t=0; t<tracks_len; ++t){
- if(tracks[t].audio != nullptr)
- mixTracksStereo(tracks[t], timestamp, output._f32s, output_len, sampleRate);
- }
- }
- //(if volume or pan is not normal, basically)
- if(opq->volumeSfx_old != 1.0f || opq->volumeSfx_new != 1.0f ||
- opq->panSfx_old != 0.0f || opq->panSfx_new != 0.0f )
- {
- //apply sfx's volume and pan, while interpolating their values
- //from their old state to its new one
- f32 t = 0.0f;
- f32 t_increment = 1.0f/output_len;
- smp_f32s volume_old = opq->volumeSfx_old;
- smp_f32s volume_new = opq->volumeSfx_new;
- opq->volumeSfx_old = volume_new; //update old volume to the new one
- if(output_mono){
- for(u32 i=0; i<output_len; ++i){
- output._f32[i] *= LERP2(volume_old.l, volume_new.l, t);
- t += t_increment; //add to interpolation t value
- }
- } else { //stereo
- //panning is only applied if samples are stereo,
- //so there's no need to declare it outside of this scope
- f32 pan_old = opq->panSfx_old;
- f32 pan_new = opq->panSfx_new;
- opq->panSfx_old = pan_new; //update old pan to the new one
- for(u32 i=0; i<output_len; ++i){
- smp_f32s sample = output._f32s[i];
- sample *= interpolateF32S(volume_old, volume_new, t);
- output._f32s[i] = applyPan(sample, LERP2(pan_old, pan_new, t) );
- t += t_increment; //add to interpolation t value
- }
- }
- }
- //wait for music thread
- if(musicThread != nullptr){
- try {
- musicThread->waitUntilDone(2000); //2 seconds of timeout should be fine
- } catch(const char* errorText){
- _printf("musicThread->waitUntilDone() error = \"%s\"\n", errorText);
- (void)errorText;
- }
- delete musicThread;
- }
- //mix intermediate music buffer into output
- smp_ptr buffer = opq->xmpBuffer;
- if(output_mono){
- for(u32 i=0; i<output_len; ++i) output._f32 [i] += s16_conv(buffer._s16[i]);
- } else {
- for(u32 i=0; i<output_len; ++i) output._f32s[i] += buffer._s16s[i];
- }
- //apply stream's volume, while interpolating their values
- //from their old state to its new one
- f32 t = 0.0f;
- f32 t_increment = 1.0f/output_len;
- smp_f32s volume_old = opq->volumeStream_old;
- smp_f32s volume_new = opq->volumeStream_new;
- opq->volumeStream_old = volume_new; //update old volume to the new one
- if(output_mono){
- for(u32 i=0; i<output_len; ++i){
- output._f32[i] *= LERP2(volume_old.l, volume_new.l, t);
- t += t_increment; //add to interpolation t value
- }
- } else { //stereo
- for(u32 i=0; i<output_len; ++i){
- output._f32s[i] *= interpolateF32S(volume_old, volume_new, t);
- t += t_increment; //add to interpolation t value
- }
- }
- //apply stream's fade in/out
- applyFadeStream(opq, output, output_len);
- LeaveCriticalSection(&opq->lock); //unlock mutex
- return returnStatus;
- }
- }; /* namespace kit */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement