snes9x/common/audio/s9x_sound_driver_portaudio.cpp

216 lines
5.1 KiB
C++
Raw Normal View History

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "s9x_sound_driver_portaudio.hpp"
#include <cstring>
#include <cstdio>
#include <cstdint>
#include <vector>
2023-06-26 20:39:33 +00:00
#include <string>
2010-09-25 15:46:12 +00:00
S9xPortAudioSoundDriver::S9xPortAudioSoundDriver()
2010-09-25 15:46:12 +00:00
{
audio_stream = NULL;
}
2023-03-23 21:53:43 +00:00
S9xPortAudioSoundDriver::~S9xPortAudioSoundDriver()
{
deinit();
}
void S9xPortAudioSoundDriver::init()
2010-09-25 15:46:12 +00:00
{
PaError err;
err = Pa_Initialize();
2010-09-25 15:46:12 +00:00
if (err != paNoError)
fprintf(stderr,
"Couldn't initialize PortAudio: %s\n",
Pa_GetErrorText(err));
2010-09-25 15:46:12 +00:00
}
void S9xPortAudioSoundDriver::deinit()
2010-09-25 15:46:12 +00:00
{
stop();
2010-09-25 15:46:12 +00:00
Pa_Terminate();
2010-09-25 15:46:12 +00:00
}
void S9xPortAudioSoundDriver::start()
2010-09-25 15:46:12 +00:00
{
PaError err;
if (audio_stream != NULL)
2010-09-25 15:46:12 +00:00
{
if ((Pa_IsStreamActive(audio_stream)))
2010-09-25 15:46:12 +00:00
return;
err = Pa_StartStream(audio_stream);
2010-09-25 15:46:12 +00:00
if (err != paNoError)
{
fprintf(stderr, "Error: %s\n", Pa_GetErrorText(err));
2010-09-25 15:46:12 +00:00
}
}
}
void S9xPortAudioSoundDriver::stop()
2010-09-25 15:46:12 +00:00
{
if (audio_stream != NULL)
{
Pa_StopStream(audio_stream);
2010-09-25 15:46:12 +00:00
}
}
2023-03-23 21:53:43 +00:00
bool S9xPortAudioSoundDriver::tryHostAPI(int index)
{
auto hostapi_info = Pa_GetHostApiInfo(index);
if (!hostapi_info)
{
printf("Host API #%d has no info\n", index);
return false;
}
auto device_info = Pa_GetDeviceInfo(hostapi_info->defaultOutputDevice);
if (!device_info)
{
printf("(%s)...No device info available.\n", hostapi_info->name);
return false;
}
PaStreamParameters param{};
param.device = hostapi_info->defaultOutputDevice;
param.suggestedLatency = buffer_size_ms * 0.001;
param.channelCount = 2;
param.sampleFormat = paInt16;
param.hostApiSpecificStreamInfo = NULL;
printf("(%s : %s, latency %dms)...\n",
hostapi_info->name,
device_info->name,
(int)(param.suggestedLatency * 1000.0));
auto err = Pa_OpenStream(&audio_stream,
NULL,
&param,
playback_rate,
0,
paNoFlag,
NULL,
NULL);
2023-06-29 22:44:13 +00:00
int frames = Pa_GetStreamWriteAvailable(audio_stream);
if (frames < 0)
{
Pa_Sleep(10);
frames = Pa_GetStreamWriteAvailable(audio_stream);
}
2023-03-23 21:53:43 +00:00
printf("PortAudio set buffer size to %d frames.\n", frames);
output_buffer_size = frames;
if (err == paNoError)
{
printf("OK\n");
return true;
}
else
{
printf("Failed (%s)\n", Pa_GetErrorText(err));
return false;
}
}
bool S9xPortAudioSoundDriver::open_device(int playback_rate, int buffer_size_ms)
2010-09-25 15:46:12 +00:00
{
const PaDeviceInfo *device_info;
2010-09-25 15:46:12 +00:00
const PaHostApiInfo *hostapi_info;
PaError err = paNoError;
2010-09-25 15:46:12 +00:00
if (audio_stream != NULL)
{
printf("Shutting down sound for reset\n");
err = Pa_CloseStream(audio_stream);
2010-09-25 15:46:12 +00:00
if (err != paNoError)
{
2023-03-23 21:53:43 +00:00
fprintf(stderr, "Couldn't reset audio stream.\nError: %s\n", Pa_GetErrorText(err));
return true;
2010-09-25 15:46:12 +00:00
}
audio_stream = NULL;
}
2023-03-23 21:53:43 +00:00
this->playback_rate = playback_rate;
this->buffer_size_ms = buffer_size_ms;
2010-09-25 15:46:12 +00:00
printf("PortAudio sound driver initializing...\n");
2010-09-25 15:46:12 +00:00
2023-06-26 20:39:33 +00:00
int host = Pa_GetDefaultHostApi();
#ifdef _WIN32
// Look for WASAPI
for (int i = 0; i < Pa_GetHostApiCount(); i++)
{
auto hostapi_info = Pa_GetHostApiInfo(i);
std::string str(hostapi_info->name);
if (str == "Windows WASAPI")
{
host = i;
break;
}
}
#endif
2023-03-23 21:53:43 +00:00
if (tryHostAPI(host))
return true;
2010-09-25 15:46:12 +00:00
2023-03-23 21:53:43 +00:00
for (int i = 0; i < Pa_GetHostApiCount(); i++)
2010-09-25 15:46:12 +00:00
{
2023-06-26 20:39:33 +00:00
if (host != i)
if (tryHostAPI(i))
return true;
2010-09-25 15:46:12 +00:00
}
2023-03-23 21:53:43 +00:00
fprintf(stderr, "Couldn't initialize sound\n");
return false;
}
2010-09-25 15:46:12 +00:00
int S9xPortAudioSoundDriver::space_free()
{
return Pa_GetStreamWriteAvailable(audio_stream) * 2;
}
2010-09-25 15:46:12 +00:00
std::pair<int, int> S9xPortAudioSoundDriver::buffer_level()
{
2023-06-29 22:44:13 +00:00
return { Pa_GetStreamWriteAvailable(audio_stream), output_buffer_size };
2010-09-25 15:46:12 +00:00
}
2023-06-28 22:58:00 +00:00
bool S9xPortAudioSoundDriver::write_samples(int16_t *data, int samples)
2010-09-25 15:46:12 +00:00
{
int frames;
frames = Pa_GetStreamWriteAvailable(audio_stream);
2019-04-04 00:23:54 +00:00
if (frames == output_buffer_size)
{
// Prime the stream
std::vector<int16_t> tmp(output_buffer_size);
memset(tmp.data(), 0, output_buffer_size << 1);
Pa_WriteStream(audio_stream, tmp.data(), output_buffer_size >> 1);
2019-04-04 00:23:54 +00:00
frames -= output_buffer_size >> 1;
}
2023-06-28 22:58:00 +00:00
bool retval = true;
if (frames > samples / 2)
2023-06-28 22:58:00 +00:00
{
retval = false;
frames = samples / 2;
2023-06-28 22:58:00 +00:00
}
2010-09-25 15:46:12 +00:00
Pa_WriteStream(audio_stream, data, frames);
2023-06-28 22:58:00 +00:00
return retval;
2010-09-25 15:46:12 +00:00
}