2018-11-16 00:42:29 +01:00
|
|
|
/*****************************************************************************\
|
|
|
|
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.
|
|
|
|
\*****************************************************************************/
|
|
|
|
|
2010-09-25 17:46:12 +02:00
|
|
|
#include "gtk_sound_driver_oss.h"
|
2019-02-07 02:41:33 +01:00
|
|
|
#include "gtk_s9x.h"
|
2020-07-05 00:53:38 +02:00
|
|
|
#include "snes9x.h"
|
|
|
|
#include "apu/apu.h"
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
#include <fcntl.h>
|
2010-09-25 17:46:12 +02:00
|
|
|
#include <sys/ioctl.h>
|
2019-02-07 02:41:33 +01:00
|
|
|
#include <sys/soundcard.h>
|
2010-09-25 17:46:12 +02:00
|
|
|
#include <sys/time.h>
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
static void oss_samples_available(void *data)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2019-02-07 02:41:33 +01:00
|
|
|
((S9xOSSSoundDriver *)data)->samples_available();
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
S9xOSSSoundDriver::S9xOSSSoundDriver()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
filedes = -1;
|
|
|
|
sound_buffer = NULL;
|
|
|
|
sound_buffer_size = 0;
|
|
|
|
}
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
void S9xOSSSoundDriver::init()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
void S9xOSSSoundDriver::terminate()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2019-02-07 02:41:33 +01:00
|
|
|
stop();
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
S9xSetSamplesAvailableCallback(NULL, NULL);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
if (filedes >= 0)
|
|
|
|
{
|
2019-02-07 02:41:33 +01:00
|
|
|
close(filedes);
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sound_buffer)
|
|
|
|
{
|
2019-02-07 02:41:33 +01:00
|
|
|
free(sound_buffer);
|
2010-09-25 17:46:12 +02:00
|
|
|
sound_buffer = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
void S9xOSSSoundDriver::start()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
void S9xOSSSoundDriver::stop()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:32:32 +01:00
|
|
|
bool S9xOSSSoundDriver::open_device()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
int temp;
|
2017-11-20 19:07:54 +01:00
|
|
|
audio_buf_info info;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
output_buffer_size = (gui_config->sound_buffer_size * Settings.SoundPlaybackRate) / 1000;
|
|
|
|
|
2019-02-06 00:21:23 +01:00
|
|
|
output_buffer_size *= 4;
|
2010-09-25 17:46:12 +02:00
|
|
|
if (output_buffer_size < 256)
|
|
|
|
output_buffer_size = 256;
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("OSS sound driver initializing...\n");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("Device: /dev/dsp: ");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
filedes = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
if (filedes < 0)
|
2017-11-20 19:07:54 +01:00
|
|
|
{
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("Failed.\n");
|
2018-11-08 22:12:47 +01:00
|
|
|
char dspstring[16] = "/dev/dspX\0";
|
|
|
|
|
|
|
|
for (int i = 1; i <= 9; i++)
|
|
|
|
{
|
|
|
|
dspstring[8] = '0' + i;
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("Trying %s: ", dspstring);
|
2018-11-08 22:12:47 +01:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
filedes = open(dspstring, O_WRONLY | O_NONBLOCK);
|
2018-11-08 22:12:47 +01:00
|
|
|
|
|
|
|
if (filedes < 0)
|
|
|
|
{
|
|
|
|
if (i == 9)
|
|
|
|
goto fail;
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("Failed.\n");
|
2018-11-08 22:12:47 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2017-11-20 19:07:54 +01:00
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("OK\n");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf(" --> (Format: 16-bit)...");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-06 00:21:23 +01:00
|
|
|
temp = AFMT_S16_LE;
|
2019-02-07 02:41:33 +01:00
|
|
|
if (ioctl(filedes, SNDCTL_DSP_SETFMT, &temp) < 0)
|
2019-02-06 00:21:23 +01:00
|
|
|
goto close_fail;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("OK\n");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-06 00:21:23 +01:00
|
|
|
temp = 2;
|
2019-02-07 02:41:33 +01:00
|
|
|
printf(" --> (Stereo)...");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
if (ioctl(filedes, SNDCTL_DSP_CHANNELS, &temp) < 0)
|
2010-09-25 17:46:12 +02:00
|
|
|
goto close_fail;
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("OK\n");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf(" --> (Frequency: %d)...", Settings.SoundPlaybackRate);
|
|
|
|
if (ioctl(filedes, SNDCTL_DSP_SPEED, &Settings.SoundPlaybackRate) < 0)
|
2010-09-25 17:46:12 +02:00
|
|
|
goto close_fail;
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("OK\n");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
/* OSS requires a power-of-two buffer size, first 16 bits are the number
|
|
|
|
* of fragments to generate, second 16 are the respective power-of-two. */
|
2019-02-07 02:41:33 +01:00
|
|
|
temp = (4 << 16) | (S9xSoundBase2log(output_buffer_size / 4));
|
2017-11-20 19:07:54 +01:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
if (ioctl(filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0)
|
2017-11-20 19:07:54 +01:00
|
|
|
goto close_fail;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
|
2017-11-20 19:07:54 +01:00
|
|
|
|
|
|
|
output_buffer_size = info.fragsize * info.fragstotal;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf(" --> (Buffer size: %d bytes, %dms latency)...",
|
|
|
|
output_buffer_size,
|
|
|
|
(output_buffer_size * 250) / Settings.SoundPlaybackRate);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("OK\n");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
S9xSetSamplesAvailableCallback(oss_samples_available, this);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-12-28 23:32:32 +01:00
|
|
|
return true;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
close_fail:
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
close(filedes);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
fail:
|
2019-02-07 02:41:33 +01:00
|
|
|
printf("failed\n");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-12-28 23:32:32 +01:00
|
|
|
return false;
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
void S9xOSSSoundDriver::samples_available()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
audio_buf_info info;
|
|
|
|
int samples_to_write;
|
|
|
|
int bytes_to_write;
|
|
|
|
int bytes_written;
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
|
2017-11-20 19:07:54 +01:00
|
|
|
|
2017-11-20 20:09:05 +01:00
|
|
|
if (Settings.DynamicRateControl)
|
2017-11-20 19:07:54 +01:00
|
|
|
{
|
2019-02-07 02:41:33 +01:00
|
|
|
S9xUpdateDynamicRate(info.bytes, output_buffer_size);
|
2017-11-20 19:07:54 +01:00
|
|
|
}
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
samples_to_write = S9xGetSampleCount();
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-10 01:48:17 +01:00
|
|
|
if (Settings.DynamicRateControl && !Settings.SoundSync)
|
2017-11-20 19:07:54 +01:00
|
|
|
{
|
|
|
|
// Using rate control, we should always keep the emulator's sound buffers empty to
|
|
|
|
// maintain an accurate measurement.
|
2019-02-06 00:21:23 +01:00
|
|
|
if (samples_to_write > (info.bytes >> 1))
|
2017-11-20 19:07:54 +01:00
|
|
|
{
|
2019-02-07 02:41:33 +01:00
|
|
|
S9xClearSamples();
|
2017-11-20 19:07:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2019-02-10 01:48:17 +01:00
|
|
|
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
|
|
|
|
{
|
|
|
|
while (info.bytes >> 1 < samples_to_write)
|
|
|
|
{
|
2019-02-12 19:00:03 +01:00
|
|
|
int usec_to_sleep = ((samples_to_write >> 1) - (info.bytes >> 2)) * 10000 /
|
|
|
|
(Settings.SoundPlaybackRate / 100);
|
|
|
|
usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
|
2019-02-10 01:48:17 +01:00
|
|
|
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
|
|
|
|
}
|
|
|
|
}
|
2019-02-12 19:00:03 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
samples_to_write = MIN(info.bytes >> 1, samples_to_write) & ~1;
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
if (samples_to_write < 0)
|
|
|
|
return;
|
|
|
|
|
2019-02-06 00:21:23 +01:00
|
|
|
if (sound_buffer_size < samples_to_write * 2)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2019-02-07 02:41:33 +01:00
|
|
|
sound_buffer = (uint8 *)realloc(sound_buffer, samples_to_write * 2);
|
2019-02-06 00:21:23 +01:00
|
|
|
sound_buffer_size = samples_to_write * 2;
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
S9xMixSamples(sound_buffer, samples_to_write);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
bytes_written = 0;
|
2019-02-06 00:21:23 +01:00
|
|
|
bytes_to_write = samples_to_write * 2;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
while (bytes_to_write > bytes_written)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
2019-02-07 02:41:33 +01:00
|
|
|
result = write(filedes,
|
|
|
|
((char *)sound_buffer) + bytes_written,
|
|
|
|
bytes_to_write - bytes_written);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bytes_written += result;
|
|
|
|
}
|
|
|
|
}
|