Files
choco-chip8/BuzzerSDL.cpp

72 lines
2.2 KiB
C++

#include "BuzzerSDL.hpp"
#include <cmath>
#include <limits>
#include <stdexcept>
#include <cstring>
void SDLCALL BuzzerSDL::audioCallback(void *userdata, Uint8 *stream, int len) {
static_cast<BuzzerSDL*>(userdata)->copySamples(stream, len);
}
BuzzerSDL::BuzzerSDL(unsigned buzzerFrequency) {
SDL_AudioSpec desired;
SDL_AudioSpec obtained;
SDL_zero(desired);
desired.channels = 1;
desired.freq = 44000;
desired.format = AUDIO_S16SYS;
desired.samples = 4096;
desired.callback = audioCallback;
desired.userdata = this;
mAudioDevice = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desired, &obtained, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
if(mAudioDevice == 0) {
throw std::runtime_error(SDL_GetError());
}
// period of the sine wave, in samples, to obtained the desired
// buffer buzzer frequency at a particular audio sample rate
double waveLength = double(obtained.freq) / buzzerFrequency;
// buffer size that fits a whole number of sine waves and is roughly the same size of obtained.samples
size_t numSamples = waveLength * std::max(std::floor(obtained.samples / waveLength), 1.0);
mSamples.resize(numSamples);
// generate the sine wave
auto amplitude = std::numeric_limits<sample_t>::max();
auto angularFreq = 2.0 * M_PI / waveLength;
for(size_t iSample = 0; iSample < numSamples; iSample++) {
mSamples[iSample] = amplitude * std::sin(angularFreq * iSample);
}
miCurrentSample = 0;
}
BuzzerSDL::~BuzzerSDL() {
SDL_CloseAudioDevice(mAudioDevice);
}
void BuzzerSDL::on() {
SDL_PauseAudioDevice(mAudioDevice, SDL_FALSE);
}
void BuzzerSDL::off() {
SDL_PauseAudioDevice(mAudioDevice, SDL_TRUE);
}
void BuzzerSDL::copySamples(Uint8 *stream, int len) {
// Copy len samples from the current position of the buffer,
// looping back to the start of the buffer when needed.
while(len > 0) {
size_t numBytes = std::min(sizeof(sample_t) * (mSamples.size() - miCurrentSample), size_t(len));
size_t numSamples = numBytes / sizeof(sample_t);
std::memcpy(stream, mSamples.data() + miCurrentSample, numBytes);
stream += numBytes;
len -= numBytes;
miCurrentSample += numSamples;
if(miCurrentSample >= mSamples.size()) {
miCurrentSample = 0;
}
}
}