rserota / wad

Web Audio DAW. Use the Web Audio API for dynamic sound synthesis. It's like jQuery for your ears.
MIT License
1.9k stars 160 forks source link

Support for multiple filters #3

Closed atran closed 10 years ago

atran commented 10 years ago

It would be great if you could pass arrays to the constructor and create combined filters:

filters : [
  {
    type : 'lowpass', 
    frequency : 600,
    q : 1,
    env : { 
      frequency : 800, 
      attack : 0.5 
    }
  },
  {
  // another filter
  }
]
rserota commented 10 years ago

Thanks for the suggestion. I think that should be doable, but let's talk about it a bit?

First of all, would you expect the filters to be applied in the same order that they appear in the array, or does it not matter?

atran commented 10 years ago

I think order does matter, so I would assume that they would be layered in order.

coleww commented 10 years ago

Presumably you would just iterate over the filters array and delegate to the filter constructor/setup on each element? I'd be interested in working on this if it hasn't been started.

atran commented 10 years ago

I haven't started working on this.

rserota commented 10 years ago

I suppose it shouldn't be too difficult, but I've been working on other things recently (3d-panning, a new demo, etc).

Cole, I have not started it yet, so if you wanted to work on it, that would be cool. There are just a couple things you might want to keep in mind.

First of all, I'd like it if all changes are backwards compatible, so that a user can specify either a single filter object (how it works now), or an array of filter objects.

Second, be aware that filters can be specified in two places: either on the constructor, or as an argument to play(). We'll have to think about how to handle situations where a user specifies multiple filters on the constructor, but then only specifies one filter as a play() argument.

If you have any questions about Wad's architecture that I could answer for you, feel free to ask me.

coleww commented 10 years ago

1- that was exactly what i was thinking. 2- I think things passed to play should take precedence over things passed to the constructor, which would take precedence over the defaults. But on the other hand, I think filters[] should take precedence over filter{}. So if the user constructs multiple filters but passes only one filter to play then that argument is ignored. The user could still pass only one filter in the filters array and get the same effect. Could console.log a warning or something... Let me know what you think.

rserota commented 10 years ago

I'm thinking that if a user sets multiple filters on the constructor, but only one filter on play(), it's unclear what they meant to do. Did they only want one filter to apply on play()? Or did they want to modify one filter, and leave the other one at its constructed defaults? I think both interpretations are reasonable. Maybe it would be best to require that users specify the same number of filters on play() as they specified on the constructor.

Also, if users specify only one filter, could we still represent that internally as a 1 element array? I think that would help make everything more consistent. I do something similar with panning. Users can specify panning as either an integer for 1-dimensional panning, or a 3 element array for 3-d panning, but either way, panning is represented internally as a 3 element array.

coleww commented 10 years ago

if the user constructs with filters[] but passes filter{} to play: throw an error? it's already specified that a user must pass a filter constructor in order to use it as an argument to play, and it looks like at the moment it just silently fails if they try to add a filter on play that was not constructed. Could add a warning or error for that case as well.

if the user constructs with both filters[] and filter{}: then, presuming we are storing all filters internally as an array of variable#elements, it is just a question of the order. In this case I would push the filters[] on first and then the filter{}. This would be a weird thing for someone to do, but I don't think it would be difficult to support it.

If the user constructs with 3 filters[] but passes 2 filters[] to play: use the default settings for the third filter. There could also be an option they pass to turn off the filter (that is, not plug in that filter on that play argument).

And on the second point => totally agree.

rserota commented 10 years ago

It sounds like we're mostly on the same page. One little thing though, could we call the argument 'filter' (singular) regardless of how many filters are passed in? We could just check whether 'filter' is an array or an object, rather than checking if the user called it 'filter' or 'filters'.

Everything else you said sounds good. I'll be excited to see more pull requests from you. Let me know if you have any more questions.

coleww commented 10 years ago

That sounds good to me, and will probably simplify a lot of things.

In that case maybe if a user passes filter{} to play but filter[] to the constructor then it should just modify the first filter for that play(). Because if we are storing the filter as an array with variable # of elements then that is what it would be doing anyways. And the user could have passed filter[] with only one filter inside of it.

Thanks! I'm super into wad, have been using it to build html-ish noise interfaces http://coleww.github.io/shreddit-hardcore/ (arpeggiator has the most work done, and probabilistic drum machine is kinda cool but needs more interface)

rserota commented 10 years ago

It sounds like we're on the same page about the filters.

Also, those apps you made are really cool! Let me know if you keep working on them! I might add a section to the readme for 'Featured Apps Using Wad.js'.

coleww commented 10 years ago

thanks! I'm trying to build a browser instrument that I could play a show with, or at least a noise show :dancers:

I think I have the fix mostly done, just need to do a lil testing. I noticed that the microphone sets up its filter sort of separately, but that it didn't use the arg.filter arguments nor did it set up filter.env. Was there any reason for that?

rserota commented 10 years ago

Yeah, microphone Wads are treated a little differently from other kinds of Wads. Calling 'play()' on a mic Wad doesn't 'trigger a note' like other Wads, it just turns on your microphone. You're not really intended to 'play()' a mic Wad more than once. Since it doesn't really have a note duration, or attack/decay etc, a filter envelope (or volume envelope) doesn't really make sense, since an envelope describes how the sound changes over the duration of a single playback. Similarly, it doesn't use arg.filter because nothing interesting is really supposed to happen with the mic on 'play()'. You should set mic filters on the constructor. Is that unclear? Should I add more to the readme about that?

coleww commented 10 years ago

makes sense.

i didnt test what happens if you construct like...100 filters. but it works for a few on my end. cant test the mic but in theory it is running the same code as before just with more filters.