#include "sounddevice.h" #include #include #include #include "sound.h" #define CHANNELCOUNT 2 #define MINSTREAMCOUNT 5 static void global_sound_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { //es kann davon ausgegangen werden, das der buffer mit stille gefüllt ist //decoder if (!pDevice->pUserData) return; ((SoundDevice*) pDevice->pUserData)->sound_callback(pOutput, frameCount); (void) pInput; //ignore input } static ma_uint64 readDecoderandAdd(ma_decoder* decoder, float volume, unsigned int streamDivider, ma_uint32 frameCount, void* outputBuffer) { assert(frameCount > 0); //read sound int16_t* buffer = new int16_t[frameCount * CHANNELCOUNT]; ma_uint64 frameCountRead = ma_decoder_read_pcm_frames(decoder, buffer, frameCount); if (frameCountRead > frameCount) frameCountRead = frameCount; //drop unwanted frames (sollte eigentlich nicht nötig sein) //zum mixen: samples durch anzahl decoder teilen (overflow verhindern), volume factor anwenden und auf outputbuffer addieren int16_t* outbuf = (int16_t*) outputBuffer; for (ma_uint64 i = 0; i < frameCountRead * CHANNELCOUNT; i++) { outbuf[i] += (buffer[i] * volume) / streamDivider; //TODO: use sse / avx } delete[] buffer; return frameCountRead; } void SoundDevice::sound_callback(void* outbuffer, ma_uint32 frameCount) { ma_mutex_lock(data.mutex); unsigned int decoderCount = data.decoderCount; unsigned int streamDivider = std::max(decoderCount, MINSTREAMCOUNT); //nicht listplaybacks.size() verwenden, da manche playbacks möglicherweise noch fertig sind, aber noch nicht aus der list entfernt wurden. (wird von anderem thread gemacht) unsigned int count = 0; //nummer des aktuellen decoders (nur für debugging) for (SoundDevice::Playback* pb : data.playbacks) { if (pb->isDone) continue; //fertige encoder ignorieren // std::cout << "Play decoder " << (++count) << " of " << decoderCount << std::endl; //DEBUG //informationen beschaffen ma_decoder* pDecoder = &(pb->decoder); float volume = pb->volume; //decoder "reinaddieren" ma_uint64 read = readDecoderandAdd(pDecoder, volume, streamDivider, frameCount, outbuffer); //decoder fertig -> nicht mehr verwenden if (read < frameCount) { pb->isDone = true; --data.decoderCount; } } ma_mutex_unlock(data.mutex); } SoundDevice::SoundDevice() { data.mutex = new ma_mutex(); ma_mutex_init(data.mutex); } SoundDevice::~SoundDevice() { ma_device_uninit(&device); ma_mutex_uninit(data.mutex); delete data.mutex; } SoundDevice* SoundDevice::createDevice(ma_context* ctx, const std::string& name) { ma_device_info* pPlaybackDeviceInfos; ma_uint32 playbackDeviceCount; ma_result result = ma_context_get_devices(ctx, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, 0); int8_t choosenDevice = -1; Log::info << " " << playbackDeviceCount << " playback devices found:"; for(uint8_t i = 0; i < playbackDeviceCount; i++) { Log::info << " " << (int) i << ": " << pPlaybackDeviceInfos[i].name; if(pPlaybackDeviceInfos[i].name == name) { choosenDevice = i; } } if(choosenDevice == -1) { Log::info << "Device \"" << name << "\" not found!"; return nullptr; } return createDevice(ctx, &pPlaybackDeviceInfos[choosenDevice].id); } SoundDevice* SoundDevice::createDevice(ma_context* ctx, const ma_device_id* did) { ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback); deviceConfig.playback.format = ma_format::ma_format_s16; deviceConfig.playback.channels = CHANNELCOUNT; if(did != NULL) deviceConfig.playback.pDeviceID = did; deviceConfig.sampleRate = 48000; // 44100; deviceConfig.dataCallback = global_sound_callback; deviceConfig.pulse.pStreamNamePlayback = "MySoundboard"; SoundDevice* sd = new SoundDevice(); deviceConfig.pUserData = sd; if (ma_device_init(ctx, &deviceConfig, &sd->device) != MA_SUCCESS) { Log::error << "Failed to open sound device."; delete sd; return nullptr; } Log::info << "Sound playback device \"" << sd->device.playback.name << "\" initilized."; return sd; } void SoundDevice::stop() { if(deviceRunning) ma_device_stop(&device); ma_mutex_lock(data.mutex); for(Playback* pb : data.playbacks) { ma_decoder_uninit(&pb->decoder); delete pb; } data.playbacks.clear(); ma_mutex_unlock(data.mutex); if(deviceRunning) Log::info << "Sound playback device \"" << device.playback.name << "\" stopped."; deviceRunning = false; } void SoundDevice::addPlayback(const std::string& name, float volume) { cleanupDecoders(); Playback* pb = new Playback(); if (ma_decoder_init_file((Sound::FOLDER + name).c_str(), NULL, &(pb->decoder)) != MA_SUCCESS) { Log::info << "Sound datei: " << name << " konnte nicht geladen werden!"; throw std::exception(); } pb->volume = volume; pb->isDone = false; ma_mutex_lock(data.mutex); data.playbacks.push_back(pb); ++data.decoderCount; ma_mutex_unlock(data.mutex); startDevice(); } void SoundDevice::startDevice() { if (deviceRunning) return; if (ma_device_start(&device) != MA_SUCCESS) { Log::error << "Failed to start sound device \"" << device.playback.name << "\""; stop(); ma_device_uninit(&device); throw std::exception(); } deviceRunning = true; Log::info << "Sound device \"" << device.playback.name << "\" started."; } void SoundDevice::cleanupDecoders() { unsigned int count = 0; ma_mutex_lock(data.mutex); for (std::list::iterator it = data.playbacks.begin(); it != data.playbacks.end(); ) { Playback* pb = *it; if (pb->isDone) { //uninit decoder ma_decoder_uninit(&pb->decoder); count++; data.playbacks.erase(it++); delete pb; } else ++it; } ma_mutex_unlock(data.mutex); if (count) Log::debug << "removed " << count << " decoder"; }