Skip to content

Commit

Permalink
Merge branch 'master' into libretro-analog-to-mouse
Browse files Browse the repository at this point in the history
  • Loading branch information
jtothebell committed Mar 7, 2023
2 parents b8993c2 + ea145dc commit e9fe530
Show file tree
Hide file tree
Showing 20 changed files with 628 additions and 179 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export APP_AUTHOR = jtothebell
export V_MAJOR = 0
export V_MINOR = 0
export V_PATCH = 2
export V_BUILD = 19
export V_BUILD = 20
export APP_VERSION = v$(V_MAJOR).$(V_MINOR).$(V_PATCH).$(V_BUILD)


Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Launch FAKE-08 either via the homebrew menu or normal system UI (depending on ho

For bittboy and similar consoles, back up `emus/pico8/pico8.elf` and replace it with the one from the release. Place your cart files in `roms/pico-8/` and use the front end of choice to launch games. Press the menu button to return to the menu (though you can also press start and exit to the FAKE-08 bios menu if you would like).

To launch on a Miyoo Mini you need to call the executable directly from the terminal emulator, or create a shortcut. I'm not sure exactly how that works and don't have a device to figure it out, so help is welcome if anyone would like to provide guidance there.
To launch on a Miyoo Mini you need to call the executable directly from the terminal emulator, or create a shortcut. I'm not sure exactly how that works and don't have a device to figure it out, so help is welcome if anyone would like to provide guidance there. If you are using onion OS just copy FAKE08 to the Emu/PICO directory, overwriting the existing build (make a backup first just in case).

Libretro cores are still in early development but should be working. Installation can be tricky however. In general you'll need to install the core (Load Core -> Install or Restore a Core -> {Navigate to core file location}) and also drop the fake08_libretro.info into the retroarch cores directory. This directory may be hidden and its location varies from platform to platform. Once that file is in place, you should be able to scan for Pico-8 carts using "Scan Directory." If that doesn't work for some reason, you can do a "Manual Scan." If doing a manual scan, choose the directory, set the custom system name to "Pico-8", set the default core to the one you just installed, and file extensions to "p8 png" and it should add a playlist of your Pico-8 carts associated with the newly installed core.

Expand Down
2 changes: 1 addition & 1 deletion libs/z8lua
Submodule z8lua updated 1 files
+20 −0 lpico8lib.c
16 changes: 12 additions & 4 deletions platform/libretro/libretro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ EXPORT void retro_get_system_info(struct retro_system_info *info)
{
memset(info, 0, sizeof(*info));
info->library_name = "fake-08";
info->library_version = "0.0.2.19"; //todo: get from build flags
info->library_version = "0.0.2.20"; //todo: get from build flags
info->valid_extensions = "p8|png";
info->need_fullpath = true; // we load our own carts for now
info->need_fullpath = false;
}

EXPORT void retro_get_system_av_info(struct retro_system_av_info *info)
Expand All @@ -161,7 +161,7 @@ EXPORT void retro_get_system_av_info(struct retro_system_av_info *info)
info->geometry.max_width = PicoScreenWidth;
info->geometry.max_height = PicoScreenHeight;
info->geometry.aspect_ratio = 1.f;
info->timing.fps = 60.f; //todo: update this to 60, then handle 30 at callback level?
info->timing.fps = 60.f;
info->timing.sample_rate = 22050.f;

retro_pixel_format pf = RETRO_PIXEL_FORMAT_RGB565;
Expand Down Expand Up @@ -505,7 +505,15 @@ EXPORT bool retro_load_game(struct retro_game_info const *info)
setCartDirectory(containingDir);
}

_vm->QueueCartChange(info->path);
if (info->size > 0) {
const unsigned char* data = reinterpret_cast<const unsigned char*>(info->data);
_vm->QueueCartChange(data, info->size);

}
else {
_vm->QueueCartChange(info->path);
}

return true;
}

Expand Down
213 changes: 133 additions & 80 deletions source/Audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ static float key_to_freq(float key)
return 440.f * exp2((key - 33.f) / 12.f);
}

const float C2_FREQ = key_to_freq(24);

int16_t Audio::getCurrentSfxId(int channel){
return _audioState._sfxChannels[channel].sfxId;
}
Expand All @@ -287,109 +289,77 @@ int16_t Audio::getMusicTickCount(){
return _audioState._musicChannel.offset * _audioState._musicChannel.speed;
}

//adapted from zepto8 sfx.cpp (wtfpl license)
int16_t Audio::getSampleForChannel(int channelId){
using std::fabs;
using std::fmod;
using std::floor;
float Audio::getSampleForSfx(rawSfxChannel &channel, float freqShift) {
using std::max;

int const samples_per_second = 22050;

int16_t sample = 0;

const int index = _audioState._sfxChannels[channelId].sfxId;

sfxChannel *channel = &_audioState._sfxChannels[channelId];

// Advance music using the master channel
if (channelId == _audioState._musicChannel.master && _audioState._musicChannel.pattern != -1)
{
float const offset_per_second = 22050.f / (183.f * _audioState._musicChannel.speed);
float const offset_per_sample = offset_per_second / samples_per_second;
_audioState._musicChannel.offset += offset_per_sample;
_audioState._musicChannel.volume += _audioState._musicChannel.volume_step / samples_per_second;
_audioState._musicChannel.volume = clamp(_audioState._musicChannel.volume, 0.f, 1.f);

if (_audioState._musicChannel.volume_step < 0 && _audioState._musicChannel.volume <= 0)
{
// Fade out is finished, stop playing the current song
for (int i = 0; i < 4; ++i) {
if (_audioState._sfxChannels[i].is_music) {
_audioState._sfxChannels[i].sfxId = -1;
}
}
_audioState._musicChannel.pattern = -1;
}
else if (_audioState._musicChannel.offset >= _audioState._musicChannel.length)
{
int16_t next_pattern = _audioState._musicChannel.pattern + 1;
int16_t next_count = _audioState._musicChannel.count + 1;
//todo: pull out these flags, get memory storage correct as well
if (_memory->songs[_audioState._musicChannel.pattern].getStop()) //stop part of the loop flag
{
next_pattern = -1;
next_count = _audioState._musicChannel.count;
}
else if (_memory->songs[_audioState._musicChannel.pattern].getLoop()){
while (--next_pattern > 0 && !_memory->songs[next_pattern].getStart())
;
}

_audioState._musicChannel.count = next_count;
set_music_pattern(next_pattern);
}
}

if (index < 0 || index > 63) {
if (channel.sfxId < 0 || channel.sfxId > 63) {
//no (valid) sfx here. return silence
return 0;
}

struct sfx const &sfx = _memory->sfx[index];
struct sfx const &sfx = _memory->sfx[channel.sfxId];

// Speed must be 1—255 otherwise the SFX is invalid
int const speed = max(1, (int)sfx.speed);

float const offset = channel->offset;

// PICO-8 exports instruments as 22050 Hz WAV files with 183 samples
// per speed unit per note, so this is how much we should advance
float const offset_per_second = 22050.f / (183.f * speed);
float const offset_per_sample = offset_per_second / samples_per_second;
float next_offset = offset + offset_per_sample;
float next_offset = channel.offset + offset_per_sample;

// Handle SFX loops. From the documentation: “Looping is turned
// off when the start index >= end index”.
float const loop_range = float(sfx.loopRangeEnd - sfx.loopRangeStart);
if (loop_range > 0.f && next_offset >= sfx.loopRangeStart && channel->can_loop) {
if (loop_range > 0.f && next_offset >= sfx.loopRangeStart && channel.can_loop) {
next_offset = fmod(next_offset - sfx.loopRangeStart, loop_range)
+ sfx.loopRangeStart;
}

int const note_idx = (int)floor(offset);
channel->current_note.n=sfx.notes[note_idx];
int const note_idx = (int)floor(channel.offset);
channel.current_note.n=sfx.notes[note_idx];
int const next_note_idx = (int)floor(next_offset);

uint8_t key = sfx.notes[note_idx].getKey();
float volume = sfx.notes[note_idx].getVolume() / 7.f;


/*
if (volume == 0.f){
//volume all the way off. return silence, but make sure to set stuff
channel.offset = next_offset;
if (next_offset >= 32.f){
channel.sfxId = -1;
if (channel.getChildChannel()) {
channel.getChildChannel()->sfxId = -1;
}
}
else if (next_note_idx != note_idx){
channel.prev_note = sfx.notes[note_idx];
}
return 0;
}
*/

// tiniest fade in/out to fix popping
// the real version uses a crossfade it looks like
// 25 samples was estimated from looking at pcm out from pico-8
float const fade_duration = offset_per_sample * 25;
float offset_part = fmod(channel->offset, 1.f);
float offset_part = fmod(channel.offset, 1.f);
float crossfade = 0;
if (offset_part < fade_duration) {
crossfade = (fade_duration-offset_part)/fade_duration;
}

float waveform = this->getSampleForNote(channel->current_note, *channel, channel->prev_note.n, false);

bool custom = (bool) sfx.notes[note_idx].getCustom() && channel.getChildChannel() != NULL;
// it seems we're not allowed to play custom instruments
// recursively inside a custom instrument.
float waveform = this->getSampleForNote(channel.current_note, channel, channel.getChildChannel(), channel.prev_note.n, freqShift, false);
if (crossfade > 0) {
waveform *= (1.0f-crossfade);
note dummyNote;
waveform+= crossfade * this->getSampleForNote(channel->prev_note, *channel, dummyNote, true);
waveform+= crossfade * this->getSampleForNote(channel.prev_note, channel, channel.getPrevChildChannel(), dummyNote, freqShift, true);
}
uint8_t len = sfx.loopRangeEnd == 0 ? 32 : sfx.loopRangeEnd;
bool lastNote = note_idx == len - 1;
Expand All @@ -399,31 +369,37 @@ int16_t Audio::getSampleForChannel(int channelId){

// Apply master music volume from fade in/out
// FIXME: check whether this should be done after distortion
if (channel->is_music) {
if (channel.is_music) {
waveform *= _audioState._musicChannel.volume;
}

channel->offset = next_offset;
channel.offset = next_offset;

if (next_offset >= 32.f){
channel->sfxId = -1;
channel.sfxId = -1;
if (custom) {
channel.getChildChannel()->sfxId = -1;
}
}
else if (next_note_idx != note_idx){
channel->prev_note = channel->current_note; //sfx.notes[note_idx].getKey();
channel->current_note.n = sfx.notes[next_note_idx];
channel->current_note.phi = channel->prev_note.phi;
channel.prev_note = channel.current_note; //sfx.notes[note_idx].getKey();
channel.current_note.n = sfx.notes[next_note_idx];
channel.current_note.phi = channel.prev_note.phi;
if (custom) {
if (!sfx.notes[next_note_idx].getCustom() ||
sfx.notes[next_note_idx].getKey() != sfx.notes[note_idx].getKey() ||
sfx.notes[next_note_idx].getWaveform() != sfx.notes[note_idx].getWaveform()
) {
channel.rotateChannels();
channel.getChildChannel()->sfxId = -1;
}
}
}
return waveform;

sample = (int16_t)(32767.99f * waveform);

// TODO: Apply hardware effects
if (_memory->hwState.distort & (1 << channelId)) {
sample = sample / 0x1000 * 0x1249;
}
return sample;
}

float Audio::getSampleForNote(noteChannel &channel, sfxChannel &parentChannel, note prev_note, bool forceRemainder) {
float Audio::getSampleForNote(noteChannel &channel, rawSfxChannel &parentChannel, rawSfxChannel *childChannel, note prev_note, float freqShift, bool forceRemainder) {
using std::max;
float offset = parentChannel.offset;
int const samples_per_second = 22050;
Expand Down Expand Up @@ -490,9 +466,86 @@ float Audio::getSampleForNote(noteChannel &channel, sfxChannel &parentChannel,
break;
}
}
freq*=freqShift;

bool custom = (bool) channel.n.getCustom() && childChannel != NULL;
float waveform;
waveform = volume * z8::synth::waveform(channel.n.getWaveform(), channel.phi);
if (custom) {
if (childChannel->sfxId == -1) {
// initialize child channel
childChannel->sfxId = channel.n.getWaveform();
childChannel->offset = 0;
childChannel->current_note.phi = 0;
childChannel->can_loop = true;
// don't want to double lower volume for music subchannel
childChannel->is_music = false;
childChannel->prev_note.n.setKey(0);
childChannel->prev_note.n.setVolume(0);
}
waveform = volume * this->getSampleForSfx(*childChannel, freq/C2_FREQ);
} else {
waveform = volume * z8::synth::waveform(channel.n.getWaveform(), channel.phi);
}
channel.phi = channel.phi + freq / samples_per_second;
return waveform;
}


//adapted from zepto8 sfx.cpp (wtfpl license)
int16_t Audio::getSampleForChannel(int channel){
using std::fabs;
using std::fmod;
using std::floor;
using std::max;

int const samples_per_second = 22050;

int16_t sample = 0;

// Advance music using the master channel
if (channel == _audioState._musicChannel.master && _audioState._musicChannel.pattern != -1)
{
float const offset_per_second = 22050.f / (183.f * _audioState._musicChannel.speed);
float const offset_per_sample = offset_per_second / samples_per_second;
_audioState._musicChannel.offset += offset_per_sample;
_audioState._musicChannel.volume += _audioState._musicChannel.volume_step / samples_per_second;
_audioState._musicChannel.volume = clamp(_audioState._musicChannel.volume, 0.f, 1.f);

if (_audioState._musicChannel.volume_step < 0 && _audioState._musicChannel.volume <= 0)
{
// Fade out is finished, stop playing the current song
for (int i = 0; i < 4; ++i) {
if (_audioState._sfxChannels[i].is_music) {
_audioState._sfxChannels[i].sfxId = -1;
}
}
_audioState._musicChannel.pattern = -1;
}
else if (_audioState._musicChannel.offset >= _audioState._musicChannel.length)
{
int16_t next_pattern = _audioState._musicChannel.pattern + 1;
int16_t next_count = _audioState._musicChannel.count + 1;
//todo: pull out these flags, get memory storage correct as well
if (_memory->songs[_audioState._musicChannel.pattern].getStop()) //stop part of the loop flag
{
next_pattern = -1;
next_count = _audioState._musicChannel.count;
}
else if (_memory->songs[_audioState._musicChannel.pattern].getLoop()){
while (--next_pattern > 0 && !_memory->songs[next_pattern].getStart())
;
}

_audioState._musicChannel.count = next_count;
set_music_pattern(next_pattern);
}
}

sample = (int16_t) (32767.99f * this->getSampleForSfx(_audioState._sfxChannels[channel]));

// TODO: Apply hardware effects
if (_memory->hwState.distort & (1 << channel)) {
sample = sample / 0x1000 * 0x1249;
}
return sample;
}
3 changes: 2 additions & 1 deletion source/Audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ class Audio {
void set_music_pattern(int pattern);

public:
float getSampleForSfx(rawSfxChannel &channel, float freqShift = 1.0f);
int16_t getSampleForChannel(int channel);
float getSampleForNote(noteChannel &channel, sfxChannel &parentChannel, note prev_note, bool forceRemainder);
float getSampleForNote(noteChannel &note_channel, rawSfxChannel &parentChannel, rawSfxChannel *childChannel, note prev_note, float freqShift, bool forceRemainder);

public:
Audio(PicoRam* memory);
Expand Down
Loading

0 comments on commit e9fe530

Please sign in to comment.