mirror of
https://github.com/libretro/hatari.git
synced 2026-11-07 10:36:00 -10:00
libretro: drive LEDs and floppy drive sound, from the hatari2014 core
This commit is contained in:
1 parent
a7413b2b32
commit
fac13aeeb8
9 files changed
+524
-6
No files matched your search
@@ -5,6 +5,7 @@ add_library(UiRetro OBJECT
|
||||
audio.c
|
||||
disk_control.c
|
||||
file_retro.c
|
||||
floppy_sound.c
|
||||
harddisk.c
|
||||
joy_ui.c
|
||||
gui_event.c
|
||||
|
||||
@@ -18,6 +18,7 @@ const char Audio_fileid[] = "Hatari audio.c";
|
||||
#include "dmaSnd.h"
|
||||
#include "crossbar.h"
|
||||
#include "video.h"
|
||||
#include "floppy_sound.h"
|
||||
|
||||
|
||||
bool bSoundWorking = false; /* Is sound OK */
|
||||
@@ -80,12 +81,18 @@ void Audio_Unlock(void)
|
||||
{
|
||||
if (AudioMixBuffer_pos_read + nGeneratedSamples <= AUDIOMIXBUFFER_SIZE)
|
||||
{
|
||||
FloppySound_Mix(&AudioMixBuffer[AudioMixBuffer_pos_read][0],
|
||||
nGeneratedSamples, nAudioFrequency);
|
||||
audio_sample_batch_cb(&AudioMixBuffer[AudioMixBuffer_pos_read][0],
|
||||
nGeneratedSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
int samples_at_end = AUDIOMIXBUFFER_SIZE - AudioMixBuffer_pos_read;
|
||||
FloppySound_Mix(&AudioMixBuffer[AudioMixBuffer_pos_read][0],
|
||||
samples_at_end, nAudioFrequency);
|
||||
FloppySound_Mix(&AudioMixBuffer[0][0],
|
||||
nGeneratedSamples - samples_at_end, nAudioFrequency);
|
||||
audio_sample_batch_cb(&AudioMixBuffer[AudioMixBuffer_pos_read][0],
|
||||
samples_at_end);
|
||||
audio_sample_batch_cb(&AudioMixBuffer[0][0],
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
Hatari - floppy_sound.c
|
||||
|
||||
This file is distributed under the GNU General Public License, version 2
|
||||
or at your option any later version. Read the file gpl.txt for details.
|
||||
|
||||
Floppy drive sounds for the libretro core, from the hatari2014 core
|
||||
(libretro/floppy_sound.c there): a click and seek sound when the led of
|
||||
drive A or B comes on, repeated for as long as the floppy controller keeps
|
||||
working on the drive, for both at the same time.
|
||||
|
||||
A sample of one's own can be put in the frontend's system directory as
|
||||
floppy.raw: signed 16-bit little-endian stereo at 44100 Hz, no header
|
||||
(ffmpeg -i click.wav -f s16le -ar 44100 -ac 2 floppy.raw). Without it a
|
||||
synthetic one is used. Both are made to fit the core's audio rate, which
|
||||
can change at run time.
|
||||
*/
|
||||
const char FloppySound_fileid[] = "Hatari floppy_sound.c";
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "main_retro.h"
|
||||
#include "floppy_sound.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define RAW_RATE 44100
|
||||
|
||||
static int16_t *raw_pcm; /* floppy.raw as loaded, at RAW_RATE */
|
||||
static int raw_frames;
|
||||
|
||||
static int16_t *sample; /* what is mixed, at sample_rate */
|
||||
static int sample_frames;
|
||||
static int sample_rate;
|
||||
|
||||
static int play_pos[2] = { -1, -1 };
|
||||
static bool enabled = true;
|
||||
static int volume = 192; /* 0-256 */
|
||||
|
||||
|
||||
/**
|
||||
* The built-in sound, ~55 ms: a low mechanical thud, the stepper motor's
|
||||
* buzz, a descending head seek chirp and a short chassis rattle.
|
||||
*/
|
||||
static int16_t *FloppySound_Synthesize(int rate, int *frames)
|
||||
{
|
||||
const float pi = 3.14159265f;
|
||||
int n = rate * 55 / 1000;
|
||||
int16_t *pcm = malloc(n * 2 * sizeof(*pcm));
|
||||
int i;
|
||||
|
||||
if (!pcm)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
float t = (float)i / (float)rate;
|
||||
float s = 0.0f;
|
||||
|
||||
/* thud: 120 Hz, decays in ~12 ms */
|
||||
s += sinf(2.0f * pi * 120.0f * t) * expf(-t / 0.012f) * 0.55f;
|
||||
|
||||
/* stepper buzz: ~800 Hz with a wobble, 2 ms onset */
|
||||
{
|
||||
float onset = t > 0.002f ? 1.0f : t / 0.002f;
|
||||
float freq = 800.0f + 60.0f * sinf(2.0f * pi * 180.0f * t);
|
||||
s += sinf(2.0f * pi * freq * t) * onset * expf(-t / 0.008f) * 0.30f;
|
||||
}
|
||||
|
||||
/* head seek chirp: 2400 -> 700 Hz over 25 ms */
|
||||
if (t < 0.025f)
|
||||
{
|
||||
float freq = 2400.0f - 1700.0f * (t / 0.025f);
|
||||
s += sinf(2.0f * pi * freq * t) * expf(-t / 0.018f) * 0.20f;
|
||||
}
|
||||
|
||||
/* rattle: products of unrelated frequencies, 5-17 ms */
|
||||
{
|
||||
float t2 = t - 0.005f;
|
||||
if (t2 > 0.0f && t2 < 0.012f)
|
||||
{
|
||||
float noise = sinf(2.0f * pi * 3700.0f * t2)
|
||||
* sinf(2.0f * pi * 2300.0f * t2)
|
||||
* sinf(2.0f * pi * 970.0f * t2);
|
||||
s += noise * expf(-t2 / 0.005f) * 0.18f;
|
||||
}
|
||||
}
|
||||
|
||||
if (s > 1.0f)
|
||||
s = 1.0f;
|
||||
if (s < -1.0f)
|
||||
s = -1.0f;
|
||||
pcm[i * 2] = pcm[i * 2 + 1] = (int16_t)(s * 28000.0f);
|
||||
}
|
||||
|
||||
*frames = n;
|
||||
return pcm;
|
||||
}
|
||||
|
||||
/**
|
||||
* floppy.raw at another rate, linearly interpolated
|
||||
*/
|
||||
static int16_t *FloppySound_Resample(int rate, int *frames)
|
||||
{
|
||||
int n = (int)((int64_t)raw_frames * rate / RAW_RATE);
|
||||
int16_t *pcm;
|
||||
int i, c;
|
||||
|
||||
if (n <= 0 || !(pcm = malloc(n * 2 * sizeof(*pcm))))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
int64_t pos = (int64_t)i * RAW_RATE * 256 / rate;
|
||||
int idx = (int)(pos >> 8), frac = (int)(pos & 255);
|
||||
int next = idx + 1 < raw_frames ? idx + 1 : idx;
|
||||
|
||||
for (c = 0; c < 2; c++)
|
||||
pcm[i * 2 + c] = (int16_t)((raw_pcm[idx * 2 + c] * (256 - frac) +
|
||||
raw_pcm[next * 2 + c] * frac) >> 8);
|
||||
}
|
||||
|
||||
*frames = n;
|
||||
return pcm;
|
||||
}
|
||||
|
||||
static void FloppySound_Prepare(int rate)
|
||||
{
|
||||
free(sample);
|
||||
sample = NULL;
|
||||
sample_frames = 0;
|
||||
sample_rate = rate;
|
||||
|
||||
if (raw_pcm)
|
||||
sample = FloppySound_Resample(rate, &sample_frames);
|
||||
if (!sample)
|
||||
sample = FloppySound_Synthesize(rate, &sample_frames);
|
||||
|
||||
play_pos[0] = play_pos[1] = -1;
|
||||
}
|
||||
|
||||
|
||||
void FloppySound_Init(const char *system_dir)
|
||||
{
|
||||
char path[FILENAME_MAX];
|
||||
VFS_FILE f;
|
||||
int64_t size;
|
||||
|
||||
FloppySound_UnInit();
|
||||
|
||||
if (!system_dir || !system_dir[0])
|
||||
return;
|
||||
|
||||
snprintf(path, sizeof(path), "%s%cfloppy.raw", system_dir, PATHSEP);
|
||||
f = VFS_fopen(path, "rb");
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
size = VFS_fsize(f);
|
||||
if (size >= 4 && size <= 16 * 1024 * 1024 && (raw_pcm = malloc(size)) != NULL)
|
||||
{
|
||||
if (VFS_fread(raw_pcm, 1, size, f) == (size_t)size)
|
||||
{
|
||||
raw_frames = (int)(size / 4);
|
||||
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < raw_frames * 2; i++)
|
||||
{
|
||||
uint16_t v = (uint16_t)raw_pcm[i];
|
||||
raw_pcm[i] = (int16_t)((v >> 8) | (v << 8));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
free(raw_pcm);
|
||||
raw_pcm = NULL;
|
||||
}
|
||||
}
|
||||
VFS_fclose(f);
|
||||
}
|
||||
|
||||
void FloppySound_UnInit(void)
|
||||
{
|
||||
free(raw_pcm);
|
||||
raw_pcm = NULL;
|
||||
raw_frames = 0;
|
||||
free(sample);
|
||||
sample = NULL;
|
||||
sample_frames = 0;
|
||||
sample_rate = 0;
|
||||
play_pos[0] = play_pos[1] = -1;
|
||||
}
|
||||
|
||||
void FloppySound_SetEnabled(bool on)
|
||||
{
|
||||
enabled = on;
|
||||
if (!on)
|
||||
play_pos[0] = play_pos[1] = -1;
|
||||
}
|
||||
|
||||
void FloppySound_SetVolume(int percent)
|
||||
{
|
||||
volume = percent * 256 / 100;
|
||||
}
|
||||
|
||||
void FloppySound_Trigger(int drive)
|
||||
{
|
||||
if (enabled && (drive == 0 || drive == 1))
|
||||
play_pos[drive] = 0;
|
||||
}
|
||||
|
||||
bool FloppySound_Playing(int drive)
|
||||
{
|
||||
return (drive == 0 || drive == 1) && play_pos[drive] >= 0;
|
||||
}
|
||||
|
||||
void FloppySound_Mix(int16_t *buf, int frames, int rate)
|
||||
{
|
||||
int d, i;
|
||||
|
||||
if (!enabled || rate <= 0 || (play_pos[0] < 0 && play_pos[1] < 0))
|
||||
return;
|
||||
|
||||
if (rate != sample_rate)
|
||||
{
|
||||
int pos[2] = { play_pos[0], play_pos[1] };
|
||||
FloppySound_Prepare(rate);
|
||||
play_pos[0] = pos[0] >= 0 ? 0 : -1;
|
||||
play_pos[1] = pos[1] >= 0 ? 0 : -1;
|
||||
}
|
||||
if (!sample)
|
||||
return;
|
||||
|
||||
for (d = 0; d < 2; d++)
|
||||
{
|
||||
if (play_pos[d] < 0)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < frames && play_pos[d] < sample_frames; i++, play_pos[d]++)
|
||||
{
|
||||
int l = buf[i * 2] + ((sample[play_pos[d] * 2] * volume) >> 8);
|
||||
int r = buf[i * 2 + 1] + ((sample[play_pos[d] * 2 + 1] * volume) >> 8);
|
||||
buf[i * 2] = l > 32767 ? 32767 : l < -32768 ? -32768 : l;
|
||||
buf[i * 2 + 1] = r > 32767 ? 32767 : r < -32768 ? -32768 : r;
|
||||
}
|
||||
|
||||
if (play_pos[d] >= sample_frames)
|
||||
play_pos[d] = -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Hatari - floppy_sound.h
|
||||
|
||||
This file is distributed under the GNU General Public License, version 2
|
||||
or at your option any later version. Read the file gpl.txt for details.
|
||||
*/
|
||||
|
||||
#ifndef HATARI_RETRO_FLOPPY_SOUND_H
|
||||
#define HATARI_RETRO_FLOPPY_SOUND_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Load floppy.raw from the system directory, if there is one */
|
||||
void FloppySound_Init(const char *system_dir);
|
||||
void FloppySound_UnInit(void);
|
||||
|
||||
void FloppySound_SetEnabled(bool enabled);
|
||||
void FloppySound_SetVolume(int percent);
|
||||
|
||||
/* Start the drive sound for drive 0 (A) or 1 (B) */
|
||||
void FloppySound_Trigger(int drive);
|
||||
|
||||
/* Whether the sound for drive 0 (A) or 1 (B) is still playing */
|
||||
bool FloppySound_Playing(int drive);
|
||||
|
||||
/* Mix the playing drive sounds into 'frames' stereo frames at 'rate' Hz */
|
||||
void FloppySound_Mix(int16_t *buf, int frames, int rate);
|
||||
|
||||
#endif /* ifndef HATARI_RETRO_FLOPPY_SOUND_H */
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "disk_control.h"
|
||||
#include "file.h"
|
||||
#include "floppy.h"
|
||||
#include "floppy_sound.h"
|
||||
#include "harddisk.h"
|
||||
#include "log.h"
|
||||
#include "memorySnapShot.h"
|
||||
@@ -197,6 +198,7 @@ RETRO_API void retro_init(void)
|
||||
if (environment_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_dir) && system_dir)
|
||||
{
|
||||
retro_system_directory = system_dir;
|
||||
FloppySound_Init(system_dir);
|
||||
|
||||
snprintf(tos_path, sizeof(tos_path), "%s%c%s",
|
||||
system_dir, PATHSEP, "tos.img");
|
||||
@@ -231,6 +233,7 @@ RETRO_API void retro_deinit(void)
|
||||
{
|
||||
scratch_path[0] = '\0';
|
||||
Main_UnInit();
|
||||
FloppySound_UnInit();
|
||||
}
|
||||
|
||||
RETRO_API void retro_get_system_info(struct retro_system_info *info)
|
||||
@@ -332,6 +335,8 @@ RETRO_API void retro_run(void)
|
||||
}
|
||||
|
||||
/* Note: video_refresh_cb is called in Screen_Draw() already! */
|
||||
/* Video callbacks have already submitted the frame and its LEDs. */
|
||||
Statusbar_FrameDone();
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device)
|
||||
|
||||
@@ -5,9 +5,21 @@
|
||||
or at your option any later version. Read the file gpl.txt for details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <libretro.h>
|
||||
|
||||
extern bool has_cpu_config_changed;
|
||||
extern const char *retro_system_directory;
|
||||
extern const char *retro_save_directory;
|
||||
extern retro_environment_t environment_cb;
|
||||
extern retro_video_refresh_t video_refresh_cb;
|
||||
extern retro_input_state_t input_state_cb;
|
||||
|
||||
/* screen.c: cache the drive LED option when runtime options are applied */
|
||||
void Screen_ShowDriveLeds(bool show);
|
||||
|
||||
/* statusbar.c: the drive leds, drawn over the frame */
|
||||
void Statusbar_ShowLeds(bool show);
|
||||
bool Statusbar_LedsVisible(void);
|
||||
void Statusbar_DrawLeds(uint32_t *pixels, int width, int height, int pitch);
|
||||
void Statusbar_FrameDone(void);
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "configuration.h"
|
||||
#include "fdc.h"
|
||||
#include "floppy_sound.h"
|
||||
#include "main_retro.h"
|
||||
#include "options.h"
|
||||
#include "reset.h"
|
||||
@@ -350,6 +351,50 @@ static struct retro_core_option_v2_definition option_defs_us[] = {
|
||||
},
|
||||
"off"
|
||||
},
|
||||
{
|
||||
"hatari_drive_leds",
|
||||
"Drive LEDs",
|
||||
"Drive LEDs",
|
||||
"Show drive activity in the top right corner of the screen: an amber LED per floppy drive, as on an STFM, and a green one for the hard disk.",
|
||||
NULL,
|
||||
"floppy",
|
||||
{
|
||||
{ "enabled", NULL },
|
||||
{ "disabled", NULL },
|
||||
{ NULL, NULL },
|
||||
},
|
||||
"enabled"
|
||||
},
|
||||
{
|
||||
"hatari_floppy_sound",
|
||||
"Floppy Drive Sound",
|
||||
"Floppy Drive Sound",
|
||||
"Play a mechanical floppy drive sound when a drive is accessed. For a sound of your own, put floppy.raw (signed 16-bit stereo at 44100 Hz, no header) in the frontend's system directory.",
|
||||
NULL,
|
||||
"floppy",
|
||||
{
|
||||
{ "enabled", NULL },
|
||||
{ "disabled", NULL },
|
||||
{ NULL, NULL },
|
||||
},
|
||||
"enabled"
|
||||
},
|
||||
{
|
||||
"hatari_floppy_sound_volume",
|
||||
"Floppy Drive Sound Volume",
|
||||
"Floppy Drive Sound Volume",
|
||||
"Volume of the floppy drive sound.",
|
||||
NULL,
|
||||
"floppy",
|
||||
{
|
||||
{ "25", "25%" },
|
||||
{ "50", "50%" },
|
||||
{ "75", "75%" },
|
||||
{ "100", "100%" },
|
||||
{ NULL, NULL },
|
||||
},
|
||||
"75"
|
||||
},
|
||||
{
|
||||
"hatari_drive_a_enable",
|
||||
"Drive A Enabled",
|
||||
@@ -817,6 +862,10 @@ void Core_ApplyRuntimeOptions(void)
|
||||
FDC_Drive_Set_Enable(1, ConfigureParams.DiskImage.EnableDriveB);
|
||||
FDC_Drive_Set_NumberOfHeads(0, ConfigureParams.DiskImage.DriveA_NumberOfHeads);
|
||||
FDC_Drive_Set_NumberOfHeads(1, ConfigureParams.DiskImage.DriveB_NumberOfHeads);
|
||||
|
||||
Screen_ShowDriveLeds(Core_VarBool("hatari_drive_leds", true));
|
||||
FloppySound_SetEnabled(Core_VarBool("hatari_floppy_sound", true));
|
||||
FloppySound_SetVolume(Core_VarInt("hatari_floppy_sound_volume", 75));
|
||||
|
||||
/* --- Atari Screen --- */
|
||||
str = Core_VarStr("hatari_monitor_type", "rgb");
|
||||
|
||||
+51
-6
@@ -47,6 +47,16 @@ bool bInFullScreen = false; /* true if in full screen */
|
||||
|
||||
static uint32_t *framebuffer;
|
||||
static int screen_width, screen_height;
|
||||
static bool drive_leds_enabled = true;
|
||||
static bool drive_leds_changed;
|
||||
|
||||
void Screen_ShowDriveLeds(bool show)
|
||||
{
|
||||
if (drive_leds_enabled != show)
|
||||
drive_leds_changed = true;
|
||||
drive_leds_enabled = show;
|
||||
Statusbar_ShowLeds(show);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -233,6 +243,31 @@ void Screen_UnLock(void)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* draw overlays (like drive LEDs) on top of the ST screen.
|
||||
*/
|
||||
static uint32_t *Core_DrawOverlays(uint32_t *pixels, int width, int height, int pitch)
|
||||
{
|
||||
static uint32_t *overlay;
|
||||
static size_t overlay_size;
|
||||
size_t size = (size_t)pitch * height;
|
||||
|
||||
if (!drive_leds_enabled || !pixels || !Statusbar_LedsVisible())
|
||||
return pixels;
|
||||
|
||||
if (size > overlay_size)
|
||||
{
|
||||
uint32_t *bigger = realloc(overlay, size);
|
||||
if (!bigger)
|
||||
return pixels;
|
||||
overlay = bigger;
|
||||
overlay_size = size;
|
||||
}
|
||||
memcpy(overlay, pixels, size);
|
||||
|
||||
Statusbar_DrawLeds(overlay, width, height, pitch);
|
||||
return overlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw ST screen to window/full-screen
|
||||
@@ -241,15 +276,22 @@ void Screen_UnLock(void)
|
||||
bool Screen_Draw(bool bForceFlip)
|
||||
{
|
||||
bool screen_changed;
|
||||
int width, height, pitch;
|
||||
uint32_t *pixels;
|
||||
|
||||
/* And draw (if screen contents changed) */
|
||||
screen_changed = ConvST_DrawFrame();
|
||||
/* Clear the last overlay once when LEDs are disabled at runtime. */
|
||||
if (!screen_changed && !bForceFlip && !drive_leds_enabled && !drive_leds_changed)
|
||||
return false;
|
||||
|
||||
if (screen_changed)
|
||||
{
|
||||
video_refresh_cb(framebuffer, screen_width, screen_height,
|
||||
screen_width * 4);
|
||||
}
|
||||
Screen_GetDimension(&pixels, &width, &height, &pitch);
|
||||
pixels = Core_DrawOverlays(pixels, width, height, pitch);
|
||||
|
||||
/* LEDs can change even when the emulated screen has not changed.
|
||||
* Submit the clean frame too, so an LED that goes off is erased. */
|
||||
video_refresh_cb(pixels, width, height, pitch);
|
||||
drive_leds_changed = false;
|
||||
|
||||
return screen_changed;
|
||||
}
|
||||
@@ -257,8 +299,11 @@ bool Screen_Draw(bool bForceFlip)
|
||||
|
||||
void Screen_GenConvUpdate(bool update_statusbar)
|
||||
{
|
||||
video_refresh_cb(framebuffer, screen_width, screen_height,
|
||||
uint32_t *pixels = Core_DrawOverlays(framebuffer, screen_width,
|
||||
screen_height, screen_width * 4);
|
||||
video_refresh_cb(pixels, screen_width, screen_height,
|
||||
screen_width * 4);
|
||||
drive_leds_changed = false;
|
||||
}
|
||||
|
||||
uint32_t Screen_GetGenConvWidth(void)
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
or at your option any later version. Read the file gpl.txt for details.
|
||||
|
||||
Code to draw statusbar area, floppy leds etc.
|
||||
|
||||
The libretro core has no statusbar area: the drive LEDs are drawn over the
|
||||
top right corner of the frame instead, a small amber bar per floppy drive
|
||||
like the one on an STFM, and a green one for the hard disk (from the
|
||||
hatari2014 core). A floppy LED going on also starts the drive sound.
|
||||
*/
|
||||
const char Statusbar_fileid[] = "Hatari statusbar.c";
|
||||
|
||||
@@ -23,8 +28,17 @@ const char Statusbar_fileid[] = "Hatari statusbar.c";
|
||||
#include "blitter.h"
|
||||
#include "str.h"
|
||||
#include "lilo.h"
|
||||
#include "floppy_sound.h"
|
||||
#include "main_retro.h"
|
||||
|
||||
|
||||
#define HD_LED_FRAMES 10 /* frames the HD led stays on */
|
||||
|
||||
static bool leds_shown = true;
|
||||
static drive_led_t floppy_led[2];
|
||||
static bool floppy_busy[2]; /* the controller has worked on the drive this frame */
|
||||
static int hd_led_frames;
|
||||
|
||||
/**
|
||||
* Return statusbar height for given width and height
|
||||
*/
|
||||
@@ -59,6 +73,8 @@ int Statusbar_GetHeight(void)
|
||||
*/
|
||||
void Statusbar_EnableHDLed(drive_led_t state)
|
||||
{
|
||||
if (state != LED_STATE_OFF)
|
||||
hd_led_frames = HD_LED_FRAMES;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,8 +84,105 @@ void Statusbar_EnableHDLed(drive_led_t state)
|
||||
void Statusbar_SetFloppyLed(drive_index_t drive, drive_led_t state)
|
||||
{
|
||||
assert(drive == DRIVE_LED_A || drive == DRIVE_LED_B);
|
||||
|
||||
if (state == LED_STATE_ON_BUSY)
|
||||
floppy_busy[drive] = true;
|
||||
floppy_led[drive] = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide the drive leds drawn over the frame
|
||||
*/
|
||||
void Statusbar_ShowLeds(bool show)
|
||||
{
|
||||
leds_shown = show;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether any drive led is to be drawn over this frame
|
||||
*/
|
||||
bool Statusbar_LedsVisible(void)
|
||||
{
|
||||
return leds_shown && (floppy_led[0] != LED_STATE_OFF ||
|
||||
floppy_led[1] != LED_STATE_OFF || hd_led_frames > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Once per retro_run: what times the HD led out, and what starts the floppy
|
||||
* sound. Drive leds are looked at once a frame, as the hatari2014 core did:
|
||||
* the sound starts when a led has come on since the last frame, and plays
|
||||
* again each time it has finished for as long as the controller has worked
|
||||
* on the drive during the frame - so a load sounds for as long as it lasts,
|
||||
* and the sample is never cut off to start it over. TOS selecting drive A
|
||||
* and B in turn every eight VBLs to check for a disk change is over within
|
||||
* the frame and carries out no command, so it is not heard.
|
||||
*/
|
||||
void Statusbar_FrameDone(void)
|
||||
{
|
||||
static bool was_on[2];
|
||||
int d;
|
||||
|
||||
for (d = 0; d < 2; d++)
|
||||
{
|
||||
bool on = floppy_led[d] != LED_STATE_OFF;
|
||||
bool busy = floppy_busy[d] || floppy_led[d] == LED_STATE_ON_BUSY;
|
||||
if ((on && !was_on[d]) || (busy && !FloppySound_Playing(d)))
|
||||
FloppySound_Trigger(d);
|
||||
was_on[d] = on;
|
||||
floppy_busy[d] = false;
|
||||
}
|
||||
|
||||
if (hd_led_frames > 0)
|
||||
hd_led_frames--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the drive leds into a copy of the frame about to be handed to the
|
||||
* frontend
|
||||
*/
|
||||
void Statusbar_DrawLeds(uint32_t *pixels, int width, int height, int pitch)
|
||||
{
|
||||
static const uint32_t floppy_color = 0xF8D800; /* STFM amber */
|
||||
static const uint32_t hd_color = 0x30E030;
|
||||
bool on[3];
|
||||
int led_w, led_h, gap, right, top, i, x, y;
|
||||
int stride = pitch / 4;
|
||||
|
||||
if (!Statusbar_LedsVisible() || !pixels)
|
||||
return;
|
||||
|
||||
on[0] = floppy_led[0] != LED_STATE_OFF;
|
||||
on[1] = floppy_led[1] != LED_STATE_OFF;
|
||||
on[2] = hd_led_frames > 0;
|
||||
|
||||
/* The hatari2014 core's 13x5 bar in a 384 pixel wide frame, scaled */
|
||||
led_w = width * 13 / 384;
|
||||
led_h = height * 5 / 276;
|
||||
if (led_w < 7)
|
||||
led_w = 7;
|
||||
if (led_w > 64)
|
||||
led_w = 64;
|
||||
if (led_h < 3)
|
||||
led_h = 3;
|
||||
if (led_h > 16)
|
||||
led_h = 16;
|
||||
gap = led_w / 2;
|
||||
right = width - led_w / 2;
|
||||
top = led_h;
|
||||
if (right - 3 * led_w - 2 * gap < 0 || top + led_h > height)
|
||||
return;
|
||||
|
||||
/* A rightmost, then B, then the hard disk */
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
int left = right - (i + 1) * led_w - i * gap;
|
||||
if (!on[i])
|
||||
continue;
|
||||
for (y = top; y < top + led_h; y++)
|
||||
for (x = left; x < left + led_w; x++)
|
||||
pixels[y * stride + x] = i < 2 ? floppy_color : hd_color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set TOS etc information and initial help message
|
||||
|
||||
Reference in new issue
Block a user