musikinformatik / SuperDirt

Tidal Audio Engine
GNU General Public License v2.0
519 stars 75 forks source link

Topic/make last event accessible #209

Closed thgrund closed 3 years ago

thgrund commented 3 years ago

I added a new attribute to the SuperDirt class to access the last event received from TidalCycles.

To ask the question whether this is a good idea, I would like to briefly explain what I have in mind: The idea is to use a playAll function to play all samples in a sample bank in parallel. This is interesting if you add live buffers with addBuffer and want to play them immediately back (e.g. for live looping to simulate an overdub mode), without changing the code.

In TidalCycles it could look like this (if playAll = false then you only hear the results from n):

d1 $ playAll "<t f>" # n "<0 1 2 3>"  # s "loop" 
   # cutoff (slow 8 $ (sine * 3000) + 300)

To this PR belongs the PR in the TidalCycles repository, because the function pB is missing to create the necessary pattern function playAll = pB "playAll" (see also: https://github.com/tidalcycles/Tidal/pull/727)

My SuperCollider code for achieving such a behaviour looks like this:

(
~playAll = 0;

~dirt.addModule('playAll', {
    if (~playAll != 0, {    
        ~dirt.soundLibrary.buffers[~s].size.do({ 
            arg item; 
            var event = ();
            event.putAll((\type: \dirt, \dirt: ~dirt ), ~dirt.lastEvent);

            event.removeAt(\playAll);

            if (item != ~n, {
                event[\n] = item;
                event.play;
            }); 
        });
    })
}, {~playAll.notNil});
)

Something like (type:\dirt, dirt: ~dirt, s: \loop, n: 1).play; is unfortunately not useful here because it does not apply any effects to the event.

telephon commented 3 years ago

Generally, you better use the event system for calling functions. Also you could store any value in the scope of such a function. Wouldn't that be enough?

(
var previous;
~dirt.soundLibrary.addSynth(\playAll,
    (
        play: {
            // your function here.

            previous = <whatever>

        }

    )
);
)
thgrund commented 3 years ago

Hmmm, but isn't it so that I would have to use playAll with the s function, like d1 $ s "[playAll, mysamples]"? Because that's how I have always been able to access functions that are added with addSynth. This would cause two different events to be triggered here and I can't access the mysamples in ~s at all.

What I want to achieve is that I play all samples of a sample bank and apply all used effects, i.e. d1 $ playAll "t" # s "[mysamples1, mysamples2, mysamples3]" So what I did is using an event and do something with it, like to create new (but modified) events based on an original event. But normally I don't have access to a received event.

Unfortunately I don't see how addSynth can do that, but maybe I'm missing something here?

If I understand you right, than with var previous in your example I can memorize the previous state of something to access them with another call of playAll. But I cannot access events with the related sample names or applied effects.

telephon commented 3 years ago

Ah I see, yes. You want this to work like an effect. So you can go the addModule, and store the currentEnvironment in a variable in local scope.

previous = currentEnvironment;
thgrund commented 3 years ago

Thank you very much, the currentEnvironment solves the problem!

The working example looks like this:

(
~playAll = 0;

~dirt.addModule('playAll', {
    if (~playAll != 0, {
        ~dirt.soundLibrary.buffers[~s].size.do({
            arg item;
            var event = ();

            event.putAll((\type: \dirt, \dirt: ~dirt ),currentEnvironment);
            event.removeAt(\playAll);

            if (item != ~n, {
                event[\n] = item;
                event.play;
            });
        });
    })

}, {~playAll.notNil});

)

So you don't need any changes to SuperDirt, everything is already there. I will close this PR and keep it online.