Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Dynamically Affecting sf::SoundBuffers  (Read 2738 times)

0 Members and 1 Guest are viewing this topic.

chjolo

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Dynamically Affecting sf::SoundBuffers
« on: June 25, 2012, 05:01:56 am »
I'm trying to make filters and other audio effects that can be applied in real time for a game engine. I've found a simple lowpass filter formula that works just fine. I have a function that processes the sf::SoundBuffer and reloads it to the sf::Sound. The problem is, I cannot seem to get the buffer to retain its processed samples. More specifically, it goes out of scope in my function.

Maybe I should let the code do the talking:

CSoundController::CSoundController(sf::Sound* target)
{
        mySound = target; // mySound's type is   sf::Sound*
        myBuffer = mySound->getBuffer(); // myBuffer's type is     const sf::SoundBuffer*
}
 

void
CSoundController::applyFilter(float val)
{
        if (mySound != NULL)
        {
                if (val <= 1.0f && val >= 0.0f)
                {
                        if (mySound->getStatus() == sf::Sound::Status::Playing)
                        {
                                mySound->stop();
                        }

                       
                        const sf::Int16* drySamples = myBuffer->getSamples();

                        int numSamples = myBuffer->getSampleCount();
                        int channels = myBuffer->getChannelCount();
                        int sampleRate = myBuffer->getSampleRate();

                        std::vector<sf::Int16> wetSamples;
                        wetSamples.reserve(numSamples);

                        float b = 1.0f - val;
                        float z = 0;

                        for (int n = 0; n < numSamples; n++)
                        {
                                z = (drySamples[n] * b) + (z * val);
                                wetSamples.push_back(z);
                        }

                        sf::SoundBuffer* wetBuffer = new sf::SoundBuffer();
                        wetBuffer->loadFromSamples(&wetSamples[0], wetSamples.size(), channels, numSamples);

                        myBuffer = wetBuffer;

                        mySound->setBuffer(*myBuffer);

                        delete wetBuffer;
                }
        }
}
 

When I run this function, the buffer is processed fine and is just peachy until wetBuffer gets deleted and the data it contained is deleted. Next time I run the function, the buffer's filled with garbage.

I can't seem to figure out a solution for this. I would appreciate some suggestions.
I found this post related to loading buffers, but I feel like my case is a little different: http://en.sfml-dev.org/forums/index.php?topic=1098.msg6998#msg6998

Also, if there is a better way to modify the buffer data without swapping vectors and buffers around, I would love to hear it. I'm quite new to this sort of thing.

Also also, it was a pain for me to find this filter algorithm and get it functioning, so for the sake of those who are pursuing the same as me, here is the link to the algorithm: http://www.musicdsp.org/archive.php?classid=3#257
I'm sure this is probably as lame of a filter as you could possibly get, but it does exactly what I want it to do, and I can actually comprehend it, so it works for me :P

Thanks for any and all help :)
« Last Edit: June 25, 2012, 05:03:28 am by chjolo »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Dynamically Affecting sf::SoundBuffers
« Reply #1 on: June 25, 2012, 08:11:49 am »
You're deleting the buffer that you assign to your sound... what else did you expect? :P

You mustn't delete it.
Laurent Gomila - SFML developer

chjolo

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Re: Dynamically Affecting sf::SoundBuffers
« Reply #2 on: June 25, 2012, 06:32:29 pm »
Right, I knew why that was happening. The thing is, this function is going to be called fairly often and on several different sounds, so I can't have all these pointers floating around that never get deleted.

I'm looking for a way to...
A) discard mySound's original buffer, and give it myBuffer (with the effect applied) in its place.
     or
B) apply the effect(s) directly to the mySound buffer.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Dynamically Affecting sf::SoundBuffers
« Reply #3 on: June 25, 2012, 06:57:34 pm »
You can change the contents of a sound buffer at any time, even if it's assigned to a sound, you don't have to delete and recreate it. Of course, the sound mustn't be playing when you do that.

You can't retrieve it with sf::Sound since it provides a read-only access to its buffer, so you'll have to pass your own pointer/reference around.
Laurent Gomila - SFML developer

chjolo

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Re: Dynamically Affecting sf::SoundBuffers
« Reply #4 on: June 26, 2012, 09:49:10 pm »
That clarifies some stuff. I got it working. I was thinking of some things the wrong way, and missed a few basic things :/ Thanks for the help :)

Here's the working version:

void
CSoundController::applyFilter(float val)
{
        if (mySound != NULL) // mySound is a sf::Sound*
        {
                if (val <= 1.0f && val >= 0.0f)
                {
                        if (mySound->getStatus() == sf::Sound::Status::Playing)
                        {
                                mySound->stop();
                        }
                               
                        const sf::Int16* drySamples = mySound->getBuffer()->getSamples();
                       
                        int numSamples = mySound->getBuffer()->getSampleCount();
                        int channels = mySound->getBuffer()->getChannelCount();
                        int sampleRate = mySound->getBuffer()->getSampleRate();
                       
                        std::vector<sf::Int16> wetSamples;
                        wetSamples.reserve(numSamples);
                               
                        float b = 1.0f - val;
                        float z = 0;

                        for (int n = 0; n < numSamples; n++)
                        {
                                z = (drySamples[n] * b) + (z * val);
                                wetSamples.push_back(z);
                        }

                        // myBuffer is a sf::SoundBuffer*
                        myBuffer->loadFromSamples(&wetSamples[0], wetSamples.size(), channels, sampleRate);
                        mySound->setBuffer(*myBuffer);
                }
        }
}
 

chjolo

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Re: Dynamically Affecting sf::SoundBuffers
« Reply #5 on: June 28, 2012, 05:08:57 pm »
If I might ask one more question, mostly unrelated to this...

In a custom class that inherits from sf::SoundStream, is it possible to stream less than one second of audio at a time? It seems to me like onGetData() gets called every second, no matter what. I very well could be doing something to cause that, though.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Dynamically Affecting sf::SoundBuffers
« Reply #6 on: June 28, 2012, 10:01:34 pm »
It's hard-coded inside sf::SoundStream, you can't change that.
Laurent Gomila - SFML developer