From: Joe E. <jo...@em...> - 2005-03-15 02:15:29
|
Looking at various synth editors for ideas, I'm seeing many bits of code that look like: addWidget(leftPane, new CheckBoxWidget("Midi Pitch Bend", patch, new MKSBitModel(patch, 16, 5), new MKSBitSender(patch, 16, 5, 9)), 0, 10, 1, 1, -3); or... addWidget(panel, new ComboBoxWidget("Footswitch Mode", patch, new DM5SysInfoModel(patch, headerSize + 0, 32, false), new DM5SysInfoSender(patch, 1),.... Notice that "patch" is used 3 times in these method calls. This seems like it could be made a little more streamlined to me. As usual, I'm willing to do the work to fix it, but I don't want to if everyone else feels that the current system is optimal. However, if anyone else would like to see this cleaned up, then I'd like to discuss ideas for it. - Joe |
From: Brian <br...@ov...> - 2005-03-15 14:52:37
|
Joe Emenaker wrote: > Looking at various synth editors for ideas, I'm seeing many bits of > code that look like: > > addWidget(leftPane, new CheckBoxWidget("Midi Pitch Bend", patch, > new MKSBitModel(patch, 16, 5), new MKSBitSender(patch, 16, 5, > 9)), 0, 10, 1, 1, -3); > > or... > > addWidget(panel, new ComboBoxWidget("Footswitch Mode", patch, > new DM5SysInfoModel(patch, > headerSize + 0, 32, false), > new DM5SysInfoSender(patch, 1),.... > > Notice that "patch" is used 3 times in these method calls. This seems > like it could be made a little more streamlined to me. > > As usual, I'm willing to do the work to fix it, but I don't want to if > everyone else feels that the current system is optimal. > > However, if anyone else would like to see this cleaned up, then I'd > like to discuss ideas for it. I think this should be pretty easy to clean up if you are willing. . The reason patch appears three times is that the Widget itself wants to see the patch, the sender wants to see it, and the model wants to see it. My gut reaction to how to fix it would be to create a method in the base class for models and senders that does nothing called setPatch (Patch p). This would be called automatically by addWidget on the Model and Sender sent to it. Models / Senders could override this to get at the Patch rather than having to take the Patch in their constructor. Existing drivers could remain unchanged but new ones could then get away without having to pass patch three times. We could always go back and upgrade older drivers later. Brian |
From: Joe E. <jo...@em...> - 2005-03-15 22:18:06
Attachments:
smime.p7s
|
Brian wrote: > Joe Emenaker wrote: > >> However, if anyone else would like to see this cleaned up, then I'd >> like to discuss ideas for it. > > I think this should be pretty easy to clean up if you are willing. . > The reason patch appears three times is that the Widget itself wants > to see the patch, > the sender wants to see it, and the model wants to see it. My gut > reaction to how to fix it would be to create a method in the > base class for models and senders that does nothing called setPatch > (Patch p). This would be called automatically by addWidget > on the Model and Sender sent to it. Models / Senders could override > this to get at the Patch rather than having to take the Patch > in their constructor. Existing drivers could remain unchanged but new > ones could then get away without having to pass patch three > times. We could always go back and upgrade older drivers later. This is very much like what I was thinking. Here's my next question. Currently, parameter names are passed to the widget, and not to the ParamModel, right? If they were given to the ParamModel instead (and ParamModel had a getName() method so that the widget could get to it), then it creates the possiblity for user-tunable patch mixing, no? For example, if I wanted to combine patches 35 and 37, I could actually tell JSL to use "Filter Envelope" from 35, "Pitch LFO" from 37, and to just numerically average all of the others. Or does JSL already do this? - Joe |
From: Brian <br...@ov...> - 2005-03-16 14:29:31
|
In the future, I would like to do a *much* more sophisticated cross breeder, including this kind of functionality. Two issues though: One is that a "Filter Envelope" would not be a name of a Param Model. An envelope widget is actually an editor for many values, each with its own model / sender. You would not have a "Filter Envelope" ParamModel, but instead, a Filte Envelope Attack, Filter Envelope Decay, etc. I think that even better than being able to name parameters by name to do a cross breed, it would be cool to highlight controls in an editor window. Anyway, this is long term stuff. I have some other ideas for the crossbreeder, Right now its pretty stupid, but we can do a lot of interesting stuff with it by giving it some more smarts about the patches its combining. Brian > > Here's my next question. Currently, parameter names are passed to the > widget, and not to the ParamModel, right? If they were given to the > ParamModel instead (and ParamModel had a getName() method so that the > widget could get to it), then it creates the possiblity for > user-tunable patch mixing, no? > > For example, if I wanted to combine patches 35 and 37, I could > actually tell JSL to use "Filter Envelope" from 35, "Pitch LFO" from > 37, and to just numerically average all of the others. Or does JSL > already do this? > > - Joe |
From: Joe E. <jo...@em...> - 2005-03-16 21:39:33
Attachments:
smime.p7s
|
Brian wrote: > In the future, I would like to do a *much* more sophisticated cross > breeder, including this kind of functionality. Two issues though: One > is that a "Filter Envelope" would not be a name of a Param Model. Actually, I've been pondering a few ideas about that. Either ParamModels could be made so that they contain *other* ParamModels (like AWT containers being able to contain more containers). The utility of this (that I can think of right now) would be limited to: 1) You'd be able to select the granularity of crossbreeding (ie, you could use the entire Filter Envelope from Patch #1, or you could use the Attack point from Patch #1 and the Hold point from Patch #2, etc.). 2) If designed well, it might make it easier to code for complex data items which appear in numerous places. For example, if your synth uses 12 different envelopes (and the format of their data in the sysex is the same, just at different offsets within the data), then it should be possible to just make a FilterEnvelopeParamModel at each of those 12 offsets. > An envelope widget is actually an editor for many values, each with > its own model / sender. Well, in a nestable-ParamModel design, the overall FilterEnvelope probably wouldn't have a sender, but the individual points within it would. Granted, this might be making things too complicated, but I wanted to just put the idea in your head to see if you could envision any extra functionality that it might allow. > Anyway, this is long term stuff. Yet, there's no reason we can't be laying the foundation for it early. > I have some other ideas for the crossbreeder, Right now its pretty > stupid, but we can do a lot of interesting stuff with it by giving it > some more smarts about the patches its combining. One interesting idea that I thought of parallels what I've seen some photo-editing software do. I think Photoshop Elements does this. You tell it that you want to correct the color balance or something, and it shows you a 4x4 grid of thumbnails of your image, each with a different color correction applied to it. You click on one, and I *think* it then shows you another grid, with each of those being variations of what you picked. So, you're able to gradually "zero-in" on what you're after. I think that we could do that with JSL. MidiQuest currently does something like this, but it's not itterative. MQ lets you pick some number of patches and then it fills the current bank with random blends of them. I think that the most-clever tool, however, would be one that works like the Photoshop tool, where the user gets to itteratively pick the closest sound, and then JSL gradually converges on what the user is after. - Joe |
From: Joe E. <jo...@em...> - 2005-03-17 00:27:54
Attachments:
smime.p7s
|
Brian wrote: > My gut reaction to how to fix it would be to create a method in the > base class for models and senders that does nothing called setPatch > (Patch p). This would be called automatically by addWidget > on the Model and Sender sent to it. Well, here's the "grand design" of what I'm thinking of: - Patch data would be given only to one of the items (widget, param-model, or sender) and the others would get it from that. My inclination would be that the param-model would house the patch data, since there are possible scenarios in which we might want to adjust parameters *outside* of the context of a graphical widget (ie, cross-breeding, or cut "Master Volume" by 90% in all patches in a bank). ParamModel would have a method for widgets to get the patch data... which Widget would use to obtain it, and then give it to Sender, if there is one. - I think parameter name should also be in ParamModel, for the same reasons (and obtained by Widget in the same way) - Lastly, I'd like for DataModel to be worked into this somehow. Like I mentioned before, the driver I'm working on now requires tat 7-bit bytes be decoded into 8-bit bytes, and it's easier to do this to the entire sysex message and than it would be for individual ParamModels to try to convert just the part they're interested in. Instead, by using a DataModel class (which holds the actual Patch object and handles all translation to/from Ints/Longs/Strings), the ParamModels would be given the DataModel object and would access the data they wanted via methods like getInt(offset). The ultimate result of this would be to stop writing things like this: addWidget(leftPane, new CheckBoxWidget("Midi Pitch Bend", patch, new MKSBitModel(patch, 16, 5), new MKSBitSender(patch, 16, 5, 9)) , 0, 10, 1, 1, -3); and start writing them like this: SingleSysexDataModel datamodel = new SingleSysexDataModel(patch); .... addWidget(leftPane, new CheckBoxWidget(new MKSBitModel("Midi Pitch Bend", datamodel, 16, 5), new MKSBitSender(16, 5, 9)) , 0, 10, 1, 1, -3); It doesn't look *that* much cleaner, but at least the patch data is only passed once. This could be cleaned up even more if ParamModels use interfaces or superclasses that define them as either BooleanParamModel, NumericParamModel, or EnumParamModel. Then, addWidget could discover what kind of parameter a ParamModel is and automatically give it the correct widget (ie, BooleanParamModel gets a CheckBoxWidget, NumericParamModel might get a spinner, EnumParamModel would get a ComboBoxWidget, etc). Then, you could use things like: addWidget(leftPane, new MKSBitModel("Midi Pitch Bend", datamodel, 16, 5), new MKSBitSender(16, 5, 9), 0, 10, 1, 1, -3); Of course, you could always do things the old way if this didn't give you the flexibility you needed. But I think that this would clean things up in the general cases. Give it some thought, if you will. - Joe |
From: Rib R. <ri...@gm...> - 2005-03-17 02:11:57
|
SysexWidgets can use IParameters. I tried to make them do the same thing you're describing. Widgets simply call get or set on the parameter. The parameter communicates with some sort of codec, and also possibly tells the driver to send the updated value to the synth. For example, the Motif driver has: (edited for clarity) <sysex> <decoder type="BE 7bit words" /> <!-- header, etc. --> <string> <name>Voice Name</name> <size>10</size> </string> <range> <name>QED ARP Type</name> <min>0</min> <max>127</max> </range> <!-- more params --> </sysex> The editor then gets these parameters by name. You could do the same type of thing in Java. Then it would look like this: /* when the driver is created */ // create params in order so addresses are inferred decoder.addBooleanParam("Midi Pitch Bend"); /* Then in the editor */ addWidget(leftPane, new CheckBoxWidget(patch, decoder.getParam("Midi Pitch Bend")), , 0, 10, 1, 1, -3); On Wed, 16 Mar 2005 16:27:15 -0800, Joe Emenaker <jo...@em...> wrote: > Brian wrote: > > > My gut reaction to how to fix it would be to create a method in the > > base class for models and senders that does nothing called setPatch > > (Patch p). This would be called automatically by addWidget > > on the Model and Sender sent to it. > > Well, here's the "grand design" of what I'm thinking of: > - Patch data would be given only to one of the items (widget, > param-model, or sender) and the others would get it from that. My > inclination would be that the param-model would house the patch data, > since there are possible scenarios in which we might want to adjust > parameters *outside* of the context of a graphical widget (ie, > cross-breeding, or cut "Master Volume" by 90% in all patches in a bank). > ParamModel would have a method for widgets to get the patch data... > which Widget would use to obtain it, and then give it to Sender, if > there is one. > - I think parameter name should also be in ParamModel, for the same > reasons (and obtained by Widget in the same way) > - Lastly, I'd like for DataModel to be worked into this somehow. Like I > mentioned before, the driver I'm working on now requires tat 7-bit bytes > be decoded into 8-bit bytes, and it's easier to do this to the entire > sysex message and than it would be for individual ParamModels to try to > convert just the part they're interested in. Instead, by using a > DataModel class (which holds the actual Patch object and handles all > translation to/from Ints/Longs/Strings), the ParamModels would be given > the DataModel object and would access the data they wanted via methods > like getInt(offset). > > The ultimate result of this would be to stop writing things like this: > > addWidget(leftPane, > new CheckBoxWidget("Midi Pitch Bend", patch, new > MKSBitModel(patch, 16, 5), new MKSBitSender(patch, 16, 5, 9)) > , 0, 10, 1, 1, -3); > > and start writing them like this: > > SingleSysexDataModel datamodel = new SingleSysexDataModel(patch); > .... > addWidget(leftPane, > new CheckBoxWidget(new MKSBitModel("Midi Pitch Bend", datamodel, > 16, 5), new MKSBitSender(16, 5, 9)) > , 0, 10, 1, 1, -3); > > It doesn't look *that* much cleaner, but at least the patch data is only > passed once. > > This could be cleaned up even more if ParamModels use interfaces or > superclasses that define them as either BooleanParamModel, > NumericParamModel, or EnumParamModel. Then, addWidget could discover > what kind of parameter a ParamModel is and automatically give it the > correct widget (ie, BooleanParamModel gets a CheckBoxWidget, > NumericParamModel might get a spinner, EnumParamModel would get a > ComboBoxWidget, etc). Then, you could use things like: > > addWidget(leftPane, > new MKSBitModel("Midi Pitch Bend", datamodel, 16, 5), > new MKSBitSender(16, 5, 9), > 0, 10, 1, 1, -3); > > Of course, you could always do things the old way if this didn't give > you the flexibility you needed. But I think that this would clean things > up in the general cases. > > Give it some thought, if you will. > > - Joe > > > |
From: Joe E. <jo...@em...> - 2005-03-17 22:42:18
Attachments:
smime.p7s
|
Bill Zwicky wrote: > Joe Emenaker wrote: > >> ... addWidget could discover what kind of parameter a ParamModel is >> and automatically give it the correct widget (ie, BooleanParamModel >> gets a CheckBoxWidget, NumericParamModel might get a spinner, >> EnumParamModel would get a ComboBoxWidget, etc). > > Wow, that would be *really* cool. I'd love to just whip up a sysex > parser, and have the GUI automatically appear. My hope is that we can reduce the work involved in writing a driver (for synths that don't do crazy stuff in their sysex) so that 90% of the work is just defining the params (ie, location, data type, and name). Of course, there will always be synths that require things to be done the old way. > Making that work for envelopes would be ugly though, expecially since > the CZ has a weird envelope. Not necessarily. Granted, the CZ might have a wierd envelope and you might have to create that widget the old way (which you'd always be able to do). However, if it was a normal 4-point ADSR envelope, then you could just instantiate some EnvelopeParamModel and define where in the patch data the four points were stored. The Widget should be able to figure out the rest. > What about when JSL provides multiple widgets for the same data type? > i.e. We have both slider and dial widgets for entering numeric values. There'd definitely still be a way for the programmer to create the widget himself and pass it to addWidget if he didn't like the defaults. But, if you didn't really care, then you could leave it up to JSL to decide what the default boolean widget is, etc. One benefit here is that it would allow JSL to let the user change the default widgets. Think of Swing's pluggable look-and-feel. JSL could do the same thing, and let the user pick between "normal" widgets, or maybe some fancy ones (like a boolean widget that looks like an LED light that lights up and turns off). Or the user could decide that they wanted knobs instead of sliders for numerical params. If they did, then any widgets in drivers which were just using the *defaults* would get changed. Drivers which we defining their widgets explicitly would be anaffected. Furthermore, I don't see any reason why individual editors couldn't choose what defaults they wanted so that they didn't have to worry about the user changing them, yet could still benefit from the shorthand addWidget() method. Of course, there are some serious widget *layout* issues to be confronted. Turning all sliders into knobs could really stir up the layout of an editor. So, perhaps we'd need to restrict how much freedom there was there.... but I think that, overall, something like this would be a benefit. > By the way, are you trying any of this stuff? (You're writing a > driver, right?) Most of what you propose can actually be inserted > into your own class tree, where you can see how well it works. I've done some of it already. I'm actually at a cross-roads in the development of the driver because of the fact that my it (sorry if I keep bringing this up) has to convert 7-bit bytes to 8-bit ones and I don't want to have each param-model do this individually (which is why I'm campaigning for some kind of DataModel or some class to separate param-models from the actual Patch object). I want to start moving again with development of my driver, which is why I presented my whole idea for (what I feel is) an improvement to the param-model/widget system. If it's well-received, then I can go make some small changes in the widgets and param-models and then move on with my driver. If it's *not* well-received, then I'm going to have to sub-class every widget and param-model that I want to use so that it works with my data-model design that I'm using for my driver. - Joe |
From: Rib R. <ri...@gm...> - 2005-03-18 01:04:32
|
On Thu, 17 Mar 2005 14:41:57 -0800, Joe Emenaker <jo...@em...> wrote: > Bill Zwicky wrote: > > > Joe Emenaker wrote: > > > >> ... addWidget could discover what kind of parameter a ParamModel is > >> and automatically give it the correct widget (ie, BooleanParamModel > >> gets a CheckBoxWidget, NumericParamModel might get a spinner, > >> EnumParamModel would get a ComboBoxWidget, etc). > > > > Wow, that would be *really* cool. I'd love to just whip up a sysex > > parser, and have the GUI automatically appear. > > My hope is that we can reduce the work involved in writing a driver (for > synths that don't do crazy stuff in their sysex) so that 90% of the work > is just defining the params (ie, location, data type, and name). That's exactly what the XML driver does. You give some basic information (manufacturer, model, etc), specify the type of checksum and encoding, then give a list of parameters. I thought about automatically generating the editor, but that would just look ugly. Instead, I'm working on EditorBuilder. You load up the XML driver you wrote, and it gives you a list of parameters. Then you drag parameters from the list into the editor. By default you get a combo box for lookup parameters, and a knob for range parameters. If you want a different type then you select the parameter and change the type in the properties window. That's about all that EB supports at the moment, but I plan to add support for lots of things including envelopes, scripts (so you can do things like make the range of one parameter depend on the value of another), and multiple containers (ie JTabbedPane, or another JSLFrame that pops up when you click a button). I think you're right that allowing the user to change the widget types would mess up the layout. But with the XML driver, you can just create your own editor in EditorBuilder if you don't like the standard one. At one point we talked about supporting multiple editors for a given patch type, and then allowing the user to choose which to use. |
From: Joe E. <jo...@em...> - 2005-03-18 01:21:39
Attachments:
smime.p7s
|
Rib Rdb wrote: >On Thu, 17 Mar 2005 14:41:57 -0800, Joe Emenaker <jo...@em...> wrote: > > >>My hope is that we can reduce the work involved in writing a driver (for >>synths that don't do crazy stuff in their sysex) so that 90% of the work >>is just defining the params (ie, location, data type, and name). >> >> >That's exactly what the XML driver does. You give some basic >information (manufacturer, model, etc), specify the type of checksum >and encoding, then give a list of parameters. > > Back when the talk of an XML driver first came up, I think I might have suggested that the Java-based param/widget infrastructure be set up like we're discussing now. If it was done well, then it would allow the XML driver to be very *thin*, in the sense that most of the work would just be in reading/verifying the XML and then just having a loop read the Parameter elements and call some kind of decoder.addParameter for each one. It's sounding like that's what you've done... with the caveat that this has not turned into the way things are done when writing Java-based drivers as well. - Joe |
From: Bill Z. <wrz...@po...> - 2005-03-19 22:57:18
|
Joe Emenaker wrote: > If it's well-received, then I can go make some small changes in the > widgets and param-models and then move on with my driver. If it's > *not* well-received, then I'm going to have to sub-class every widget > and param-model that I want to use so that it works with my data-model > design that I'm using for my driver. Ah, I just thought that was one of your many proposals. I don't understand your complaint though; you never need to subclass widgets, and you need to write lots of IParamModel classes anyway. Since you want to decode and encode your sysex all at once, you need to extend IPatch, not Patch. You can then hide your byte array, and require all access to go through getByte, getInt, etc. I believe Driver is the place where you'd instantiate your new patch class. You're other suggestion make for an interesting new design (and I'm guessing the XML package is going that way) but aren't critical to your current driver. -Bill |
From: Joe E. <jo...@em...> - 2005-03-20 00:37:23
|
Bill Zwicky wrote: > Since you want to decode and encode your sysex all at once, you need > to extend IPatch, not Patch. You can then hide your byte array, and > require all access to go through getByte, getInt, etc. Oh... so the addWidget and ParamModels are all looking for IPatch, and not Patch? If that's the case, then my data-model could just implement that and I could pass the data-model directly in place of a Patch object. I'll try that.... - Joe |
From: Joe E. <jo...@em...> - 2005-03-17 22:52:03
Attachments:
smime.p7s
|
Rib Rdb wrote: >You could do the same type of thing in Java. Then it would look like this: >/* when the driver is created */ >// create params in order so addresses are inferred >decoder.addBooleanParam("Midi Pitch Bend"); > >/* Then in the editor */ >addWidget(leftPane, > new CheckBoxWidget(patch, decoder.getParam("Midi Pitch Bend")), > , 0, 10, 1, 1, -3); > > Hey.... that's pretty neat! It looks like your "decoder" is what I'm calling a "data-model", no? Based on that, all we'd have to do is teach addWidget to figure out what kind of param it's getting and select the right widget automatically. Is it possible to explicitly define the addresses of the params? - Joe |
From: Rib R. <ri...@gm...> - 2005-03-18 00:37:49
|
On Thu, 17 Mar 2005 14:51:45 -0800, Joe Emenaker <jo...@em...> wrote: > Rib Rdb wrote: > > >You could do the same type of thing in Java. Then it would look like this: > >/* when the driver is created */ > >// create params in order so addresses are inferred > >decoder.addBooleanParam("Midi Pitch Bend"); > > > >/* Then in the editor */ > >addWidget(leftPane, > > new CheckBoxWidget(patch, decoder.getParam("Midi Pitch Bend")), > > , 0, 10, 1, 1, -3); > > > > > Hey.... that's pretty neat! > > It looks like your "decoder" is what I'm calling a "data-model", no? Yep > Based on that, all we'd have to do is teach addWidget to figure out what > kind of param it's getting and select the right widget automatically. > > Is it possible to explicitly define the addresses of the params? You can add whatever methods you want to your parameters and/or data model. This is supported by the XML driver as well. If your Decoder returns parameters that have the method setAddress(String), then all of the parameters accept <address>0xf00</address> This could either be required or optional (you only have to use it when the value that would be inferred is wrong) |
From: Joe E. <jo...@em...> - 2005-03-18 01:09:52
Attachments:
smime.p7s
|
Rib Rdb wrote: >On Thu, 17 Mar 2005 14:51:45 -0800, Joe Emenaker <jo...@em...> wrote: > > >>Hey.... that's pretty neat! >> >>It looks like your "decoder" is what I'm calling a "data-model", no? >> >> >Yep > > I'll have to take a look. Is it currently located in the XML stuff? Any interest in working it into the core.* stuff? >>Is it possible to explicitly define the addresses of the params? >> >> >You can add whatever methods you want to your parameters and/or data model. > > I guess my question should have been "Is is *already* possible...". >This could either be required or optional (you only have to use it >when the value that would be inferred is wrong) > > That was my thought. For the Boss GT-3, they use the same location for different things depending upon other settings. For example, if your modulation effect is set to "Chorus", then a certain address might contain the chorus regeneration. If the effect is set to "Flanger", then the same address might contain the wet mix level. So, you can't just start at offset 0 and start enumerating the parameters. For the GT-3, you'd have to specify actual addresses. But, even then, you'd only have to do it when you needed to jump backward. So, most of the addresses could still be inferred. - Joe |