lundi 13 juin 2016

SFML Audio, SoundStream SoundBuffer and Sound

Overview

I am experimenting with SFML and audio in a project I am working on.

Essentially I am attempting to implement a sort of digital synthesizer as a kind of artistic scientific experiment.

What I want to do is play two different frequency tones, one after the other, to check I can get this to work as I intend.

Work so far

So far I have implemented some stuff... I'm not really sure what I'm doing yet, so this is all a bit rough.

I have 2 std::vectors which I will fill with data to play. I think I need a sf::Mutex to prevent data races or something because I have more than one thread working with the same data.

In my int main I have some stuff, the below section works and plays 1 second of audio, single 440 Hz tone.

sf::Mutex mutex;
std::vector<int16_t> samples_buffer_0;
std::vector<int16_t> samples_buffer_1;
int samples_buffer_switch = 0;
size_t samples_buffer_0_offset = 0; // flag do not need data
size_t samples_buffer_1_offset = -1; // flag need data

samples_buffer_0.resize(44100);
samples_buffer_1.resize(44100);

int hz_flag_0 = 0; // intend to use later
int hz_flag_1 = 1; // default to play this tone after 1 s of 440 Hz

double hz_freq_0 = 440; // tones to play
double hz_freq_1 = 880;

double time = 0.0;
double time_step = 1.0/44100.0;

sf::SoundBuffer soundbuffer;
sf::Sound sound;

// initialize a buffer
for(size_t i = 0; i < samples_buffer_0.size(); ++ i)
{
    samples_buffer_0[i] = 0;

    samples_buffer_0[i] += 10000.0 * std::sin(2.0 * 3.14159 * hz_freq_0 * time);

    time += time_step;
}

// load buffer to soundbuffer object
soundbuffer.loadFromSamples(samples_buffer_0.data(), samples_buffer_0.size(), 1, 44100);

// play from this soundbuffer object
sound.setBuffer(soundbuffer);
sound.play();

The code which follows does not appear to be working. After 1 second, the tone stops, and nothing further happens...

while(window.isOpen())
{
    sf::Event event;
    while(window.pollEvent(event))
    {
        if(event.type == sf::Event::Closed)
            window.close();

        if(event.type == sf::Event::KeyPressed)
        {
            if(event.key.code == sf::Keyboard::Key::Escape)
                window.close();

            if(event.key.code == sf::Keyboard::Key::Z)
            {
                if(hz_flag_0 == 0)
                {
                    hz_flag_0 = 1;
                }
                else
                {
                    hz_flag_0 = 0;
                }
            }

            if(event.key.code == sf::Keyboard::Key::X)
            {
                if(hz_flag_1 == 0)
                {
                    hz_flag_1 = 1;
                }
                else
                {
                    hz_flag_1 = 0;
                }
            }
        }

        if(event.type == sf::Event::KeyReleased)
        {

        }
    }

    if(samples_buffer_switch == 0)
    {
        // if need next data
        if(samples_buffer_1_offset != 0)
        {
            for(size_t i = 0; i < samples_buffer_1.size(); ++ i)
            {
                samples_buffer_1[i] = 0;

                if(hz_flag_0 == 1)
                {
                    samples_buffer_1[i] += 10000.0 * std::sin(2.0 * 3.14159 * hz_freq_0 * time);
                }

                if(hz_flag_1 == 1)
                {
                    samples_buffer_1[i] += 5000.0 * std::sin(2.0 * 3.14159 * hz_freq_1 * time);
                }

                time += time_step;
            }

            samples_buffer_1_offset = 0;
        }
    }
    else if(samples_buffer_switch == 1)
    {
        // if need next data
        if(samples_buffer_0_offset != 0)
        {

            for(size_t i = 0; i < samples_buffer_0.size(); ++ i)
            {
                samples_buffer_0[i] = 0;

                if(hz_flag_0 == 1)
                {
                    samples_buffer_0[i] += 10000.0 * std::sin(2.0 * 3.14159 * hz_freq_0 * time);
                }

                if(hz_flag_1 == 1)
                {
                    samples_buffer_0[i] += 5000.0 * std::sin(2.0 * 3.14159 * hz_freq_1 * time);
                }

                time += time_step;
            }

            samples_buffer_0_offset = 0;
        }
    }


    // very unsure of what to do here...
    // tried using the code which is now commented out but
    // computer just made a horrific noise
    // didn't really expect this to work, as it will continually
    // restart playing of the buffer from the start...
    //        
    // What should I do?

    if(samples_buffer_switch == 0)
    {
        //sf::Lock lock(mutex);
        //soundbuffer.loadFromSamples(&samples_buffer_0[0], samples_buffer_0.size(), 1, 44100);
        //sound.play();
        //samples_buffer_switch = 1;
    }
    else if(samples_buffer_switch == 1)
    {
        //sf::Lock lock(mutex);
        //soundbuffer.loadFromSamples(&samples_buffer_1[0], samples_buffer_1.size(), 1, 44100);
        //sound.play();
        //samples_buffer_switch = 0;
    }


    window.clear();
    keyboard.Draw(window);
    window.display();
}

Additional

So far my understanding is as follows:

  • sf::Sound can launch a new thread via the play() function, which then plays some audio samples, which are stored in a sf::SoundBuffer, in an independent thread.

  • I think I may need to use a sf::Soundstream object to accomplish what I am trying to do? I think I need to create my own class which inherits from sf::Soundstream, overloading the onGetData() and onSeek() functions? (But I am not sure about this.)

Questions I have include:

  • What happens when the sf::Sound runs out of data to play? Do I have to call play() again when I fill a soundbuffer with new samples to play?

  • How can I tell when I need to fill the next buffer with data? How can I tell when the sf::Sound object is about to run out of data to play?

  • Should I be doing something with a sf::Soundbuffer instead of what I am attempting?

Aucun commentaire:

Enregistrer un commentaire