2016-06-18 14:46:12 +02:00
/*************************************************************************/
/* audio_driver_rtaudio.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:11:45 +02:00
/* https://godotengine.org */
2016-06-18 14:46:12 +02:00
/*************************************************************************/
2018-01-02 11:27:24 +01:00
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
2016-06-18 14:46:12 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2014-02-10 02:10:30 +01:00
# include "audio_driver_rtaudio.h"
2016-10-13 20:58:40 +02:00
2014-02-10 02:10:30 +01:00
# include "globals.h"
# include "os/os.h"
2016-10-13 20:58:40 +02:00
2014-02-10 02:10:30 +01:00
# ifdef RTAUDIO_ENABLED
2017-03-19 00:36:26 +01:00
const char * AudioDriverRtAudio : : get_name ( ) const {
2014-02-10 02:10:30 +01:00
# ifdef OSX_ENABLED
return " RtAudio-OSX " ;
# elif defined(UNIX_ENABLED)
return " RtAudio-ALSA " ;
# elif defined(WINDOWS_ENABLED)
return " RtAudio-DirectSound " ;
# else
return " RtAudio-None " ;
# endif
}
2017-03-19 00:36:26 +01:00
int AudioDriverRtAudio : : callback ( void * outputBuffer , void * inputBuffer , unsigned int nBufferFrames , double streamTime , RtAudioStreamStatus status , void * userData ) {
2014-02-10 02:10:30 +01:00
2016-06-08 11:26:54 +02:00
if ( status ) {
if ( status & RTAUDIO_INPUT_OVERFLOW ) {
WARN_PRINT ( " RtAudio input overflow! " ) ;
}
if ( status & RTAUDIO_OUTPUT_UNDERFLOW ) {
WARN_PRINT ( " RtAudio output underflow! " ) ;
}
}
2017-03-19 00:36:26 +01:00
int32_t * buffer = ( int32_t * ) outputBuffer ;
2014-02-10 02:10:30 +01:00
2017-03-19 00:36:26 +01:00
AudioDriverRtAudio * self = ( AudioDriverRtAudio * ) userData ;
2014-02-10 02:10:30 +01:00
2017-03-19 00:36:26 +01:00
if ( self - > mutex - > try_lock ( ) ! = OK ) {
2014-02-10 02:10:30 +01:00
// what should i do..
2017-03-19 00:36:26 +01:00
for ( unsigned int i = 0 ; i < nBufferFrames ; i + + )
buffer [ i ] = 0 ;
2014-02-10 02:10:30 +01:00
return 0 ;
}
2017-03-19 00:36:26 +01:00
self - > audio_server_process ( nBufferFrames , buffer ) ;
2014-02-10 02:10:30 +01:00
2017-01-14 18:03:38 +01:00
self - > mutex - > unlock ( ) ;
2014-02-10 02:10:30 +01:00
return 0 ;
}
Error AudioDriverRtAudio : : init ( ) {
2017-03-19 00:36:26 +01:00
active = false ;
mutex = NULL ;
dac = memnew ( RtAudio ) ;
2014-02-10 02:10:30 +01:00
ERR_EXPLAIN ( " Cannot initialize RtAudio audio driver: No devices present. " )
2017-03-19 00:36:26 +01:00
ERR_FAIL_COND_V ( dac - > getDeviceCount ( ) < 1 , ERR_UNAVAILABLE ) ;
2014-02-10 02:10:30 +01:00
2017-03-19 00:36:26 +01:00
String channels = GLOBAL_DEF ( " audio/output " , " stereo " ) ;
2014-02-10 02:10:30 +01:00
2017-03-19 00:36:26 +01:00
if ( channels = = " 5.1 " )
output_format = OUTPUT_5_1 ;
else if ( channels = = " quad " )
output_format = OUTPUT_QUAD ;
else if ( channels = = " mono " )
output_format = OUTPUT_MONO ;
2014-02-10 02:10:30 +01:00
else
2017-03-19 00:36:26 +01:00
output_format = OUTPUT_STEREO ;
2014-02-10 02:10:30 +01:00
RtAudio : : StreamParameters parameters ;
parameters . deviceId = dac - > getDefaultOutputDevice ( ) ;
RtAudio : : StreamOptions options ;
2017-01-03 19:00:31 +01:00
// set the desired numberOfBuffers
unsigned int target_number_of_buffers = 4 ;
options . numberOfBuffers = target_number_of_buffers ;
2017-03-19 00:36:26 +01:00
// options.
// RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE). *///
// unsigned int numberOfBuffers; /*!< Number of stream buffers. */
// std::string streamName; /*!< A stream name (currently used only in Jack). */
// int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */
2014-02-10 02:10:30 +01:00
parameters . firstChannel = 0 ;
2017-03-19 00:36:26 +01:00
mix_rate = GLOBAL_DEF ( " audio/mix_rate " , 44100 ) ;
2014-02-10 02:10:30 +01:00
2017-03-19 00:36:26 +01:00
int latency = GLOBAL_DEF ( " audio/output_latency " , 25 ) ;
2017-01-03 19:00:31 +01:00
// calculate desired buffer_size, taking the desired numberOfBuffers into account (latency depends on numberOfBuffers*buffer_size)
2017-08-27 10:12:25 +02:00
unsigned int buffer_size = next_power_of_2 ( latency * mix_rate / 1000 / target_number_of_buffers ) ;
2017-01-03 19:00:31 +01:00
2014-02-10 02:10:30 +01:00
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
2017-03-19 00:36:26 +01:00
print_line ( " audio buffer size: " + itos ( buffer_size ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-01-03 19:00:31 +01:00
short int tries = 2 ;
2014-02-10 02:10:30 +01:00
2017-03-19 00:36:26 +01:00
while ( true ) {
while ( true ) {
switch ( output_format ) {
2017-01-03 19:00:31 +01:00
case OUTPUT_MONO : parameters . nChannels = 1 ; break ;
case OUTPUT_STEREO : parameters . nChannels = 2 ; break ;
case OUTPUT_QUAD : parameters . nChannels = 4 ; break ;
case OUTPUT_5_1 : parameters . nChannels = 6 ; break ;
} ;
2014-02-10 02:10:30 +01:00
2017-01-03 19:00:31 +01:00
try {
2017-03-19 00:36:26 +01:00
dac - > openStream ( & parameters , NULL , RTAUDIO_SINT32 , mix_rate , & buffer_size , & callback , this , & options ) ;
2017-01-03 19:00:31 +01:00
mutex = Mutex : : create ( true ) ;
2017-03-19 00:36:26 +01:00
active = true ;
2017-01-03 19:00:31 +01:00
break ;
2017-03-19 00:36:26 +01:00
} catch ( RtAudioError & e ) {
2017-01-03 19:00:31 +01:00
// try with less channels
ERR_PRINT ( " Unable to open audio, retrying with fewer channels.. " ) ;
2017-03-19 00:36:26 +01:00
switch ( output_format ) {
case OUTPUT_MONO :
ERR_EXPLAIN ( " Unable to open audio. " ) ;
ERR_FAIL_V ( ERR_UNAVAILABLE ) ;
break ;
case OUTPUT_STEREO : output_format = OUTPUT_MONO ; break ;
case OUTPUT_QUAD : output_format = OUTPUT_STEREO ; break ;
case OUTPUT_5_1 : output_format = OUTPUT_QUAD ; break ;
2017-01-03 19:00:31 +01:00
} ;
}
}
2014-02-10 02:10:30 +01:00
2017-01-03 19:00:31 +01:00
// compare actual numberOfBuffers with the desired one. If not equal, close and reopen the stream with adjusted buffer size, so the desired output_latency is still correct
2017-03-19 00:36:26 +01:00
if ( target_number_of_buffers ! = options . numberOfBuffers ) {
if ( tries < = 0 ) {
2017-01-03 19:00:31 +01:00
ERR_EXPLAIN ( " RtAudio: Unable to set correct number of buffers. " ) ;
2017-03-19 00:36:26 +01:00
ERR_FAIL_V ( ERR_UNAVAILABLE ) ;
2017-01-03 19:00:31 +01:00
break ;
}
2017-03-19 00:36:26 +01:00
2017-01-03 19:00:31 +01:00
try {
dac - > closeStream ( ) ;
2017-03-19 00:36:26 +01:00
} catch ( RtAudioError & e ) {
2017-01-03 19:00:31 +01:00
ERR_PRINT ( e . what ( ) ) ;
2017-03-19 00:36:26 +01:00
ERR_FAIL_V ( ERR_UNAVAILABLE ) ;
2017-01-03 19:00:31 +01:00
break ;
}
2017-03-19 00:36:26 +01:00
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) )
2017-01-03 19:00:31 +01:00
print_line ( " RtAudio: Desired number of buffers ( " + itos ( target_number_of_buffers ) + " ) not available. Using " + itos ( options . numberOfBuffers ) + " instead. Reopening stream with adjusted buffer_size. " ) ;
// new buffer size dependent on the ratio between set and actual numberOfBuffers
buffer_size = buffer_size / ( options . numberOfBuffers / target_number_of_buffers ) ;
target_number_of_buffers = options . numberOfBuffers ;
tries - - ;
} else {
2014-02-10 02:10:30 +01:00
break ;
}
2017-01-03 19:00:31 +01:00
}
2014-02-10 02:10:30 +01:00
return OK ;
}
int AudioDriverRtAudio : : get_mix_rate ( ) const {
return mix_rate ;
}
AudioDriverSW : : OutputFormat AudioDriverRtAudio : : get_output_format ( ) const {
return output_format ;
}
void AudioDriverRtAudio : : start ( ) {
if ( active )
dac - > startStream ( ) ;
}
void AudioDriverRtAudio : : lock ( ) {
if ( mutex )
mutex - > lock ( ) ;
}
void AudioDriverRtAudio : : unlock ( ) {
if ( mutex )
mutex - > unlock ( ) ;
}
void AudioDriverRtAudio : : finish ( ) {
2017-03-19 00:36:26 +01:00
if ( active & & dac - > isStreamOpen ( ) )
dac - > closeStream ( ) ;
if ( mutex )
memdelete ( mutex ) ;
if ( dac )
memdelete ( dac ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-19 00:36:26 +01:00
AudioDriverRtAudio : : AudioDriverRtAudio ( ) {
2014-02-10 02:10:30 +01:00
2017-09-03 22:12:15 +02:00
active = false ;
2017-03-19 00:36:26 +01:00
mutex = NULL ;
mix_rate = 44100 ;
output_format = OUTPUT_STEREO ;
2014-02-10 02:10:30 +01:00
}
# endif