Skip to content

Commit

Permalink
Merge pull request #567 from paulscottrobson/newsound
Browse files Browse the repository at this point in the history
PWM single channel sound system working emulator/hardware
  • Loading branch information
paulscottrobson authored Aug 7, 2024
2 parents 791173c + ade7d6a commit 47f64aa
Show file tree
Hide file tree
Showing 14 changed files with 262 additions and 235 deletions.
4 changes: 4 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
TODO List

- extend the functionality, now emulator/hardware should be the same save for the sample rate.

10 changes: 3 additions & 7 deletions basic/test.bsc
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
cls
repeat
for x = 0 to 319
y = x * 2 \ 3
rect frame 0,0 ink rand(16) to x,y
next
until false
sound 0,740,30
sound 0,320,30
sound 0,100,100,14
46 changes: 6 additions & 40 deletions emulator/include/gfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
28 changes: 16 additions & 12 deletions emulator/src/core/hardware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,23 @@ void KBDInitialise(void) { }
void SNDInitialise(void) {
}

// *******************************************************************************************************************************
//
// Sound system set pitch
//
// *******************************************************************************************************************************

void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) {
if (c->isPlayingNote != 0) {
GFXSetFrequency(c->currentFrequency,1);
} else {
GFXSilence();
}
}



// // *******************************************************************************************************************************
// //
// // Sound system set pitch
// //
// // *******************************************************************************************************************************

// void SNDUpdateSoundChannel(uint8_t channel,SOUND_CHANNEL *c) {
// if (c->isPlayingNote != 0) {
// GFXSetFrequency(c->currentFrequency,1);
// } else {
// GFXSilence();
// }
// }

// *******************************************************************************************************************************
//
Expand Down
171 changes: 83 additions & 88 deletions emulator/src/framework/beeper.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// *******************************************************************************************************************************
// *******************************************************************************************************************************
// ***************************************************************************************
// ***************************************************************************************
//
// Name: beeper.cpp
// Purpose: SoundSupport library for SDL.
// Created: 12th February 2024.
// Author: qxxxb (https://github.com/qxxxb/sdl2-beeper)
// Paul Robson ([email protected])
//
// *******************************************************************************************************************************
// *******************************************************************************************************************************
// ***************************************************************************************
// ***************************************************************************************

#include <stdio.h>
#include <stdlib.h>
Expand All @@ -19,100 +19,73 @@
#include <cmath>
#include "sys_processor.h"
#include <hardware.h>

#ifdef EMSCRIPTEN
#include "emscripten.h"
#endif

// *******************************************************************************************************************************
//
// Audio : 3 channel + noise.
//
// *******************************************************************************************************************************
#include <common.h>

#include <iostream>
#include <string>
#include <math.h>

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;

// 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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
11 changes: 4 additions & 7 deletions emulator/src/framework/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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 ?
Expand Down
Loading

0 comments on commit 47f64aa

Please sign in to comment.