-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #567 from paulscottrobson/newsound
PWM single channel sound system working emulator/hardware
- Loading branch information
Showing
14 changed files
with
262 additions
and
235 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
|
@@ -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) { | ||
|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.