#include "Buzzer.hpp" #include #include #include #include void SDLCALL Buzzer::audioCallback(void *userdata, Uint8 *stream, int len) { static_cast(userdata)->copySamples(stream, len); } Buzzer::Buzzer(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::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; } Buzzer::~Buzzer() { SDL_CloseAudioDevice(mAudioDevice); } void Buzzer::on() { SDL_PauseAudioDevice(mAudioDevice, SDL_FALSE); } void Buzzer::off() { SDL_PauseAudioDevice(mAudioDevice, SDL_TRUE); } void Buzzer::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; } } }