2021-02-11 19:18:35 +00:00
/*
2021-05-23 15:09:41 +00:00
This class handles both RX and TX audio , each is created as a seperate instance of the class
but as the setup / handling if output ( RX ) and input ( TX ) devices is so similar I have combined them .
2021-02-11 19:18:35 +00:00
*/
# include "audiohandler.h"
2021-05-16 20:16:59 +00:00
2021-02-23 21:21:22 +00:00
# include "logcategories.h"
2021-05-24 17:00:38 +00:00
# include "ulaw.h"
2021-02-13 00:45:59 +00:00
2021-06-04 07:24:26 +00:00
audioHandler : : audioHandler ( QObject * parent )
2021-02-11 19:18:35 +00:00
{
2021-05-30 11:07:51 +00:00
Q_UNUSED ( parent )
2021-02-11 19:18:35 +00:00
}
audioHandler : : ~ audioHandler ( )
{
2021-06-02 17:36:33 +00:00
2021-06-03 11:05:28 +00:00
if ( isInitialized ) {
2021-06-04 07:24:26 +00:00
# if defined(RTAUDIO)
2021-06-02 17:36:33 +00:00
2021-06-02 18:18:44 +00:00
try {
audio - > abortStream ( ) ;
audio - > closeStream ( ) ;
}
catch ( RtAudioError & e ) {
qInfo ( logAudio ( ) ) < < " Error closing stream: " < < aParams . deviceId < < " : " < < QString : : fromStdString ( e . getMessage ( ) ) ;
}
2021-06-02 17:36:33 +00:00
delete audio ;
2021-06-04 07:24:26 +00:00
# elif defined(PORTAUDIO)
# else
stop ( ) ;
# endif
2021-06-02 17:36:33 +00:00
}
2021-06-03 11:05:28 +00:00
if ( ringBuf ! = Q_NULLPTR ) {
2021-05-27 10:41:08 +00:00
delete ringBuf ;
2021-06-03 11:05:28 +00:00
}
2021-06-04 07:24:26 +00:00
if ( resampler ! = Q_NULLPTR ) {
speex_resampler_destroy ( resampler ) ;
qDebug ( logAudio ( ) ) < < " Resampler closed " ;
}
2021-02-11 19:18:35 +00:00
}
2021-06-04 07:24:26 +00:00
bool audioHandler : : init ( audioSetup setupIn )
2021-02-11 19:18:35 +00:00
{
2021-05-23 15:09:41 +00:00
if ( isInitialized ) {
return false ;
}
2021-02-11 19:18:35 +00:00
2021-06-04 07:24:26 +00:00
/*
0x01 uLaw 1 ch 8 bit
0x02 PCM 1 ch 8 bit
0x04 PCM 1 ch 16 bit
0x08 PCM 2 ch 8 bit
0x10 PCM 2 ch 16 bit
0x20 uLaw 2 ch 8 bit
*/
setup = setupIn ;
setup . radioChan = 1 ;
setup . bits = 8 ;
if ( setup . codec = = 0x01 | | setup . codec = = 0x20 ) {
setup . ulaw = true ;
}
if ( setup . codec = = 0x08 | | setup . codec = = 0x10 | | setup . codec = = 0x20 ) {
setup . radioChan = 2 ;
}
if ( setup . codec = = 0x04 | | setup . codec = = 0x10 ) {
setup . bits = 16 ;
}
2021-03-09 17:22:16 +00:00
2021-05-27 10:41:08 +00:00
ringBuf = new wilt : : Ring < audioPacket > ( 100 ) ; // Should be customizable.
2021-06-02 17:35:04 +00:00
2021-06-04 07:24:26 +00:00
tempBuf . sent = 0 ;
2021-07-06 06:24:35 +00:00
if ( ! setup . isinput )
{
this - > setVolume ( setup . localAFgain ) ;
}
2021-06-04 07:24:26 +00:00
# if defined(RTAUDIO)
# if !defined(Q_OS_MACX)
options . flags = ( ( ! RTAUDIO_HOG_DEVICE ) | ( RTAUDIO_MINIMIZE_LATENCY ) ) ;
# endif
2021-06-02 19:15:31 +00:00
# if defined(Q_OS_LINUX)
audio = new RtAudio ( RtAudio : : Api : : LINUX_ALSA ) ;
# elif defined(Q_OS_WIN)
audio = new RtAudio ( RtAudio : : Api : : WINDOWS_WASAPI ) ;
# elif defined(Q_OS_MACX)
audio = new RtAudio ( RtAudio : : Api : : MACOSX_CORE ) ;
# endif
2021-06-02 17:35:04 +00:00
2021-06-06 16:56:48 +00:00
if ( setup . port > 0 ) {
aParams . deviceId = setup . port ;
2021-05-23 15:09:41 +00:00
}
2021-06-04 07:24:26 +00:00
else if ( setup . isinput ) {
2021-06-02 17:35:04 +00:00
aParams . deviceId = audio - > getDefaultInputDevice ( ) ;
2021-05-23 15:09:41 +00:00
}
else {
2021-06-02 17:35:04 +00:00
aParams . deviceId = audio - > getDefaultOutputDevice ( ) ;
2021-05-23 15:09:41 +00:00
}
aParams . firstChannel = 0 ;
2021-02-11 19:18:35 +00:00
2021-05-24 17:00:38 +00:00
try {
2021-06-02 17:35:04 +00:00
info = audio - > getDeviceInfo ( aParams . deviceId ) ;
2021-05-24 17:00:38 +00:00
}
catch ( RtAudioError & e ) {
qInfo ( logAudio ( ) ) < < " Device error: " < < aParams . deviceId < < " : " < < QString : : fromStdString ( e . getMessage ( ) ) ;
2021-06-03 11:05:28 +00:00
return isInitialized ;
2021-05-24 17:00:38 +00:00
}
2021-05-22 09:43:57 +00:00
2021-05-23 15:09:41 +00:00
if ( info . probed )
{
2021-06-02 11:35:10 +00:00
// if "preferred" sample rate is 44100, try 48K instead
if ( info . preferredSampleRate = = ( unsigned int ) 44100 ) {
2021-06-02 11:21:06 +00:00
qDebug ( logAudio ( ) ) < < " Preferred sample rate 44100, trying 48000 " ;
this - > nativeSampleRate = 48000 ;
}
else {
this - > nativeSampleRate = info . preferredSampleRate ;
}
2021-06-02 11:35:10 +00:00
// Per channel chunk size.
2021-06-02 11:21:06 +00:00
this - > chunkSize = ( this - > nativeSampleRate / 50 ) ;
2021-05-29 17:59:45 +00:00
2021-06-04 07:24:26 +00:00
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < QString : : fromStdString ( info . name ) < < " ( " < < aParams . deviceId < < " ) successfully probed " ;
2021-05-23 15:09:41 +00:00
if ( info . nativeFormats = = 0 )
2021-05-29 19:13:52 +00:00
{
2021-05-23 15:09:41 +00:00
qInfo ( logAudio ( ) ) < < " No natively supported data formats! " ;
2021-05-29 19:13:52 +00:00
return false ;
}
2021-05-23 15:09:41 +00:00
else {
qDebug ( logAudio ( ) ) < < " Supported formats: " < <
( info . nativeFormats & RTAUDIO_SINT8 ? " 8-bit int, " : " " ) < <
( info . nativeFormats & RTAUDIO_SINT16 ? " 16-bit int, " : " " ) < <
( info . nativeFormats & RTAUDIO_SINT24 ? " 24-bit int, " : " " ) < <
( info . nativeFormats & RTAUDIO_SINT32 ? " 32-bit int, " : " " ) < <
( info . nativeFormats & RTAUDIO_FLOAT32 ? " 32-bit float, " : " " ) < <
( info . nativeFormats & RTAUDIO_FLOAT64 ? " 64-bit float, " : " " ) ;
2021-05-29 19:13:52 +00:00
2021-05-23 15:09:41 +00:00
qInfo ( logAudio ( ) ) < < " Preferred sample rate: " < < info . preferredSampleRate ;
2021-06-04 07:24:26 +00:00
if ( setup . isinput ) {
2021-05-29 19:13:52 +00:00
devChannels = info . inputChannels ;
2021-06-04 07:24:26 +00:00
}
2021-05-29 19:13:52 +00:00
else {
devChannels = info . outputChannels ;
}
qInfo ( logAudio ( ) ) < < " Channels: " < < devChannels ;
if ( devChannels > 2 ) {
devChannels = 2 ;
}
aParams . nChannels = devChannels ;
2021-03-01 20:44:09 +00:00
}
2021-05-23 15:09:41 +00:00
2021-06-04 07:24:26 +00:00
qInfo ( logAudio ( ) ) < < " chunkSize: " < < chunkSize ;
2021-05-23 15:09:41 +00:00
try {
2021-06-04 07:24:26 +00:00
if ( setup . isinput ) {
2021-06-06 16:56:48 +00:00
audio - > openStream ( NULL , & aParams , RTAUDIO_SINT16 , this - > nativeSampleRate , & this - > chunkSize , & staticWrite , this , & options ) ;
2021-06-04 07:24:26 +00:00
}
else {
audio - > openStream ( & aParams , NULL , RTAUDIO_SINT16 , this - > nativeSampleRate , & this - > chunkSize , & staticRead , this , & options ) ;
}
2021-06-02 17:35:04 +00:00
audio - > startStream ( ) ;
2021-06-03 11:05:28 +00:00
isInitialized = true ;
2021-06-04 07:24:26 +00:00
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " device successfully opened " ;
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " detected latency: " < < audio - > getStreamLatency ( ) ;
2021-05-23 15:09:41 +00:00
}
catch ( RtAudioError & e ) {
qInfo ( logAudio ( ) ) < < " Error opening: " < < QString : : fromStdString ( e . getMessage ( ) ) ;
2021-03-01 20:44:09 +00:00
}
2021-03-01 20:31:05 +00:00
}
2021-05-23 15:09:41 +00:00
else
{
2021-06-04 07:24:26 +00:00
qCritical ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < QString : : fromStdString ( info . name ) < < " ( " < < aParams . deviceId < < " ) could not be probed, check audio configuration! " ;
2021-05-23 15:09:41 +00:00
}
2021-05-16 20:16:59 +00:00
2021-05-29 17:59:45 +00:00
2021-06-04 07:24:26 +00:00
# elif defined(PORTAUDIO)
# else
format . setSampleSize ( 16 ) ;
format . setChannelCount ( 2 ) ;
format . setSampleRate ( INTERNAL_SAMPLE_RATE ) ;
format . setCodec ( " audio/pcm " ) ;
format . setByteOrder ( QAudioFormat : : LittleEndian ) ;
format . setSampleType ( QAudioFormat : : SignedInt ) ;
2021-08-01 06:35:47 +00:00
2021-06-04 07:24:26 +00:00
if ( setup . port . isNull ( ) )
{
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " No audio device was found. You probably need to install libqt5multimedia-plugins. " ;
return false ;
}
else if ( ! setup . port . isFormatSupported ( format ) )
{
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Format not supported, choosing nearest supported format - which may not work! " ;
format = setup . port . nearestFormat ( format ) ;
}
if ( format . channelCount ( ) > 2 ) {
format . setChannelCount ( 2 ) ;
}
2021-06-07 13:04:52 +00:00
else if ( format . channelCount ( ) < 1 )
{
qCritical ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " No channels found, aborting setup. " ;
return false ;
}
2021-06-04 07:24:26 +00:00
devChannels = format . channelCount ( ) ;
nativeSampleRate = format . sampleRate ( ) ;
// chunk size is always relative to Internal Sample Rate.
this - > chunkSize = ( nativeSampleRate / 50 ) ;
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Internal: sample rate " < < format . sampleRate ( ) < < " channel count " < < format . channelCount ( ) ;
// We "hopefully" now have a valid format that is supported so try connecting
2021-06-07 09:40:04 +00:00
2021-06-04 07:24:26 +00:00
if ( setup . isinput ) {
audioInput = new QAudioInput ( setup . port , format , this ) ;
connect ( audioInput , SIGNAL ( notify ( ) ) , SLOT ( notified ( ) ) ) ;
connect ( audioInput , SIGNAL ( stateChanged ( QAudio : : State ) ) , SLOT ( stateChanged ( QAudio : : State ) ) ) ;
isInitialized = true ;
}
else {
audioOutput = new QAudioOutput ( setup . port , format , this ) ;
2021-06-07 09:40:04 +00:00
# ifdef Q_OS_MAC
audioOutput - > setBufferSize ( chunkSize * 4 ) ;
# endif
2021-06-04 07:24:26 +00:00
connect ( audioOutput , SIGNAL ( notify ( ) ) , SLOT ( notified ( ) ) ) ;
connect ( audioOutput , SIGNAL ( stateChanged ( QAudio : : State ) ) , SLOT ( stateChanged ( QAudio : : State ) ) ) ;
isInitialized = true ;
}
# endif
// Setup resampler if it is needed.
int resample_error = 0 ;
if ( setup . isinput ) {
resampler = wf_resampler_init ( devChannels , nativeSampleRate , setup . samplerate , setup . resampleQuality , & resample_error ) ;
}
else {
resampler = wf_resampler_init ( devChannels , setup . samplerate , this - > nativeSampleRate , setup . resampleQuality , & resample_error ) ;
}
2021-03-13 09:50:43 +00:00
wf_resampler_get_ratio ( resampler , & ratioNum , & ratioDen ) ;
2021-06-04 07:24:26 +00:00
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " wf_resampler_init() returned: " < < resample_error < < " ratioNum " < < ratioNum < < " ratioDen " < < ratioDen ;
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " thread id " < < QThread : : currentThreadId ( ) ;
2021-08-01 06:35:47 +00:00
// Plugin init
if ( ! setup . isinput )
{
SAMPLE_RATE = format . sampleRate ( ) ;
setupLADSP ( ) ;
if ( psDescriptor - > activate ! = NULL )
psDescriptor - > activate ( handle ) ;
}
2021-06-06 16:56:48 +00:00
# if !defined (RTAUDIO) && !defined(PORTAUDIO)
2021-06-04 07:24:26 +00:00
if ( isInitialized ) {
this - > start ( ) ;
}
2021-06-06 16:56:48 +00:00
# endif
2021-03-13 09:50:43 +00:00
2021-05-23 15:09:41 +00:00
return isInitialized ;
2021-02-11 19:18:35 +00:00
}
2021-06-06 16:56:48 +00:00
# if !defined (RTAUDIO) && !defined(PORTAUDIO)
2021-06-04 07:24:26 +00:00
void audioHandler : : start ( )
{
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " start() running " ;
if ( ( audioOutput = = Q_NULLPTR | | audioOutput - > state ( ) ! = QAudio : : StoppedState ) & &
( audioInput = = Q_NULLPTR | | audioInput - > state ( ) ! = QAudio : : StoppedState ) ) {
return ;
}
if ( setup . isinput ) {
2021-06-07 09:58:58 +00:00
# ifdef Q_OS_MACX
this - > open ( QIODevice : : WriteOnly ) ;
# else
this - > open ( QIODevice : : WriteOnly | QIODevice : : Unbuffered ) ;
# endif
2021-06-04 07:24:26 +00:00
audioInput - > start ( this ) ;
}
else {
2021-06-07 09:58:58 +00:00
# ifdef Q_OS_MACX
this - > open ( QIODevice : : ReadOnly ) ;
# else
this - > open ( QIODevice : : ReadOnly | QIODevice : : Unbuffered ) ;
# endif
2021-06-04 07:24:26 +00:00
audioOutput - > start ( this ) ;
}
}
2021-06-06 16:56:48 +00:00
# endif
2021-06-04 07:24:26 +00:00
2021-08-01 01:24:30 +00:00
void audioHandler : : setupLADSP ( )
{
// setup for the plugin:
int lPluginIndex ;
2021-08-01 22:24:31 +00:00
pre_control = 0.99f ;
2021-08-01 01:24:30 +00:00
void * pvPluginHandle = loadLADSPAPluginLibrary ( pcPluginFilename ) ;
dlerror ( ) ;
pfDescriptorFunction
= ( LADSPA_Descriptor_Function ) dlsym ( pvPluginHandle , " ladspa_descriptor " ) ;
if ( ! pfDescriptorFunction ) {
const char * pcError = dlerror ( ) ;
if ( pcError )
2021-08-02 18:03:48 +00:00
qInfo ( logAudio ( ) ) < < __PRETTY_FUNCTION__ < < QString ( " Unable to find ladspa_descriptor() function in plugin library file %1: %2. Are you sure this is a LADSPA plugin file? " ) . arg ( pcPluginFilename ) . arg ( pcError ) ;
return ;
2021-08-01 01:24:30 +00:00
}
2021-08-01 06:35:47 +00:00
2021-08-01 01:24:30 +00:00
2021-08-01 22:52:09 +00:00
BUF_SIZE = 2728 ; // starting with the largest likely size. I think we can do half this actually, due to 16 bit needing half as much space as 8 bit.
2021-08-01 22:24:31 +00:00
qDebug ( logAudio ( ) ) < < __PRETTY_FUNCTION__ < < " : Setting BUF_SIZE to " < < BUF_SIZE ;
qDebug ( logAudio ( ) ) < < __PRETTY_FUNCTION__ < < " : Setting sample rate to: " < < SAMPLE_RATE ;
2021-08-01 01:24:30 +00:00
2021-08-01 07:14:36 +00:00
pInBuffer [ 0 ] = ( float * ) malloc ( sizeof ( LADSPA_Data ) * BUF_SIZE / 2 ) ;
pInBuffer [ 1 ] = ( float * ) malloc ( sizeof ( LADSPA_Data ) * BUF_SIZE / 2 ) ;
pOutBuffer [ 0 ] = ( float * ) malloc ( sizeof ( LADSPA_Data ) * BUF_SIZE / 2 ) ;
pOutBuffer [ 1 ] = ( float * ) malloc ( sizeof ( LADSPA_Data ) * BUF_SIZE / 2 ) ;
2021-08-01 01:24:30 +00:00
for ( lPluginIndex = 0 ; ; lPluginIndex + + ) {
psDescriptor = pfDescriptorFunction ( lPluginIndex ) ;
if ( ! psDescriptor )
break ;
if ( pcPluginLabel ! = NULL ) {
if ( strcmp ( pcPluginLabel , psDescriptor - > Label ) ! = 0 )
continue ;
}
// got mono_amp
handle = psDescriptor - > instantiate ( psDescriptor , SAMPLE_RATE ) ;
if ( handle = = NULL ) {
2021-08-02 18:03:48 +00:00
qInfo ( logAudio ( ) ) < < QString ( " Can't instantiate plugin %1 " ) . arg ( pcPluginLabel ) ;
return ;
2021-08-01 01:24:30 +00:00
}
// get ports
int lPortIndex ;
2021-08-02 00:13:28 +00:00
int lControlPortIndex = 0 ;
2021-08-02 18:03:48 +00:00
qDebug ( logAudio ( ) ) < < QString ( " Num ports %1 " ) . arg ( psDescriptor - > PortCount ) ;
for ( lPortIndex = 0 ;
2021-08-01 01:24:30 +00:00
lPortIndex < psDescriptor - > PortCount ;
lPortIndex + + ) {
if ( LADSPA_IS_PORT_AUDIO
( psDescriptor - > PortDescriptors [ lPortIndex ] ) ) {
if ( LADSPA_IS_PORT_INPUT
( psDescriptor - > PortDescriptors [ lPortIndex ] ) ) {
2021-08-02 18:03:48 +00:00
qDebug ( logAudio ( ) ) < < QString ( " input %1 " ) . arg ( lPortIndex ) ;
2021-08-01 01:24:30 +00:00
lInputPortIndex = lPortIndex ;
psDescriptor - > connect_port ( handle ,
lInputPortIndex , pInBuffer [ inBufferIndex + + ] ) ;
} else if ( LADSPA_IS_PORT_OUTPUT
( psDescriptor - > PortDescriptors [ lPortIndex ] ) ) {
2021-08-02 18:03:48 +00:00
qDebug ( logAudio ( ) ) < < QString ( " output %1 " ) . arg ( lPortIndex ) ;
lOutputPortIndex = lPortIndex ;
2021-08-01 01:24:30 +00:00
psDescriptor - > connect_port ( handle ,
lOutputPortIndex , pOutBuffer [ outBufferIndex + + ] ) ;
}
}
if ( LADSPA_IS_PORT_CONTROL
( psDescriptor - > PortDescriptors [ lPortIndex ] ) ) {
2021-08-02 18:03:48 +00:00
qDebug ( logAudio ( ) ) < < QString ( " control %1 " ) . arg ( lPortIndex ) ;
psDescriptor - > connect_port ( handle ,
2021-08-02 00:13:28 +00:00
lPortIndex , & controls [ lControlPortIndex + + ] ) ;
2021-08-01 01:24:30 +00:00
}
}
// we've got what we wanted, get out of this loop
break ;
}
if ( ( psDescriptor = = NULL ) | |
( lInputPortIndex = = - 1 ) | |
( lOutputPortIndex = = - 1 ) ) {
2021-08-02 18:03:48 +00:00
qInfo ( logAudio ( ) ) < < " Can't find plugin information " ;
return ;
2021-08-01 01:24:30 +00:00
}
2021-08-02 00:13:28 +00:00
if ( psDescriptor - > activate ! = NULL )
psDescriptor - > activate ( handle ) ;
2021-08-02 08:21:01 +00:00
pluginOutputEnable = true ;
2021-08-01 01:24:30 +00:00
2021-08-02 08:21:01 +00:00
}
2021-08-01 01:24:30 +00:00
2021-08-02 08:21:01 +00:00
void audioHandler : : enablePluginOutput ( bool enable )
{
this - > pluginOutputEnable = enable ;
2021-08-01 01:24:30 +00:00
}
2021-08-02 08:21:01 +00:00
2021-08-01 01:24:30 +00:00
void audioHandler : : runPlugin ( )
{
2021-08-01 22:24:31 +00:00
// numread actually sets the
int numSamplesToProcess = BUF_SIZE / 2 ;
2021-08-02 00:13:28 +00:00
// if (psDescriptor->activate != NULL)
// psDescriptor->activate(handle);
2021-08-01 01:24:30 +00:00
// grab audio (already done, no sound file to read from)
// sfload does this: copy from input file to pInBuffer, at size BUF_SIZE
2021-08-01 22:24:31 +00:00
psDescriptor - > run ( handle , numSamplesToProcess ) ; // run the audio through the plugin. Number of pieces processed is numread. Compare to size of data needing to be read and run again if needed.
//qDebug(logAudio()) << "Number of samples processed: " << numSamplesToProcess;
2021-08-01 01:24:30 +00:00
}
2021-03-22 18:53:34 +00:00
void audioHandler : : setVolume ( unsigned char volume )
2021-03-22 16:02:22 +00:00
{
2021-07-06 19:02:09 +00:00
//this->volume = (qreal)volume/255.0;
this - > volume = audiopot [ volume ] ;
2021-06-04 07:24:26 +00:00
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " setVolume: " < < volume < < " ( " < < this - > volume < < " ) " ;
2021-02-11 19:18:35 +00:00
}
2021-03-09 17:22:16 +00:00
/// <summary>
/// This function processes the incoming audio FROM the radio and pushes it into the playback buffer *data
/// </summary>
/// <param name="data"></param>
/// <param name="maxlen"></param>
/// <returns></returns>
2021-06-04 07:24:26 +00:00
# if defined(RTAUDIO)
2021-05-23 15:09:41 +00:00
int audioHandler : : readData ( void * outputBuffer , void * inputBuffer , unsigned int nFrames , double streamTime , RtAudioStreamStatus status )
2021-02-11 19:18:35 +00:00
{
2021-05-29 17:59:45 +00:00
Q_UNUSED ( inputBuffer ) ;
Q_UNUSED ( streamTime ) ;
2021-05-24 08:27:18 +00:00
if ( status = = RTAUDIO_OUTPUT_UNDERFLOW )
qDebug ( logAudio ( ) ) < < " Underflow detected " ;
2021-05-29 19:13:52 +00:00
int nBytes = nFrames * devChannels * 2 ; // This is ALWAYS 2 bytes per sample and 2 channels
2021-06-04 07:24:26 +00:00
quint8 * buffer = ( quint8 * ) outputBuffer ;
# elif defined(PORTAUDIO)
# else
qint64 audioHandler : : readData ( char * buffer , qint64 nBytes )
{
# endif
// Calculate output length, always full samples
int sentlen = 0 ;
2021-07-06 09:04:35 +00:00
if ( ! isReady ) {
isReady = true ;
}
2021-05-27 10:41:08 +00:00
if ( ringBuf - > size ( ) > 0 )
2021-02-27 00:37:00 +00:00
{
2021-05-23 21:45:10 +00:00
// Output buffer is ALWAYS 16 bit.
2021-05-29 17:59:45 +00:00
//qDebug(logAudio()) << "Read: nFrames" << nFrames << "nBytes" << nBytes;
2021-05-27 10:41:08 +00:00
while ( sentlen < nBytes )
2021-05-23 21:45:10 +00:00
{
2021-05-27 10:41:08 +00:00
audioPacket packet ;
if ( ! ringBuf - > try_read ( packet ) )
{
qDebug ( ) < < " No more data available but buffer is not full! sentlen: " < < sentlen < < " nBytes: " < < nBytes ;
2021-05-29 17:59:45 +00:00
break ;
2021-05-23 21:45:10 +00:00
}
2021-05-27 10:41:08 +00:00
currentLatency = packet . time . msecsTo ( QTime : : currentTime ( ) ) ;
2021-05-23 21:45:10 +00:00
2021-05-27 10:41:08 +00:00
// This shouldn't be required but if we did output a partial packet
// This will add the remaining packet data to the output buffer.
if ( tempBuf . sent ! = tempBuf . data . length ( ) )
2021-05-23 21:45:10 +00:00
{
2021-05-27 10:41:08 +00:00
int send = qMin ( ( int ) nBytes - sentlen , tempBuf . data . length ( ) - tempBuf . sent ) ;
memcpy ( buffer + sentlen , tempBuf . data . constData ( ) + tempBuf . sent , send ) ;
tempBuf . sent = tempBuf . sent + send ;
sentlen = sentlen + send ;
2021-05-28 17:13:08 +00:00
if ( tempBuf . sent ! = tempBuf . data . length ( ) )
{
// We still don't have enough buffer space for this?
2021-05-29 17:59:45 +00:00
break ;
2021-05-28 17:13:08 +00:00
}
2021-05-27 22:23:23 +00:00
//qDebug(logAudio()) << "Adding partial:" << send;
2021-05-27 10:41:08 +00:00
}
2021-05-23 21:45:10 +00:00
2021-06-05 07:26:58 +00:00
while ( currentLatency > setup . latency ) {
2021-06-04 07:24:26 +00:00
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Packet " < < hex < < packet . seq < <
2021-05-27 22:23:23 +00:00
" arrived too late (increase output latency!) " < <
dec < < packet . time . msecsTo ( QTime : : currentTime ( ) ) < < " ms " ;
lastSeq = packet . seq ;
if ( ! ringBuf - > try_read ( packet ) )
2021-05-29 17:59:45 +00:00
break ;
2021-05-27 10:41:08 +00:00
currentLatency = packet . time . msecsTo ( QTime : : currentTime ( ) ) ;
}
2021-05-23 21:45:10 +00:00
2021-05-27 10:41:08 +00:00
int send = qMin ( ( int ) nBytes - sentlen , packet . data . length ( ) ) ;
memcpy ( buffer + sentlen , packet . data . constData ( ) , send ) ;
sentlen = sentlen + send ;
if ( send < packet . data . length ( ) )
{
2021-05-27 22:23:23 +00:00
//qDebug(logAudio()) << "Asking for partial, sent:" << send << "packet length" << packet.data.length();
2021-05-27 10:41:08 +00:00
tempBuf = packet ;
tempBuf . sent = tempBuf . sent + send ;
2021-05-27 22:23:23 +00:00
lastSeq = packet . seq ;
break ;
2021-05-27 10:41:08 +00:00
}
2021-05-23 21:45:10 +00:00
2021-05-27 10:41:08 +00:00
if ( packet . seq < = lastSeq ) {
2021-06-04 07:24:26 +00:00
qDebug ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Duplicate/early audio packet: " < < hex < < lastSeq < < " got " < < hex < < packet . seq ;
2021-05-23 21:45:10 +00:00
}
2021-05-27 10:41:08 +00:00
else if ( packet . seq ! = lastSeq + 1 ) {
2021-06-04 07:24:26 +00:00
qDebug ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Missing audio packet(s) from: " < < hex < < lastSeq + 1 < < " to " < < hex < < packet . seq - 1 ;
2021-05-23 21:45:10 +00:00
}
2021-05-27 10:41:08 +00:00
lastSeq = packet . seq ;
2021-05-23 21:45:10 +00:00
}
}
2021-05-29 19:50:27 +00:00
//qDebug(logAudio()) << "looking for: " << nBytes << " got: " << sentlen;
2021-05-23 21:45:10 +00:00
2021-05-30 10:36:13 +00:00
// fill the rest of the buffer with silence
if ( nBytes > sentlen ) {
memset ( buffer + sentlen , 0 , nBytes - sentlen ) ;
}
2021-06-04 07:24:26 +00:00
# if defined(RTAUDIO)
2021-05-23 15:09:41 +00:00
return 0 ;
2021-06-04 07:24:26 +00:00
# elif defined(PORTAUDIO)
# else
return nBytes ;
# endif
2021-05-23 15:09:41 +00:00
}
2021-06-04 07:24:26 +00:00
# if defined(RTAUDIO)
2021-05-23 15:09:41 +00:00
int audioHandler : : writeData ( void * outputBuffer , void * inputBuffer , unsigned int nFrames , double streamTime , RtAudioStreamStatus status )
2021-02-11 19:18:35 +00:00
{
2021-05-29 17:59:45 +00:00
Q_UNUSED ( outputBuffer ) ;
Q_UNUSED ( streamTime ) ;
2021-05-30 11:07:51 +00:00
Q_UNUSED ( status ) ;
2021-06-04 07:24:26 +00:00
int nBytes = nFrames * devChannels * 2 ; // This is ALWAYS 2 bytes per sample and 2 channels
2021-05-27 12:54:52 +00:00
const char * data = ( const char * ) inputBuffer ;
2021-06-04 07:24:26 +00:00
# elif defined(PORTAUDIO)
# else
qint64 audioHandler : : writeData ( const char * data , qint64 nBytes )
{
# endif
2021-07-06 09:04:35 +00:00
if ( ! isReady ) {
isReady = true ;
}
2021-06-04 07:24:26 +00:00
int sentlen = 0 ;
2021-05-29 17:59:45 +00:00
//qDebug(logAudio()) << "nFrames" << nFrames << "nBytes" << nBytes;
2021-06-04 12:33:57 +00:00
int chunkBytes = chunkSize * devChannels * 2 ;
2021-05-27 12:54:52 +00:00
while ( sentlen < nBytes ) {
2021-06-04 12:33:57 +00:00
if ( tempBuf . sent ! = chunkBytes )
2021-02-27 11:00:44 +00:00
{
2021-06-04 12:33:57 +00:00
int send = qMin ( ( int ) ( nBytes - sentlen ) , chunkBytes - tempBuf . sent ) ;
2021-05-27 12:54:52 +00:00
tempBuf . data . append ( QByteArray : : fromRawData ( data + sentlen , send ) ) ;
sentlen = sentlen + send ;
tempBuf . seq = 0 ; // Not used in TX
tempBuf . time = QTime : : currentTime ( ) ;
2021-05-27 22:23:23 +00:00
tempBuf . sent = tempBuf . sent + send ;
2021-05-27 12:54:52 +00:00
}
else {
2021-06-01 19:19:06 +00:00
ringBuf - > write ( tempBuf ) ;
/*
2021-05-27 12:54:52 +00:00
if ( ! ringBuf - > try_write ( tempBuf ) )
2021-02-27 11:00:44 +00:00
{
2021-05-27 12:54:52 +00:00
qDebug ( logAudio ( ) ) < < " outgoing audio buffer full! " ;
2021-05-27 13:09:12 +00:00
break ;
2021-06-01 19:19:06 +00:00
} */
2021-05-27 12:54:52 +00:00
tempBuf . data . clear ( ) ;
tempBuf . sent = 0 ;
2021-02-27 11:00:44 +00:00
}
2021-05-23 15:09:41 +00:00
}
2021-05-27 12:54:52 +00:00
2021-05-29 17:59:45 +00:00
//qDebug(logAudio()) << "sentlen" << sentlen;
2021-06-04 07:24:26 +00:00
# if defined(RTAUDIO)
return 0 ;
# elif defined(PORTAUDIO)
# else
return nBytes ;
# endif
2021-02-11 19:18:35 +00:00
}
2021-05-29 17:59:45 +00:00
void audioHandler : : incomingAudio ( audioPacket inPacket )
2021-02-11 19:18:35 +00:00
{
2021-05-23 21:45:10 +00:00
// No point buffering audio until stream is actually running.
2021-05-29 17:59:45 +00:00
// Regardless of the radio stream format, the buffered audio will ALWAYS be
// 16bit sample interleaved stereo 48K (or whatever the native sample rate is)
2021-07-06 09:04:35 +00:00
if ( ! isInitialized & & ! isReady )
2021-05-23 21:45:10 +00:00
{
2021-06-07 11:31:58 +00:00
qDebug ( logAudio ( ) ) < < " Packet received when stream was not ready " ;
2021-05-27 17:34:44 +00:00
return ;
2021-05-23 21:45:10 +00:00
}
2021-05-29 19:50:27 +00:00
//qDebug(logAudio()) << "Got" << radioSampleBits << "bits, length" << inPacket.data.length();
2021-05-27 10:41:08 +00:00
// Incoming data is 8bits?
2021-06-04 07:24:26 +00:00
if ( setup . bits = = 8 )
2021-05-27 10:41:08 +00:00
{
2021-05-29 17:59:45 +00:00
// Current packet is 8bit so need to create a new buffer that is 16bit
2021-07-06 09:04:35 +00:00
QByteArray outPacket ( ( int ) inPacket . data . length ( ) * 2 * ( devChannels / setup . radioChan ) , ( char ) 0xff ) ;
2021-05-27 10:41:08 +00:00
qint16 * out = ( qint16 * ) outPacket . data ( ) ;
2021-05-29 17:59:45 +00:00
for ( int f = 0 ; f < inPacket . data . length ( ) ; f + + )
2021-03-09 17:22:16 +00:00
{
2021-06-04 07:24:26 +00:00
for ( int g = setup . radioChan ; g < = devChannels ; g + + )
2021-03-09 17:22:16 +00:00
{
2021-05-29 19:13:52 +00:00
if ( isUlaw )
2021-05-29 17:59:45 +00:00
* out + + = ulaw_decode [ ( quint8 ) inPacket . data [ f ] ] * this - > volume ;
else
* out + + = ( qint16 ) ( ( ( quint8 ) inPacket . data [ f ] < < 8 ) - 32640 * this - > volume ) ;
2021-03-09 17:22:16 +00:00
}
}
2021-05-29 17:59:45 +00:00
inPacket . data . clear ( ) ;
inPacket . data = outPacket ; // Replace incoming data with converted.
2021-05-27 10:41:08 +00:00
}
2021-05-29 17:59:45 +00:00
else
{
// This is already a 16bit stream, do we need to convert to stereo?
2021-06-04 07:24:26 +00:00
if ( setup . radioChan = = 1 & & devChannels > 1 ) {
2021-05-29 17:59:45 +00:00
// Yes
QByteArray outPacket ( inPacket . data . length ( ) * 2 , ( char ) 0xff ) ; // Preset the output buffer size.
qint16 * in = ( qint16 * ) inPacket . data . data ( ) ;
qint16 * out = ( qint16 * ) outPacket . data ( ) ;
for ( int f = 0 ; f < inPacket . data . length ( ) / 2 ; f + + )
{
* out + + = ( qint16 ) * in * this - > volume ;
* out + + = ( qint16 ) * in + + * this - > volume ;
}
inPacket . data . clear ( ) ;
inPacket . data = outPacket ; // Replace incoming data with converted.
}
else
{
2021-05-29 19:13:52 +00:00
// We already have the same number of channels so just update volume.
2021-05-29 17:59:45 +00:00
qint16 * in = ( qint16 * ) inPacket . data . data ( ) ;
for ( int f = 0 ; f < inPacket . data . length ( ) / 2 ; f + + )
{
2021-05-29 19:50:27 +00:00
* in = * in * this - > volume ;
2021-05-30 11:07:51 +00:00
in + + ;
2021-05-29 17:59:45 +00:00
}
}
2021-03-09 17:22:16 +00:00
2021-05-29 17:59:45 +00:00
}
2021-03-09 17:22:16 +00:00
2021-05-27 10:41:08 +00:00
/* We now have an array of 16bit samples in the NATIVE samplerate of the radio
If the radio sample rate is below 48000 , we need to resample .
*/
2021-05-29 19:50:27 +00:00
//qDebug(logAudio()) << "Now 16 bit stereo, length" << inPacket.data.length();
2021-03-09 17:22:16 +00:00
2021-05-27 10:41:08 +00:00
if ( ratioDen ! = 1 ) {
2021-03-09 17:22:16 +00:00
2021-05-27 10:41:08 +00:00
// We need to resample
2021-05-29 17:59:45 +00:00
// We have a stereo 16bit stream.
2021-05-29 19:13:52 +00:00
quint32 outFrames = ( ( inPacket . data . length ( ) / 2 / devChannels ) * ratioDen ) ;
quint32 inFrames = ( inPacket . data . length ( ) / 2 / devChannels ) ;
2021-05-29 17:59:45 +00:00
QByteArray outPacket ( outFrames * 4 , ( char ) 0xff ) ; // Preset the output buffer size.
const qint16 * in = ( qint16 * ) inPacket . data . constData ( ) ;
qint16 * out = ( qint16 * ) outPacket . data ( ) ;
2021-03-09 17:22:16 +00:00
2021-05-27 10:41:08 +00:00
int err = 0 ;
2021-05-29 17:59:45 +00:00
err = wf_resampler_process_interleaved_int ( resampler , in , & inFrames , out , & outFrames ) ;
2021-05-27 10:41:08 +00:00
if ( err ) {
2021-06-04 07:24:26 +00:00
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Resampler error " < < err < < " inFrames: " < < inFrames < < " outFrames: " < < outFrames ;
2021-03-09 17:22:16 +00:00
}
2021-05-29 17:59:45 +00:00
inPacket . data . clear ( ) ;
inPacket . data = outPacket ; // Replace incoming data with converted.
2021-05-27 10:41:08 +00:00
}
2021-05-29 19:50:27 +00:00
//qDebug(logAudio()) << "Adding packet to buffer:" << inPacket.seq << ": " << inPacket.data.length();
2021-05-27 12:54:52 +00:00
2021-08-01 01:24:30 +00:00
// Begin plugin code:
2021-08-01 07:59:29 +00:00
pluginMutex . lock ( ) ;
2021-08-01 22:24:31 +00:00
// out has size inPacket.data = BUF_SIZE
// pOut/inBuffer[1] has size BUF_SIZE/2
// inPacket.data has size BUF_SIZE
// pluginOutput16bitInterlaced has size BUF_SIZE
// Flow:
// inPacket --> out --> pInBuffer --> runPlugin() --> pOutBuffer --> pluginOutput16bitInterlaced --> inPacket
2021-08-01 01:24:30 +00:00
//
// Note, size of qint16 array is data.size() / 2 .
//
qint16 * out = ( qint16 * ) inPacket . data . constData ( ) ;
2021-08-01 22:24:31 +00:00
BUF_SIZE = inPacket . data . size ( ) / 2 ; // divide by two because the inPacket is 8 bits per member
// and all our usage is at 16 bits per.
// --- Input and Output buffer strategy:
// The plugin does not look at the size of the allocation. And the pointer
// needs to be to the same original location as the plugin runs
// Therefore, we allocate a large amount in the setupLADSPA() function,
// and simply adjust the BUF_SIZE so that the runPlugin() call only goes
// as far as is needed into the memory.
// I think these lines caused the pointer location at the base of the array to change, no good.
// pInBuffer[0]= (float*)realloc(pInBuffer[0], sizeof(LADSPA_Data) * BUF_SIZE / 2);
// pInBuffer[1]= (float*)realloc(pInBuffer[1], sizeof(LADSPA_Data) * BUF_SIZE / 2);
// pOutBuffer[0]= (float*)realloc(pOutBuffer[0], sizeof(LADSPA_Data) * BUF_SIZE / 2);
// pOutBuffer[1]= (float*)realloc(pOutBuffer[1], sizeof(LADSPA_Data) * BUF_SIZE / 2);
2021-08-01 07:14:36 +00:00
2021-08-01 01:24:30 +00:00
2021-08-01 22:24:31 +00:00
//qDebug(logAudio()) << "size of inPacket data: " << inPacket.data.size();
//qDebug(logAudio()) << "size of BUF_SIZE: " << BUF_SIZE;
// Allocated for 16 bit samples, interlaced stereo
pluginOutput16bitInterlaced = ( uint16_t * ) malloc ( sizeof ( uint16_t ) * BUF_SIZE ) ;
2021-08-01 01:24:30 +00:00
// Copy data from "out" to pInBuffer:
// copyDataIn();
float max = 65535 ;
2021-08-01 22:40:35 +00:00
float mid = max / 2.0f ;
float scalingFactor = 1.0f / max ;
2021-08-01 01:24:30 +00:00
// NOTE: The plugin library expects audio to be floats from -1.0 to +1.0
for ( int n = 0 ; n < BUF_SIZE ; n + = 2 ) {
pInBuffer [ 0 ] [ n / 2 ] = ( out [ n ] - mid ) * scalingFactor ;
pInBuffer [ 1 ] [ n / 2 ] = ( out [ n + 1 ] - mid ) * scalingFactor ;
}
2021-08-01 22:24:31 +00:00
// control is a number that the plugin has a pointer to.
// In this case, control is a float from 0 to 1.
2021-08-02 00:13:28 +00:00
control = pre_control = 0.5f ;
2021-08-02 06:18:13 +00:00
// How to get parameters:
// cd /usr/lib/ladspa
// export LADSPA_PATH=$PWD
// listplugins
// analyzeplugin gverb_1216.so
// Place filename and label name into the audiohandler.h file
// (When you install the SDK, you get the listplugins and analyzeplugin programs.)
// The setupLADSPA() function will assign pointers to the controls array into the plugin's instance.
// The controls array has 15 slots (per the audiohandler.h file where it is allocated on the stack).
// // Modify controls: Dyson Compressor:
2021-08-02 08:21:01 +00:00
controls [ 0 ] = 0.0f ; // peak limit (dB) -30 to 0, default 0. For the amp plugin, this is the gain.
controls [ 1 ] = 0.01f ; // release time, in seconds
controls [ 2 ] = 0.8f ; // "fast compression ratio", 0 to 1
controls [ 3 ] = 0.8f ; // compression ratio, 0 to 1
2021-08-02 06:18:13 +00:00
// Modify controls: sc1_1425.so
// controls[0] = 10.0f; // "Attack time (ms)" input, control, 2 to 400, default 101.5
// controls[1] = 10.0f; // "Release time (ms)" input, control, 2 to 800, default 401
// controls[2] = -25.0f; // "Threshold level (dB)" input, control, -30 to 0, default 0
// controls[3] = 6.0f; // "Ratio (1:n)" input, control, 1 to 10, default 1
// controls[4] = 3.25f; // "Knee radius (dB)" input, control, 1 to 10, default 3.25
// controls[5] = 12.0f; // "Makeup gain (dB)" input, control, 0 to 24, default 0
// Modify controls: fad_delay_1192.so
// controls[0] = 1.0f; // delay in seconds, 1 to 10
2021-08-02 08:21:01 +00:00
// controls[1] = -6.0f; // feedback (dB), -70 to 0
2021-08-02 06:18:13 +00:00
// Modify controls: gold reverv gverb_1216.so
// controls[0] = 75.0f; // "Roomsize (m)" input, control, 1 to 300, default 75.75
// controls[1] = 15.0f; // "Reverb time (s)" input, control, 0.1 to 30, default 7.575
// controls[2] = 0.5f; // "Damping" input, control, 0 to 1, default 0.5
// controls[3] = 0.75f; // "Input bandwidth" input, control, 0 to 1, default 0.75
// controls[4] = 0.0f; // "Dry signal level (dB)" input, control, -70 to 0, default -70
// controls[5] = 0.0f; // "Early reflection level (dB)" input, control, -70 to 0, default 0
// controls[6] = -15.0f; // "Tail level (dB)" input, control, -70 to 0, default -17.5
// multi-band EQ. Experiment to see if I only adjust one if the defaults hold out.
// file: mbeq_1197.so
2021-08-02 08:21:01 +00:00
// controls[3] = 12.0f; // 220 Hz
// controls[4] = 12.0f; // 440 Hz
// controls[8] = -25.0f; // "1250Hz gain" input, control, -70 to 30, default 0
// controls[9] = -12.0f;
2021-08-02 06:18:13 +00:00
2021-08-02 00:13:28 +00:00
2021-08-01 01:24:30 +00:00
runPlugin ( ) ;
// copy data back out from pOutBuffer to inPacket.data
2021-08-01 20:51:14 +00:00
// pOut/inBuffer[0] has size BUF_SIZE/2
2021-08-01 22:24:31 +00:00
2021-08-01 07:59:29 +00:00
// Maybe the issue is here?
2021-08-01 01:24:30 +00:00
2021-08-02 08:21:01 +00:00
// For now, force "dual mono" since most plugins we are looking at are mono output anyway.
2021-08-01 01:24:30 +00:00
for ( int n = 0 ; n + 1 < BUF_SIZE ; n + = 2 ) {
2021-08-02 08:21:01 +00:00
pluginOutput16bitInterlaced [ n ] = ( pOutBuffer [ 0 ] [ n / 2 ] + 1 ) * mid ; // L
pluginOutput16bitInterlaced [ n + 1 ] = ( pOutBuffer [ 0 ] [ n / 2 ] + 1 ) * mid ; // Copy L
//pluginOutput16bitInterlaced[n+1] = (pOutBuffer[1][n/2] + 1) * mid; // R
2021-08-01 01:24:30 +00:00
}
// copyDataOut();
2021-08-01 22:40:35 +00:00
// CAREFUL, get the 16 bits of plugin output into the 8 bit inPacket format:
2021-08-02 08:21:01 +00:00
// To bypass the function temporarly, just comment out this final copy loop:
if ( pluginOutputEnable )
2021-08-01 20:51:14 +00:00
{
2021-08-02 08:21:01 +00:00
for ( int i = 0 ; i < BUF_SIZE * 2 ; i + = 2 )
{
inPacket . data [ i ] = pluginOutput16bitInterlaced [ i / 2 ] & 0x00ff ;
inPacket . data [ i + 1 ] = ( pluginOutput16bitInterlaced [ i / 2 ] & 0xff00 ) > > 8 ;
2021-08-01 22:24:31 +00:00
2021-08-02 08:21:01 +00:00
}
2021-08-01 20:51:14 +00:00
}
2021-08-01 01:24:30 +00:00
// --- End plugin code.
2021-08-01 20:51:14 +00:00
if ( ! ringBuf - > try_write ( inPacket ) )
{
qDebug ( logAudio ( ) ) < < " Buffer full! capacity: " < < ringBuf - > capacity ( ) < < " length " < < ringBuf - > size ( ) ;
}
free ( pluginOutput16bitInterlaced ) ;
2021-08-01 07:59:29 +00:00
pluginMutex . unlock ( ) ;
2021-08-01 01:24:30 +00:00
2021-05-27 17:34:44 +00:00
return ;
2021-02-11 19:18:35 +00:00
}
2021-08-01 02:53:17 +00:00
void * audioHandler : : dlopenLADSPA ( const char * pcFilename , int iFlag )
{
2021-08-02 18:03:48 +00:00
char * pcBuffer ;
2021-08-01 02:53:17 +00:00
const char * pcEnd ;
const char * pcLADSPAPath ;
const char * pcStart ;
int iEndsInSO ;
int iNeedSlash ;
2021-08-02 18:03:48 +00:00
void * pvResult ;
size_t iFilenameLength ;
2021-08-01 02:53:17 +00:00
iFilenameLength = strlen ( pcFilename ) ;
pvResult = NULL ;
if ( pcFilename [ 0 ] = = ' / ' ) {
/* The filename is absolute. Assume the user knows what he/she is
doing and simply dlopen ( ) it . */
2021-08-02 18:03:48 +00:00
pvResult = dlopen ( pcFilename , iFlag ) ;
if ( pvResult ! = NULL )
return pvResult ;
2021-08-01 02:53:17 +00:00
}
else {
/* If the filename is not absolute then we wish to check along the
LADSPA_PATH path to see if we can find the file there . We do
NOT call dlopen ( ) directly as this would find plugins on the
LD_LIBRARY_PATH , whereas the LADSPA_PATH is the correct place
to search . */
pcLADSPAPath = getenv ( " LADSPA_PATH " ) ;
if ( pcLADSPAPath ) {
pcStart = pcLADSPAPath ;
while ( * pcStart ! = ' \0 ' ) {
pcEnd = pcStart ;
while ( * pcEnd ! = ' : ' & & * pcEnd ! = ' \0 ' )
pcEnd + + ;
pcBuffer = ( char * ) malloc ( iFilenameLength + ( size_t ) 2 + ( size_t ) ( pcEnd - pcStart ) ) ;
if ( pcEnd > pcStart )
strncpy ( pcBuffer , pcStart , pcEnd - pcStart ) ;
iNeedSlash = 0 ;
if ( pcEnd > pcStart )
if ( * ( pcEnd - 1 ) ! = ' / ' ) {
iNeedSlash = 1 ;
pcBuffer [ pcEnd - pcStart ] = ' / ' ;
}
strcpy ( pcBuffer + iNeedSlash + ( pcEnd - pcStart ) , pcFilename ) ;
pvResult = dlopen ( pcBuffer , iFlag ) ;
free ( pcBuffer ) ;
if ( pvResult ! = NULL )
return pvResult ;
pcStart = pcEnd ;
if ( * pcStart = = ' : ' )
pcStart + + ;
}
}
}
/* As a last ditch effort, check if filename does not end with
" .so " . In this case , add this suffix and recurse . */
iEndsInSO = 0 ;
if ( iFilenameLength > 3 )
iEndsInSO = ( strcmp ( pcFilename + iFilenameLength - 3 , " .so " ) = = 0 ) ;
if ( ! iEndsInSO ) {
pcBuffer = ( char * ) malloc ( iFilenameLength + 4 ) ;
strcpy ( pcBuffer , pcFilename ) ;
strcat ( pcBuffer , " .so " ) ;
pvResult = dlopenLADSPA ( pcBuffer , iFlag ) ;
free ( pcBuffer ) ;
}
if ( pvResult ! = NULL )
return pvResult ;
/* If nothing has worked, then at least we can make sure we set the
correct error message - and this should correspond to a call to
dlopen ( ) with the actual filename requested . The dlopen ( ) manual
page does not specify whether the first or last error message
will be kept when multiple calls are made to dlopen ( ) . We ' ve
covered the former case - now we can handle the latter by calling
dlopen ( ) again here . */
return dlopen ( pcFilename , iFlag ) ;
}
void * audioHandler : : loadLADSPAPluginLibrary ( const char * pcPluginFilename )
{
void * pvPluginHandle ;
pvPluginHandle = dlopenLADSPA ( pcPluginFilename , RTLD_NOW ) ;
if ( ! pvPluginHandle ) {
2021-08-02 18:03:48 +00:00
qInfo ( logAudio ( ) ) < < QString ( " Failed to load plugin %1: %2 " ) . arg ( pcPluginFilename ) . arg ( dlerror ( ) ) ;
return NULL ;
2021-08-01 02:53:17 +00:00
}
return pvPluginHandle ;
}
void unloadLADSPAPluginLibrary ( void * pvLADSPAPluginLibrary )
{
dlclose ( pvLADSPAPluginLibrary ) ;
}
const LADSPA_Descriptor * audioHandler : : findLADSPAPluginDescriptor ( void * pvLADSPAPluginLibrary ,
const char * pcPluginLibraryFilename ,
const char * pcPluginLabel )
{
const LADSPA_Descriptor * psDescriptor ;
LADSPA_Descriptor_Function pfDescriptorFunction ;
unsigned long lPluginIndex ;
dlerror ( ) ;
pfDescriptorFunction
= ( LADSPA_Descriptor_Function ) dlsym ( pvLADSPAPluginLibrary ,
" ladspa_descriptor " ) ;
if ( ! pfDescriptorFunction ) {
const char * pcError = dlerror ( ) ;
if ( pcError ) {
2021-08-02 18:03:48 +00:00
qInfo ( logAudio ( ) ) < <
QString ( " Unable to find ladspa_descriptor() function in plugin library file %1: %2. Are you sure this is a LADSPA plugin file? " ) . arg ( pcPluginLibraryFilename ) . arg ( pcError ) ;
return NULL ;
2021-08-01 02:53:17 +00:00
}
}
for ( lPluginIndex = 0 ; ; lPluginIndex + + ) {
psDescriptor = pfDescriptorFunction ( lPluginIndex ) ;
if ( psDescriptor = = NULL ) {
2021-08-02 18:03:48 +00:00
qInfo ( logAudio ( ) ) < < QString ( " Unable to find label %1: %2 " ) . arg ( pcPluginLabel ) . arg ( pcPluginLibraryFilename ) ;
return NULL ;
2021-08-01 02:53:17 +00:00
}
if ( strcmp ( psDescriptor - > Label , pcPluginLabel ) = = 0 )
return psDescriptor ;
}
}
2021-02-27 00:37:00 +00:00
void audioHandler : : changeLatency ( const quint16 newSize )
2021-02-11 19:18:35 +00:00
{
2021-06-05 07:26:58 +00:00
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Changing latency to: " < < newSize < < " from " < < setup . latency ;
setup . latency = newSize ;
2021-02-11 19:18:35 +00:00
}
2021-05-27 17:34:44 +00:00
int audioHandler : : getLatency ( )
2021-02-11 19:18:35 +00:00
{
2021-05-27 17:34:44 +00:00
return currentLatency ;
2021-02-11 19:18:35 +00:00
}
2021-02-13 11:04:26 +00:00
void audioHandler : : getNextAudioChunk ( QByteArray & ret )
2021-02-12 20:42:56 +00:00
{
2021-05-27 12:54:52 +00:00
audioPacket packet ;
packet . sent = 0 ;
2021-05-29 17:59:45 +00:00
2021-06-03 11:05:28 +00:00
if ( isInitialized & & ringBuf ! = Q_NULLPTR & & ringBuf - > try_read ( packet ) )
2021-02-27 09:34:56 +00:00
{
2021-06-05 07:42:00 +00:00
currentLatency = packet . time . msecsTo ( QTime : : currentTime ( ) ) ;
2021-05-29 17:59:45 +00:00
2021-06-05 07:42:00 +00:00
while ( currentLatency > setup . latency ) {
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Packet " < < hex < < packet . seq < <
" arrived too late (increase output latency!) " < <
dec < < packet . time . msecsTo ( QTime : : currentTime ( ) ) < < " ms " ;
if ( ! ringBuf - > try_read ( packet ) )
break ;
currentLatency = packet . time . msecsTo ( QTime : : currentTime ( ) ) ;
}
2021-05-29 17:59:45 +00:00
//qDebug(logAudio) << "Chunksize" << this->chunkSize << "Packet size" << packet.data.length();
// Packet will arrive as stereo interleaved 16bit 48K
2021-05-27 12:54:52 +00:00
if ( ratioNum ! = 1 )
2021-02-27 09:34:56 +00:00
{
2021-05-29 19:13:52 +00:00
quint32 outFrames = ( ( packet . data . length ( ) / 2 / devChannels ) / ratioNum ) ;
quint32 inFrames = ( packet . data . length ( ) / 2 / devChannels ) ;
QByteArray outPacket ( ( int ) outFrames * 2 * devChannels , ( char ) 0xff ) ;
2021-05-27 12:54:52 +00:00
const qint16 * in = ( qint16 * ) packet . data . constData ( ) ;
2021-05-29 17:59:45 +00:00
qint16 * out = ( qint16 * ) outPacket . data ( ) ;
2021-05-27 12:54:52 +00:00
int err = 0 ;
2021-05-29 17:59:45 +00:00
err = wf_resampler_process_interleaved_int ( resampler , in , & inFrames , out , & outFrames ) ;
2021-05-27 12:54:52 +00:00
if ( err ) {
2021-06-04 07:24:26 +00:00
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Resampler error " < < err < < " inFrames: " < < inFrames < < " outFrames: " < < outFrames ;
2021-05-27 12:54:52 +00:00
}
//qInfo(logAudio()) << "Resampler run " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
//qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length();
packet . data . clear ( ) ;
2021-05-29 17:59:45 +00:00
packet . data = outPacket ; // Copy output packet back to input buffer.
2021-05-27 12:54:52 +00:00
}
2021-05-29 17:59:45 +00:00
//qDebug(logAudio()) << "Now resampled, length" << packet.data.length();
// Do we need to convert mono to stereo?
2021-06-04 07:24:26 +00:00
if ( setup . radioChan = = 1 & & devChannels > 1 )
2021-05-29 17:59:45 +00:00
{
// Strip out right channel?
QByteArray outPacket ( packet . data . length ( ) / 2 , ( char ) 0xff ) ;
const qint16 * in = ( qint16 * ) packet . data . constData ( ) ;
qint16 * out = ( qint16 * ) outPacket . data ( ) ;
for ( int f = 0 ; f < outPacket . length ( ) / 2 ; f + + )
{
* out + + = * in + + ;
2021-05-30 11:07:51 +00:00
in + + ; // Skip each even channel.
2021-05-29 17:59:45 +00:00
}
packet . data . clear ( ) ;
packet . data = outPacket ; // Copy output packet back to input buffer.
}
//qDebug(logAudio()) << "Now mono, length" << packet.data.length();
2021-05-27 12:54:52 +00:00
// Do we need to convert 16-bit to 8-bit?
2021-06-04 07:24:26 +00:00
if ( setup . bits = = 8 ) {
2021-05-29 17:59:45 +00:00
QByteArray outPacket ( ( int ) packet . data . length ( ) / 2 , ( char ) 0xff ) ;
2021-05-27 12:54:52 +00:00
qint16 * in = ( qint16 * ) packet . data . data ( ) ;
2021-05-29 17:59:45 +00:00
for ( int f = 0 ; f < outPacket . length ( ) ; f + + )
2021-05-27 12:54:52 +00:00
{
quint8 outdata = 0 ;
if ( isUlaw ) {
qint16 enc = qFromLittleEndian < quint16 > ( in + f ) ;
if ( enc > = 0 )
outdata = ulaw_encode [ enc ] ;
else
outdata = 0x7f & ulaw_encode [ - enc ] ;
2021-02-27 13:43:53 +00:00
}
else {
2021-05-27 12:54:52 +00:00
outdata = ( quint8 ) ( ( ( qFromLittleEndian < qint16 > ( in + f ) > > 8 ) ^ 0x80 ) & 0xff ) ;
2021-02-27 13:43:53 +00:00
}
2021-05-29 17:59:45 +00:00
outPacket [ f ] = ( char ) outdata ;
2021-02-27 13:43:53 +00:00
}
2021-05-27 12:54:52 +00:00
packet . data . clear ( ) ;
2021-05-29 17:59:45 +00:00
packet . data = outPacket ; // Copy output packet back to input buffer.
2021-02-27 09:34:56 +00:00
}
2021-05-27 12:54:52 +00:00
ret = packet . data ;
2021-05-29 17:59:45 +00:00
//qDebug(logAudio()) << "Now radio format, length" << packet.data.length();
2021-02-27 09:34:56 +00:00
}
2021-05-27 12:54:52 +00:00
2021-05-29 17:59:45 +00:00
2021-05-23 15:09:41 +00:00
return ;
2021-05-27 12:54:52 +00:00
2021-02-12 20:42:56 +00:00
}
2021-05-23 15:09:41 +00:00
2021-06-06 16:56:48 +00:00
# if !defined (RTAUDIO) && !defined(PORTAUDIO)
2021-06-04 07:24:26 +00:00
qint64 audioHandler : : bytesAvailable ( ) const
{
return 0 ;
}
bool audioHandler : : isSequential ( ) const
{
return true ;
}
void audioHandler : : notified ( )
{
}
void audioHandler : : stateChanged ( QAudio : : State state )
{
// Process the state
switch ( state )
{
case QAudio : : IdleState :
{
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Audio now in idle state: " < < audioBuffer . size ( ) < < " packets in buffer " ;
if ( audioOutput ! = Q_NULLPTR & & audioOutput - > error ( ) = = QAudio : : UnderrunError )
{
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " buffer underrun " ;
//audioOutput->suspend();
}
break ;
}
case QAudio : : ActiveState :
{
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Audio now in active state: " < < audioBuffer . size ( ) < < " packets in buffer " ;
break ;
}
case QAudio : : SuspendedState :
{
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Audio now in suspended state: " < < audioBuffer . size ( ) < < " packets in buffer " ;
break ;
}
case QAudio : : StoppedState :
{
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Audio now in stopped state: " < < audioBuffer . size ( ) < < " packets in buffer " ;
break ;
}
default : {
qInfo ( logAudio ( ) ) < < ( setup . isinput ? " Input " : " Output " ) < < " Unhandled audio state: " < < audioBuffer . size ( ) < < " packets in buffer " ;
}
}
}
void audioHandler : : stop ( )
{
if ( audioOutput ! = Q_NULLPTR & & audioOutput - > state ( ) ! = QAudio : : StoppedState ) {
// Stop audio output
audioOutput - > stop ( ) ;
this - > stop ( ) ;
this - > close ( ) ;
2021-06-07 11:31:58 +00:00
delete audioOutput ;
audioOutput = Q_NULLPTR ;
2021-06-04 07:24:26 +00:00
}
if ( audioInput ! = Q_NULLPTR & & audioInput - > state ( ) ! = QAudio : : StoppedState ) {
// Stop audio output
audioInput - > stop ( ) ;
this - > stop ( ) ;
this - > close ( ) ;
2021-06-07 11:31:58 +00:00
delete audioInput ;
audioInput = Q_NULLPTR ;
2021-06-04 07:24:26 +00:00
}
2021-06-07 11:31:58 +00:00
isInitialized = false ;
2021-06-04 07:24:26 +00:00
}
2021-06-06 16:56:48 +00:00
# endif
2021-06-04 07:24:26 +00:00
2021-02-11 19:18:35 +00:00