-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Tutorial: Play Sine Wave
This is intended for newcomers to SFML who want to experiment a bit with the audio library in version 3.
The way sound is loaded in SFML is via the SoundBuffer
class. We give loadFromSamples()
an array of 16 bit integers to load up along with how many samples there are in the array, how many channels we're dealing with (we're only going to be working with mono in this tutorial), the sample rate of the audio and a "channel map" (again, just mono here). Sample rate just tells the computer exactly how many samples it should be processing in one second. We're going to use a standard sample rate of 44100 for this and we're going to load up exactly one second of audio. This means that the number of samples will have to match the sample rate.
Once we load up the SoundBuffer
, we just create a Sound
class that points at the buffer we just created and set it to loop with setLooping()
so that it continuously plays. After this, the audio is playing but if the program ends, the audio will end, so we put in a while loop that checks for an Escape keypress without taking up too many resources by forcing the program to sleep every 100 milliseconds. Here's the code just for playing an empty 'array of samples', but there's no real data in it yet.
#include <SFML/Audio.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <vector>
#include <iostream>
int main()
{
constexpr std::size_t SAMPLES = 44100;
constexpr std::size_t SAMPLE_RATE = 44100;
std::vector<std::int16_t> raw(SAMPLES); // using an std::vector keeps this large resource off the stack and stores it in the heap
sf::SoundBuffer buffer{};
if (!buffer.loadFromSamples(raw.data(), raw.size(), 1, SAMPLE_RATE, { sf::SoundChannel::Mono }))
{
std::cerr << "Loading failed!" << std::endl;
return EXIT_FAILURE;
}
sf::Sound sound(buffer);
sound.setLooping(true);
sound.play();
while (!sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) // we can quit by holding Escape
{
sf::sleep(sf::milliseconds(100));
}
}
Now here comes the fun part. We want to load up raw
with a sine wave that plays at 440hz (A4). We know that specifying the frequency of a sine wave is as simple as sin(x*2π*frequency)
, but we have to remember that the sample rate dictates our time domain. So we have to multiply by increments of 440/44100
in order to get a sine wave that properly fits our sample rate. The last part is the amplitude. We will pick a nice and loud amplitude so you can hear it clearly over your speakers. 30,000 should do.
constexpr std::int16_t AMPLITUDE = 30000;
constexpr double TWO_PI = 6.28318;
constexpr double increment = 440.0 / 44100.0;
double x = 0.0;
for (std::size_t i = 0; i < SAMPLES; ++i)
{
raw[i] = static_cast<std::int16_t>(AMPLITUDE * sin(x * TWO_PI));
x += increment;
}
And that's it! Run the code and see if you can hear it. Remember to link with the audio module; you'll also need to link* to the window module if you use the keypress check provided. Here is the final code if you just want to copy & paste it:
#include <SFML/Audio.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <vector>
#include <cmath>
#include <iostream>
int main()
{
constexpr std::size_t SAMPLES = 44100;
constexpr std::size_t SAMPLE_RATE = 44100;
std::vector<std::int16_t> raw(SAMPLES); // using an std::vector keeps this large resource off the stack and stores it in the heap
constexpr std::int16_t AMPLITUDE = 30000;
constexpr double TWO_PI = 6.28318;
constexpr double increment = 440.0 / 44100.0;
double x = 0.0;
for (std::size_t i = 0; i < SAMPLES; ++i)
{
raw[i] = static_cast<std::int16_t>(AMPLITUDE * sin(x * TWO_PI));
x += increment;
}
sf::SoundBuffer buffer{};
if (!buffer.loadFromSamples(raw.data(), raw.size(), 1, SAMPLE_RATE, { sf::SoundChannel::Mono }))
{
std::cerr << "Loading failed!" << std::endl;
return EXIT_FAILURE;
}
sf::Sound sound(buffer);
sound.setLooping(true);
sound.play();
while (!sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) // we can quit by holding Escape
{
sf::sleep(sf::milliseconds(100));
}
}
*for more information on how to link SFML modules, please see the related official Getting Started tutorials: https://www.sfml-dev.org/tutorials/3.0/
a similar (to the original version aimed at SFML 2) code sample is also available in OCaml
Now try to get this playing the US dial tone. Here's a hint: it's made up of the frequencies 350hz and 440hz. Sine waves are added when combined.