I have read this tutorial.
https://github.com/SFML/SFML/wiki/Tutorial:-Play-Sine-WaveThis is good if I can change the sound buffer tone real time.
Means if I press some key,then I will change the sine wave buffer to other pitch or completely change sine wave to saw or square wave or noise.
And I need to play an editable buffer and the buffer will play endlessly,or loop I guess.
========================================
OK.After a few days work,finally figure it out.
I will paste the code here in case someone may need it.
This is a Simple Real-time Audio Synthesizer which can generate tones on the fly.
I'm using Win7 VS2017 SFML2.5.1.And I only tested this on my computer.Not sure if it is 100% work for others.
Use Keys QWERT ... or ZXCVB ... just below Keys 12345 will trigger the notes,similar to FL Studio Keys.
Code is not optimized, but should be easy enough to understand how to fill audio callback real-time with SFML Audio.
#include <string>
#include <vector>
#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>
using namespace std;
int ScreenWidth = 800;
int ScreenHeight = 600;
#define MAXVOL (0.22*32768)
#define DECAY (MAXVOL/4000)
#define KEYS 32
#define M_PI 3.14159265358979323846
float vol[KEYS] = { 0 };
float phase[KEYS] = { 0 };
int sampleRate = 44100;
int numChannel = 2;
int audioBufSize = 2048;
int octave = 0;
//0 sin
//1 triangle
//2 saw
//3 square
//4 white noise
int waveShapeType = 0;
class ShapeMode
{
public:
static int const sine = 0;
static int const triangle = 1;
static int const saw = 2;
static int const square = 3;
static int const whiteNoise = 4;
};
sf::RenderWindow window;
string titleStr = "";
string noteStr = "0";
string octStr = "0";
string waveShapeStr = "0";
void UpdateAllString()
{
titleStr = "waveShape: " + waveShapeStr + ", octave: " + octStr + ", note: " + noteStr;
window.setTitle(titleStr);
}
const double PI2 = M_PI * 2;
float baseAFreq = 440;
bool keyBuf[sf::Keyboard::Key::KeyCount] = { false };
bool keyBuf_last[sf::Keyboard::Key::KeyCount] = { false };
int keymap[]{
sf::Keyboard::Key::Z,
sf::Keyboard::Key::S,
sf::Keyboard::Key::X,
sf::Keyboard::Key::D,
sf::Keyboard::Key::C,
sf::Keyboard::Key::V,
sf::Keyboard::Key::G,
sf::Keyboard::Key::B,
sf::Keyboard::Key::H,
sf::Keyboard::Key::N,
sf::Keyboard::Key::J,
sf::Keyboard::Key::M,
sf::Keyboard::Key::Q,
sf::Keyboard::Key::Num2,
sf::Keyboard::Key::W,
sf::Keyboard::Key::Num3,
sf::Keyboard::Key::E,
sf::Keyboard::Key::R,
sf::Keyboard::Key::Num5,
sf::Keyboard::Key::T,
sf::Keyboard::Key::Num6,
sf::Keyboard::Key::Y,
sf::Keyboard::Key::Num7,
sf::Keyboard::Key::U,
sf::Keyboard::Key::I,
sf::Keyboard::Key::Num9,
sf::Keyboard::Key::O,
sf::Keyboard::Key::Num0,
sf::Keyboard::Key::P,
sf::Keyboard::Key::LBracket,
sf::Keyboard::Key::RBracket,
sf::Keyboard::Key::Backslash
};
int numKeys = sizeof(keymap) / sizeof(keymap[0]);
bool IsKeyPressOnce(int keyCode)
{
if (keyBuf[keyCode] && !keyBuf_last[keyCode]) return true;
else return false;
}
class MyStream : public sf::SoundStream
{
public:
void SetBufSize(int sz, int numChannel, int sampleRate)
{
m_samples.resize(sz, 0);
m_currentSample = 0;
initialize(numChannel, sampleRate);
}
private:
virtual bool onGetData(Chunk& data)
{
int len = m_samples.size();
for (int i = 0; i < len; ++i)
{
m_samples[i] = 0;
}
data.samples = &m_samples[m_currentSample];
int k = 0;
int c = 0;
float s = 0;
float increment = 0;
for (k = 0; k < numKeys; k++)
{
if (vol[k] <= 0) continue;
increment = PI2 * pow(2.0, (k + 3 + 12 * octave) / 12.0) * baseAFreq / sampleRate;
for (c = 0; c < audioBufSize; c += numChannel)
{
if (waveShapeType == ShapeMode::sine)
{
s = m_samples[c] + sin(phase[k])*vol[k];
}
else if (waveShapeType == ShapeMode::triangle)
{
double t2 = fmodf(phase[k], PI2) / (PI2);
int t3 = floor(t2 + 0.5f);
double t4 = fabs(t2 - t3) * 4 - 1;
s = m_samples[c] + t4 * vol[k];
}
else if (waveShapeType == ShapeMode::saw)
{
double t2 = phase[k] / PI2;
int t3 = floor(t2 + 0.5f);
double t4 = (t2 - t3) * 2;
s = m_samples[c] + t4 * vol[k];
}
else if (waveShapeType == ShapeMode::square)
{
double t2 = fmodf(phase[k], PI2) / (PI2);
double t3 = t2 > 0.5 ? 1 : -1;
s = m_samples[c] + t3 * vol[k];
}
else if (waveShapeType == ShapeMode::whiteNoise)
{
s = rand() % 65536 - 32768;
}
if (s > 32767) s = 32767;
else if (s < -32768) s = -32768;
m_samples[c] = s;
m_samples[c + 1] = s;
phase[k] += increment;
if (vol[k] < MAXVOL)
{
vol[k] -= DECAY;
if (vol[k] <= 0)
{
vol[k] = 0;
break;
}
}
}
phase[k] = fmod(phase[k], PI2);
}
data.sampleCount = audioBufSize;
m_currentSample = 0;
return true;
}
virtual void onSeek(sf::Time timeOffset)
{
m_currentSample = static_cast<std::size_t>(timeOffset.asSeconds() * getSampleRate() * getChannelCount());
}
std::vector<sf::Int16> m_samples;
std::size_t m_currentSample;
};
int main()
{
window.create(sf::VideoMode(ScreenWidth, ScreenHeight), "SFML2 Simple Audio Synth");
MyStream stream;
stream.SetBufSize(audioBufSize, numChannel, sampleRate);
stream.play();
while (window.isOpen())
{
sf::Event ev;
while (window.pollEvent(ev))
{
switch (ev.type)
{
case sf::Event::Closed: window.close(); break;
case sf::Event::KeyPressed:
keyBuf[ev.key.code] = true;
break;
case sf::Event::KeyReleased:
keyBuf[ev.key.code] = false;
break;
default:
break;
}
}
if (keyBuf[sf::Keyboard::Escape])
{
window.close();
}
for (int key = 0; key < numKeys; ++key)
{
if (keyBuf[keymap[key]] && vol[key] < MAXVOL)
{
phase[key] = 0;
vol[key] = MAXVOL + DECAY / 2;
int note = key + 12 * octave;
noteStr = to_string(note);
UpdateAllString();
}
if(!keyBuf[keymap[key]] && vol[key] > 0)
{
vol[key] -= DECAY;
}
}
if (IsKeyPressOnce(sf::Keyboard::Key::Left))
{
--waveShapeType;
waveShapeType = waveShapeType < 0 ? 4 : waveShapeType;
waveShapeStr = to_string(waveShapeType);
UpdateAllString();
}
if (IsKeyPressOnce(sf::Keyboard::Key::Right))
{
++waveShapeType;
waveShapeType = waveShapeType > 4 ? 0 : waveShapeType;
waveShapeStr = to_string(waveShapeType);
UpdateAllString();
}
if (IsKeyPressOnce(sf::Keyboard::Key::Down))
{
--octave;
octave = octave < -3 ? -3 : octave;
octStr = to_string(octave);
UpdateAllString();
}
if (IsKeyPressOnce(sf::Keyboard::Key::Up))
{
++octave;
octave = octave > 4 ? 4 : octave;
octStr = to_string(octave);
UpdateAllString();
}
window.display();
window.clear();
sf::sleep(sf::microseconds(1));
for (int k = 0; k < sf::Keyboard::Key::KeyCount; ++k)
{
keyBuf_last[k] = keyBuf[k];
}
}
return 0;
}