kylestetz / lissajous

:musical_note: A tool for programmatic audio performance in the browser using Javascript.
http://lissajousjs.com
MIT License
398 stars 25 forks source link

Feedback on Lissajous #7

Open kylestetz opened 9 years ago

kylestetz commented 9 years ago

If you've used lissajous and you have any kind of feedback at all, feel free to post it here.

Shihpin commented 9 years ago

Hi

http://qiita.com/Shihpin/items/e8355b167f9cf20117bd

This article is written in Japanese a while ago.

Shihpin 

----- Original Message -----

From: Kyle Stetz notifications@github.com To: kylestetz/lissajous lissajous@noreply.github.com Date: 2014/12/2, Tue 01:55 Subject: [lissajous] Feedback on Lissajous (#7)

If you've used lissajous and you have any kind of feedback at all, feel free to post it here. — Reply to this email directly or view it on GitHub.

  Official blog: http://is.shihp.in

enedrio commented 9 years ago

Hi Kyle, thanks for the track.destroy() function. Here's a little bit I built into the groups prototype:

// apply different beat schemes to all tracks in the group self.beats = function () { var args = Array.prototype.slice.call(arguments); if(args.length > 0) { var i = 0; self.tracks.forEach( function (t) { t.beat(args[i]); i = (i + 1) % args.length; });
} else { self.tracks.forEach( function (t) { t.beat(); });
} return self; }

One can call different beat schemes on the tracks in the group. If more tracks than schemes are available the schemes will be wrapped around. That makes synchronous playback of individual tracks possible. I found it quite useful.

Best Eric

kylestetz commented 9 years ago

That's very cool @enedrio! So, just to be clear, you would pass multiple arrays into beats representing the beat pattern for each individual track like this:

t1 = new track(), t2 = new track()
g = new group(t1,t2)
g.beats([4,3,1,2,2,1,3], [4])
// t1 now has the beat pattern [4,3,1,2,2,1,3]
// t2 now has the beat pattern [4]

Is that right? I'm confused by your comment about synchronous playback of individual tracks. Can you elaborate on that?

enedrio commented 9 years ago

Yes it would be used as you showed. With synchronous I meant having them all start their patterns at the same time. Without beats() I had to start a track, then start up the next and shift it 'till its pattern fell in the right place. But knowing up front which patterns I wanted to combine and how, the beats() function offers me a convenient way to express this in code. instead of going:

t1.beat(2, 3, 4, 7)
t2.beat(3, 5, 7, 1)
// then shift till it fits 
t2.shift(1)

I can now write:

mygroup = new group()
mygroup.add(t1, t2)
mygroup.beats( [2,3,4,7], [3,5,7,1] )
// and the two tracks will start with their patterns at the same time

If mygroup would contain 3 tracks then the patterns will be wrapped around, like so:

mygroupWith3Tracks.beats( [1,2,3], [4,5,6] )
// t1 --> [ 1, 2, 3 ] 
// t2 --> [ 4, 5, 6 ]  
// t3 --> [ 1, 2, 3 ]

I hope that was somewhat more clear, than my previous post. I'm a bit out of practice writing in english, sorry. :)

Best Eric

kylestetz commented 9 years ago

That's great, I totally get it.

It's worth noting that commas and semicolons allow you to string together several commands to be run at the same time. What you wrote above with t1 and t2 separately can be expressed as:

t1.beat(2, 3, 4, 7), t2.beat(3, 5, 7, 1)

So that you hit enter once and both beats start up in sync.

The syntax of group.beats is interesting and provides another way to do this, so I like it! I would gladly accept a pull request if you want to submit one.

enedrio commented 9 years ago

Hi, I just sent the pull request. And I realized that what the group interface needs, to make the beats() function even more useful, would be a synchronize function, like the one below.

// in group()
self.sync = function () {
    var args = Array.prototype.slice.call(arguments);
    if(args.length === 0) {
      self.tracks.forEach( function (t) {
        t._beatPattern.currentStep = 0;
        t._beatPattern.untilNextBeat = 0;
      });  
    } else {
      args.forEach( function (t) {
        t._beatPattern.currentStep = 0;
        t._beatPattern.untilNextBeat = 0;
      });  
    }
    return self;
  }

with such a function one could group tracks together and suddenly bring them in sync playing some crazy beat or whatnot. like so:

// t1 plays something, t2 playing something else
var crazyBeatGroup = new group(t1, t2).sync().beats([2, 3, 4], [4,5,6])

Sounds fun to me. What do you think? Maybe a global sync function makes more sense?

The way it's written now it could sync any given tracks even if they're not in the group. So that's a bit dirty, I guess.

Greets Eric

EDIT: I just realized, that the code:

t._beatPattern.currentStep = 0;
t._beatPattern.untilNextBeat = 0;

is breaking your model of public and private fields. So the track.api would need a reset / zero scheduler function to stay consistent.

kylestetz commented 9 years ago

Wow. You are blowing my mind right now. I love it! Let's give this decision its own space for discussion: #14

enedrio commented 9 years ago

Hey Kyle, I'm still playing around with lissajous. I had three more ideas, but I'm not sure how to implement them in a good way, yet.

1) a "seemless" sync Function, that waits until the scheduler of the receiver of the sync call arrives at 0 and only then starts the track that wants to get sync-ed to it. Because a hard reset of all running schedulers is a bit unmusical in some situations. some sample code:

t1 = new track();
t2 = new track();
t1.beat(1,2,3);
t2.beat(2,3,4);
// t2 keeps running and receives the reset call when t1's scheduler hits zero
t1.sync(t2); 

2) delete samples from track 3) list all samples asigned to a track

I know 3) is available by calling track._samples, but that's not an api function :) 2) is not available as far as I understand.

Greets Eric

kylestetz commented 9 years ago

Hey @enedrio, I like your idea to sync to the downbeat of a track. It should be possible!

Deleting samples from a track is a great idea, I will add that.

A far as listing the samples assigned to a track... I could add a function to log this, but the issue is that it will log AudioBuffer objects— at the moment we don't keep track of the name of the variable, and in fact that is not exactly a straightforward task.r