2019-10-25 19:51:40 -04:00
# include <cstring>
# include <string>
2020-02-06 17:59:09 -05:00
# include <misc/spin_lock.h>
2019-10-25 19:51:40 -04:00
# include "AudioInput.h"
# include "AudioReframer.h"
# include "../logger.h"
using namespace std ;
using namespace tc ;
using namespace tc : : audio ;
2020-02-08 10:50:48 -05:00
# if false
2020-02-06 17:59:09 -05:00
class AudioInputSource {
public :
constexpr static auto kChannelCount { 2 } ;
constexpr static auto kSampleRate { 48000 } ;
explicit AudioInputSource ( PaHostApiIndex index ) : device_index { index } { }
~ AudioInputSource ( ) = default ;
/* its blocking! */
void register_consumer ( AudioInput * consumer ) {
std : : lock_guard lock { this - > registered_inputs_lock } ;
if ( find ( this - > registered_inputs . begin ( ) , this - > registered_inputs . end ( ) , consumer ) ! = this - > registered_inputs . end ( ) )
return ;
this - > registered_inputs . push_back ( consumer ) ;
}
/* its blocking */
void remove_consumer ( AudioInput * consumer ) {
std : : lock_guard lock { this - > registered_inputs_lock } ;
auto index = find ( this - > registered_inputs . begin ( ) , this - > registered_inputs . end ( ) , consumer ) ;
if ( index = = this - > registered_inputs . end ( ) )
return ;
this - > registered_inputs . erase ( index ) ;
if ( ! this - > registered_inputs . empty ( ) )
return ;
}
/* this could take a bit longer! */
bool begin_recording ( std : : string & error ) {
std : : lock_guard lock { this - > state_lock } ;
if ( this - > state = = RECORDING ) return true ;
if ( this - > state ! = STOPPED ) {
if ( this - > state = = DELETED ) {
error = " stream has been deleted " ;
return false ;
}
error = " invalid state " ;
return false ;
}
this - > current_device = Pa_GetDeviceInfo ( this - > device_index ) ;
if ( ! this - > current_device ) {
error = " failed to get device info " ;
return false ;
}
PaStreamParameters parameters { } ;
memset ( & parameters , 0 , sizeof ( parameters ) ) ;
parameters . channelCount = ( int ) kChannelCount ;
parameters . device = this - > device_index ;
parameters . sampleFormat = paFloat32 ;
parameters . suggestedLatency = this - > current_device - > defaultLowOutputLatency ;
auto err = Pa_OpenStream (
& this - > input_stream ,
& parameters ,
nullptr ,
( double ) kSampleRate ,
paFramesPerBufferUnspecified ,
paClipOff ,
& AudioInputSource : : pa_audio_callback ,
this ) ;
if ( err ! = paNoError ) {
this - > input_stream = nullptr ;
error = to_string ( err ) + " / " + Pa_GetErrorText ( err ) ;
return false ;
}
err = Pa_StartStream ( this - > input_stream ) ;
if ( err ! = paNoError ) {
error = " recording failed " + to_string ( err ) + " / " + Pa_GetErrorText ( err ) ;
err = Pa_CloseStream ( this - > input_stream ) ;
if ( err ! = paNoError )
log_critical ( category : : audio , tr ( " Failed to close opened pa stream. This will cause memory leaks. Error: {}/{} " ) , err , Pa_GetErrorText ( err ) ) ;
return false ;
}
this - > state = RECORDING ;
return true ;
}
void stop_recording_if_possible ( ) {
std : : lock_guard lock { this - > state_lock } ;
if ( this - > state ! = RECORDING ) return ;
{
std : : lock_guard client_lock { this - > registered_inputs_lock } ;
if ( ! this - > registered_inputs . empty ( ) ) return ;
}
this - > state = STOPPED ;
if ( Pa_IsStreamActive ( this - > input_stream ) )
Pa_AbortStream ( this - > input_stream ) ;
auto error = Pa_CloseStream ( this - > input_stream ) ;
if ( error ! = paNoError )
log_error ( category : : audio , tr ( " Failed to close PA stream: {} " ) , error ) ;
this - > input_stream = nullptr ;
}
const PaDeviceIndex device_index ;
private :
static int pa_audio_callback ( const void * input , void * output , unsigned long frameCount , const PaStreamCallbackTimeInfo * timeInfo , PaStreamCallbackFlags statusFlags , void * _input_source ) {
if ( ! input ) return 0 ; /* this should never happen */
auto input_source = ( AudioInputSource * ) _input_source ;
std : : lock_guard lock { input_source - > registered_inputs_lock } ;
for ( auto & client : input_source - > registered_inputs )
2020-02-08 10:50:48 -05:00
client - > consume ( input , frameCount , 2 ) ;
2020-02-06 17:59:09 -05:00
return 0 ;
}
std : : mutex state_lock { } ;
enum _state {
STOPPED ,
RECORDING ,
DELETED
} state { STOPPED } ;
PaStream * input_stream { nullptr } ;
const PaDeviceInfo * current_device = nullptr ;
std : : mutex registered_inputs_lock { } ;
std : : vector < AudioInput * > registered_inputs { } ;
} ;
std : : mutex input_sources_lock { } ;
static std : : deque < std : : shared_ptr < AudioInputSource > > input_sources { } ;
std : : shared_ptr < AudioInputSource > get_input_source ( PaDeviceIndex device_index , bool create = true ) {
std : : lock_guard sources_lock { input_sources_lock } ;
for ( const auto & input : input_sources )
if ( input - > device_index = = device_index )
return input ;
if ( ! create )
return nullptr ;
auto input = std : : make_shared < AudioInputSource > ( device_index ) ;
input_sources . push_back ( std : : make_shared < AudioInputSource > ( device_index ) ) ;
return input ;
}
2020-02-08 10:50:48 -05:00
# endif
2020-02-06 17:59:09 -05:00
2019-10-25 19:51:40 -04:00
AudioConsumer : : AudioConsumer ( tc : : audio : : AudioInput * handle , size_t channel_count , size_t sample_rate , size_t frame_size ) :
handle ( handle ) ,
channel_count ( channel_count ) ,
sample_rate ( sample_rate ) ,
frame_size ( frame_size ) {
if ( this - > frame_size > 0 ) {
this - > reframer = make_unique < Reframer > ( channel_count , frame_size ) ;
this - > reframer - > on_frame = [ & ] ( const void * buffer ) { this - > handle_framed_data ( buffer , this - > frame_size ) ; } ;
}
}
void AudioConsumer : : handle_framed_data ( const void * buffer , size_t samples ) {
unique_lock read_callback_lock ( this - > on_read_lock ) ;
auto function = this - > on_read ; /* copy */
read_callback_lock . unlock ( ) ;
if ( ! function )
return ;
function ( buffer , samples ) ;
}
void AudioConsumer : : process_data ( const void * buffer , size_t samples ) {
if ( this - > reframer )
this - > reframer - > process ( buffer , samples ) ;
else
this - > handle_framed_data ( buffer , samples ) ;
}
AudioInput : : AudioInput ( size_t channels , size_t rate ) : _channel_count ( channels ) , _sample_rate ( rate ) { }
AudioInput : : ~ AudioInput ( ) {
this - > close_device ( ) ;
lock_guard lock ( this - > consumers_lock ) ;
for ( const auto & consumer : this - > _consumers )
consumer - > handle = nullptr ;
}
2020-02-08 10:50:48 -05:00
void AudioInput : : set_device ( const std : : shared_ptr < AudioDevice > & device ) {
2020-02-06 17:59:09 -05:00
lock_guard lock ( this - > input_source_lock ) ;
2020-02-08 10:50:48 -05:00
if ( device = = this - > input_device ) return ;
2019-10-25 19:51:40 -04:00
2020-02-08 10:50:48 -05:00
this - > close_device ( ) ;
this - > input_device = device ;
2020-02-06 17:59:09 -05:00
}
2019-10-25 19:51:40 -04:00
2020-02-06 17:59:09 -05:00
void AudioInput : : close_device ( ) {
lock_guard lock ( this - > input_source_lock ) ;
2020-02-08 10:50:48 -05:00
if ( this - > input_recorder ) {
this - > input_recorder - > remove_consumer ( this ) ;
this - > input_recorder - > stop_if_possible ( ) ;
this - > input_recorder . reset ( ) ;
2020-02-06 17:59:09 -05:00
}
2020-02-08 10:50:48 -05:00
this - > input_device = nullptr ;
2019-10-25 19:51:40 -04:00
}
2020-02-08 10:50:48 -05:00
bool AudioInput : : record ( std : : string & error ) {
2020-02-06 17:59:09 -05:00
lock_guard lock ( this - > input_source_lock ) ;
2020-02-08 10:50:48 -05:00
if ( ! this - > input_device ) {
error = " no device " ;
return false ;
}
if ( this - > input_recorder ) return true ;
2019-11-09 16:16:08 -05:00
2020-02-08 10:50:48 -05:00
this - > input_recorder = this - > input_device - > record ( ) ;
if ( ! this - > input_recorder ) {
error = " failed to get recorder " ;
return false ;
}
this - > input_recorder - > register_consumer ( this ) ;
if ( ! this - > input_recorder - > start ( error ) ) {
this - > input_recorder - > remove_consumer ( this ) ;
this - > input_recorder . reset ( ) ;
return false ;
}
2020-02-06 17:59:09 -05:00
return true ;
2019-10-25 19:51:40 -04:00
}
bool AudioInput : : recording ( ) {
2020-02-08 10:50:48 -05:00
return ! ! this - > input_recorder ;
2019-10-25 19:51:40 -04:00
}
void AudioInput : : stop ( ) {
2020-02-08 10:50:48 -05:00
if ( ! this - > input_recorder ) return ;
this - > input_recorder - > remove_consumer ( this ) ;
this - > input_recorder - > stop_if_possible ( ) ;
this - > input_recorder . reset ( ) ;
2019-10-25 19:51:40 -04:00
}
std : : shared_ptr < AudioConsumer > AudioInput : : create_consumer ( size_t frame_length ) {
auto result = shared_ptr < AudioConsumer > ( new AudioConsumer ( this , this - > _channel_count , this - > _sample_rate , frame_length ) ) ;
{
lock_guard lock ( this - > consumers_lock ) ;
this - > _consumers . push_back ( result ) ;
}
return result ;
}
void AudioInput : : delete_consumer ( const std : : shared_ptr < AudioConsumer > & source ) {
{
lock_guard lock ( this - > consumers_lock ) ;
auto it = find ( this - > _consumers . begin ( ) , this - > _consumers . end ( ) , source ) ;
if ( it ! = this - > _consumers . end ( ) )
this - > _consumers . erase ( it ) ;
}
source - > handle = nullptr ;
}
2020-02-08 10:50:48 -05:00
void AudioInput : : consume ( const void * input , unsigned long frameCount , size_t /* channels */ ) {
2020-02-06 17:59:09 -05:00
if ( this - > _volume ! = 1 & & false ) {
2019-10-25 19:51:40 -04:00
auto ptr = ( float * ) input ;
auto left = frameCount * this - > _channel_count ;
while ( left - - > 0 )
* ( ptr + + ) * = this - > _volume ;
}
auto begin = chrono : : system_clock : : now ( ) ;
for ( const auto & consumer : this - > consumers ( ) ) {
consumer - > process_data ( input , frameCount ) ;
}
auto end = chrono : : system_clock : : now ( ) ;
auto ms = chrono : : duration_cast < chrono : : milliseconds > ( end - begin ) . count ( ) ;
if ( ms > 5 ) {
log_warn ( category : : audio , tr ( " Processing of audio input needed {}ms. This could be an issue! " ) , chrono : : duration_cast < chrono : : milliseconds > ( end - begin ) . count ( ) ) ;
}
}