2020-02-09 13:15:21 +01:00
# include "./AudioOutput.h"
# include "./AudioMerger.h"
# include "./AudioResampler.h"
2019-10-26 01:51:40 +02:00
# include "../logger.h"
# include <cstring>
# include <algorithm>
# include <string>
using namespace std ;
using namespace tc ;
using namespace tc : : audio ;
void AudioOutputSource : : clear ( ) {
2020-02-08 16:50:48 +01:00
this - > buffer . clear ( ) ;
this - > buffering = true ;
2019-10-26 01:51:40 +02:00
}
ssize_t AudioOutputSource : : pop_samples ( void * buffer , size_t samples ) {
2020-02-08 16:50:48 +01:00
size_t written { 0 } , written_bytes { 0 } ;
load_buffer :
auto available_bytes = this - > buffer . fill_count ( ) ;
if ( available_bytes < sizeof ( float ) * this - > channel_count ) return written ;
auto available_samples = available_bytes / sizeof ( float ) / this - > channel_count ;
//log_trace(category::audio, tr("Min: {}, Max: {}, Current: {}, Buffering: {}"), this->min_buffered_samples, this->max_buffered_samples, available_samples, this->buffering);
if ( this - > buffering & & available_samples < this - > min_buffered_samples ) return - 2 ;
this - > buffering = false ;
if ( available_samples > = samples - written ) {
const auto byte_length = ( samples - written ) * sizeof ( float ) * this - > channel_count ;
2020-02-09 21:19:11 +01:00
if ( buffer ) memcpy ( ( char * ) buffer + written_bytes , this - > buffer . read_ptr ( ) , byte_length ) ;
2020-02-08 16:50:48 +01:00
this - > buffer . advance_read_ptr ( byte_length ) ;
return samples ;
} else {
const auto byte_length = available_samples * sizeof ( float ) * this - > channel_count ;
2020-02-09 21:19:11 +01:00
if ( buffer ) memcpy ( ( char * ) buffer + written_bytes , this - > buffer . read_ptr ( ) , byte_length ) ;
2020-02-08 16:50:48 +01:00
this - > buffer . advance_read_ptr ( byte_length ) ;
written + = available_samples ;
written_bytes + = byte_length ;
}
if ( auto fn = this - > on_underflow ; fn )
2020-02-09 14:53:39 +01:00
if ( fn ( samples - written ) )
2020-02-08 16:50:48 +01:00
goto load_buffer ;
2020-02-09 21:19:11 +01:00
if ( buffer ) memset ( buffer , 0 , ( samples - written ) * sizeof ( float ) * this - > channel_count ) ;
2020-02-08 16:50:48 +01:00
this - > buffering = true ;
2019-10-26 01:51:40 +02:00
if ( this - > on_read )
this - > on_read ( ) ;
2020-02-08 16:50:48 +01:00
return written ; /* return the written samples */
2019-10-26 01:51:40 +02:00
}
ssize_t AudioOutputSource : : enqueue_samples ( const void * buffer , size_t samples ) {
2020-02-08 16:50:48 +01:00
size_t enqueued { 0 } ;
auto free_bytes = this - > buffer . free_count ( ) ;
auto free_samples = free_bytes / sizeof ( float ) / this - > channel_count ;
if ( this - > max_buffered_samples & & free_samples > this - > max_buffered_samples ) free_samples = this - > max_buffered_samples ;
if ( free_samples > = samples ) {
const auto byte_length = samples * sizeof ( float ) * this - > channel_count ;
memcpy ( this - > buffer . write_ptr ( ) , buffer , byte_length ) ;
this - > buffer . advance_write_ptr ( byte_length ) ;
return samples ;
} else {
const auto byte_length = free_samples * sizeof ( float ) * this - > channel_count ;
memcpy ( this - > buffer . write_ptr ( ) , buffer , byte_length ) ;
this - > buffer . advance_write_ptr ( byte_length ) ;
enqueued + = free_samples ;
}
if ( auto fn = this - > on_overflow ; fn )
fn ( samples - enqueued ) ;
switch ( this - > overflow_strategy ) {
case overflow_strategy : : discard_input :
return - 2 ;
case overflow_strategy : : discard_buffer_all :
this - > buffer . clear ( ) ;
break ;
case overflow_strategy : : discard_buffer_half :
this - > buffer . advance_read_ptr ( this - > buffer . fill_count ( ) / 2 ) ;
break ;
case overflow_strategy : : ignore :
break ;
}
return enqueued ;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
ssize_t AudioOutputSource : : enqueue_samples_no_interleave ( const void * buffer , size_t samples ) {
auto free_bytes = this - > buffer . free_count ( ) ;
auto free_samples = free_bytes / sizeof ( float ) / this - > channel_count ;
if ( this - > max_buffered_samples & & free_samples > this - > max_buffered_samples ) free_samples = this - > max_buffered_samples ;
auto samples_to_write { samples } ;
if ( samples_to_write > free_samples ) samples_to_write = free_samples ;
const auto enqueued { samples_to_write } ;
{
auto src_buffer = ( const float * ) buffer ;
auto target_buffer = ( float * ) this - > buffer . write_ptr ( ) ;
while ( samples_to_write - - > 0 ) {
* target_buffer = * src_buffer ;
* ( target_buffer + 1 ) = * ( src_buffer + samples ) ;
target_buffer + = 2 ;
src_buffer + + ;
}
}
if ( auto fn = this - > on_overflow ; fn )
fn ( samples - enqueued ) ;
switch ( this - > overflow_strategy ) {
case overflow_strategy : : discard_input :
return - 2 ;
case overflow_strategy : : discard_buffer_all :
this - > buffer . clear ( ) ;
break ;
case overflow_strategy : : discard_buffer_half :
this - > buffer . advance_read_ptr ( this - > buffer . fill_count ( ) / 2 ) ;
break ;
case overflow_strategy : : ignore :
break ;
}
return enqueued ;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
AudioOutput : : AudioOutput ( size_t channels , size_t rate ) : _channel_count ( channels ) , _sample_rate ( rate ) { }
2019-10-26 01:51:40 +02:00
AudioOutput : : ~ AudioOutput ( ) {
this - > close_device ( ) ;
this - > cleanup_buffers ( ) ;
}
std : : shared_ptr < AudioOutputSource > AudioOutput : : create_source ( ) {
auto result = shared_ptr < AudioOutputSource > ( new AudioOutputSource ( this , this - > _channel_count , this - > _sample_rate ) ) ;
{
lock_guard lock ( this - > sources_lock ) ;
this - > _sources . push_back ( result ) ;
}
return result ;
}
void AudioOutput : : delete_source ( const std : : shared_ptr < tc : : audio : : AudioOutputSource > & source ) {
{
lock_guard lock ( this - > sources_lock ) ;
auto it = find ( this - > _sources . begin ( ) , this - > _sources . end ( ) , source ) ;
if ( it ! = this - > _sources . end ( ) )
this - > _sources . erase ( it ) ;
}
source - > handle = nullptr ;
}
void AudioOutput : : cleanup_buffers ( ) {
2020-02-09 14:53:39 +01:00
free ( this - > source_buffer ) ;
free ( this - > source_merge_buffer ) ;
free ( this - > resample_overhead_buffer ) ;
2019-10-26 01:51:40 +02:00
this - > source_merge_buffer = nullptr ;
this - > source_buffer = nullptr ;
2020-02-09 14:53:39 +01:00
this - > resample_overhead_buffer = nullptr ;
2019-10-26 01:51:40 +02:00
this - > source_merge_buffer_length = 0 ;
this - > source_buffer_length = 0 ;
2020-02-09 14:53:39 +01:00
this - > resample_overhead_buffer_length = 0 ;
this - > resample_overhead_samples = 0 ;
2019-10-26 01:51:40 +02:00
}
2020-02-09 14:53:39 +01:00
void AudioOutput : : fill_buffer ( void * output , size_t out_frame_count , size_t channels ) {
if ( channels ! = this - > _channel_count ) {
log_critical ( category : : audio , tr ( " Channel count miss match (output)! Fixme! " ) ) ;
return ;
}
const auto local_frame_count = this - > _resampler ? this - > _resampler - > input_size ( out_frame_count ) : out_frame_count ;
2020-02-09 21:19:11 +01:00
void * const original_output { output } ;
2020-02-09 14:53:39 +01:00
if ( this - > resample_overhead_samples > 0 ) {
const auto samples_to_write = this - > resample_overhead_samples > out_frame_count ? out_frame_count : this - > resample_overhead_samples ;
const auto byte_length = samples_to_write * sizeof ( float ) * channels ;
2020-02-09 21:19:11 +01:00
if ( output ) memcpy ( output , this - > resample_overhead_buffer , byte_length ) ;
2020-02-09 14:53:39 +01:00
if ( samples_to_write = = out_frame_count ) {
this - > resample_overhead_samples - = samples_to_write ;
memcpy ( this - > resample_overhead_buffer , ( char * ) this - > resample_overhead_buffer + byte_length , this - > resample_overhead_samples * this - > _channel_count * sizeof ( float ) ) ;
return ;
} else {
this - > resample_overhead_samples = 0 ;
output = ( char * ) output + byte_length ;
out_frame_count - = samples_to_write ;
}
}
2020-02-09 21:19:11 +01:00
if ( ! original_output ) {
for ( auto & source : this - > _sources )
source - > pop_samples ( nullptr , local_frame_count ) ;
return ;
} else if ( this - > _volume < = 0 ) {
2020-02-08 16:50:48 +01:00
for ( auto & source : this - > _sources )
2020-02-09 14:53:39 +01:00
source - > pop_samples ( nullptr , local_frame_count ) ;
memset ( output , 0 , local_frame_count * channels * sizeof ( float ) ) ;
2020-02-08 16:50:48 +01:00
return ;
}
2020-02-09 14:53:39 +01:00
const size_t local_buffer_length = local_frame_count * 4 * this - > _channel_count ;
const size_t out_buffer_length = out_frame_count * 4 * this - > _channel_count ;
2019-10-26 01:51:40 +02:00
size_t sources = 0 ;
size_t actual_sources = 0 ;
{
lock_guard lock ( this - > sources_lock ) ;
sources = this - > _sources . size ( ) ;
actual_sources = sources ;
if ( sources > 0 ) {
2020-02-08 16:50:48 +01:00
/* allocate the required space */
2020-02-09 14:53:39 +01:00
const auto required_source_buffer_length = ( out_buffer_length > local_buffer_length ? out_buffer_length : local_buffer_length ) * sources ; /* ensure enough space for later resample */
const auto required_source_merge_buffer_length = sizeof ( void * ) * sources ;
2020-02-08 16:50:48 +01:00
{
2020-02-09 14:53:39 +01:00
if ( this - > source_buffer_length < required_source_buffer_length | | ! this - > source_buffer ) {
2020-02-08 16:50:48 +01:00
if ( this - > source_buffer )
free ( this - > source_buffer ) ;
2020-02-09 14:53:39 +01:00
this - > source_buffer = malloc ( required_source_buffer_length ) ;
this - > source_buffer_length = required_source_buffer_length ;
2020-02-08 16:50:48 +01:00
}
2020-02-09 14:53:39 +01:00
if ( this - > source_merge_buffer_length < required_source_merge_buffer_length | | ! this - > source_merge_buffer ) {
2020-02-08 16:50:48 +01:00
if ( this - > source_merge_buffer )
free ( this - > source_merge_buffer ) ;
2020-02-09 14:53:39 +01:00
this - > source_merge_buffer = ( void * * ) malloc ( required_source_merge_buffer_length ) ;
this - > source_merge_buffer_length = required_source_merge_buffer_length ;
2020-02-08 16:50:48 +01:00
}
}
2019-10-26 01:51:40 +02:00
for ( size_t index = 0 ; index < sources ; index + + ) {
auto & source = this - > _sources [ index ] ;
2020-02-09 14:53:39 +01:00
this - > source_merge_buffer [ index ] = ( char * ) this - > source_buffer + ( local_buffer_length * index ) ;
auto written_frames = this - > _sources [ index ] - > pop_samples ( this - > source_merge_buffer [ index ] , local_frame_count ) ;
if ( written_frames ! = local_frame_count ) {
2020-02-08 16:50:48 +01:00
if ( written_frames < = 0 ) {
this - > source_merge_buffer [ index ] = nullptr ;
actual_sources - - ;
} else {
/* fill up the rest with silence (0) */
auto written = written_frames * this - > _channel_count * 4 ;
2020-02-09 14:53:39 +01:00
memset ( ( char * ) this - > source_merge_buffer [ index ] + written , 0 , ( local_frame_count - written_frames ) * this - > _channel_count * 4 ) ;
2020-02-08 16:50:48 +01:00
}
}
2019-10-26 01:51:40 +02:00
}
2020-02-09 14:53:39 +01:00
} else
goto clear_buffer_exit ;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
if ( actual_sources > 0 ) {
2020-02-09 14:53:39 +01:00
if ( local_frame_count = = out_frame_count ) {
if ( ! merge : : merge_n_sources ( output , this - > source_merge_buffer , sources , this - > _channel_count , local_frame_count ) )
log_warn ( category : : audio , tr ( " failed to merge buffers! " ) ) ;
} else {
if ( ! merge : : merge_n_sources ( this - > source_buffer , this - > source_merge_buffer , sources , this - > _channel_count , local_frame_count ) )
log_warn ( category : : audio , tr ( " failed to merge buffers! " ) ) ;
/* this->source_buffer could hold the amount of resampled data (checked above) */
auto resampled_samples = this - > _resampler - > process ( this - > source_buffer , this - > source_buffer , local_frame_count ) ;
if ( resampled_samples ! = out_frame_count ) {
if ( resampled_samples > out_frame_count ) {
const auto diff_length = resampled_samples - out_frame_count ;
const auto overhead_buffer_offset = this - > resample_overhead_samples * sizeof ( float ) * this - > _channel_count ;
const auto diff_byte_length = diff_length * sizeof ( float ) * this - > _channel_count ;
if ( this - > resample_overhead_buffer_length < diff_byte_length + overhead_buffer_offset ) {
this - > resample_overhead_buffer_length = diff_byte_length + overhead_buffer_offset ;
auto new_buffer = malloc ( this - > resample_overhead_buffer_length ) ;
if ( this - > resample_overhead_buffer )
memcpy ( new_buffer , this - > resample_overhead_buffer , overhead_buffer_offset ) ;
free ( this - > resample_overhead_buffer ) ;
this - > resample_overhead_buffer = new_buffer ;
}
memcpy (
( char * ) this - > resample_overhead_buffer + overhead_buffer_offset ,
( char * ) this - > source_buffer + out_frame_count * sizeof ( float ) * this - > _channel_count ,
diff_byte_length
) ;
this - > resample_overhead_samples + = diff_length ;
} else {
log_warn ( category : : audio , tr ( " Resampled samples does not match requested sampeles: {} <> {} " ) , resampled_samples , out_frame_count ) ;
}
}
memcpy ( output , this - > source_buffer , out_frame_count * sizeof ( float ) * this - > _channel_count ) ;
}
2020-02-08 16:50:48 +01:00
2020-02-09 14:53:39 +01:00
/* lets apply the volume */
2020-02-08 16:50:48 +01:00
auto volume = this - > _volume ;
if ( volume ! = 1 ) {
2020-02-09 14:53:39 +01:00
auto float_length = this - > _channel_count * out_frame_count ;
2020-02-08 16:50:48 +01:00
auto data = ( float * ) output ;
while ( float_length - - > 0 )
* data + + * = volume ;
2019-10-26 01:51:40 +02:00
}
} else {
2020-02-09 14:53:39 +01:00
clear_buffer_exit :
memset ( output , 0 , this - > _channel_count * sizeof ( float ) * out_frame_count ) ;
2019-10-26 01:51:40 +02:00
}
}
2020-02-09 14:53:39 +01:00
void AudioOutput : : set_device ( const std : : shared_ptr < AudioDevice > & new_device ) {
2020-02-08 16:50:48 +01:00
lock_guard lock ( this - > device_lock ) ;
2020-02-09 14:53:39 +01:00
if ( this - > device = = new_device ) return ;
2019-10-26 01:51:40 +02:00
2020-02-08 16:50:48 +01:00
this - > close_device ( ) ;
2020-02-09 14:53:39 +01:00
this - > device = new_device ;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
void AudioOutput : : close_device ( ) {
lock_guard lock ( this - > device_lock ) ;
if ( this - > _playback ) {
this - > _playback - > remove_source ( this ) ;
this - > _playback - > stop_if_possible ( ) ;
this - > _playback . reset ( ) ;
}
2020-02-09 14:53:39 +01:00
this - > _resampler = nullptr ;
2020-02-08 16:50:48 +01:00
this - > device = nullptr ;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
bool AudioOutput : : playback ( std : : string & error ) {
lock_guard lock ( this - > device_lock ) ;
if ( ! this - > device ) {
error = " invalid device handle " ;
return false ;
}
if ( this - > _playback ) return true ;
2019-10-26 01:51:40 +02:00
2020-02-08 16:50:48 +01:00
this - > _playback = this - > device - > playback ( ) ;
if ( ! this - > _playback ) {
error = " failed to allocate memory " ;
return false ;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
2020-02-09 14:53:39 +01:00
if ( this - > _playback - > sample_rate ( ) ! = this - > sample_rate ( ) ) {
this - > _resampler = std : : make_unique < AudioResampler > ( this - > sample_rate ( ) , this - > _playback - > sample_rate ( ) , this - > channel_count ( ) ) ;
if ( ! this - > _resampler - > valid ( ) ) {
error = " failed to allocate a resampler " ;
this - > _playback = nullptr ;
return false ;
}
}
2020-02-08 16:50:48 +01:00
this - > _playback - > register_source ( this ) ;
return this - > _playback - > start ( error ) ;
2019-10-26 01:51:40 +02:00
}