Sound playback not working when using QueueAudio on C++ - c++

I am trying to play a sin wave sound with SDL2 by using the audio queue on C++. In order to do that, I have created a class "Speaker", which has a pushBeep function that is called every time a beep needs to be generated. I have created an AudioDevice successfully, and it is also successful when I do the QueueAudio to the device (I have checked on the debugger) but I can't seem to get any sound out of it.
I have tried changing the way I generate the samples in numerous ways, also, as I said previously, I have checked that the device is properly opened and the QueueAudio returns 0 for success.
This is the class
Speaker::Speaker()
{
SDL_AudioSpec ds;
ds.freq = Speaker::SPEAKER_FREQUENCY;
ds.format = AUDIO_F32;
ds.channels = 1;
ds.samples = 4096;
ds.callback = NULL;
ds.userdata = this;
SDL_AudioSpec os;
this->dev = SDL_OpenAudioDevice(NULL, 0, &ds, &os, NULL);
std::cout << "DEVICE: " << this->dev << std::endl;
SDL_PauseAudioDevice(this->dev, 0);
}
Speaker::~Speaker()
{
SDL_CloseAudioDevice(this->dev);
}
void Speaker::pushBeep(double freq, int duration) {
int nSamples = duration * Speaker::SPEAKER_FREQUENCY / 1000;
float* samples = new float[nSamples];
double v = 0.0;
for (int idx = 0; idx < nSamples; idx++) {
//float value = (float)Speaker::SPEAKER_AMPLITUDE * std::sin(v * 2 * M_PI / Speaker::SPEAKER_FREQUENCY);
float value = 440.0;
samples[idx] = value;
v += freq;
}
int a = SDL_QueueAudio(this->dev, (void*)samples, nSamples * sizeof(float));
std::cout << a << std::endl;
delete[] samples;
samples = NULL;
}
And this is how I call it
Speaker s;
s.pushBeep(440.0, 1000);
When I try with the sin wave generation code (commented) it gives me a "double to float loss of precision" error. When I use the fixed value (not commented) it does not give the error, but it still does not work.
I expect the program to output the sound.

Couple of things you are missing, or maybe you didn't add to your code snippet. You didn't specify an audio callback so when you call SDL_QueueAudio(); it didn't know what to do with the data I'm pretty sure. And you weren't calling SDL_PauseAudioDevice() in your example with the delay.
#include <math.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <iostream>
namespace AudioGen
{
const int AMPLITUDE = 1;
const int SAMPLE_RATE = 44000;
// Globals
float *in_buffer;
SDL_atomic_t callback_sample_pos;
SDL_Event event;
SDL_bool running = SDL_TRUE;
/**
* Structure for holding audio metadata such as frequency
*/
struct AudioData
{
int sampleNum;
float frequency;
};
void audio_callback(void *user_data, Uint8 *raw_buffer, int bytes)
{
float *buffer = (float*)raw_buffer;
AudioData &audio_data(*static_cast<AudioData*>(user_data));
int nSamples = bytes / 4; // For F32
std::cout << nSamples << std::endl;
for(int i = 0; i < nSamples; i++, audio_data.sampleNum++)
{
double time = (double)audio_data.sampleNum / (double)SAMPLE_RATE;
buffer[i] = (float)(AMPLITUDE * sin(2.0f * M_PI * audio_data.frequency * time));
}
}
int buffer_length;
void callback(void *user_data, Uint8 *raw_buffer, int bytes)
{
float *buffer = (float*)raw_buffer;
int nSamples = bytes/4;
auto local_sample_pos = SDL_AtomicGet(&callback_sample_pos);
for(int i = 0; i < nSamples; ++i)
{
// Stop running audio if all samples are finished playing
if(buffer_length == local_sample_pos)
{
running = SDL_FALSE;
break;
}
buffer[i] = in_buffer[local_sample_pos];
++local_sample_pos;
}
SDL_AtomicSet(&callback_sample_pos, local_sample_pos);
}
class Speaker
{
public:
Speaker()
{
SDL_Init(SDL_INIT_AUDIO);
SDL_AudioSpec ds;
ds.freq = SAMPLE_RATE;
ds.format = AUDIO_F32;
ds.channels = 1;
ds.samples = 4096;
ds.callback = callback;
ds.userdata = &ad; // metadata for frequency
SDL_AudioSpec os;
dev = SDL_OpenAudioDevice(NULL, 0, &ds, &os, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
}
~Speaker()
{
SDL_CloseAudioDevice(dev);
SDL_Quit();
}
void pushBeep(float frequency, int duration)
{
ad.frequency = frequency; // set the frequency for the beep
SDL_PauseAudioDevice(dev, 0);
SDL_Delay(duration); // wait while sound is playing
SDL_PauseAudioDevice(dev, 1);
}
void pushBeep2(float frequency, int duration )
{
int nSamples = duration * SAMPLE_RATE / 1000;
in_buffer = new float[nSamples];
buffer_length = nSamples;
for (int idx = 0; idx < nSamples; idx++) {
double time = (double)idx / (double)SAMPLE_RATE;
in_buffer[idx] = (float)(AMPLITUDE * std::sin(2.0f * M_PI * frequency * time));
}
SDL_QueueAudio(dev, in_buffer, nSamples * sizeof(float));
SDL_PauseAudioDevice(dev, 0);
while(running){
while(SDL_PollEvent(&event)!=0);
}
delete[] in_buffer;
}
private:
SDL_AudioDeviceID dev;
AudioData ad;
int sampleNum = 0;
};
} // End of namespace AudioGen
int main(int argc, char *argv[])
{
AudioGen::Speaker speaker;
//speaker.pushBeep(440, 1000);
speaker.pushBeep2(440.0f, 1000);
return 0;
}

Related

Anyone who has used WaveOut API CPP please explain what's wrong with my code?

I have been struggling with this for ages and ages and I'm starting to really get fed up,
No matter what I try or study on I simply cannot output a sound that is not choppy regardless of how I set up the memory, few blocks, more blocks, big blocks, small blocks, different loops, different call-backs, etc, nothing seems to work and I always get a choppy sound,
I'd really appreciate if someone who know what they're doing look over my code and explain where I go wrong, the littlest of detail is appreciated to the upmost degree.
Even if you can recommend someone or somewhere to contact / post please inform me!
#include <iostream>
#include <Windows.h>
#include <cmath>
#pragma comment(lib, "Winmm.lib")
VOID ParseError(MMRESULT Result, LPCWSTR FunctionName)
{
if (Result != MMSYSERR_NOERROR)
{
WCHAR Text[256];
waveOutGetErrorText(Result, Text, 256);
#ifdef _IOSTREAM_
std::wcout << "\'" << FunctionName << "\'" << " : " << Text << std::endl;
#endif
MessageBox(NULL, Text, FunctionName, MB_OK);
}
}
typedef short TYPE;
UINT16 U_BlockCount;
UINT16 U_BlockSize;
UINT16 U_BlockCurrent;
UINT8 U_Channel;
UINT32 U_SampleRate; // Sample Rate In Hz
UINT32 U_SampleSize; // Sample Size In Bits
TYPE* T_Memory = nullptr;
WAVEHDR* W_Header;
WAVEFORMATEX S_WaveFormat;
HWAVEOUT H_Waveout;
HANDLE H_Mutex;
double OutPutWave()
{
double Output;
static double Time = 0;
Output = sin(Time * 2 * 3.153);
Time += 0.01;
return Output;
}
void Write(HWAVEOUT Waveout)
{
ParseError(waveOutUnprepareHeader(Waveout, &W_Header[U_BlockCurrent], sizeof(W_Header[U_BlockCurrent])), L"waveOutUnprepareHeader");
ParseError(waveOutPrepareHeader(Waveout, &W_Header[U_BlockCurrent], sizeof(W_Header[U_BlockCurrent])), L"waveOutPrepareHeader");
ParseError(waveOutWrite(Waveout, &W_Header[U_BlockCurrent], sizeof(WAVEHDR)), L"waveOutWrite");
}
void CALLBACK waveOutProc(HWAVEOUT Waveout, UINT uMsg)
{
U_BlockCurrent = (U_BlockCurrent + 1) % U_BlockCount;
if (uMsg == WOM_DONE) Write(Waveout);
}
INT main()
{
H_Mutex = CreateMutex(NULL, FALSE, NULL);
U_BlockCount = 4;
U_BlockSize = 1000;
U_BlockCurrent = 0;
U_SampleRate = 44100;
U_SampleSize = sizeof(TYPE) * 8;
U_Channel = 2;
S_WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
S_WaveFormat.cbSize = 0;
S_WaveFormat.nChannels = U_Channel;
S_WaveFormat.nSamplesPerSec = U_SampleRate;
S_WaveFormat.wBitsPerSample = U_SampleSize;
S_WaveFormat.nBlockAlign = S_WaveFormat.nChannels * (S_WaveFormat.wBitsPerSample / 8);
S_WaveFormat.nAvgBytesPerSec = S_WaveFormat.nSamplesPerSec * S_WaveFormat.nBlockAlign;
T_Memory = new TYPE[U_BlockCount * U_BlockSize];
ZeroMemory(T_Memory, sizeof(TYPE) * U_BlockCount * U_BlockSize);
W_Header = new WAVEHDR[U_BlockCount];
ZeroMemory(W_Header, sizeof(WAVEHDR) * U_BlockCount);
for (UINT Idx = 0; Idx < (U_BlockCount * U_BlockSize); Idx++)
{
T_Memory[Idx] = 440 * OutPutWave() * 10;
}
for (UINT Idx = 0; Idx < (U_BlockCount); Idx++)
{
W_Header[Idx].dwBufferLength = sizeof(TYPE) * U_BlockSize;
W_Header[Idx].lpData = (LPSTR)T_Memory + (Idx * U_BlockSize);
}
ParseError(waveOutOpen(&H_Waveout, WAVE_MAPPER, &S_WaveFormat, (DWORD_PTR)waveOutProc, (DWORD_PTR)NULL, CALLBACK_FUNCTION), L"waveOutOpen");
Write(H_Waveout);
while (true);
return 0;
}

How do I generate a tone using SDL_audio?

I am trying to generate a simple, constant sine tone using SDL_audio. I have a small helper class that can be called to turn the tone on/off, change the frequency, and change the wave shape. I have followed some examples I could find on the web and got the following:
beeper.h
#pragma once
#include <SDL.h>
#include <SDL_audio.h>
#include <cmath>
#include "logger.h"
class Beeper {
private:
//Should there be sound right now
bool soundOn = true;
//Type of wave that should be generated
int waveType = 0;
//Tone that the wave will produce (may or may not be applicable based on wave type)
float waveTone = 440;
//Running index for sampling
float samplingIndex = 0;
//These are useful variables that cannot be changed outside of this file:
//Volume
const Sint16 amplitude = 32000;
//Sampling rate
const int samplingRate = 44100;
//Buffer size
const int bufferSize = 1024;
//Samples a sine wave at a given index
float sampleSine(float index);
//Samples a square wave at a given index
float sampleSquare(float index);
public:
//Initializes SDL audio, audio device, and audio specs
void initializeAudio();
//Function called by SDL audio_callback that fills stream with samples
void generateSamples(short* stream, int length);
//Turn sound on or off
void setSoundOn(bool soundOnOrOff);
//Set timbre of tone produced by beeper
void setWaveType(int waveTypeID);
//Set tone (in Hz) produced by beeper
void setWaveTone(int waveHz);
};
beeper.cpp
#include <beeper.h>
void fillBuffer(void* userdata, Uint8* _stream, int len) {
short * stream = reinterpret_cast<short*>(_stream);
int length = len;
Beeper* beeper = (Beeper*)userdata;
beeper->generateSamples(stream, length);
}
void Beeper::initializeAudio() {
SDL_AudioSpec desired, returned;
SDL_AudioDeviceID devID;
SDL_zero(desired);
desired.freq = samplingRate;
desired.format = AUDIO_S16SYS; //16-bit audio
desired.channels = 1;
desired.samples = bufferSize;
desired.callback = &fillBuffer;
desired.userdata = this;
devID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0,0), 0, &desired, &returned, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
SDL_PauseAudioDevice(devID, 0);
}
void Beeper::generateSamples(short *stream, int length) {
int samplesToWrite = length / sizeof(short);
for (int i = 0; i < samplesToWrite; i++) {
if (soundOn) {
if (waveType == 0) {
stream[i] = (short)(amplitude * sampleSine(samplingIndex));
}
else if (waveType == 1) {
stream[i] = (short)(amplitude * 0.8 * sampleSquare(samplingIndex));
}
}
else {
stream[i] = 0;
}
//INFO << "Sampling index: " << samplingIndex;
samplingIndex += (waveTone * M_PI * 2) / samplingRate;
//INFO << "Stream input: " << stream[i];
if (samplingIndex >= (M_PI*2)) {
samplingIndex -= M_PI * 2;
}
}
}
void Beeper::setSoundOn(bool soundOnOrOff) {
soundOn = soundOnOrOff;
//if (soundOnOrOff) {
// samplingIndex = 0;
//}
}
void Beeper::setWaveType(int waveTypeID) {
waveType = waveTypeID;
//samplingIndex = 0;
}
void Beeper::setWaveTone(int waveHz) {
waveTone = waveHz;
//samplingIndex = 0;
}
float Beeper::sampleSine(float index) {
double result = sin((index));
//INFO << "Sine result: " << result;
return result;
}
float Beeper::sampleSquare(float index)
{
int unSquaredSin = sin((index));
if (unSquaredSin >= 0) {
return 1;
}
else {
return -1;
}
}
The callback function is being called and the generateSamples function is loading data into the stream, but I cannot hear anything but a very slight click at irregular periods. I have had a look at the data inside the stream and it follows a pattern that I would expect for a scaled sine wave with a 440 Hz frequency. Is there something obvious that I am missing? I did notice that the size of the stream is double what I put when declaring the SDL_AudioSpec and calling SDL_OpenAudioDevice. Why is that?
Answered my own question! When opening the audio device I used the flag SDL_AUDIO_ALLOW_FORMAT_CHANGE which meant that SDL was actually using a float buffer instead of the short buffer that I expected. This was causing issues in a couple of places that were hard to detect (the stream being double the amount of bytes I was expecting should have tipped me off). I changed that parameter in SDL_OpenAudioDevice() to 0 and it worked as expected!

How to make a thread stop excution (eg: std::this_thread::sleep_for) for an accturate interval

I am currently making a small discord bot that can play music to improve my skill. That's why i don't use any discord lib.
I want the music as smooth as possible, but when i played some piece of music, the music produced is very choppy.
here is my code:
concurrency::task<void> play(std::string id) {
auto shared_token = std::make_shared<concurrency::cancellation_token*>(&p_token);
auto shared_running = std::make_shared<bool*>(&running);
return concurrency::create_task([this, id, shared_token] {
audio* source = new audio(id); // create a s16le binary stream using FFMPEG
speak(); // sending speak packet
printf("creating opus encoder\n");
const unsigned short FRAME_MILLIS = 20;
const unsigned short FRAME_SIZE = 960;
const unsigned short SAMPLE_RATE = 48000;
const unsigned short CHANNELS = 2;
const unsigned int BITRATE = 64000;
#define MAX_PACKET_SIZE FRAME_SIZE * 5
int error;
OpusEncoder* encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, OPUS_APPLICATION_AUDIO, &error);
if (error < 0) {
throw "failed to create opus encoder: " + std::string(opus_strerror(error));
}
error = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));
if (error < 0) {
throw "failed to set bitrate for opus encoder: " + std::string(opus_strerror(error));
}
if (sodium_init() == -1) {
throw "libsodium initialisation failed";
}
int num_opus_bytes;
unsigned char* pcm_data = new unsigned char[FRAME_SIZE * CHANNELS * 2];
opus_int16* in_data;
std::vector<unsigned char> opus_data(MAX_PACKET_SIZE);
class timer_event {
bool is_set = false;
public:
bool get_is_set() { return is_set; };
void set() { is_set = true; };
void unset() { is_set = false; };
};
timer_event* run_timer = new timer_event();
run_timer->set();
//this is the send loop
concurrency::create_task([run_timer, this, shared_token] {
while (run_timer->get_is_set()) {
speak();
int i = 0;
while (i < 15) {
utils::sleep(1000);
if (run_timer->get_is_set() == false) {
std::cout << "Stop sending speak packet due to turn off\n";
concurrency::cancel_current_task();
return;
}
if ((*shared_token)->is_canceled()) {
std::cout << "Stop sending speak packet due to cancel\n";
concurrency::cancel_current_task();
return;
}
}
}});
std::deque<std::string>* buffer = new std::deque<std::string>();
auto timer = concurrency::create_task([run_timer, this, buffer, FRAME_MILLIS, shared_token] {
while (run_timer->get_is_set() || buffer->size() > 0) {
utils::sleep(5 * FRAME_MILLIS); //std::this_thread::sleep_for
int loop = 0;
int sent = 0;
auto start = boost::chrono::high_resolution_clock::now();
while (buffer->size() > 0) {
if (udpclient.send(buffer->front()) != 0) { //send frame
//udpclient.send ~ winsock sendto
std::cout << "Stop sendding voice data due to udp error\n";
return;
}
buffer->pop_front();
if ((*shared_token)->is_canceled()) {
std::cout << "Stop sending voice data due to cancel\n";
concurrency::cancel_current_task();
}
sent++; //count sent frame
//calculate next time point we should (in theory) send next frame and store in *delay*
long long next_time = (long long)(sent+1) * (long long)(FRAME_MILLIS) * 1000 ;
auto now = boost::chrono::high_resolution_clock::now();
long long mcs_elapsed = (boost::chrono::duration_cast<boost::chrono::microseconds>(now - start)).count(); // elapsed time from start loop
long long delay = std::max((long long)0, (next_time - mcs_elapsed));
//wait for next time point
boost::asio::deadline_timer timer(context_io);
timer.expires_from_now(boost::posix_time::microseconds(delay));
timer.wait();
}
}
});
unsigned short _sequence = 0;
unsigned int _timestamp = 0;
while (1) {
if (buffer->size() >= 50) {
utils::sleep(FRAME_MILLIS);
}
if (source->read((char*)pcm_data, FRAME_SIZE * CHANNELS * 2) != true)
break;
if ((*shared_token)->is_canceled()) {
std::cout << "Stop encoding due to cancel\n";
break;
}
in_data = (opus_int16*)pcm_data;
num_opus_bytes = opus_encode(encoder, in_data, FRAME_SIZE, opus_data.data(), MAX_PACKET_SIZE);
if (num_opus_bytes <= 0) {
throw "failed to encode frame: " + std::string(opus_strerror(num_opus_bytes));
}
opus_data.resize(num_opus_bytes);
std::vector<unsigned char> packet(12 + opus_data.size() + crypto_secretbox_MACBYTES);
packet[0] = 0x80; //Type
packet[1] = 0x78; //Version
packet[2] = _sequence >> 8; //Sequence
packet[3] = (unsigned char)_sequence;
packet[4] = _timestamp >> 24; //Timestamp
packet[5] = _timestamp >> 16;
packet[6] = _timestamp >> 8;
packet[7] = _timestamp;
packet[8] = (unsigned char)(ssrc >> 24); //SSRC
packet[9] = (unsigned char)(ssrc >> 16);
packet[10] = (unsigned char)(ssrc >> 8);
packet[11] = (unsigned char)ssrc;
_sequence++;
_timestamp += SAMPLE_RATE / 1000 * FRAME_MILLIS; //48000Hz / 1000 * 20(ms)
unsigned char nonce[crypto_secretbox_NONCEBYTES];
memset(nonce, 0, crypto_secretbox_NONCEBYTES);
for (int i = 0; i < 12; i++) {
nonce[i] = packet[i];
}
crypto_secretbox_easy(packet.data() + 12, opus_data.data(), opus_data.size(), nonce, key.data());
packet.resize(12 + opus_data.size() + crypto_secretbox_MACBYTES);
std::string msg;
msg.resize(packet.size(), '\0');
for (unsigned int i = 0; i < packet.size(); i++) {
msg[i] = packet[i];
}
buffer->push_back(msg);
}
run_timer->unset();
timer.wait();
unspeak();
delete run_timer;
delete buffer;
opus_encoder_destroy(encoder);
delete[] pcm_data;
});
}
There are 3 possible causes:
I send packet late so server-end buffer run out, so the sound produced has some silence between each each 2 packets. Maybe the timer is not accurate so the sound is out of sync.
The encode process is wrong which causes lost data somehow.
Bad network (i have tested an open source bot written on java, it worked so i can assume that my network is good enough)
So i post this question, hope someone has experienced this situation show me what wrong and what should i do to correct it.
I figured out the problem myself. I want to post solution here for someone who need.
The problem is the timer is unstable so it's usually sleep more than it should, so it makes the music broken.
I changed it to an accurate sleep function which i found somewhere on the internet(i don't remember the source, sorry for that, if you know it please credit it bellow).
Function source code:
#include <math.h>
#include <chrono>
#include <window.h>
static void timerSleep(double seconds) {
using namespace std::chrono;
static HANDLE timer = CreateWaitableTimer(NULL, FALSE, NULL);
static double estimate = 5e-3;
static double mean = 5e-3;
static double m2 = 0;
static int64_t count = 1;
while (seconds - estimate > 1e-7) {
double toWait = seconds - estimate;
LARGE_INTEGER due;
due.QuadPart = -int64_t(toWait * 1e7);
auto start = high_resolution_clock::now();
SetWaitableTimerEx(timer, &due, 0, NULL, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
auto end = high_resolution_clock::now();
double observed = (end - start).count() / 1e9;
seconds -= observed;
++count;
double error = observed - toWait;
double delta = error - mean;
mean += delta / count;
m2 += delta * (error - mean);
double stddev = sqrt(m2 / (count - 1));
estimate = mean + stddev;
}
// spin lock
auto start = high_resolution_clock::now();
while ((high_resolution_clock::now() - start).count() / 1e9 < seconds);
}
Thank you for your support!

OpenAl Beep function fails with more threads

I tried to write a Beep function using OpenAl. But only a few threads of the function can be run. The following code fails after a while:
#include <cmath>
#include <iostream>
#include <thread>
using namespace std;
#include <AL/alut.h> // OpenAl
void init_al()
{
const char *defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
ALCdevice* dev = alcOpenDevice(defname);
ALCcontext *ctx = alcCreateContext(dev, NULL);
alcMakeContextCurrent(ctx);
}
void exit_al()
{
ALCcontext* ctx = alcGetCurrentContext();
ALCdevice* dev = alcGetContextsDevice(ctx);
alcMakeContextCurrent(0);
alcDestroyContext(ctx);
alcCloseDevice(dev);
}
void Beep(float freq = 440, float seconds = 0.5)
{
init_al();
ALuint buf;
alGenBuffers(1, &buf);
unsigned sample_rate = 10000;
size_t buf_size = seconds * sample_rate;
short* samples = new short[buf_size];
if(samples == 0)
{
cout<< "It seems there is no more heap memory. Sorry we cannot make a beep!";
}
for(unsigned i = 0; i < buf_size; i++)
samples[i] = 32760*sin(2*M_PI*i*freq/sample_rate);
alBufferData(buf,AL_FORMAT_MONO16,samples,buf_size,sample_rate);
ALuint src;
alGenSources(1,&src);
alSourcei(src,AL_BUFFER,buf);
alSourcePlay(src);
alutSleep(seconds + 0.5);
delete[] samples;
exit_al();
}
int main()
{
for(int i = 1; i < 1000; i++)
{
thread t(Beep, 440,0.5);
t.detach();
alutSleep(0.01);
}
}
What's wrong with Beep function? And how can I fix it?
I used this Beep function in a normal program where a problem occured. I have reproduced the error in program above. The problem is that Beeb becomes unresponsive after a while, even if you leave it alone for a few seconds to complete its previous tasks. So it uses something up and doesn't return it. If you change 1000 to 20 nothing changes.
I added 3 new lines to the program and placed init_al and exit_al in main and it seems the problem is solved now. btw, I don't know why is it necessary to place init_al and exit_al in main so this is just a partial answer to the question:
#include <cmath>
#include <iostream>
#include <thread>
using namespace std;
#include <AL/alut.h> // OpenAl
void init_al()
{
const char *defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
ALCdevice* dev = alcOpenDevice(defname);
ALCcontext *ctx = alcCreateContext(dev, NULL);
alcMakeContextCurrent(ctx);
}
void exit_al()
{
ALCcontext* ctx = alcGetCurrentContext();
ALCdevice* dev = alcGetContextsDevice(ctx);
alcMakeContextCurrent(0);
alcDestroyContext(ctx);
alcCloseDevice(dev);
}
void Beep(float freq = 440, float seconds = 0.5)
{
ALuint buf;
alGenBuffers(1, &buf);
unsigned sample_rate = 10000;
size_t buf_size = seconds * sample_rate;
short* samples = new short[buf_size];
if(samples == 0)
{
cout<< "It seems there is no more heap memory. Sorry we cannot make a beep!";
}
for(unsigned i = 0; i < buf_size; i++)
samples[i] = 32760*sin(2*M_PI*i*freq/sample_rate);
alBufferData(buf,AL_FORMAT_MONO16,samples,buf_size,sample_rate);
ALuint src;
alGenSources(1,&src);
alSourcei(src,AL_BUFFER,buf);
alSourcePlay(src);
alutSleep(seconds + 0.5);
delete[] samples;
alSourceStopv(1,&src); // new line
alDeleteSources(1,&src); // new line
alDeleteBuffers(1,&buf); // new line
}
int main()
{
init_al();
for(int i = 1; i < 1000; i++)
{
thread t(Beep, 440,0.5);
t.detach();
alutSleep(0.01);
cout<< i << "\n";
}
exit_al();
}

Simple sound wave generator with SDL in c++

i am having problems understanding how the audio part of the sdl library works
now, i know that when you initialize it, you have to specify the frequency and a >>callback<< function, which i think is then called automatically at the given frequency.
can anyone who worked with the sdl library write a simple example that would use sdl_audio to generate a 440 hz square wave (since it is the simplest waveform) at a sampling frequency of 44000 hz?
The Introduction to SDL (2011 cached version: 2) has got a neat example of using SDL Sound library that should get you started: http://www.libsdl.org/intro.en/usingsound.html
EDIT: Here is a working program that does what you asked for. I modified a bit the code found here: http://www.dgames.org/beep-sound-with-sdl/
#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#include <queue>
#include <cmath>
const int AMPLITUDE = 28000;
const int FREQUENCY = 44100;
struct BeepObject
{
double freq;
int samplesLeft;
};
class Beeper
{
private:
double v;
std::queue<BeepObject> beeps;
public:
Beeper();
~Beeper();
void beep(double freq, int duration);
void generateSamples(Sint16 *stream, int length);
void wait();
};
void audio_callback(void*, Uint8*, int);
Beeper::Beeper()
{
SDL_AudioSpec desiredSpec;
desiredSpec.freq = FREQUENCY;
desiredSpec.format = AUDIO_S16SYS;
desiredSpec.channels = 1;
desiredSpec.samples = 2048;
desiredSpec.callback = audio_callback;
desiredSpec.userdata = this;
SDL_AudioSpec obtainedSpec;
// you might want to look for errors here
SDL_OpenAudio(&desiredSpec, &obtainedSpec);
// start play audio
SDL_PauseAudio(0);
}
Beeper::~Beeper()
{
SDL_CloseAudio();
}
void Beeper::generateSamples(Sint16 *stream, int length)
{
int i = 0;
while (i < length) {
if (beeps.empty()) {
while (i < length) {
stream[i] = 0;
i++;
}
return;
}
BeepObject& bo = beeps.front();
int samplesToDo = std::min(i + bo.samplesLeft, length);
bo.samplesLeft -= samplesToDo - i;
while (i < samplesToDo) {
stream[i] = AMPLITUDE * std::sin(v * 2 * M_PI / FREQUENCY);
i++;
v += bo.freq;
}
if (bo.samplesLeft == 0) {
beeps.pop();
}
}
}
void Beeper::beep(double freq, int duration)
{
BeepObject bo;
bo.freq = freq;
bo.samplesLeft = duration * FREQUENCY / 1000;
SDL_LockAudio();
beeps.push(bo);
SDL_UnlockAudio();
}
void Beeper::wait()
{
int size;
do {
SDL_Delay(20);
SDL_LockAudio();
size = beeps.size();
SDL_UnlockAudio();
} while (size > 0);
}
void audio_callback(void *_beeper, Uint8 *_stream, int _length)
{
Sint16 *stream = (Sint16*) _stream;
int length = _length / 2;
Beeper* beeper = (Beeper*) _beeper;
beeper->generateSamples(stream, length);
}
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_AUDIO);
int duration = 1000;
double Hz = 440;
Beeper b;
b.beep(Hz, duration);
b.wait();
return 0;
}
Good luck.
A boiled-down variant of the beeper-example, reduced to the bare minimum (with error-handling).
#include <math.h>
#include <SDL.h>
#include <SDL_audio.h>
const int AMPLITUDE = 28000;
const int SAMPLE_RATE = 44100;
void audio_callback(void *user_data, Uint8 *raw_buffer, int bytes)
{
Sint16 *buffer = (Sint16*)raw_buffer;
int length = bytes / 2; // 2 bytes per sample for AUDIO_S16SYS
int &sample_nr(*(int*)user_data);
for(int i = 0; i < length; i++, sample_nr++)
{
double time = (double)sample_nr / (double)SAMPLE_RATE;
buffer[i] = (Sint16)(AMPLITUDE * sin(2.0f * M_PI * 441.0f * time)); // render 441 HZ sine wave
}
}
int main(int argc, char *argv[])
{
if(SDL_Init(SDL_INIT_AUDIO) != 0) SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
int sample_nr = 0;
SDL_AudioSpec want;
want.freq = SAMPLE_RATE; // number of samples per second
want.format = AUDIO_S16SYS; // sample type (here: signed short i.e. 16 bit)
want.channels = 1; // only one channel
want.samples = 2048; // buffer-size
want.callback = audio_callback; // function SDL calls periodically to refill the buffer
want.userdata = &sample_nr; // counter, keeping track of current sample number
SDL_AudioSpec have;
if(SDL_OpenAudio(&want, &have) != 0) SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to open audio: %s", SDL_GetError());
if(want.format != have.format) SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to get the desired AudioSpec");
SDL_PauseAudio(0); // start playing sound
SDL_Delay(1000); // wait while sound is playing
SDL_PauseAudio(1); // stop playing sound
SDL_CloseAudio();
return 0;
}
SDL 2 C example
The following code produces a sinusoidal sound, it is adapted from: https://codereview.stackexchange.com/questions/41086/play-some-sine-waves-with-sdl2
main.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <SDL2/SDL.h>
const double ChromaticRatio = 1.059463094359295264562;
const double Tao = 6.283185307179586476925;
Uint32 sampleRate = 48000;
Uint32 frameRate = 60;
Uint32 floatStreamLength = 1024;
Uint32 samplesPerFrame;
Uint32 msPerFrame;
double practicallySilent = 0.001;
Uint32 audioBufferLength = 48000;
float *audioBuffer;
SDL_atomic_t audioCallbackLeftOff;
Sint32 audioMainLeftOff;
Uint8 audioMainAccumulator;
SDL_AudioDeviceID AudioDevice;
SDL_AudioSpec audioSpec;
SDL_Event event;
SDL_bool running = SDL_TRUE;
typedef struct {
float *waveform;
Uint32 waveformLength;
double volume;
double pan;
double frequency;
double phase;
} voice;
void speak(voice *v) {
float sample;
Uint32 sourceIndex;
double phaseIncrement = v->frequency/sampleRate;
Uint32 i;
if (v->volume > practicallySilent) {
for (i = 0; (i + 1) < samplesPerFrame; i += 2) {
v->phase += phaseIncrement;
if (v->phase > 1)
v->phase -= 1;
sourceIndex = v->phase*v->waveformLength;
sample = v->waveform[sourceIndex]*v->volume;
audioBuffer[audioMainLeftOff+i] += sample*(1-v->pan);
audioBuffer[audioMainLeftOff+i+1] += sample*v->pan;
}
}
else {
for (i=0; i<samplesPerFrame; i+=1)
audioBuffer[audioMainLeftOff+i] = 0;
}
audioMainAccumulator++;
}
double getFrequency(double pitch) {
return pow(ChromaticRatio, pitch-57)*440;
}
int getWaveformLength(double pitch) {
return sampleRate / getFrequency(pitch)+0.5f;
}
void buildSineWave(float *data, Uint32 length) {
Uint32 i;
for (i=0; i < length; i++)
data[i] = sin(i*(Tao/length));
}
void logSpec(SDL_AudioSpec *as) {
printf(
" freq______%5d\n"
" format____%5d\n"
" channels__%5d\n"
" silence___%5d\n"
" samples___%5d\n"
" size______%5d\n\n",
(int) as->freq,
(int) as->format,
(int) as->channels,
(int) as->silence,
(int) as->samples,
(int) as->size
);
}
void logVoice(voice *v) {
printf(
" waveformLength__%d\n"
" volume__________%f\n"
" pan_____________%f\n"
" frequency_______%f\n"
" phase___________%f\n",
v->waveformLength,
v->volume,
v->pan,
v->frequency,
v->phase
);
}
void logWavedata(float *floatStream, Uint32 floatStreamLength, Uint32 increment) {
printf("\n\nwaveform data:\n\n");
Uint32 i=0;
for (i = 0; i < floatStreamLength; i += increment)
printf("%4d:%2.16f\n", i, floatStream[i]);
printf("\n\n");
}
void audioCallback(void *unused, Uint8 *byteStream, int byteStreamLength) {
float* floatStream = (float*) byteStream;
Sint32 localAudioCallbackLeftOff = SDL_AtomicGet(&audioCallbackLeftOff);
Uint32 i;
for (i = 0; i < floatStreamLength; i++) {
floatStream[i] = audioBuffer[localAudioCallbackLeftOff];
localAudioCallbackLeftOff++;
if (localAudioCallbackLeftOff == audioBufferLength)
localAudioCallbackLeftOff = 0;
}
SDL_AtomicSet(&audioCallbackLeftOff, localAudioCallbackLeftOff);
}
int init(void) {
SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_AudioSpec want;
SDL_zero(want);
want.freq = sampleRate;
want.format = AUDIO_F32;
want.channels = 2;
want.samples = floatStreamLength;
want.callback = audioCallback;
AudioDevice = SDL_OpenAudioDevice(NULL, 0, &want, &audioSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
if (AudioDevice == 0) {
printf("\nFailed to open audio: %s\n", SDL_GetError());
return 1;
}
printf("want:\n");
logSpec(&want);
printf("audioSpec:\n");
logSpec(&audioSpec);
if (audioSpec.format != want.format) {
printf("\nCouldn't get Float32 audio format.\n");
return 2;
}
sampleRate = audioSpec.freq;
floatStreamLength = audioSpec.size / 4;
samplesPerFrame = sampleRate / frameRate;
msPerFrame = 1000 / frameRate;
audioMainLeftOff = samplesPerFrame * 8;
SDL_AtomicSet(&audioCallbackLeftOff, 0);
if (audioBufferLength % samplesPerFrame)
audioBufferLength += samplesPerFrame - (audioBufferLength % samplesPerFrame);
audioBuffer = malloc(sizeof(float) * audioBufferLength);
return 0;
}
int onExit(void) {
SDL_CloseAudioDevice(AudioDevice);
SDL_Quit();
return 0;
}
int main(int argc, char *argv[]) {
float syncCompensationFactor = 0.0016;
Sint32 mainAudioLead;
Uint32 i;
voice testVoiceA;
voice testVoiceB;
voice testVoiceC;
testVoiceA.volume = 1;
testVoiceB.volume = 1;
testVoiceC.volume = 1;
testVoiceA.pan = 0.5;
testVoiceB.pan = 0;
testVoiceC.pan = 1;
testVoiceA.phase = 0;
testVoiceB.phase = 0;
testVoiceC.phase = 0;
testVoiceA.frequency = getFrequency(45);
testVoiceB.frequency = getFrequency(49);
testVoiceC.frequency = getFrequency(52);
Uint16 C0waveformLength = getWaveformLength(0);
testVoiceA.waveformLength = C0waveformLength;
testVoiceB.waveformLength = C0waveformLength;
testVoiceC.waveformLength = C0waveformLength;
float sineWave[C0waveformLength];
buildSineWave(sineWave, C0waveformLength);
testVoiceA.waveform = sineWave;
testVoiceB.waveform = sineWave;
testVoiceC.waveform = sineWave;
if (init())
return 1;
SDL_Delay(42);
SDL_PauseAudioDevice(AudioDevice, 0);
while (running) {
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
running = SDL_FALSE;
}
}
for (i = 0; i < samplesPerFrame; i++)
audioBuffer[audioMainLeftOff+i] = 0;
speak(&testVoiceA);
speak(&testVoiceB);
speak(&testVoiceC);
if (audioMainAccumulator > 1) {
for (i=0; i<samplesPerFrame; i++) {
audioBuffer[audioMainLeftOff+i] /= audioMainAccumulator;
}
}
audioMainAccumulator = 0;
audioMainLeftOff += samplesPerFrame;
if (audioMainLeftOff == audioBufferLength)
audioMainLeftOff = 0;
mainAudioLead = audioMainLeftOff - SDL_AtomicGet(&audioCallbackLeftOff);
if (mainAudioLead < 0)
mainAudioLead += audioBufferLength;
if (mainAudioLead < floatStreamLength)
printf("An audio collision may have occured!\n");
SDL_Delay(mainAudioLead * syncCompensationFactor);
}
onExit();
return 0;
}
Compile and run:
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -lSDL2 -lm
./main.out
Should be easy to turn this into a simple piano with: https://github.com/cirosantilli/cpp-cheat/blob/f734a2e76fbcfc67f707ae06be7a2a2ef5db47d1/c/interactive/audio_gen.c#L44
For wav manipulation, also check the official examples:
http://hg.libsdl.org/SDL/file/e12c38730512/test/testresample.c
http://hg.libsdl.org/SDL/file/e12c38730512/test/loopwave.c
Tested on Ubuntu 19.10, SDL 2.0.10.
This is a minimal example of how to play a sine wave in SDL2.
Make sure to call SDL_Init(SDL_INIT_AUDIO) before creating an instance of Sound.
Sound.h
#include <cstdint>
#include <SDL2/SDL.h>
class Sound
{
public:
Sound();
~Sound();
void play();
void stop();
const double m_sineFreq;
const double m_sampleFreq;
const double m_samplesPerSine;
uint32_t m_samplePos;
private:
static void SDLAudioCallback(void *data, Uint8 *buffer, int length);
SDL_AudioDeviceID m_device;
};
Sound.cpp
#include "Sound.h"
#include <cmath>
#include <iostream>
Sound::Sound()
: m_sineFreq(1000),
m_sampleFreq(44100),
m_samplesPerSine(m_sampleFreq / m_sineFreq),
m_samplePos(0)
{
SDL_AudioSpec wantSpec, haveSpec;
SDL_zero(wantSpec);
wantSpec.freq = m_sampleFreq;
wantSpec.format = AUDIO_U8;
wantSpec.channels = 1;
wantSpec.samples = 2048;
wantSpec.callback = SDLAudioCallback;
wantSpec.userdata = this;
m_device = SDL_OpenAudioDevice(NULL, 0, &wantSpec, &haveSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
if (m_device == 0)
{
std::cout << "Failed to open audio: " << SDL_GetError() << std::endl;
}
}
Sound::~Sound()
{
SDL_CloseAudioDevice(m_device);
}
void Sound::play()
{
SDL_PauseAudioDevice(m_device, 0);
}
void Sound::stop()
{
SDL_PauseAudioDevice(m_device, 1);
}
void Sound::SDLAudioCallback(void *data, Uint8 *buffer, int length)
{
Sound *sound = reinterpret_cast<Sound*>(data);
for(int i = 0; i < length; ++i)
{
buffer[i] = (std::sin(sound->m_samplePos / sound->m_samplesPerSine * M_PI * 2) + 1) * 127.5;
++sound->m_samplePos;
}
}