mirror of
https://github.com/libretro/libretro-samples.git
synced 2025-04-02 10:31:48 -04:00
331 lines
7.4 KiB
C
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) {}
|