2021-12-13 00:28:04 +01:00
# include "sounddevice.h"
# include <cassert>
# include <iostream>
2021-12-13 14:48:43 +01:00
# include <Log.h>
2021-12-13 00:28:04 +01:00
# 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 < unsigned int > ( 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 ;
2021-12-13 14:48:43 +01:00
Log : : info < < " " < < playbackDeviceCount < < " playback devices found: " ;
2021-12-13 00:28:04 +01:00
for ( uint8_t i = 0 ; i < playbackDeviceCount ; i + + ) {
2021-12-13 14:48:43 +01:00
Log : : info < < " " < < ( int ) i < < " : " < < pPlaybackDeviceInfos [ i ] . name ;
2021-12-13 00:28:04 +01:00
if ( pPlaybackDeviceInfos [ i ] . name = = name ) {
choosenDevice = i ;
}
}
if ( choosenDevice = = - 1 ) {
2021-12-13 14:48:43 +01:00
Log : : info < < " Device \" " < < name < < " \" not found! " ;
2021-12-13 00:28:04 +01:00
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 ;
2021-12-13 01:23:03 +01:00
deviceConfig . sampleRate = 48000 ; // 44100;
2021-12-13 00:28:04 +01:00
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 ) {
2021-12-13 14:48:43 +01:00
Log : : error < < " Failed to open sound device. " ;
2021-12-13 00:28:04 +01:00
delete sd ;
return nullptr ;
}
2021-12-13 14:48:43 +01:00
Log : : info < < " Sound playback device \" " < < sd - > device . playback . name < < " \" initilized. " ;
2021-12-13 00:28:04 +01:00
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 )
2021-12-13 14:48:43 +01:00
Log : : info < < " Sound playback device \" " < < device . playback . name < < " \" stopped. " ;
2021-12-13 00:28:04 +01:00
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 ) {
2021-12-13 14:48:43 +01:00
Log : : info < < " Sound datei: " < < name < < " konnte nicht geladen werden! " ;
2021-12-13 00:28:04 +01:00
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 ) {
2021-12-13 14:48:43 +01:00
Log : : error < < " Failed to start sound device \" " < < device . playback . name < < " \" " ;
2021-12-13 00:28:04 +01:00
stop ( ) ;
ma_device_uninit ( & device ) ;
throw std : : exception ( ) ;
}
deviceRunning = true ;
2021-12-13 14:48:43 +01:00
Log : : info < < " Sound device \" " < < device . playback . name < < " \" started. " ;
2021-12-13 00:28:04 +01:00
}
void SoundDevice : : cleanupDecoders ( ) {
unsigned int count = 0 ;
ma_mutex_lock ( data . mutex ) ;
for ( std : : list < Playback * > : : 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 )
2021-12-13 14:48:43 +01:00
Log : : debug < < " removed " < < count < < " decoder " ;
2021-12-13 00:28:04 +01:00
}