summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2026-07-06 16:36:57 +0900
committerIan C <ianc@noddybox.co.uk>2026-07-06 16:36:57 +0900
commit77399aa2b0e70c7712b1720300b202a9fdced1e3 (patch)
tree10b8427774534acc12f916395c1b3014d7aff942
parentc527acfc79f3bda8cfe13a7b51b0b16a32683960 (diff)
Sound now working
-rw-r--r--src/audio.c6
-rw-r--r--src/audio.h10
-rw-r--r--src/main.c2
-rw-r--r--src/spec.c168
-rw-r--r--src/spec.h4
5 files changed, 111 insertions, 79 deletions
diff --git a/src/audio.c b/src/audio.c
index 7eef433..e55520b 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -37,11 +37,11 @@ static SDL_AudioDeviceID device;
/* ---------------------------------------- INTERFACES
*/
-int AUDIOInit(void)
+int AUDIOInit(int frequency)
{
SDL_AudioSpec spec = {0};
- spec.freq = SAMPLE_RATE;
+ spec.freq = frequency;
spec.format = AUDIO_S8;
spec.channels = 1;
spec.samples = 4096;
@@ -56,7 +56,7 @@ int AUDIOInit(void)
return device != 0;
}
-void AUDIOQueue(Uint8 *buffer, size_t len)
+void AUDIOQueue(Sint8 *buffer, size_t len)
{
if (device != 0)
{
diff --git a/src/audio.h b/src/audio.h
index b799abd..77fd67b 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -30,10 +30,6 @@
#include <stdlib.h>
#include <SDL.h>
-/* The sample rate
-*/
-#define SAMPLE_RATE 48000
-
/* ---------------------------------------- INTERFACES
*/
@@ -41,14 +37,14 @@
audio. All other interfaces will silently do nothing if the device
can't be opened.
*/
-int AUDIOInit(void);
+int AUDIOInit(int frequency);
-/* Add the passed buffer to the sound queue. It is an unsigned 8-bit mono
+/* Add the passed buffer to the sound queue. It is a signed 8-bit mono
sample which will be converted as appropriate by the audio interface.
Well turns out SDL does that for you.
*/
-void AUDIOQueue(Uint8 *buffer, size_t len);
+void AUDIOQueue(Sint8 *buffer, size_t len);
#endif
diff --git a/src/main.c b/src/main.c
index 1766cd1..930a087 100644
--- a/src/main.c
+++ b/src/main.c
@@ -90,7 +90,7 @@ int main(int argc, char *argv[])
if (IConfig(CONF_SOUND))
{
- if (!AUDIOInit())
+ if (!AUDIOInit(SPECAudioFrequency()))
{
fprintf(stderr, "warning: couldn't initialise audio\n");
}
diff --git a/src/spec.c b/src/spec.c
index 6393ef2..d1d7fb2 100644
--- a/src/spec.c
+++ b/src/spec.c
@@ -206,13 +206,23 @@ static const MatrixMap keymap[]=
{SDLK_UNKNOWN, 0,0,0,0},
};
-static size_t sample_buff_sz;
-static double cycles_per_sample;
+/* Timers
+*/
+#define SCANLINE_TIMER eZ80_Timer_1
+#define AUDIO_TIMER eZ80_Timer_2
+
+/* Sound emulation
+*/
+#define AUDIO_FRAMES 2
+#define AUDIO_SCALE 6 /* This is used as a shift value */
+#define AUDIO_CYCLES (AUDIO_FRAMES * FRAME_CYCLES)
+
+static size_t audio_sample_sz;
typedef struct SoundRecord
{
Z80Val cycle;
- Uint8 level;
+ Sint8 level;
struct SoundRecord *next;
} SoundRecord;
@@ -220,7 +230,7 @@ static struct
{
SoundRecord *head;
SoundRecord *tail;
- Uint8 last_level;
+ Sint8 last_level;
} sound;
/* ---------------------------------------- DEBUG FUNCTIONS
@@ -271,7 +281,7 @@ static void ClearKeys(void)
}
}
-static void AddSoundRecord(Z80Val cycle, Uint8 level)
+static void AddSoundRecord(Z80Val cycle, Sint8 level)
{
SoundRecord *new = Malloc(sizeof *new);
@@ -496,14 +506,76 @@ static int EDCallback(Z80 *z80, Z80Val data)
return TRUE;
}
+static void CheckSound(Z80 *z80)
+{
+ if (IConfig(CONF_SOUND))
+ {
+ if (Z80GetTimer(z80, AUDIO_TIMER) > AUDIO_CYCLES)
+ {
+ static Sint8 *sample;
+ SoundRecord *current = sound.head;
+ int offset = 0;
+
+ sample = Malloc(audio_sample_sz);
-static int CheckTimers(Z80 *z80, Z80Val val)
+ memset(sample, 0, audio_sample_sz);
+
+ if (current)
+ {
+ int f;
+ int first_offset = current->cycle >> AUDIO_SCALE;
+
+ for(f = 0; f < first_offset; f++)
+ {
+ sample[f] = sound.last_level;
+ }
+
+ while(current)
+ {
+ int next_offset;
+
+ if (current->next)
+ {
+ next_offset = current->cycle >> AUDIO_SCALE;
+ }
+ else
+ {
+ next_offset = audio_sample_sz;
+ }
+
+ for(f = offset; f < next_offset; f++)
+ {
+ sample[f] = current->level;
+ }
+
+ offset = next_offset;
+ sound.last_level = current->level;
+ current = current->next;
+ }
+ }
+
+ AUDIOQueue(sample, audio_sample_sz);
+
+ ClearSoundRecord();
+
+ Z80SetTimer(z80, AUDIO_TIMER, 0);
+ free(sample);
+ }
+ }
+}
+
+
+static void CheckScanlines(Z80 *z80)
{
- if (val>SCAN_CYCLES)
+ Z80Val val;
+
+ val = Z80GetTimer(z80, SCANLINE_TIMER);
+
+ if (val > SCAN_CYCLES)
{
int y;
- Z80ResetCycles(z80,val-SCAN_CYCLES);
+ Z80SetTimer(z80, SCANLINE_TIMER, val - SCAN_CYCLES);
/* Increment scan line and check for frame flyback
*/
@@ -538,55 +610,6 @@ static int CheckTimers(Z80 *z80, Z80Val val)
{
selected_out_tape--;
}
-
- if (IConfig(CONF_SOUND))
- {
- Uint8 *sample = Malloc(sample_buff_sz);
- SoundRecord *current = sound.head;
- int offset = 0;
-
- memset(sample, 0, sample_buff_sz);
-
- if (current)
- {
- int f;
- int first_offset = (int)(current->cycle / cycles_per_sample);
-
- for(f = 0; f < first_offset; f++)
- {
- sample[f] = sound.last_level;
- }
-
- while(current)
- {
- int next_offset;
-
- if (current->next)
- {
- next_offset = (int)(current->cycle / cycles_per_sample);
- }
- else
- {
- next_offset = sample_buff_sz;
- }
-
- for(f = offset; f < next_offset; f++)
- {
- sample[f] = current->level;
- }
-
- offset = next_offset;
- sound.last_level = current->level;
- current = current->next;
- }
- }
-
- AUDIOQueue(sample, sample_buff_sz);
-
- free(sample);
- ClearSoundRecord();
- }
-
}
/* Draw scanline
@@ -595,10 +618,14 @@ static int CheckTimers(Z80 *z80, Z80Val val)
if (y>=0 && y<GFX_HEIGHT && enable_screen)
DrawScanline(y);
-
- Z80SetTimer(z80, eZ80_Timer_1, 0);
}
+}
+
+static int CheckTimers(Z80 *z80, Z80Val val)
+{
+ CheckSound(z80);
+ CheckScanlines(z80);
return TRUE;
}
@@ -634,14 +661,6 @@ void SPECInit(Z80 *z80)
Z80LodgeCallback(z80,eZ80_EDHook,EDCallback);
Z80LodgeCallback(z80,eZ80_Instruction,CheckTimers);
- /* Calculate the audio buffer sizes
- */
- if (IConfig(CONF_SOUND))
- {
- sample_buff_sz = SAMPLE_RATE / IConfig(CONF_FRAMES_PER_SEC);
- cycles_per_sample = (double)FRAME_CYCLES / (double)SAMPLE_RATE;
- }
-
SPECReset(z80);
}
@@ -732,7 +751,7 @@ void SPECWritePort(Z80 *z80, Z80Word port, Z80Byte val)
{
case 0xfe: /* ULA */
border=val&0x07;
- AddSoundRecord(Z80GetTimer(z80, eZ80_Timer_1), val & 0x10 ? 64 : 0);
+ AddSoundRecord(Z80GetTimer(z80, AUDIO_TIMER), val & 0x10 ? 64 : 0);
break;
case 0xfb: /* TODO: ZX Printer */
@@ -1012,4 +1031,17 @@ void SPECReset(Z80 *z80)
}
+int SPECAudioFrequency(void)
+{
+ int freq = FRAME_CYCLES * IConfig(CONF_FRAMES_PER_SEC) >> AUDIO_SCALE;
+
+ audio_sample_sz =
+ (int)((double)freq /
+ (double)IConfig(CONF_FRAMES_PER_SEC) *
+ (double)AUDIO_FRAMES);
+
+ return freq;
+}
+
+
/* END OF FILE */
diff --git a/src/spec.h b/src/spec.h
index 28c89fd..412c92d 100644
--- a/src/spec.h
+++ b/src/spec.h
@@ -59,6 +59,10 @@ void SPECShowScreen(void);
*/
void SPECReset(Z80 *z80);
+/* Return the audio frequency wanted by the Spectrum
+*/
+int SPECAudioFrequency(void);
+
#endif