From c70df11789c295a49a10086c2487d11f35df8df8 Mon Sep 17 00:00:00 2001 From: Paul Robson Date: Tue, 6 Aug 2024 19:27:07 +0100 Subject: [PATCH 1/6] Added PWM interrupt driven code --- basic/test.bsc | 23 ++---- firmware/sources/hardware/sound.cpp | 63 ++++++++++----- .../hardware/sound_beeper.cpp.disabled | 76 +++++++++++++++++++ 3 files changed, 125 insertions(+), 37 deletions(-) create mode 100644 firmware/sources/hardware/sound_beeper.cpp.disabled diff --git a/basic/test.bsc b/basic/test.bsc index b7bd585e..0ab42ff7 100644 --- a/basic/test.bsc +++ b/basic/test.bsc @@ -1,16 +1,7 @@ -f$ = "test.xxx" -f$ = "testdir2" -fName = $D000 -poke fName,len(f$) -for i = 1 to len(f$):poke fname+i,asc(mid$(f$,i)):next - -while peek($FF00):wend -doke $FF04,$D000 -poke $FF01,16 -poke $FF00,3 -while peek($FF00):wend - -result = peek($FF02) -size = deek($FF04) -attrib = peek($FF08) -print result,size,attrib +cls +repeat + for x = 0 to 319 + y = x * 2 \ 3 + rect frame 0,0 ink rand(16) to x,y + next +until false diff --git a/firmware/sources/hardware/sound.cpp b/firmware/sources/hardware/sound.cpp index ea017e9e..5de7f2cc 100644 --- a/firmware/sources/hardware/sound.cpp +++ b/firmware/sources/hardware/sound.cpp @@ -17,9 +17,9 @@ #include "dvi.h" #include "system/dvi_video.h" -#define SOUND_PIN (20) // Beeper pin. +#define AUDIO_PIN (20) // Beeper pin. -static uint sliceNumber,channel; +// static uint sliceNumber,channel; // *************************************************************************************** // @@ -27,19 +27,25 @@ static uint sliceNumber,channel; // // *************************************************************************************** -static int32_t SNDSetPWMFrequencyDuty(uint slice_num,uint chan,uint32_t f, int d) -{ - uint32_t clock = DVI_TIMING.bit_clk_khz * 1024; - uint32_t divider16 = clock / f / 4096 + (clock % (f * 4096) != 0); - if (divider16 / 16 == 0) divider16 = 16; - uint32_t wrap = clock * 16 / divider16 / f - 1; - pwm_set_clkdiv_int_frac(slice_num, divider16/16,divider16 & 0xF); - pwm_set_wrap(slice_num, wrap); - pwm_set_chan_level(slice_num, chan, wrap * d / 100); - return wrap; -} +// static int32_t SNDSetPWMFrequencyDuty(uint slice_num,uint chan,uint32_t f, int d) +// { +// // uint32_t clock = DVI_TIMING.bit_clk_khz * 1024; +// // uint32_t divider16 = clock / f / 4096 + (clock % (f * 4096) != 0); +// // if (divider16 / 16 == 0) divider16 = 16; +// // uint32_t wrap = clock * 16 / divider16 / f - 1; +// // pwm_set_clkdiv_int_frac(slice_num, divider16/16,divider16 & 0xF); +// // pwm_set_wrap(slice_num, wrap); +// // pwm_set_chan_level(slice_num, chan, wrap * d / 100); +// // return wrap; +// } +static int wav_position; +void pwm_interrupt_handler() { + wav_position++; + pwm_clear_irq(pwm_gpio_to_slice_num(AUDIO_PIN)); + pwm_set_gpio_level(AUDIO_PIN,(wav_position & 0x100) ? 0x00:0xFF); +} // *************************************************************************************** // @@ -48,10 +54,25 @@ static int32_t SNDSetPWMFrequencyDuty(uint slice_num,uint chan,uint32_t f, int d // *************************************************************************************** void SNDInitialise(void) { - gpio_set_function(SOUND_PIN, GPIO_FUNC_PWM); - sliceNumber = pwm_gpio_to_slice_num(SOUND_PIN); - channel = pwm_gpio_to_channel(SOUND_PIN); - pwm_set_enabled(sliceNumber,false); + gpio_set_function(AUDIO_PIN, GPIO_FUNC_PWM); + int audio_pin_slice = pwm_gpio_to_slice_num(AUDIO_PIN); + // Setup PWM interrupt to fire when PWM cycle is complete + pwm_clear_irq(audio_pin_slice); + pwm_set_irq_enabled(audio_pin_slice, true); + // set the handle function above + irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_interrupt_handler); + irq_set_enabled(PWM_IRQ_WRAP, true); + // Setup PWM for audio output + pwm_config config = pwm_get_default_config(); + pwm_config_set_clkdiv(&config, 8.0f); + pwm_config_set_wrap(&config, 250); + pwm_init(audio_pin_slice, &config, true); + // + pwm_set_gpio_level(AUDIO_PIN, 0); + // gpio_set_function(SOUND_PIN, GPIO_FUNC_PWM); + // sliceNumber = pwm_gpio_to_slice_num(SOUND_PIN); + // channel = pwm_gpio_to_channel(SOUND_PIN); + // pwm_set_enabled(sliceNumber,false); } // *************************************************************************************** @@ -61,10 +82,10 @@ void SNDInitialise(void) { // *************************************************************************************** void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { - if (channel < SOUND_CHANNELS) { - SNDSetPWMFrequencyDuty(sliceNumber,channel, c->currentFrequency, 50); - pwm_set_enabled(sliceNumber,c->isPlayingNote); - } + // if (channel < SOUND_CHANNELS) { + // SNDSetPWMFrequencyDuty(sliceNumber,channel, c->currentFrequency, 50); + // pwm_set_enabled(sliceNumber,c->isPlayingNote); + // } } // *************************************************************************************** diff --git a/firmware/sources/hardware/sound_beeper.cpp.disabled b/firmware/sources/hardware/sound_beeper.cpp.disabled new file mode 100644 index 00000000..ea017e9e --- /dev/null +++ b/firmware/sources/hardware/sound_beeper.cpp.disabled @@ -0,0 +1,76 @@ +// *************************************************************************************** +// *************************************************************************************** +// +// Name : sound.cpp +// Authors : Paul Robson (paul@robsons.org.uk) +// Harry Fairhead +// Date : 21st November 2023 +// Reviewed : No +// Purpose : Audio support for Neo6502 +// +// *************************************************************************************** +// *************************************************************************************** + +#include "common.h" +#include "stdlib.h" +#include "hardware/pwm.h" +#include "dvi.h" +#include "system/dvi_video.h" + +#define SOUND_PIN (20) // Beeper pin. + +static uint sliceNumber,channel; + +// *************************************************************************************** +// +// Set up code, borrowed from Harry Fairhead +// +// *************************************************************************************** + +static int32_t SNDSetPWMFrequencyDuty(uint slice_num,uint chan,uint32_t f, int d) +{ + uint32_t clock = DVI_TIMING.bit_clk_khz * 1024; + uint32_t divider16 = clock / f / 4096 + (clock % (f * 4096) != 0); + if (divider16 / 16 == 0) divider16 = 16; + uint32_t wrap = clock * 16 / divider16 / f - 1; + pwm_set_clkdiv_int_frac(slice_num, divider16/16,divider16 & 0xF); + pwm_set_wrap(slice_num, wrap); + pwm_set_chan_level(slice_num, chan, wrap * d / 100); + return wrap; +} + + + +// *************************************************************************************** +// +// Initialise sound channel, return # of supported channels +// +// *************************************************************************************** + +void SNDInitialise(void) { + gpio_set_function(SOUND_PIN, GPIO_FUNC_PWM); + sliceNumber = pwm_gpio_to_slice_num(SOUND_PIN); + channel = pwm_gpio_to_channel(SOUND_PIN); + pwm_set_enabled(sliceNumber,false); +} + +// *************************************************************************************** +// +// Play note on channel +// +// *************************************************************************************** + +void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { + if (channel < SOUND_CHANNELS) { + SNDSetPWMFrequencyDuty(sliceNumber,channel, c->currentFrequency, 50); + pwm_set_enabled(sliceNumber,c->isPlayingNote); + } +} + +// *************************************************************************************** +// +// Date Revision +// ==== ======== +// 16-01-24 Moved tick callback stuff to tick source for generalised usage +// +// *************************************************************************************** From 9b7e0cdb1d965f5ea1de1e8962eafbfb451925fc Mon Sep 17 00:00:00 2001 From: Paul Robson Date: Wed, 7 Aug 2024 07:10:11 +0100 Subject: [PATCH 2/6] PWM Normal audio working --- basic/test.bsc | 7 ---- firmware/sources/hardware/sound.cpp | 65 +++++++++++++---------------- 2 files changed, 29 insertions(+), 43 deletions(-) diff --git a/basic/test.bsc b/basic/test.bsc index 0ab42ff7..e69de29b 100644 --- a/basic/test.bsc +++ b/basic/test.bsc @@ -1,7 +0,0 @@ -cls -repeat - for x = 0 to 319 - y = x * 2 \ 3 - rect frame 0,0 ink rand(16) to x,y - next -until false diff --git a/firmware/sources/hardware/sound.cpp b/firmware/sources/hardware/sound.cpp index 5de7f2cc..710afa99 100644 --- a/firmware/sources/hardware/sound.cpp +++ b/firmware/sources/hardware/sound.cpp @@ -3,10 +3,9 @@ // // Name : sound.cpp // Authors : Paul Robson (paul@robsons.org.uk) -// Harry Fairhead -// Date : 21st November 2023 +// Date : 7th August 2024 // Reviewed : No -// Purpose : Audio support for Neo6502 +// Purpose : PWM Audio support for Neo6502 // // *************************************************************************************** // *************************************************************************************** @@ -18,33 +17,31 @@ #include "system/dvi_video.h" #define AUDIO_PIN (20) // Beeper pin. +#define SAMPLE_DIVIDER (32) -// static uint sliceNumber,channel; +static int adder = 0; +static int wrapper = 0; +static int state = 0; -// *************************************************************************************** -// -// Set up code, borrowed from Harry Fairhead -// -// *************************************************************************************** - -// static int32_t SNDSetPWMFrequencyDuty(uint slice_num,uint chan,uint32_t f, int d) -// { -// // uint32_t clock = DVI_TIMING.bit_clk_khz * 1024; -// // uint32_t divider16 = clock / f / 4096 + (clock % (f * 4096) != 0); -// // if (divider16 / 16 == 0) divider16 = 16; -// // uint32_t wrap = clock * 16 / divider16 / f - 1; -// // pwm_set_clkdiv_int_frac(slice_num, divider16/16,divider16 & 0xF); -// // pwm_set_wrap(slice_num, wrap); -// // pwm_set_chan_level(slice_num, chan, wrap * d / 100); -// // return wrap; -// } +static int sampleFrequency = -1; -static int wav_position; +int SNDGetSampleFrequency(void) { + if (sampleFrequency < 0) { + sampleFrequency = DVI_TIMING.bit_clk_khz * 1024 / SAMPLE_DIVIDER / 255; + } + return sampleFrequency; +} void pwm_interrupt_handler() { - wav_position++; pwm_clear_irq(pwm_gpio_to_slice_num(AUDIO_PIN)); - pwm_set_gpio_level(AUDIO_PIN,(wav_position & 0x100) ? 0x00:0xFF); + + if (adder == 0) return; + + if (wrapper++ >= adder) { + wrapper = 0; + state = state ^ 0xFF; + pwm_set_gpio_level(AUDIO_PIN,state); + } } // *************************************************************************************** @@ -58,21 +55,17 @@ void SNDInitialise(void) { int audio_pin_slice = pwm_gpio_to_slice_num(AUDIO_PIN); // Setup PWM interrupt to fire when PWM cycle is complete pwm_clear_irq(audio_pin_slice); - pwm_set_irq_enabled(audio_pin_slice, true); // set the handle function above irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_interrupt_handler); irq_set_enabled(PWM_IRQ_WRAP, true); // Setup PWM for audio output pwm_config config = pwm_get_default_config(); - pwm_config_set_clkdiv(&config, 8.0f); - pwm_config_set_wrap(&config, 250); + pwm_config_set_clkdiv(&config, SAMPLE_DIVIDER); + pwm_config_set_wrap(&config, 255); pwm_init(audio_pin_slice, &config, true); // pwm_set_gpio_level(AUDIO_PIN, 0); - // gpio_set_function(SOUND_PIN, GPIO_FUNC_PWM); - // sliceNumber = pwm_gpio_to_slice_num(SOUND_PIN); - // channel = pwm_gpio_to_channel(SOUND_PIN); - // pwm_set_enabled(sliceNumber,false); + pwm_set_irq_enabled(audio_pin_slice, true); } // *************************************************************************************** @@ -82,16 +75,16 @@ void SNDInitialise(void) { // *************************************************************************************** void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { - // if (channel < SOUND_CHANNELS) { - // SNDSetPWMFrequencyDuty(sliceNumber,channel, c->currentFrequency, 50); - // pwm_set_enabled(sliceNumber,c->isPlayingNote); - // } + if (c->isPlayingNote) { + adder = SNDGetSampleFrequency() / c->currentFrequency / 2; + } else { + adder = 0; + } } // *************************************************************************************** // // Date Revision // ==== ======== -// 16-01-24 Moved tick callback stuff to tick source for generalised usage // // *************************************************************************************** From 47023a9d167b4430e9f244249d3fce6b86298654 Mon Sep 17 00:00:00 2001 From: Paul Robson Date: Wed, 7 Aug 2024 08:37:51 +0100 Subject: [PATCH 3/6] Added new API function, and white noise, added sndcreator, needs to move code in there from hardware --- .../common/config/miscellany/group8_sound.inc | 20 +++++++++++++ firmware/common/include/interface/sound.h | 15 ++++++++-- .../common/sources/interface/sfxmanager.cpp | 2 +- .../common/sources/interface/sndcreator.cpp | 30 +++++++++++++++++++ .../common/sources/interface/sndmanager.cpp | 18 ++++++----- firmware/sources/hardware/sound.cpp | 23 ++++++++++++-- .../hardware/sound_beeper.cpp.disabled | 2 +- 7 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 firmware/common/sources/interface/sndcreator.cpp diff --git a/firmware/common/config/miscellany/group8_sound.inc b/firmware/common/config/miscellany/group8_sound.inc index bcdd4f2a..20245440 100644 --- a/firmware/common/config/miscellany/group8_sound.inc +++ b/firmware/common/config/miscellany/group8_sound.inc @@ -37,6 +37,8 @@ GROUP 8 Sound su.frequency = DSPGetInt16(DCOMMAND,5); su.timeCS = DSPGetInt16(DCOMMAND,7); su.slide = DSPGetInt16(DCOMMAND,9); + su.type = 0; + su.volume = 100; *DERROR = SNDPlay(DPARAMS[0],&su); DOCUMENTATION Queue a sound. Refer to Section \#\ref{sound} "Sound" for details. @@ -55,3 +57,21 @@ GROUP 8 Sound DOCUMENTATION Deposit in Parameter:0 the number of notes outstanding before silence in the queue of the channel specified in Parameter:0, including the current playing sound, if any. + FUNCTION 7 Queue Sound Extended + su.frequency = DSPGetInt16(DCOMMAND,5); + su.timeCS = DSPGetInt16(DCOMMAND,7); + su.slide = DSPGetInt16(DCOMMAND,9); + su.type = DCOMMAND[11]; + su.volume = DCOMMAND[12]; + *DERROR = SNDPlay(DPARAMS[0],&su); + DOCUMENTATION + Queue a sound. Refer to Section \#\ref{sound} "Sound" for details. This is an extension of call 4 to support different waveform types and volumes. The source parameter is no longer used. + + The parameters are : Channel, Frequency Low, Frequency High, Duration Low, Duration High, Slide Low, Slide High, Sound Type and Sound Volume. All these + are 16 bit parameters except the sound type and volume, and the channel number. + + FUNCTION 8 Get Channel Count + DPARAMS[0] = SNDGetChannelCount(); + DOCUMENTATION + This returns the number of channels in Parameter #0 + diff --git a/firmware/common/include/interface/sound.h b/firmware/common/include/interface/sound.h index ced3d790..79b09dc3 100644 --- a/firmware/common/include/interface/sound.h +++ b/firmware/common/include/interface/sound.h @@ -13,20 +13,25 @@ #ifndef _XSOUND_H #define _XSOUND_H -#define SOUND_CHANNELS (1) - +#define SOUND_CHANNELS_MAX (4) #define SOUND_QUEUE_SIZE (32) +#define SOUNDTYPE_SQUARE (0) +#define SOUNDTYPE_NOISE (1) + typedef struct _sound_queue_item { uint16_t frequency; uint16_t timeCS; int16_t slide; - uint8_t soundType; + uint8_t type; + uint8_t volume; } SOUND_QUEUE_ELEMENT; typedef struct _sound_channel { + uint8_t currentType; int currentFrequency; int currentSlide; + int currentVolume; bool isPlayingNote; int tick50Remaining; int queueCount; @@ -37,10 +42,14 @@ typedef struct _sound_update { int frequency; int slide; int timeCS; + int type; + int volume; } SOUND_UPDATE; void SNDInitialise(void); void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c); +int SNDGetChannelCount(void); + void SNDManager(void); void SNDResetAll(void); diff --git a/firmware/common/sources/interface/sfxmanager.cpp b/firmware/common/sources/interface/sfxmanager.cpp index 2f0c186d..0a531064 100644 --- a/firmware/common/sources/interface/sfxmanager.cpp +++ b/firmware/common/sources/interface/sfxmanager.cpp @@ -22,7 +22,7 @@ // *************************************************************************************** uint8_t SFXPlay(int channelID,int effect) { - if (channelID >= SOUND_CHANNELS) return 1; // Check legal channel/sfx + if (channelID >= SNDGetChannelCount()) return 1; // Check legal channel/sfx if (effect >= SFX_COUNT) return 2; const uint16_t *notes = sfxData[effect]; // List to queue while (*notes != 0xFFFF) { // Queue them diff --git a/firmware/common/sources/interface/sndcreator.cpp b/firmware/common/sources/interface/sndcreator.cpp new file mode 100644 index 00000000..0e76f523 --- /dev/null +++ b/firmware/common/sources/interface/sndcreator.cpp @@ -0,0 +1,30 @@ +// *************************************************************************************** +// *************************************************************************************** +// +// Name : sndcreator.cpp +// Authors : Paul Robson (paul@robsons.org.uk) +// Date : 7th August 2024 +// Reviewed : No +// Purpose : Waveform generator, default sound system. +// +// *************************************************************************************** +// *************************************************************************************** + +#include "common.h" + +// *************************************************************************************** +// +// Return number of channels supported by this implementation +// +// *************************************************************************************** + +int SNDGetChannelCount(void) { + return 1; +} + +// *************************************************************************************** +// +// Date Revision +// ==== ======== +// +// *************************************************************************************** diff --git a/firmware/common/sources/interface/sndmanager.cpp b/firmware/common/sources/interface/sndmanager.cpp index d3656bae..86e55e9e 100644 --- a/firmware/common/sources/interface/sndmanager.cpp +++ b/firmware/common/sources/interface/sndmanager.cpp @@ -13,7 +13,7 @@ #include "common.h" static bool isInitialised = false; -static SOUND_CHANNEL channel[SOUND_CHANNELS]; +static SOUND_CHANNEL channel[SOUND_CHANNELS_MAX]; // *************************************************************************************** // @@ -22,7 +22,7 @@ static SOUND_CHANNEL channel[SOUND_CHANNELS]; // *************************************************************************************** void SNDResetAll(void) { - for (int i = 0;i < SOUND_CHANNELS;i++) { + for (int i = 0;i < SNDGetChannelCount();i++) { SNDResetChannel(i); } } @@ -34,7 +34,7 @@ void SNDResetAll(void) { // *************************************************************************************** uint8_t SNDResetChannel(int channelID) { - if (channelID >= SOUND_CHANNELS) return 1; + if (channelID >= SNDGetChannelCount()) return 1; SOUND_CHANNEL *c = &channel[channelID]; // Initialise channel c->isPlayingNote = false; c->tick50Remaining = 0; @@ -66,7 +66,7 @@ void SNDStartup(void) { // *************************************************************************************** int SNDGetNoteCount(int channelID) { - if (channelID < 0 || channelID > SOUND_CHANNELS) return -1; + if (channelID < 0 || channelID > SNDGetChannelCount()) return -1; SOUND_CHANNEL *c = &channel[channelID]; return c->queueCount + (c->isPlayingNote ? 1 : 0); // # in queue + 1 if playing } @@ -83,7 +83,8 @@ void SNDPlayNextNote(int channelID) { SOUND_QUEUE_ELEMENT *qe = &(c->queue[0]); // Head of the queue. c->currentFrequency = qe->frequency; c->currentSlide = qe->slide; - + c->currentType = qe->type; + c->currentVolume = qe->volume; c->isPlayingNote = true; // Set up the channel data c->tick50Remaining = qe->timeCS / 2; SNDUpdateSoundChannel(channelID,c); // Update it @@ -100,7 +101,7 @@ void SNDPlayNextNote(int channelID) { // *************************************************************************************** uint8_t SNDPlay(int channelID,SOUND_UPDATE *u) { - if (channelID >= SOUND_CHANNELS) return 1; + if (channelID >= SNDGetChannelCount()) return 1; SOUND_CHANNEL *c = &channel[channelID]; if (c->queueCount != SOUND_QUEUE_SIZE) { // If queue not full SOUND_QUEUE_ELEMENT *qe = &(c->queue[c->queueCount]); // Add to queue. @@ -110,7 +111,8 @@ uint8_t SNDPlay(int channelID,SOUND_UPDATE *u) { qe->slide = (u->slide & 0x8000)?-1:1; } qe->timeCS = u->timeCS; - qe->soundType = 0; + qe->type = u->type; + qe->volume = u->volume; c->queueCount++; } if (!c->isPlayingNote) { // Not playing anything, try a new note. @@ -130,7 +132,7 @@ void SNDManager(void) { SNDResetAll(); isInitialised = true; } - for (int channelID = 0;channelID < SOUND_CHANNELS;channelID++) { // Process each. + for (int channelID = 0;channelID < SNDGetChannelCount();channelID++) { // Process each. SOUND_CHANNEL *c = &channel[channelID]; if (c->isPlayingNote) { // Playing a note. if (c->tick50Remaining == 0) { // End of note ? diff --git a/firmware/sources/hardware/sound.cpp b/firmware/sources/hardware/sound.cpp index 710afa99..cf433d75 100644 --- a/firmware/sources/hardware/sound.cpp +++ b/firmware/sources/hardware/sound.cpp @@ -17,14 +17,20 @@ #include "system/dvi_video.h" #define AUDIO_PIN (20) // Beeper pin. -#define SAMPLE_DIVIDER (32) +#define SAMPLE_DIVIDER (32) // Divider, affects the interrupts / second of the PWM sample output static int adder = 0; static int wrapper = 0; static int state = 0; - +static int soundType = 0; static int sampleFrequency = -1; +// *************************************************************************************** +// +// Function that returns the sample rate in Hz of the implementeing hardware +// +// *************************************************************************************** + int SNDGetSampleFrequency(void) { if (sampleFrequency < 0) { sampleFrequency = DVI_TIMING.bit_clk_khz * 1024 / SAMPLE_DIVIDER / 255; @@ -32,6 +38,12 @@ int SNDGetSampleFrequency(void) { return sampleFrequency; } +// *************************************************************************************** +// +// Interrupt Handler +// +// *************************************************************************************** + void pwm_interrupt_handler() { pwm_clear_irq(pwm_gpio_to_slice_num(AUDIO_PIN)); @@ -40,7 +52,11 @@ void pwm_interrupt_handler() { if (wrapper++ >= adder) { wrapper = 0; state = state ^ 0xFF; - pwm_set_gpio_level(AUDIO_PIN,state); + if (soundType == SOUNDTYPE_NOISE) { + pwm_set_gpio_level(AUDIO_PIN,rand() & 0xFF); + } else { + pwm_set_gpio_level(AUDIO_PIN,state); + } } } @@ -77,6 +93,7 @@ void SNDInitialise(void) { void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { if (c->isPlayingNote) { adder = SNDGetSampleFrequency() / c->currentFrequency / 2; + soundType = c->currentType; } else { adder = 0; } diff --git a/firmware/sources/hardware/sound_beeper.cpp.disabled b/firmware/sources/hardware/sound_beeper.cpp.disabled index ea017e9e..c9e871e6 100644 --- a/firmware/sources/hardware/sound_beeper.cpp.disabled +++ b/firmware/sources/hardware/sound_beeper.cpp.disabled @@ -61,7 +61,7 @@ void SNDInitialise(void) { // *************************************************************************************** void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { - if (channel < SOUND_CHANNELS) { + if (channel < SNDGetChannelCount()) { SNDSetPWMFrequencyDuty(sliceNumber,channel, c->currentFrequency, 50); pwm_set_enabled(sliceNumber,c->isPlayingNote); } From 6d695442de26937c521252bb82894e6bcc0b4ba2 Mon Sep 17 00:00:00 2001 From: Paul Robson Date: Wed, 7 Aug 2024 08:52:21 +0100 Subject: [PATCH 4/6] Hardware code correctly distributed. Emulator sound is disabled for now --- TODO | 5 +++ emulator/src/core/hardware.cpp | 30 ++++++++++---- firmware/common/include/interface/sound.h | 6 ++- .../common/sources/interface/sndcreator.cpp | 41 +++++++++++++++++++ firmware/sources/hardware/sound.cpp | 32 +-------------- 5 files changed, 73 insertions(+), 41 deletions(-) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 00000000..3bb6ec2d --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +TODO List + +- implement the emulator hardware, SNDUpdateSndChannel is disabled so emulator sound will not currently work. + - emulator has to correctly return the sample buffers and fill them by calling nextsample. + \ No newline at end of file diff --git a/emulator/src/core/hardware.cpp b/emulator/src/core/hardware.cpp index e65ab0dd..f7119c12 100644 --- a/emulator/src/core/hardware.cpp +++ b/emulator/src/core/hardware.cpp @@ -112,20 +112,32 @@ void KBDInitialise(void) { } void SNDInitialise(void) { } -// ******************************************************************************************************************************* + +// *************************************************************************************** // -// Sound system set pitch +// Function that returns the sample rate in Hz of the implementeing hardware // -// ******************************************************************************************************************************* +// *************************************************************************************** -void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { - if (c->isPlayingNote != 0) { - GFXSetFrequency(c->currentFrequency,1); - } else { - GFXSilence(); - } +int SNDGetSampleFrequency(void) { + return 22100; } + +// // ******************************************************************************************************************************* +// // +// // Sound system set pitch +// // +// // ******************************************************************************************************************************* + +// void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { +// if (c->isPlayingNote != 0) { +// GFXSetFrequency(c->currentFrequency,1); +// } else { +// GFXSilence(); +// } +// } + // ******************************************************************************************************************************* // // Receive SDL Keyboard events diff --git a/firmware/common/include/interface/sound.h b/firmware/common/include/interface/sound.h index 79b09dc3..c8ebd3f8 100644 --- a/firmware/common/include/interface/sound.h +++ b/firmware/common/include/interface/sound.h @@ -47,11 +47,13 @@ typedef struct _sound_update { } SOUND_UPDATE; void SNDInitialise(void); -void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c); +int SNDGetSampleFrequency(void); + int SNDGetChannelCount(void); +uint16_t SNDGetNextSample(void); +void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c); void SNDManager(void); - void SNDResetAll(void); uint8_t SNDResetChannel(int channel); uint8_t SNDPlay(int channelID,SOUND_UPDATE *u); diff --git a/firmware/common/sources/interface/sndcreator.cpp b/firmware/common/sources/interface/sndcreator.cpp index 0e76f523..f34f239f 100644 --- a/firmware/common/sources/interface/sndcreator.cpp +++ b/firmware/common/sources/interface/sndcreator.cpp @@ -12,6 +12,12 @@ #include "common.h" +static int adder = 0; +static int wrapper = 0; +static int state = 0; +static int soundType = 0; +static int level = 0; + // *************************************************************************************** // // Return number of channels supported by this implementation @@ -22,6 +28,41 @@ int SNDGetChannelCount(void) { return 1; } +// *************************************************************************************** +// +// Get the next sample for the driver provided hardware rate. +// +// *************************************************************************************** + +uint16_t SNDGetNextSample(void) { + + if (adder == 0) return 0; + + if (wrapper++ >= adder) { + wrapper = 0; + level = state = state ^ 0xFF; + if (soundType == SOUNDTYPE_NOISE) { + level = rand() & 0xFF; + } + } + return level; +} + +// *************************************************************************************** +// +// Play note on channel +// +// *************************************************************************************** + +void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { + if (c->isPlayingNote) { + adder = SNDGetSampleFrequency() / c->currentFrequency / 2; + soundType = c->currentType; + } else { + adder = 0; + } +} + // *************************************************************************************** // // Date Revision diff --git a/firmware/sources/hardware/sound.cpp b/firmware/sources/hardware/sound.cpp index cf433d75..72467d2d 100644 --- a/firmware/sources/hardware/sound.cpp +++ b/firmware/sources/hardware/sound.cpp @@ -19,10 +19,7 @@ #define AUDIO_PIN (20) // Beeper pin. #define SAMPLE_DIVIDER (32) // Divider, affects the interrupts / second of the PWM sample output -static int adder = 0; -static int wrapper = 0; -static int state = 0; -static int soundType = 0; + static int sampleFrequency = -1; // *************************************************************************************** @@ -46,18 +43,7 @@ int SNDGetSampleFrequency(void) { void pwm_interrupt_handler() { pwm_clear_irq(pwm_gpio_to_slice_num(AUDIO_PIN)); - - if (adder == 0) return; - - if (wrapper++ >= adder) { - wrapper = 0; - state = state ^ 0xFF; - if (soundType == SOUNDTYPE_NOISE) { - pwm_set_gpio_level(AUDIO_PIN,rand() & 0xFF); - } else { - pwm_set_gpio_level(AUDIO_PIN,state); - } - } + pwm_set_gpio_level(AUDIO_PIN,SNDGetNextSample()); } // *************************************************************************************** @@ -84,20 +70,6 @@ void SNDInitialise(void) { pwm_set_irq_enabled(audio_pin_slice, true); } -// *************************************************************************************** -// -// Play note on channel -// -// *************************************************************************************** - -void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) { - if (c->isPlayingNote) { - adder = SNDGetSampleFrequency() / c->currentFrequency / 2; - soundType = c->currentType; - } else { - adder = 0; - } -} // *************************************************************************************** // From 55690a355e058d7fb2d791abf2f583b2ccd4a87e Mon Sep 17 00:00:00 2001 From: Paul Robson Date: Wed, 7 Aug 2024 12:03:28 +0100 Subject: [PATCH 5/6] Emulator & Hardware now in sync with new sound system --- TODO | 3 +- basic/test.bsc | 3 + emulator/include/gfx.h | 46 +---- emulator/src/core/hardware.cpp | 8 - emulator/src/framework/beeper.cpp | 171 +++++++++--------- emulator/src/framework/debugger.cpp | 11 +- emulator/src/framework/gfx.cpp | 32 +--- firmware/common/include/interface/sound.h | 1 + .../common/sources/interface/sndcreator.cpp | 3 + 9 files changed, 105 insertions(+), 173 deletions(-) diff --git a/TODO b/TODO index 3bb6ec2d..cabdf96a 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ TODO List -- implement the emulator hardware, SNDUpdateSndChannel is disabled so emulator sound will not currently work. - - emulator has to correctly return the sample buffers and fill them by calling nextsample. +- extend the functionality, now emulator/hardware should be the same save for the sample rate. \ No newline at end of file diff --git a/basic/test.bsc b/basic/test.bsc index e69de29b..0b464fbf 100644 --- a/basic/test.bsc +++ b/basic/test.bsc @@ -0,0 +1,3 @@ +sound 0,740,30 +sound 0,320,30 +sound 0,100,100,14 diff --git a/emulator/include/gfx.h b/emulator/include/gfx.h index e42496d7..5d748f61 100644 --- a/emulator/include/gfx.h +++ b/emulator/include/gfx.h @@ -78,47 +78,13 @@ int GFXReadJoystick0(void); int GFXControllerCount(void); unsigned int GFXReadController(int id); -class Beeper -{ -public: - static void open(); // Open the audio device - static void close(); // Close the audio device +void SOUNDOpen(); // Open the audio device +void SOUNDClose(); // Close the audio device - static void setFrequency(double frequency); // Units: Hz - static void setVolume(double volume); // Range: 0.0 .. 1.0 +void SOUNDSetFrequency(double frequency); // Units: Hz +void SOUNDSetVolume(double volume); // Range: 0.0 .. 1.0 - static void play(); - static void stop(); - - static SDL_AudioSpec m_obtainedSpec; - -private: - static SDL_AudioDeviceID m_audioDevice; - static double m_frequency; // Units: Hz - static double m_volume; // Range: 0.0 .. 1.0 - - // The current playback position, according to `getData()` and - // `audioCallback()`. Units: samples - static int m_pos; - - // Pointer to function for offset calculate. Differs between different - // audio formats. - static int (*m_calculateOffset)(int sample, int channel); - - // Pointer to function for writing data. Differs between different audio - // formats. - static void (*m_writeData)(uint8_t* ptr, double data); - - // Called by `audioCallback` to generate audio data. - static double getData(); - - // This is function is called repeatedly by SDL2 to send data to the audio - // device. - static void audioCallback( - void* userdata, - uint8_t* stream, - int len - ); -}; +void SOUNDPlay(); +void SOUNDStop(); #endif diff --git a/emulator/src/core/hardware.cpp b/emulator/src/core/hardware.cpp index f7119c12..6b7ee7a9 100644 --- a/emulator/src/core/hardware.cpp +++ b/emulator/src/core/hardware.cpp @@ -113,15 +113,7 @@ void SNDInitialise(void) { } -// *************************************************************************************** -// -// Function that returns the sample rate in Hz of the implementeing hardware -// -// *************************************************************************************** -int SNDGetSampleFrequency(void) { - return 22100; -} // // ******************************************************************************************************************************* diff --git a/emulator/src/framework/beeper.cpp b/emulator/src/framework/beeper.cpp index eaaffdfb..b70ac9b9 100644 --- a/emulator/src/framework/beeper.cpp +++ b/emulator/src/framework/beeper.cpp @@ -1,5 +1,5 @@ -// ******************************************************************************************************************************* -// ******************************************************************************************************************************* +// *************************************************************************************** +// *************************************************************************************** // // Name: beeper.cpp // Purpose: SoundSupport library for SDL. @@ -7,8 +7,8 @@ // Author: qxxxb (https://github.com/qxxxb/sdl2-beeper) // Paul Robson (paul@robsons.org.uk) // -// ******************************************************************************************************************************* -// ******************************************************************************************************************************* +// *************************************************************************************** +// *************************************************************************************** #include #include @@ -19,91 +19,64 @@ #include #include "sys_processor.h" #include - -#ifdef EMSCRIPTEN -#include "emscripten.h" -#endif - -// ******************************************************************************************************************************* -// -// Audio : 3 channel + noise. -// -// ******************************************************************************************************************************* +#include #include #include -#include -SDL_AudioDeviceID Beeper::m_audioDevice; -SDL_AudioSpec Beeper::m_obtainedSpec; -double Beeper::m_frequency; -double Beeper::m_volume; -int Beeper::m_pos; -void (*Beeper::m_writeData)(uint8_t* ptr, double data); -int (*Beeper::m_calculateOffset)(int sample, int channel); +static SDL_AudioDeviceID m_audioDevice; +static SDL_AudioSpec m_obtainedSpec; + +void (*m_writeData)(uint8_t* ptr, double data); +int (*m_calculateOffset)(int sample, int channel); -// --- -// Calculate the offset in bytes from the start of the audio stream to the -// memory address at `sample` and `channel`. +// *************************************************************************************** +// +// Calculate the offset in bytes from the start of the audio stream to the +// memory address at `sample` and `channel`. // -// Channels are interleaved. +// Channels are interleaved. +// +// *************************************************************************************** -int calculateOffset_s16(int sample, int channel) { +static int calculateOffset_s16(int sample, int channel) { return - (sample * sizeof(int16_t) * Beeper::m_obtainedSpec.channels) + + (sample * sizeof(int16_t) * m_obtainedSpec.channels) + (channel * sizeof(int16_t)); } -int calculateOffset_f32(int sample, int channel) { +static int calculateOffset_f32(int sample, int channel) { return - (sample * sizeof(float) * Beeper::m_obtainedSpec.channels) + + (sample * sizeof(float) * m_obtainedSpec.channels) + (channel * sizeof(float)); } -// --- -// Convert a normalized data value (range: 0.0 .. 1.0) to a data value matching -// the audio format. +// *************************************************************************************** +// +// Convert a normalized data value (range: 0.0 .. 1.0) to a data value matching +// the audio format. +// +// *************************************************************************************** -void writeData_s16(uint8_t* ptr, double data) { +static void writeData_s16(uint8_t* ptr, double data) { int16_t* ptrTyped = (int16_t*)ptr; double range = (double)INT16_MAX - (double)INT16_MIN; double dataScaled = data * range / 2.0; *ptrTyped = dataScaled; } -void writeData_f32(uint8_t* ptr, double data) { +static void writeData_f32(uint8_t* ptr, double data) { float* ptrTyped = (float*)ptr; *ptrTyped = data; } -// --- -// Generate audio data. This is how the waveform is generated. - -double Beeper::getData() { - double sampleRate = (double)(m_obtainedSpec.freq); - - // Units: samples - double period = sampleRate / m_frequency; - - // Reset m_pos when it reaches the start of a period so it doesn't run off - // to infinity (though this won't happen unless you are playing sound for a - // very long time) - if (m_pos % (int)period == 0) { - m_pos = 0; - } - - double pos = m_pos; - double angular_freq = (1.0 / period) * 2.0 * M_PI; - double amplitude = m_volume; - - return (sin(pos * angular_freq) > 0) ? -amplitude:amplitude; -} +// *************************************************************************************** +// +// Callback when requesting buffer be filled +// +// *************************************************************************************** -void Beeper::audioCallback( - void* userdata, - uint8_t* stream, - int len -) { +static void audioCallback(void* userdata,uint8_t* stream,int len) { // Unused parameters (void)userdata; (void)len; @@ -111,8 +84,8 @@ void Beeper::audioCallback( // Write data to the entire buffer by iterating through all samples and // channels. for (int sample = 0; sample < m_obtainedSpec.samples; ++sample) { - double data = getData(); - m_pos++; + uint16_t nextSample = SNDGetNextSample(); + double data = (nextSample-128)/128.0; // Write the same data to all channels for (int channel = 0; channel < m_obtainedSpec.channels; ++channel) { @@ -123,7 +96,13 @@ void Beeper::audioCallback( } } -void Beeper::open() { +// *************************************************************************************** +// +// Open a sound device +// +// *************************************************************************************** + +void SOUNDOpen() { // First define the specifications we want for the audio device SDL_AudioSpec desiredSpec; SDL_zero(desiredSpec); @@ -156,7 +135,7 @@ void Beeper::open() { // will be called by SDL2 in a separate thread when it needs to write data // to the audio buffer. In other words, we don't control when this function // is called; SDL2 manages it. - desiredSpec.callback = Beeper::audioCallback; + desiredSpec.callback = audioCallback; // When we open the audio device, we tell SDL2 what audio specifications we // desire. SDL2 will try to get these specifications when opening the audio @@ -194,40 +173,56 @@ void Beeper::open() { // TODO: throw exception } - std::cout << "[Beeper] frequency: " << m_obtainedSpec.freq << std::endl; - std::cout << "[Beeper] format: " << formatName << std::endl; + // std::cout << "[Beeper] frequency: " << m_obtainedSpec.freq << std::endl; + // std::cout << "[Beeper] format: " << formatName << std::endl; - std::cout - << "[Beeper] channels: " - << (int)(m_obtainedSpec.channels) - << std::endl; + // std::cout + // << "[Beeper] channels: " + // << (int)(m_obtainedSpec.channels) + // << std::endl; - std::cout << "[Beeper] samples: " << m_obtainedSpec.samples << std::endl; - std::cout << "[Beeper] padding: " << m_obtainedSpec.padding << std::endl; - std::cout << "[Beeper] size: " << m_obtainedSpec.size << std::endl; + // std::cout << "[Beeper] samples: " << m_obtainedSpec.samples << std::endl; + // std::cout << "[Beeper] padding: " << m_obtainedSpec.padding << std::endl; + // std::cout << "[Beeper] size: " << m_obtainedSpec.size << std::endl; } } -void Beeper::close() { +// *************************************************************************************** +// +// End the Sound system +// +// *************************************************************************************** + +void SOUNDClose() { SDL_CloseAudioDevice(m_audioDevice); } -// -- - -void Beeper::setFrequency(double frequency) { - m_frequency = frequency; -} +// *************************************************************************************** +// +// Start playing sound +// +// *************************************************************************************** -void Beeper::setVolume(double volume) { - m_volume = volume; +void SOUNDPlay() { + SDL_PauseAudioDevice(m_audioDevice, 0); } -// --- +// *************************************************************************************** +// +// Stop playing sound +// +// *************************************************************************************** -void Beeper::play() { - SDL_PauseAudioDevice(m_audioDevice, 0); +void SOUNDStop() { + SDL_PauseAudioDevice(m_audioDevice, 1); } -void Beeper::stop() { - SDL_PauseAudioDevice(m_audioDevice, 1); +// *************************************************************************************** +// +// Function that returns the sample rate in Hz of the implementeing hardware +// +// *************************************************************************************** + +int SNDGetSampleFrequency(void) { + return m_obtainedSpec.freq; } diff --git a/emulator/src/framework/debugger.cpp b/emulator/src/framework/debugger.cpp index 1b14a943..6ea8b0ba 100644 --- a/emulator/src/framework/debugger.cpp +++ b/emulator/src/framework/debugger.cpp @@ -14,7 +14,8 @@ #include "gfx.h" #include "sys_processor.h" #include "debugger.h" - +#include "common.h" + static int isInitialised = 0; // Flag to initialise first time static int addressSettings[] = { 0,0,0,0x20FFFF }; // Adjustable values : Code, Data, Other, Break. static int keyMapping[16]; // Mapping for control keys to key values @@ -24,11 +25,7 @@ static int stepBreakPoint; // Extra breakpoint used for step over. static Uint32 nextFrame = 0; // Time of next frame. static int frameCount = 0; -#ifdef EMSCRIPTEN -#define FRAMESKIP (1) -#else #define FRAMESKIP (0) -#endif // ******************************************************************************************************************************* // Handle one frame of rendering etc. for the debugger. @@ -83,11 +80,11 @@ int GFXXRender(SDL_Surface *surface) { if (CMDKEY(DBGKEY_RESET)) { // Reset processor (F1) DEBUG_RESET(); addressSettings[0] = DEBUG_HOMEPC(); - GFXSilence(); + SNDMuteAllChannels(); } if (inRunMode == 0) { - GFXSilence(); // Will drive us mental otherwise. + SNDMuteAllChannels(); if (isxdigit(currentKey)) { // Is it a hex digit 0-9 A-F. int digit = isdigit(currentKey)?currentKey:(currentKey-'A'+10); // Convert to a number. int setting = 0; // Which value is being changed ? diff --git a/emulator/src/framework/gfx.cpp b/emulator/src/framework/gfx.cpp index 25321499..7c3406f8 100644 --- a/emulator/src/framework/gfx.cpp +++ b/emulator/src/framework/gfx.cpp @@ -20,10 +20,6 @@ #include #include -#ifdef EMSCRIPTEN -#include "emscripten.h" -#endif - #define MAX_CONTROLLERS (4) static SDL_Window *mainWindow = NULL; @@ -66,9 +62,8 @@ void GFXOpenWindow(const char *title,int width,int height,int colour) { background = colour; // Remember required backgrounds. _GFXInitialiseKeyRecord(); // Set up key system. - Beeper::open(); - Beeper::setVolume(0.0); - Beeper::play(); + SOUNDOpen(); + SOUNDPlay(); SDL_ShowCursor(SDL_DISABLE); // Hide mouse cursor } @@ -84,14 +79,10 @@ static int isRunning = -1; // Is app running static void _GFXMainLoop(void *arg); void GFXStart(void) { - #ifdef EMSCRIPTEN - emscripten_set_main_loop_arg(_GFXMainLoop, NULL, -1, 1); - #else while(isRunning) { // While still running. _GFXMainLoop(NULL); } - #endif - Beeper::stop(); + SOUNDStop(); SDL_CloseAudio(); } @@ -402,25 +393,10 @@ int GFXToASCII(int ch,int applyModifiers) { // // ******************************************************************************************************************************* -int GFXTimer(void) { +int GFXTimer(void) { return SDL_GetTicks(); } -// ******************************************************************************************************************************* -// -// Beeper interface -// -// ******************************************************************************************************************************* - -void GFXSetFrequency(int freq,int channel) { - Beeper::setVolume(1.0); - Beeper::setFrequency(freq); -} - -void GFXSilence(void) { - Beeper::setVolume(0.0); -} - // ******************************************************************************************************************************* // // Get controller count diff --git a/firmware/common/include/interface/sound.h b/firmware/common/include/interface/sound.h index c8ebd3f8..c864ef0a 100644 --- a/firmware/common/include/interface/sound.h +++ b/firmware/common/include/interface/sound.h @@ -54,6 +54,7 @@ uint16_t SNDGetNextSample(void); void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c); void SNDManager(void); +void SNDMuteAllChannels(void); void SNDResetAll(void); uint8_t SNDResetChannel(int channel); uint8_t SNDPlay(int channelID,SOUND_UPDATE *u); diff --git a/firmware/common/sources/interface/sndcreator.cpp b/firmware/common/sources/interface/sndcreator.cpp index f34f239f..18ebddf7 100644 --- a/firmware/common/sources/interface/sndcreator.cpp +++ b/firmware/common/sources/interface/sndcreator.cpp @@ -28,6 +28,9 @@ int SNDGetChannelCount(void) { return 1; } +void SNDMuteAllChannels(void) { +} + // *************************************************************************************** // // Get the next sample for the driver provided hardware rate. From c17c050b49e0c83395ebe1db349b89087d4f662b Mon Sep 17 00:00:00 2001 From: Paul Robson Date: Wed, 7 Aug 2024 12:16:05 +0100 Subject: [PATCH 6/6] Fixed sound.cpp --- firmware/sources/hardware/sound.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware/sources/hardware/sound.cpp b/firmware/sources/hardware/sound.cpp index 72467d2d..6b1c18d2 100644 --- a/firmware/sources/hardware/sound.cpp +++ b/firmware/sources/hardware/sound.cpp @@ -19,7 +19,6 @@ #define AUDIO_PIN (20) // Beeper pin. #define SAMPLE_DIVIDER (32) // Divider, affects the interrupts / second of the PWM sample output - static int sampleFrequency = -1; // ***************************************************************************************