libretro-samples/audio/audio_playback_wav/libretro-test.c
twinaphex 0b70a1bde2 Audio callback function is not thread-safe, stub out audio
callback (but still doesn't work with WAV file anyway)
2016-12-11 10:23:56 +01:00

331 lines
7.4 KiB
C

#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include "libretro.h"
/* NOTE: This core does not work on big endian systems. */
#define BUFSIZE 128
#define AMP_MUL 64
retro_environment_t environ_cb = NULL;
retro_video_refresh_t video_cb = NULL;
retro_audio_sample_t audio_cb = NULL;
retro_audio_sample_batch_t audio_batch_cb = NULL;
retro_input_poll_t poller_cb = NULL;
retro_input_state_t input_state_cb = NULL;
#ifndef EXTERNC
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
#endif
#ifndef EXPORT
#if defined(CPPCLI)
#define EXPORT EXTERNC
#elif defined(_WIN32)
#define EXPORT EXTERNC __declspec(dllexport)
#else
#define EXPORT EXTERNC __attribute__((visibility("default")))
#endif
#endif
struct WAVhead
{
char ChunkID[4];
uint32_t ChunkSize;
char Format[4];
char Subchunk1ID[4];
uint32_t Subchunk1Size;
uint16_t AudioFormat;
uint16_t NumChannels;
uint32_t SampleRate;
uint32_t ByteRate;
uint16_t BlockAlign;
uint16_t BitsPerSample;
char Subchunk2ID[4];
uint32_t Subchunk2Size;
} head;
void* rawsamples = NULL;
unsigned int sample_pos = 0;
unsigned int samples_tot = 0;
unsigned int g_samples_to_play = 0;
unsigned int sample_rate = 0;
unsigned int bytes_per_sample = 0;
enum
{
ST_OFF = 0,
ST_ON,
ST_AUTO
} state;
static void emit_audio(void)
{
unsigned int samples_to_play;
unsigned int samples_played = 0;
if (state == ST_OFF)
return;
if (state == ST_ON)
samples_to_play=BUFSIZE;
if (state == ST_AUTO)
samples_to_play=g_samples_to_play;
/* no locking here, despite threading;
* if we touch this variable, threading is off. */
while (samples_to_play >= BUFSIZE)
{
unsigned int played;
int16_t samples[2*BUFSIZE];
unsigned int samples_to_read = samples_to_play;
if (samples_to_read > BUFSIZE)
samples_to_read = BUFSIZE;
if (sample_pos > samples_tot)
sample_pos = samples_tot;
if (sample_pos + samples_to_read > samples_tot)
samples_to_read = samples_tot-sample_pos;
if (samples_to_read != 0)
{
unsigned i;
uint8_t* rawsamples8 = (uint8_t*)rawsamples + bytes_per_sample * sample_pos;
int16_t* rawsamples16 = (int16_t*)rawsamples8;
for (i = 0; i < samples_to_read; i++)
{
int16_t left = 0;
int16_t right = 0;
if (head.NumChannels == 1 && head.BitsPerSample==8)
{
left = rawsamples8[i] * AMP_MUL;
right = rawsamples8[i] * AMP_MUL;
}
if (head.NumChannels == 2 && head.BitsPerSample==8)
{
left = rawsamples8[i*2] * AMP_MUL;
right = rawsamples8[i*2+1]*AMP_MUL;
}
if (head.NumChannels==1 && head.BitsPerSample==16)
{
left = rawsamples16[i];
right = rawsamples16[i];
}
if (head.NumChannels==2 && head.BitsPerSample==16)
{
left = rawsamples16[i*2];
right = rawsamples16[i*2+1];
}
samples[i*2+0] = left;
samples[i*2+1] = right;
}
}
if (samples_to_read!=BUFSIZE)
memset(samples + samples_to_read * 2,
0,
sizeof(int16_t) * 2 * (BUFSIZE-samples_to_read));
played = audio_batch_cb(samples, BUFSIZE);
sample_pos += played;
samples_played += played;
if (samples_to_play < played)
break;
samples_to_play -= played;
if (played != BUFSIZE)
break;
}
if (state == ST_AUTO)
g_samples_to_play-=samples_played;
}
static void enable_audio(bool enabled)
{
state = enabled;
}
EXPORT void retro_set_video_refresh(retro_video_refresh_t cb)
{
video_cb = cb;
}
EXPORT void retro_set_audio_sample(retro_audio_sample_t cb)
{
audio_cb = cb;
}
EXPORT void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
{
audio_batch_cb = cb;
}
EXPORT void retro_set_input_poll(retro_input_poll_t cb)
{
poller_cb = cb;
}
EXPORT void retro_set_input_state(retro_input_state_t cb)
{
input_state_cb = cb;
}
EXPORT void retro_set_environment(retro_environment_t cb)
{
struct retro_audio_callback aud = { emit_audio, enable_audio };
environ_cb = cb;
state = ST_AUTO;
#if 0
if (environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK, &aud))
state = ST_OFF;
#endif
}
EXPORT void retro_deinit(void) {}
EXPORT unsigned retro_api_version(void)
{
return RETRO_API_VERSION;
}
EXPORT void retro_init(void)
{
rawsamples = NULL;
}
EXPORT void retro_get_system_info(struct retro_system_info* info)
{
const struct retro_system_info myinfo={ "WAV player", "v1", "wav", false, false };
memcpy(info, &myinfo, sizeof(myinfo));
}
EXPORT void retro_get_system_av_info(struct retro_system_av_info* info)
{
const struct retro_system_av_info myinfo={
{ 320, 240, 320, 240, 0.0 },
{ 60.0, head.SampleRate }
};
memcpy(info, &myinfo, sizeof(myinfo));
}
EXPORT void retro_reset(void)
{
sample_pos=0; g_samples_to_play=0;
}
EXPORT void retro_run(void)
{
static uint16_t pixels[240][320];
unsigned int x;
poller_cb();
if (state == ST_AUTO)
{
g_samples_to_play += head.SampleRate / 60.0;
emit_audio();
}
memset(pixels, 0xFF, sizeof(pixels));
x = 320 * sample_pos / samples_tot;
if (x<320)
{
unsigned y;
for (y = 0; y < 240; y++)
pixels[y][x] = 0x0000;
}
video_cb(pixels, 320, 240, sizeof(uint16_t) * 320);
}
EXPORT size_t retro_serialize_size(void)
{
return sizeof(sample_pos);
}
EXPORT bool retro_serialize(void* data, size_t size)
{
memcpy(data, &sample_pos, sizeof(sample_pos));
return true;
}
EXPORT bool retro_unserialize(const void* data, size_t size)
{
memcpy(&sample_pos, data, sizeof(sample_pos));
return true;
}
EXPORT bool retro_load_game(const struct retro_game_info* game)
{
enum retro_pixel_format rgb565 = RETRO_PIXEL_FORMAT_RGB565;
sample_pos=0;
g_samples_to_play=0;
if (game->size < 44)
return false;
memcpy(&head, game->data, 44);
if (game->size != 44 + head.Subchunk2Size)
return false;
if (head.NumChannels != 1 && head.NumChannels != 2)
return false;
if (head.BitsPerSample != 8 && head.BitsPerSample != 16)
return false;
bytes_per_sample = head.NumChannels * head.BitsPerSample / 8;
samples_tot = head.Subchunk2Size / bytes_per_sample;
rawsamples = malloc(head.Subchunk2Size);
memcpy(rawsamples, (uint8_t*)game->data + 44, game->size - 44);
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565))
return false;
return true;
}
EXPORT bool retro_load_game_special(unsigned game_type,
const struct retro_game_info* info, size_t num_info)
{
return false;
}
EXPORT void retro_unload_game(void)
{
free(rawsamples);
}
EXPORT unsigned retro_get_region(void)
{
return RETRO_REGION_NTSC;
}
EXPORT void* retro_get_memory_data(unsigned id) { return NULL; }
EXPORT size_t retro_get_memory_size(unsigned id) { return 0; }
EXPORT void retro_cheat_reset(void) {}
EXPORT void retro_cheat_set(unsigned index, bool enabled, const char* code) {}
EXPORT void retro_set_controller_port_device(unsigned port, unsigned device) {}