5 #include "framerate.hpp"
8 #include "settings.hpp"
12 #include "keymapper.hpp"
13 #include "framerate.hpp"
17 #include <boost/lexical_cast.hpp>
19 #include <portaudio.h>
26 uint32_t audio_playback_freq
= 0;
27 const size_t audiobuf_size
= 8192;
28 uint16_t audiobuf
[audiobuf_size
];
29 volatile size_t audiobuf_get
= 0;
30 volatile size_t audiobuf_put
= 0;
31 uint64_t sampledup_ctr
= 0;
32 uint64_t sampledup_inc
= 0;
33 uint64_t sampledup_mod
= 0;
34 uint32_t use_rate_n
= 32000;
35 uint32_t use_rate_d
= 1;
36 PaDeviceIndex current_device
= paNoDevice
;
39 bool init_flag
= false;
41 void calculate_sampledup(uint32_t rate_n
, uint32_t rate_d
)
43 if(!audio_playback_freq
) {
50 sampledup_inc
= rate_n
;
51 sampledup_mod
= rate_d
* audio_playback_freq
+ rate_n
;
55 int audiocb(const void *input
, void *output
, unsigned long frame_count
,
56 const PaStreamCallbackTimeInfo
* time_info
, PaStreamCallbackFlags flags
, void* user
)
58 static uint16_t lprev
, rprev
;
59 int16_t* _output
= reinterpret_cast<int16_t*>(output
);
60 for(unsigned long i
= 0; i
< frame_count
; i
++) {
62 if(audiobuf_get
== audiobuf_put
) {
66 l
= lprev
= audiobuf
[audiobuf_get
++];
67 r
= rprev
= audiobuf
[audiobuf_get
++];
68 if(audiobuf_get
== audiobuf_size
)
73 *(_output
++) = l
- 32768;
75 *(_output
++) = r
- 32768;
80 bool switch_devices(PaDeviceIndex newdevice
)
82 //Check that the new device index is valid at all.
83 if((newdevice
>= 0 && !Pa_GetDeviceInfo(newdevice
)) || (newdevice
< 0 && newdevice
!= paNoDevice
)) {
84 window::out() << "Invalid device " << newdevice
<< std::endl
;
87 if(newdevice
!= paNoDevice
&& Pa_GetDeviceInfo(newdevice
)->maxOutputChannels
< 1) {
88 window::out() << "Portaudio: Device " << newdevice
<< " is not capable of playing sound."
92 //If audio is open somewhere, close it.
93 if(current_device
!= paNoDevice
) {
94 PaError err
= Pa_StopStream(s
);
95 if(err
!= paNoError
) {
96 window::out() << "Portaudio error (stop): " << Pa_GetErrorText(err
) << std::endl
;
99 err
= Pa_CloseStream(s
);
100 if(err
!= paNoError
) {
101 window::out() << "Portaudio error (close): " << Pa_GetErrorText(err
) << std::endl
;
104 current_device
= paNoDevice
;
110 //If new audio device is to be opened, try to do it.
111 if(newdevice
!= paNoDevice
) {
112 const PaDeviceInfo
* inf
= Pa_GetDeviceInfo(newdevice
);
113 PaStreamParameters output
;
114 memset(&output
, 0, sizeof(output
));
115 output
.device
= newdevice
;
116 output
.channelCount
= (inf
->maxOutputChannels
> 1) ? 2 : 1;
117 output
.sampleFormat
= paInt16
;
118 output
.suggestedLatency
= inf
->defaultLowOutputLatency
;
119 output
.hostApiSpecificStreamInfo
= NULL
;
120 PaError err
= Pa_OpenStream(&s
, NULL
, &output
, inf
->defaultSampleRate
, 0, 0, audiocb
, NULL
);
121 if(err
!= paNoError
) {
122 window::out() << "Portaudio: error (open): " << Pa_GetErrorText(err
) << std::endl
123 << "\tOn device: '" << inf
->name
<< "'" << std::endl
;
127 stereo
= (output
.channelCount
== 2);
128 err
= Pa_StartStream(s
);
129 if(err
!= paNoError
) {
130 window::out() << "Portaudio error (start): " << Pa_GetErrorText(err
) << std::endl
131 << "\tOn device: '" << inf
->name
<< "'" << std::endl
;
134 audio_playback_freq
= inf
->defaultSampleRate
;
135 calculate_sampledup(use_rate_n
, use_rate_d
);
136 window::out() << "Portaudio: Opened " << inf
->defaultSampleRate
<< "Hz "
137 << (stereo
? "Stereo" : "Mono") << " sound on '" << inf
->name
<< "'" << std::endl
;
138 window::out() << "Switched to sound device '" << inf
->name
<< "'" << std::endl
;
140 window::out() << "Switched to sound device NULL" << std::endl
;
141 current_device
= newdevice
;
147 void window::sound_enable(bool enable
) throw()
151 err
= Pa_StartStream(s
);
153 err
= Pa_StopStream(s
);
155 window::out() << "Portaudio error (start/stop): " << Pa_GetErrorText(err
) << std::endl
;
160 PaError err
= Pa_Initialize();
161 if(err
!= paNoError
) {
162 window::out() << "Portaudio error (init): " << Pa_GetErrorText(err
) << std::endl
;
163 window::out() << "Audio can't be initialized, audio playback disabled" << std::endl
;
168 PaDeviceIndex forcedevice
= paNoDevice
;
169 if(getenv("LSNES_FORCE_AUDIO_OUT")) {
170 forcedevice
= atoi(getenv("LSNES_FORCE_AUDIO_OUT"));
171 window::out() << "Attempting to force sound output " << forcedevice
<< std::endl
;
173 window::out() << "Detected " << Pa_GetDeviceCount() << " sound output devices." << std::endl
;
174 for(PaDeviceIndex j
= 0; j
< Pa_GetDeviceCount(); j
++)
175 window::out() << "Audio device " << j
<< ": " << Pa_GetDeviceInfo(j
)->name
<< std::endl
;
176 bool any_success
= false;
177 if(forcedevice
== paNoDevice
) {
178 for(PaDeviceIndex j
= 0; j
< Pa_GetDeviceCount(); j
++) {
179 any_success
|= switch_devices(j
);
184 any_success
|= switch_devices(forcedevice
);
186 window::out() << "Portaudio: Can't open any sound device, audio disabled" << std::endl
;
193 switch_devices(paNoDevice
);
198 void window::play_audio_sample(uint16_t left
, uint16_t right
) throw()
202 sampledup_ctr
+= sampledup_inc
;
203 while(sampledup_ctr
< sampledup_mod
) {
204 audiobuf
[audiobuf_put
++] = left
;
205 audiobuf
[audiobuf_put
++] = right
;
206 if(audiobuf_put
== audiobuf_size
)
208 sampledup_ctr
+= sampledup_inc
;
210 sampledup_ctr
-= sampledup_mod
;
213 void window::set_sound_rate(uint32_t rate_n
, uint32_t rate_d
)
217 uint32_t g
= gcd(rate_n
, rate_d
);
218 use_rate_n
= rate_n
/ g
;
219 use_rate_d
= rate_d
/ g
;
220 calculate_sampledup(use_rate_n
, use_rate_d
);
223 bool window::sound_initialized()
228 void window::set_sound_device(const std::string
& dev
)
231 if(!switch_devices(paNoDevice
))
232 throw std::runtime_error("Failed to switch sound outputs");
236 idx
= boost::lexical_cast
<PaDeviceIndex
>(dev
);
237 if(idx
< 0 || !Pa_GetDeviceInfo(idx
))
238 throw std::runtime_error("foo");
239 } catch(std::exception
& e
) {
240 throw std::runtime_error("Invalid device '" + dev
+ "'");
242 if(!switch_devices(idx
))
243 throw std::runtime_error("Failed to switch sound outputs");
247 std::string
window::get_current_sound_device()
249 if(current_device
== paNoDevice
)
252 std::ostringstream str
;
253 str
<< current_device
;
258 std::map
<std::string
, std::string
> window::get_sound_devices()
260 std::map
<std::string
, std::string
> ret
;
261 ret
["null"] = "null sound output";
262 for(PaDeviceIndex j
= 0; j
< Pa_GetDeviceCount(); j
++) {
263 std::ostringstream str
;
265 ret
[str
.str()] = Pa_GetDeviceInfo(j
)->name
;
270 const char* sound_plugin_name
= "Portaudio sound plugin";