micknoise / Maximilian

C++ Audio and Music DSP Library
http://www.maximilian.strangeloop.co.uk
MIT License
1.58k stars 279 forks source link

Simple granular synthesis #25

Closed og0 closed 9 years ago

og0 commented 10 years ago

Hi!

First, thanks and congratulations with Maximilian, it works great and is well optimized.

I use it on OpenFrameworks for iOS. I don't manage to do a simple stuff: a very basic granular synthesis. I would like to play a orchestral wav file with position (in the wav file) as a parameter. I explain, i would like to have 16 grain players, and every xx milliseconds (xx = random numbers) one of these players plays a very short part of the file (at the right play head position), with no loop. Because there are 16 players and they play very quickly small grains, it gives you the impression that the play head stays immobile. I made a Max/MSP patch and it works well, but i cannot make it in OpenFrameworks / Maximilian.

Hope my explanation is obvious. Thanks for your help!

Cheers, Olivier

micknoise commented 10 years ago

Hi

If I understand you correctly you want to make a granular synth. There's already one of these in the ofxMaxim branch, and an old example of how to use it here (requires openframeworks)

https://github.com/micknoise/Maximilian/tree/master/ofxMaxim/ofxMaximExamples0062OSX/ofMaxim_example_granular/src

On Tuesday, July 15, 2014, og0 notifications@github.com wrote:

Hi!

First, thanks and congratulations with Maximilian, it works great and is well optimized.

I use it on OpenFrameworks for iOS. I don't manage to do a simple stuff: a very basic granular synthesis. I would like to play a orchestral wav file with position (in the wav file) as a parameter. I explain, i would like to have 16 grain players, and every xx milliseconds (xx = random numbers) one of these players plays a very short part of the file (at the right play head position), with no loop. Because there are 16 players and they play very quickly small grains, it gives you the impression that the play head stays immobile. I made a Max/MSP patch and it works well, but i cannot make it in OpenFrameworks / Maximilian.

Hope my explanation is obvious. Thanks for your help!

Cheers, Olivier

— Reply to this email directly or view it on GitHub https://github.com/micknoise/Maximilian/issues/25.

Sent from my mobile

og0 commented 10 years ago

Hi,

Thank you for you quick answer. Yes i started with this example. My problem is that (if i'm right) it only plays one grain at a time. I would like to play each grain once (and not in loop) and trigger them with random offset and a random length.

BTW i tried to play several grains (with a maxiTimestretch array) in loop, like in your example, but i think i play them all in the same time, although i give to each player a random grain length. So it doesn't give the feeling that the play head stays immobile because we hear the loops (they're not mixed together) so it's still very percussive.

Here is the audioRequest function

for (i = 0; i < bufferSize; i++) { mix = 0.0f;

    for (j = 0; j < NBGRAINS; j++)
    {
        //_wave[j] = _ts[j]->play2(_pos, _grainLength[j], 1);
        _wave[j] = _ts[j]->play(1.0f, 0.05, 1, _pos);
        if (_fft[j].process(_wave[j]))
        {
            _oct[j].calculate(_fft[j].magnitudes);
        }

        if (j == 1)
        {
            _wave[j] = _wave[j] * 0.05;
        }
        mix += _wave[j];
    }

    // Mix
    _channel.stereo(mix, _outputs, 0.5);
    output[i*nChannels    ] = _outputs[0];
output[i*nChannels + 1] = _outputs[1];
}

Maybe i should use only the _sample->play() function ? Thank you for your advice.

og0 commented 10 years ago

The _pos variable is a parameter updated at each update() function, this is the most important because it can stay at the same position. The sample is a violin long note so if _pos stays at 0.2 for example, my goal is to hear only the same violin note playing endless, but without hearing a loop or a percussive grain. It stays always at speed = 1. I think this is a trigger problem (in my code all grain triggers at the same moment, don't they ?), but i'm not familiar with audio in OpenFrameworks... Thank you!

og0 commented 10 years ago

OK i managed to do what i wanted with maxiGrain class. Thank you.

og0 commented 10 years ago

But i have another problem :) I have a huge memory leak. Here's my code :

for (i = 0; i < bufferSize; i++) { mix = 0.0f;

    if (_active)
    {
        for (j = 0; j < NBGRAINS; j++)
        {
            if (_grain[j] != NULL)
            {
                if (_grain[j]->finished)
                {
                    delete(_grain[j]);
                    _grain[j] = new maxiGrain<grainPlayerWin>(&_sample,
                                                              _pos + ofRandom(0.05),
                                                              ofRandom(FOURNIER_MINGRAINLENGTH, FOURNIER_MAXGRAINLENGTH),
                                                              1,
                                                              _windowCache);
                }
                else if (!_grain[j]->finished)
                {
                    _wave[j] = _grain[j]->play();
                }
            }

            mix += _wave[j];
        }
    }

    // Mix
    _channel.stereo(mix, _outputs, 0.5);

    output[i*nChannels    ] = _outputs[0] * _volume;
    output[i*nChannels + 1] = _outputs[1] * _volume;

The memory leaks seem to be linked with the _windowCache (of type maxiGrainWindowCache*). In the constructor of maxiGrain there is window = windowCache->getWindow(sampleDur); This functions always mallocs cache but the class never frees it.

What do i do wrong ? Can you help please ? Thank you.

chriskiefer commented 10 years ago

Hi,

I'm not sure where your memory leak is coming from, but instead you could try to override or make a modified copy of the maxiGrainPlayer class to get the behaviour you need - we know this runs already without memory leaks.

Cheers,

Chris

On 17 Jul 2014, at 21:40, og0 notifications@github.com<mailto:notifications@github.com> wrote:

But i have another problem :) I have a huge memory leak. Here's my code :

for (i = 0; i < bufferSize; i++) { mix = 0.0f;

if (_active)
{
    for (j = 0; j < NBGRAINS; j++)
    {
        if (_grain[j] != NULL)
        {
            if (_grain[j]->finished)
            {
                delete(_grain[j]);
                _grain[j] = new maxiGrain<grainPlayerWin>(&_sample,
                                                          _pos + ofRandom(0.05),
                                                          ofRandom(FOURNIER_MINGRAINLENGTH, FOURNIER_MAXGRAINLENGTH),
                                                          1,
                                                          _windowCache);
            }
            else if (!_grain[j]->finished)
            {
                _wave[j] = _grain[j]->play();
            }
        }

        mix += _wave[j];
    }
}

// Mix
_channel.stereo(mix, _outputs, 0.5);

output[i*nChannels    ] = _outputs[0] * _volume;
output[i*nChannels + 1] = _outputs[1] * _volume;

The memory leaks seem to be linked with the _windowCache (of type maxiGrainWindowCache*). In the constructor of maxiGrain there is window = windowCache->getWindow(sampleDur); This functions always mallocs cache but the class never frees it.

What do i do wrong ? Can you help please ? Thank you.

— Reply to this email directly or view it on GitHubhttps://github.com/micknoise/Maximilian/issues/25#issuecomment-49362502.

og0 commented 10 years ago

Thanks for you help. I found another simple solution. I don't know why but memory leaks came from the fact the length of grains always changed (the ofRandom(MINGRAINLENGTH, MAXGRAINLENGTH) argument). I just deleted and re-created my window before the re-creation of the grain and it works.

for (i = 0; i < bufferSize; i++) { mix = 0.0f;

if (_active)
{
    for (j = 0; j < NBGRAINS; j++)
    {
        if (_grain[j] != NULL)
        {
            if (_grain[j]->finished)
            {
                // Delete old stuff
                delete(_grain[j]);
                delete(_windowCache[j]);

                // Create new window cache
                _windowCache[j] = new maxiGrainWindowCache<grainPlayerWin>();
                _windowCache[j]->cacheSize = MAXGRAINLENGTH * AUDIO_SAMPLERATE;

                // Create new grain
                _grain[j] = new maxiGrain<grainPlayerWin>(&_sample,
                                                          _pos + ofRandom(0.05),
                                                          ofRandom(MINGRAINLENGTH, MAXGRAINLENGTH),
                                                          1,
                                                          _windowCache[j]);
            }
            else if (!_grain[j]->finished)
            {
                _wave[j] = _grain[j]->play();
            }
        }

        mix += _wave[j];
    }
}

// Mix
_channel.stereo(mix, _outputs, 0.5);

output[i*nChannels    ] = _outputs[0] * _volume;
output[i*nChannels + 1] = _outputs[1] * _volume;

} Thank you again !

chriskiefer commented 10 years ago

that makes sense now - it wasn't really a memory leak, it's the windowcache object storing each size of window in the cache - the system should stop using memory once it has a copy of each size of window you are using. It's a memory-speed trade of, and with a lot of variance in the window size, you might be better off recalculating the window each time

og0 commented 10 years ago

OK, thanks for the explanation. I meant memory leak in my software, not in Maximilian of course :)