Online music education courseware for non-musicians who want to learn how to write their own rock songs.
About Me
Name: Jim Plamondon
Location: Austin, Texas, United States
This blog documents the development of JIMS iGetIt! Music System (JIMS). JIMS' goal is to help you Understand Music in 24 Hours™, if you are (a) a non-musician (b) who wants to learn how to write your own rock songs. Requiring no instrument other than your own computer, and without using traditional notation, JIMS is being designed to deliver a deep understanding of tonal structure...in just 24 hours.
Passing arguments to Flex event handlers using closures
Today, with help from my new friend Jack Kennedy (in San Antonio) and various online sources, I learned how to use "closures" to pass arguments to event handlers in Flex. I remember learning about function closures in a abstract way when studying for my Computer Science degree back in the mid-1980's, but I'd never had the opportunity/need to use them professionally. They weren't supported by any of the languages I was using then, but they are more widely supported now. They are supported in ActionScript 3.0.
Here's the problem I faced this week. I wanted to use a Flex timer to drive an animation, so I created a Timer object and registered an event listener function on it. The listener callback would be called whenever the timer dispatched a TIMER or TIMER_COMPLETE message. But...the callback function needed to access information that was not available within the scope of the callback function. I could put that information into a global variable, but that would smell bad. There did not seem to be a way to hook Timer to dispatch a custom subclass of TimerEvent, which was my first thought -- and my local info wouldn't be available to Timer's TimerEvent factory, anyway. I was stumped.
I recalled that Jack had mentioned, over dinner recently, that he used Timers frequently (and Flash animation rarely), so I figured that he must know how to overcome this problem. Between his cogent, concise response to my email inquiry, and some research to help me understand what closures were and how to use them correctly, I got my code working properly.
First, though, I put together this simple test app to experiment with:
Press the "Draw Circles" button, and the code draws a bunch of randomly-sized circles. The first circle is red, with subsequent circles increasing in hue through a cyclic rainbow until reaching red again, at which point all of the circles are erased. Clearly, the callback function "knows something" about color, else it could not progress smoothly through the rainbow as it does. How is the "Draw Circles" button's click handler providing that state information to the TIMER event callback function?
The answer: through the use of a closure. See the code here.
All of the magic happens in drawCircleHandler(). Here's the code from inside of drawCircleHandler() that registers the callback function for TIMER events: // pass args to TIMER event handler using a "closure" var count:Number = 0; t.addEventListener(TimerEvent.TIMER, function TimerHandler():void { // Every time TimerHandler() is called, it increments // drawCircleHandler()'s local variable "count" count += DEGREES_IN_CIRCLE/MAX_CIRCLES; drawColoredCircleInComponent(degreesToRGB(count), cCanvas); });
(Clearly, I need to figure out how to embed pretty-printed source code in these blog posts.)
The essential point is that TimerHandler(), being defined within the body of drawCircleHandler(), can access drawCircleHandler()'s local variables, such as "count". Every time TimerHandler() is called by Flex's event-dispatching code, it increments the value of "count". This is rather magical, since drawCircleHandler() will long since have completed its execution by then, so one might think that its local (stack frame) variables would have been liquidated. But apparently, the closure's environment is allocated dynamically (from the heap) and retained (i.e., not garbage-collected) so long as anything points at it...anything, including as a Timer's listener-list.
You'll note that "count" is incremented in TimerHandler(). That wasn't my first design. First, I passed "count" into drawColoredCircleInComponent(), in which I incremented it and calculated its RGB equivalent. I figured that since "count" was a Number and therefore an Object, it was being passed by reference, so anything I did to "count" inside of drawColoredCircleInComponent() would act on the local "count" in drawCircleHandler(). Not so; the value of "count" stayed zero -- its initial value. Bummer.
In the second design, I incremented "count" in TimerHandler() and then passed it into drawColoredCircleInComponent(), where its RGB equivalent was calculated. This worked, but it didn't smell right -- why should drawColoredCircleInComponent() know about both "count" and color? My first design had the same smell, once I thought about it. It smelled better -- to my nose, at least -- to have TimerHandler() do handle both incrementing "count" and the calculation of its RGB equivalent, thereby localizing the semantics of "count" within the scope of drawCircleHandler().
That's my story, anyway, and I'm sticking to it. ;-)
I think I've made a small step towards figuring out how drawing works in Flex. Flex's drawing system appears to use a "retained mode" approach, in which the calls you make to "draw stuff" don't actually draw stuff. Instead, they (in effect) define a (new, hidden, secret, inaccessible) method which Flash/Flex uses to draw stuff whenever it feels like it.
Here's a tiny little test applet, called "bar" (because "foo" was already taken):
The above applet draws a small black circle that slides across the applet from left to right, eventually leaving the stage. No biggie...but it's drawing, which is big progress for me. ;-)
This source code is a minor variation of recipe 11.1, "Moving an Object," from O'Reilly's ActionScript 3.0 Cookbook.
In bar.as, a class bar is declared to extend Flash's Sprite class. (Yes, I know that bar should, by ActionScript/Flex convention, be capitalized as Bar, because it is the name of a class. Gimme a break. It's not germane.)
Every bone in my body tells me that this code is INSANE. All of the drawing code is in bar's CONSTRUCTOR, fer cryin' out loud. How can this code be *found*, let alone called, when an instance of bar is to be drawn?
I would expect that this drawing code would be placed in a draw() method of the bar class (i.e., bar.draw()), which would be called by Flash whenever an instance of bar was to be drawn, and that this draw() method would be an override of a draw() method inherited, ultimately, from Flash's DisplayObject class (from which bar's superclass, Sprite, descends). But neither DisplayObject nor Sprite have a draw() method, or anything similar to a draw() method. (They do have a RENDER event, but it is used to advise an object that it is about to be rendered; it is NOT a request that the receiving object now render itself.)
It gets weirder. Every time the Flash player needs to draw a new animation frame, it dispatches an ENTER_FRAME event. The bar class listens for this message in its onEnterFrame() method. No problems there. To animate the circle rightwards, I would expect bar.onEnterFrame() to (a) shift the location of the bar instance slightly rightward, and then (b) call bar.draw().
But nooooo! As I mentioned above, bar has nothing like a draw() method, nor does any of its super-classes. The only thing that bar.onEnterFrame() does -- and remember, I stole this code *directly* from the published, well-regarded recipe mentioned above -- is offset the bar object slightly rightward (more on that later). That's it. There's no drawing code in bar.onEnterEvent() at all, nor any calls to any other drawing code.
Somehow, the Flash player is accessing the drawing code that's defined in bar's constructor, and executing it sometime after bar.onEnterFrame() method is called.
WTF?
I see this pattern all over the sample code for Flash & Flex, from many different authors. Adobe's online documentation for Flex 3 is rife with it. They put drawing code in the class' constructor, or into a function which is only ever called by the class' constructor (to verify this, I've set breakpoints on such drawing functions, and seen that they are never called from anywhere except the constructor).
HOW DOES THE FLASH PLAYER FIND THIS DRAWING CODE, in order to execute it when it's time to actually draw the object?
I'm not sure, but I think I have identified a clue: "retained mode."
Apparently, the Flash player (and hence ActionScript and Flex) uses "retained mode" for all drawing. Unfortunately, only initiates into Adobe's secret society are taught the secrets of "retained mode." It's not documented anywhere that I've been able to find.
If you know where Adobe's use of retained mode is explained in detail, please let me know where I can find this documentation.
The Wikipedia entry for "retained mode" is cryptic. The phrase "retained mode" does not occur in Adobe's online documentation for Flash, Flex, ActionScript, or Flex Builder. It's a big secret, and you can't really understand drawing in Flash/Flex/ActionScript unless you understand the Secret of Retained Mode. Or, at least, *I* can't. (Maybe I'm just stupid.)
Sure, I could just mindlessly copy other people's sample code, and put all of my drawing code into my classes' constructors. What the heck, it seems to work, right?
However, I've never been able to do that kind of "thought-free programming." It's one thing to reuse a well-documented component without understanding its implementation; that's what components (and their interfaces) are for. It is quite another to re-use a pattern without understanding it; that's a disaster waiting to happen. I want to understand. That makes me a slower "production" programmer initially, but eventually makes me more efficient and creative, because I come to understand how everything works, which allows me to make new and (hopefully) useful insights.
This Cartesian refusal to accept anything that one doesn't understand is *the* fundamental basis of all scientific advancement, fer cryin' out loud, so I'd think Adobe -- which had a strongly scientific culture, last time I interacted with it -- would give scientific skepticism a bit more respect by (for example) documenting its platform's architecture more clearly.
Here's what I think is going on deep in the secret bowels of Flash Player.
All "drawing" is effected by interacting with an instance of Flash's Graphics class. For example, in bar's constructor, the following code is called to effect the drawing of a circle: this.graphics.beginFill(0x0000f, 100); this.graphics.drawCircle(0, 0, 5); this.graphics.endFill();
"this" is the instance of the bar class being constructed. Its graphics property is inherited from Sprite, which is bar's superclass.
Apparently, when you call the method of the graphics property -- such as the calls to beginFill(), drawCircle(), and endFill() above -- the methods don't actually draw anything. The method calls are simply recorded by the graphics object -- that is, they are retained. These method calls are (in effect) defining an implicitbar.draw() method (via delegation to bar.graphics), which Flash then calls whenever it needs to draw a bar instance.
In the bar.onEnterFrame() method, this.x is incremented by delta (initialized to 1 in bar's constructor). this.x += delta;
Notice that incrementing this.x does not involve this.graphics, so it isn't "drawing code," per se. That is, it doesn't say what to draw, it just says where to draw.
Sometime after this.x is incremented in bar.onEnterFrame(), Flash calls the implicit (secret, hidden, retained) bar.draw() method that it generated from bar's calls to this.graphics's methods.
That is, I think, how drawing happens in Flash/Flex/ActionScript. If this is wrong in any significant way, please let me know.
If this is how it works, then it strikes me as being bad, bad, bad.
Retained mode is an example of self-modifying code, which always makes me nervous. It smells bad. Specifically, retained mode's implicit generation of a (secret, hidden, inaccessible) bar.draw() method makes it impossible to use time-honored object-oriented programming practices to modify a class' drawing behavior. In brief: I can't override a secret, hidden, retaineddraw() method.
I mean, really: drawing code in the constructor? The single-least-over-rideable method of any class? Are you insane?
I *must* be missing something, here. That just *can't* be right. I hope that someone will point out my misunderstanding and clarify how Flash player's drawing really works.
The opposite of retained mode is "immediate mode," in which calling the function drawCircle() draws a circle, right now. The Mac OS/Toolbox API, the Win32 API, the MacApp framework, the MFC framework -- every API I've ever worked with used immediate mode.
It is only by merest chance that I have any awareness of "retained mode" at all. I was on Microsoft's DirectX evangelism team back in the mid-1990's when Direct3D was first introduced. The first version of Direct3D used a retained mode API to improve efficiency (probably the same reason why Flash uses it). However, Direct3D's use of retained mode confused the heck out of game developers. As a result, they simply refused to use Direct3D, choosing to use OpenGL instead. The next version of Direct3D deprecated retained mode in favor of immediate mode, but it had lost so much mindshare in the meantime that it was a decade before Direct3D finally won over OpenGL's most vocal proponents.
Maybe all of this has changed since I stopped actively programming in 1992. Maybe now all Computer Science students are taught to think in terms of retained mode, so Adobe doesn't need to explain it anymore. Or maybe Adobe doesn't want to explain it; perhaps Adobe has too darn many ISVs already, and it wants to keep out the riff-raff by ensuring that only those who already know the secret "retained mode" handshake can get past the gate. That would be remarkably self-defeating, and although such self-defeating actions are surprisingly common, it doesn't sound like Adobe at all, but...what do I know?
Or maybe I'm barking up the wrong tree entirely, and Flash/Flex/ActionScript's drawing system doesn't used retained mode at all...in which case I'll apologize profusely.
Now, right-click (or, on a Mac, Command-click) on it, and a menu will appear; choose View Source, and voila, you're taken to a little applet that allows you to view Tweener's source, which it correctly looks for in http://www.igetitmusic.com/blog/SWFs/Tweener/srcview/index.html.
Bring up its View Source menu, select it, and voila! you're taken to the wrong @#$%& page.
Apparently, the Tweener SWF contains an algorithm -- generated by Flex Builder, not me -- that constructs a path to where it expects to find the source code for its View Source menu item. This algorithm appears to be based on the path to the file in which the SWF is embedded, rather than the path to the SWF file itself.
But...that's a bug, isn't it? If I want to embed a given SWF in (say) ten different web pages, then using the source-path algorithm than Flex is currently using, I'd have to copy the SWF's source code to each of those ten web page's servers...which I can't possibly know in advance. That's a major violation of information-hiding, isn't it -- in addition to being a huge pain in the posterior.
I would think that a much better default behavior would be to look for [somepath]/foo.swf's source code in [somepath]/srcview/index.html, no matter what the path to the html in which foo.swf was embedded.
Design Patterns Are Unimplemented Language Features
It seems to me that design patterns are nothing more (and nothing less!) than recipes for systematically maximizing the benefits of information-hiding (Parnas, 1972). Everybody knew that information hiding was good, but...how did you DO it, exactly? Design patterns provide well-defined recipes for many common architectural problems -- recipes that push an application's inevitable fragility out to its fringes.
It also seems to me that many of these patterns exist only to overcome the deficiencies of imperative, statically-typed programming languages.
Parenthetically -- I was always a bit of a language slut, and being Microsoft's evangelist for the .NET Common Languge Runtime ("Project 7") allowed me to indulge myself by interacting with Bertrand Meyer (Eiffel), Niklaus Wirth (Pascal/Modula-2/Oberon), Simon Peyton Jones (Haskell), Luca Cardelli (ML), Zoltán Somogyi (Mercury), and a host of others. We got their languages working on the pre-release version of the .NET Runtime and Visual Studio.NET, and used these implementations to identify architectural weaknesses in (and potential improvements to) .NET's design. I particuarly enjoyed working with Erik Meijer (on Haskell), whom I helped hire out of the University of Utrecht into Microsoft Research, where he developed LINQ. What fun! :-)
Back to the point -- each subsequent generation of programming languages succeeds by providing a higher level of abstraction over the previous problem-solving tools, thus making it brain-dead simple to do something that was previously difficult. For example, I'm old enough to remember hand-coding vTables in order to do object-oriented programming in C. Then, C++ coded vTables for me -- yahoo! C++ made it trivially-easy to code vTables, because the language did it instead of me. Likewise, Java trivialized programming interfaces (and other information-hiding techniques). Dynamic languages take this a step further, and functional programming languages another step after that. The rise of PHP, Ruby, Python, etc. are examples of the abstracting-away of programmers' pain-points. This is usually expressed in terms of "increased programmer efficiency," which is the result of such abstraction. (Importantly, Moore's Law makes the computing burden of these ever-increasing levels of abstraction affordable).
However, offering a valuable abstraction is not, in itself, sufficient to drive a new language to success. It also have to promote itself as the Gateway to a Proftable Career. Java's provides the clearest example of this. Objective-C is making a comeback as the de facto standard language for iPhone programming, due to the iPhone's popularity.
ActionScript 3.0 is a fairly old-fashioned declarative language, compared to (say) Scala. As such, it does not provide the asbtractions needed to trivialize the GoF's design patterns. Even if I were able to find a lovely little dynamic/functional tool suite (language, framework, IDE, etc.) for the Flash Player, there are no job openings for those who have mastered that tool suite, as there are (and will soon be even more) for ActionScript/Flex/FlexBuilder. Equipping myself with salable skills is an important sub-goal of this development project, so that I can help pay for the continued development of JiMS iGetIt! Music System through contract programming.
So, for now, I'm just going to have to wade through these design patterns, and learn to do them the hard way, like everybody else.
Clearly, this is a job for Flex's "Shape" class. So I write a nice little subclass of Shape called Arc, which draws a circle-segment from beginAngle to endAngle. The details aren't relevant, because the code never gets far enough for the details to matter.
In the test application's MXML code, I add an Arc object:
No problems there; the Arc object is instantiated, its constructor is called, all is spiffy-keen. I'll just set the arc's radius to half of either the Shape object's width or height, whichever is less. No worries, right?
Except that, when the constructor is called, the width and height of the Arc are both 0 (zero). Excuse me? Well, maybe that's because its properties haven't been initialized yet. No worries; there's a handy-dandy event called "CREATION_COMPLETE" that one can listen for, just for this purpose.
But noooooo. CREATION_COMPLETE is an event of class FlexEvent, and Shapes don't post events of that class. Reading the documentation for the Shape class and its ancestors, there appears to be no equivalent event.
Well, fine, whatever, I'll write a brutal hack: in Arc's constructor, I'll (a) set the radius to NaN (Not a Number), (b) add a listener for events of type ENTER_FRAME (which ARE available to Shapes), and (c) in that listener's code -- which gets called very frequently, which is why this is such a brutal hack -- check the radius, and if it's NaN, set it to the proper value, based on the arc's width and height. Disgusting, perhaps, but it ought to work.
But noooo. When the code gets to the ENTER_FRAME event, the width and height of the shape are still zero. WTF? I check the Shapes docs, and yes, they have width and height properties. For some reason, they just aren't being initialized from my MXML.
This is totally bizarre...but it gets worse.
As a test, I set the width and height of the arc explicitly in the ENTER_FRAME code, as follows: width = 162; height = 100;
So, Mr. Smarty-Pants Flex Framework, you don't to initialize my parameters? @#$%^* you, I'll initialize them myself.
But noooo! I step through the execution of the code with the debugger, and to my amazement, the above code does not change the values of width and height! *Other* stuff changes; for example, the value of the Arc's inherited "transform" property, previously null, points to a flash.geom.Transform object after the call to "width = 162;" and the value of the inherited property "visible" changes from false to true...but the width property remains zero! Same with the height property. WTF?
OK, so I'll just step through the Shape code, and that of its ancestors as necessary, to figure out what the @#$%^*& is going on. Source code is the ultimate documentation, right?
But noooo! Shape's code isn't accessible using Eclipse's usual click-on-the-class-name-and-its-source-file-opens mechanism. Every time I do the click, instead of seeing the source code, I get a @#$%^ alert titled Code Navigation Error telling me that
"Source code could not be found for [the Flex class I clicked on] in /eclipse/Adobe Flex Builder 3 Plug-in/sdks/3.2.0/frameworks/libs/player/10/playerglobal.swc."
@#$%^*&! Mother-@#$%^&-ing piece of @#$%!
OK, fine, whatever. Om Mani Padme Hum. I am One with the Universe. That which does not kill me, and all that.
Screw width and height, anyway. Who needs 'em. I'll just draw an arc with hard-coded numbers.
Nothing is drawn.
Well, maybe my arc-drawing code is wrong. I'll just call the built-in drawCircle() method instead, just to see that i can draw SOMETHING.
Nothing.
Do I have my graphics environment set right? Yep, I'm calling lineStyle(5) to set a wide black line, and beginFill(0) to fill the circle with black. Nothing.
The source code for some of Flex's classes is available -- UIComponent, for example -- but not for others. Not for the ones I need now. Googling "Code Navigation Error" and "Source code could not be found for" has turned up no fixes, workarounds, or brutal but embarassingly-effective kludges.
So, I'm dead in the water. I simply cannot figure out how to draw a Shape in Flex.
Here's a stripped-down version of the app, with just a tiny MXML file and a short, heavily commented TestDraw.as class that shows the problems:
Of course, the application does absolutely nothing.
Furthermore, I still can't get the @#$%^&* "View source" menu item to work. In the sample app above, for example, it insists on trying to find a file at "http://www.igetitmusic.com/blog/srcview/index.html", even though the app is at "http://www.igetitmusic.com/blog/SWFs/Tweener/Tweener.swf". I have not been able to find *any* documentation -- not one word -- on how to set this pathname.
For example, the book "Flex 3: Training from the Source" -- which is not a bad book, overall -- has a chapter titled "Deploying Flex Applications." Sounds perfect, right? Unfortunately, like everything else I've been able to find on this topic, the chapter says "choose Project -> Export Release Build, and you're done!" -- when, actually, that's just the first step. Executing the "Export Release Build" command produces a bunch of files in the project's bin-release folder. Cool! But...now what? Which files need to be copied to the file server? How *exactly* do I tell a web page to include the resulting Flash doohickey? Should I tell the web page to include the doohickey.swf, the doohickey.html, or one of the other files in the doohickey project's bin-release folder? And how, for the Holy Love of God, does one define the path to the doohickey's source code?
I am not frustrated by computer programming. I LOVE computer programming. What is frustrating is NOT being able to do computer programming due to chickenshit problems like those above.
I have discovered what may prove to be a very helpful resource: Adobe's Flex forum. It appears to be a reasonably well-organized community self-help forum, in which Flex users are encouraged to post their questions and answer those posted by others.
Back in the day (by which I mean "the late 1980's," before many of today's eager young Flex programmers were born), I participated regularly in a similar forum: MacApp.Tech$, devoted to MacApp, the first widely-used object-oriented application framework. It took me a while, but I eventually posted a lot more answers than questions. I hope to be able to be similarly useful to the Flex forum's participants, eventually.
I stopped programming professionally on February 3rd, 1992, when I joined Microsoft as a Technology Evangelist. After that, any time I needed sample code showing how to use some new Microsoft technology, it was more effective for me to have a consultant code it up than to code it up myself. That way, I'd have the code, and a consultant that was up-to-speed on the new technology, too. I could then aim that consultant at companies who I knew were struggling to implement the technology themselves. Very efficient.
Unfortunately, this put me increasingly out of touch with rapidly-evolving programming practices.
A good example is the notion of "design patterns," which were first described (to my knowledge) as such in late 1994, nearly three years after I'd stopped programming. This is a double whammy, for two reasons.
First, I don't understand "design patterns." It's another learning curve I have to climb. It's clearly possible to climb it, and I will make it to the top eventually, but it's just one more damn thing between me and my objective.
Secondly, and more importantly, in the 15 years since "design patterns" were first described, they have become so deeply absorbed by the professional programming community that knowledge of these patterns is simply assumed. This is a huge pain in my ass.
For example, I eventually figured out that, to meet my need for "growing arrows," I would need to implement a custom Tween effect associated with a custom Arrow component. So I read Adobe's online article About Tween Effects, which drove me nuts. It seemed to be using three classes (Tween, TweenEffect, and TweenInstance) to do something that I thought could just as well be done by a single class. Since I'm trying to *understand* what I'm doing, rather than just programming by numbers, this apparently-neededless complexity was a real barrier to my progress. Adobe's documentation said nothing about why this complexity was present.
Then I noticed the word "factory," just once, in the About Tween Effects documentation:
"mx.effects.TweenEffect: The base factory class for all tween effects."
I vaguely remembered once knowing the meaning of the phrase "class factory," which gave me just enough information to connect the dots from the documentation to the design pattern. So, I drive over to my local Barnes & Noble and bought the original Design Patterns book, plus another written specifically for Actionscript 3. These are ladders of knowledge, helping me scale yet another learning cliff.
I recognize that my situation -- of returning to programming after a 17-year hiatus -- is not common, and that Adobe can not reasonably be expected to write its documentation to accomodate such an uncommon reader. However, it does not seem unreasonable to suggest that, when a given feature of Flex relies on a specific design pattern, the feature's documentation should say so, and perhaps even refer to an online description of that pattern.
I do enjoy learning new things, but I *am* first and foremost trying to get a product out the door. It seems like I'm having to do a depth-first search among the roots of every darn language, tool, and framework, platform, and paradigm I encounter along the way -- and I have no idea how many more of these diversions I'm going to encounter. The number seems to be unbounded. That makes it very hard to plan ahead. This concerns me.
On a piano keyboard, the notes of the C Major scale have one shape and color (the "white keys"), while the notes that are chromatic to C Major have a different shape and color (the "black keys"). This seems awfully arbitrary. Why C? Why not A, B, or Bb?
On a digital piano, one can easily transpose the keyboard so that any given diatonic scale's notes fall on the white keys. That's cool. But what about other scales? There's more to music than the diatonic scale.
One of the nice things about a "virtual" on-screen keyboard is that one can change the way individual buttons are drawn "on the fly." That allows us to draw the notes/buttons that are in "the current scale" one way, while drawing those that are out of the scale a different way.
The latest version of my Flex iGetIt! keyboard, shown below, allows the user to select one of the Prime Scales from a ComboBox, with the coloration of the keyboard's notes/buttons changing accordingly. The notes of the current scale are the "white keys," whereas the out-of-scale notes are the "black keys."
Why is this a "good thing"?
There are scads of "scale books" that describe scads of "scales." I don't understand this.
As far as I can tell, there are really only a half-dozen scales that are relevant to tonal music -- the previously-mentioned Prime Scales. The scale books are fat with "scales" because
they confuse scale and mode, treating different modes of the same scale as if they were different scales, and
they confuse scale and key, treating different keys of the same scale as if they were different scales.
This conceptual confusion can lead to a combinatorial explosion of "scales." Do the math: six Prime Scales times seven modes per scale times fifteen possible keys is 6 * 7 * 15 = 630 possible "scales." Six hundred and thirty! From an educational perspective, that might as well be "infinity." No wonder people hate learning scales! Using the traditional instruments, notation, and theory, there is no end to it. You could study and practice your whole life and never master them all.
I don't see the point. This explosion of "scales" is an artifact of traditional music technology (i.e., traditional instruments and notation). They are not meaningfully distict, except that -- using traditional technology -- they require different notation and different performance gestures. The explosion is not a theory problem; it's a technology problem. Using JiMS, you only have to master the six Prime Scales. Six! That's less than 1/100'th of the traditional number. Two orders of magnitude!
More importantly, by clarifying the relationships between scales, modes, and keys (and chords, and melodies, and tunings, and so on), JiMS inter-relates these important musical concepts, thereby providing a strong theoretical foundation for understanding music.
Here's the latest version of my keyboard component:
It looks and behaves much better now. I copied and pasted the code from Flex's Button class into a new class (KBButton) and hacked away everything that I didn't need, which was a lot. I then did the same with the Flex classes needed for programmatic skinning, so that I could draw the keyboard's buttons as I saw fit. The drawing isn't perfect by any means, but at least now I know how control-drawing works.
My next step is to move the sound-generation code out of the KBCanvas (which holds the KBButtons) and into a stand-alone Synth class. In effect, the KBCanvas and its KBButtons (and their KBButtonSkins, and so on) will know only about drawing things on the screen in response to NoteEvents, and the Synths will know only about playing sounds in response to NoteEvents. Importantly, that means that the KBCanvas and the Synth will *not* know about the other. I will be able to change the design and implementation of either one completely indepently of the other. The Flex application will act as the central controller, intercepting keypress events from the user's computer keyboard and re-packaging them as NoteEvents. The KBCanvas and Synth will listen for these events, changing their visual/sonic states, respectively, when such NoteEvents occur. The application will keep its current state in a data structure (the "model") that doesn't know about Synths or on-screen views, either. Hence, a nice clean separation of model, view, and controller (with the application as the controller, and the Synth as a sort of "sonic view").
My progress in implementing these custom controls has been glacially slow, by the standards of commercial software development. But right now, speed isn't the point. It's more important that I soak up Flex's design patterns, Actionscript's idioms, and eclipse's workflow, thereby increasing my overall efficiency, than to get these components done "yesterday." I expect that my efficiency will be improving rapidly from here on out (albeit from a still-low level).
Well, it turned out to be more complicated than "it's the constructor, stupid."
For keyboard events, at least, the event handler has to be installed in the view's application's stage. Unfortunately, the view's application and stage are both "null" when the view's constructor is called.
Therefore, in the view's constructor, I added a listener for the view's "creationComplete" event, which is dispatched at the very end of the view's creation and initialization process.
When the view's "creationComplete" message handler is called, the view's application is initialized, but it's stage isn't. So, in the view's "creationComplete" message handler, I added a listener for the application's "applicationComplete" message.
public function creationCompleteHandler(e:Event): void { parentApplication.addEventListener(FlexEvent.APPLICATION_COMPLETE,
applicationCompleteHandler); }
Finally, in the "applicationComplete" handler, the view's application's stage is initialized, allowing me to install listeners for keyboard events, which was the whole purpose of the exercise.
public function applicationCompleteHandler(e:Event): void { parentApplication.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyHandler); parentApplication.stage.addEventListener(KeyboardEvent.KEY_UP, keyHandler); }
This solution requires two "preparatory" layers of event listeners just to get to a point in the initialization cycle where the view's application's stage's state is stable. It strikes me as being an obscure kludge rather than an elegant design. Its only virtue is that is honors encapsulation. It reaches *up* into its super-classes' properties (parentApplication and parentApplication.stage), which is perfectly legit.
The only other solution I could think of was to install the keyboard listener within the scope of the application's code, rather than the view's. This, however, would require having the application "know about" its sub-view's keyHandler function -- that is, reaching *down* into a component's implementation details, which is a heinous violation of encapsulation.
If anyone knows of a cleaner solution, I'd love to hear about it.
In my earlier post What I Hate About Computer Programming, I described a series of failed attempts and work-arounds that I flailed through in an attempt to solve a given problem.
In it, I wrote:
The right time to add these event listeners is when the PitchSlider's instance is done being created. The Slider class' superclass, UIComponent, has a nice little "creationCompleteHandler()" method that's called at just the right time, too.
Well, apparently, the right time to add such event listeners is in the view's constructors, which I would have thought was WAY too early in the construction/initialization process. The constructor happens to be one of the few methods that isn't marked as "mx_internal" (because it can't be, being a constructor, and all).
How did I learn this? By studying the source code to Adobe's Flex framework, which is installed along with the Flex SDK.
I've spent much of the last few days doing nothing but studying Flex's source code. There's no better tool for learning a framework that walking through its source code, both linearly (reading straight through a file) and interactively (stepping through it with a debugger). That's how I learned to use MacApp (the first widely-used application framework for personal computers) back in the mid-1980's. I don't think there were any books on MacApp then; the source was the only info available. You can hardly walk through a bookstore today without tripping over books on Flex, which is great, but nothing beats access to the source code.
Here's my first crack at implementing the iGetIt!/JIMS note-layout on a standard computer keyboard:
PLEASE NOTE: you must click on the component with your mouse before it will start recognizing keyboard presses. Other than that, the on-screen keyboard does not respond to mouse clicks; you play it by pressing the keys of your computer's physical keyboard.
This component is not done by any means. It suffers from myriad annoying little problems. However, the basic functionality is there. You can use your computer keyboard to control musical notes in the iGetIt!/JIMS note-layout.
The pitch slider changes the pitch of "Re0," which is the note controlled by the QWERTY keyboard's 'H' key. This allows you to "change key" to any arbitrary frequency in a one-octave range. It should be noted that the selected pitch does NOT need to match the pitches defined by the standard A4=440Hz concert pitch in 12-tone equal temperament tuning; the structure of (harmonic) musical sound is the same for every frequency and tuning.
I do not propose that such a pitch slider is a good user interface for changing keys during performace. It's just a tool for demonstrating that the pattern of (tonic solfa) notes used by the iGetIt! keyboard stays the same, no matter what frequency is chosen as its key.
Each button displays its QWERTY label, its note name (e.g., Re0 on the keyboard's H key), and the coordinates of that note in tonal space (e.g., [0, 0] for Re0). The note Re0 is the "origin," which sounds the pitch chosen using the Pitch Slider. The pitch of note [x, y] is (x*P8 + y*P5) cents away from the pitch of Re0, where P8 is the octave (1200 cents) and P5 is the tempered perfect fifth (currently defaulting to 700 cents, with no user interface for changing that default). (If you think that sounds complicated, check out the formula for determining the pitch of a note in 12-tone equal temperament.) The coordinates are displayed for debugging purposes; no novice music student needs to see them.
An octave's notes are numbered from Re, rather than from Do as is typical. This is not a feature of the iGetIt!/JIMS music system; it's a bug that I haven't addressed yet. Notes are traditionally numbered from D0 and I see no reason to change that.
Each button's information is drawn poorly, due to problems that I do not yet know how to fix, but it's legible.
If it doesn't work on your browser/computer configuration, please let me know.
"View source..." doesn't work, because I don't yet know how to make the source available. Once I figure that out, I'll come back and make it work.
By and large, I'm enjoying my return to computer programming after a 17-year hiatus. However, the experience is also reminding me of the things about computer programming that I hated before, and still hate today.
Here's an example.
My PitchSlider component is a subclass of the Flex class named VSlider. No problems there. In PitchSlider's current implementation, one must specify, in one's application, a "thumbHandler()" function that deals with mouse presses on the slider's thumb (the thing you drag around to change the slider's value, and hence the pitch of the PitchSlider). This implementation is kinda grungy, because the application using the PitchSlider needs to know a lot about how of the PitchSlider's internal details. It's a "tightly-coupled" design. What I'd like to do is move the thumb-handling code into the PitchSlider class instead -- hiding it from the application -- and then dispatch a custom event when the thumb is released, advising all listeners that the pitch slider's pitch has changed. This latter approach would enable a looser coupling between the PitchSlider and its container/application, which is a Good Thing.
Unfortunately, writing PitchSlider this way appears to be *&^%$#@ing impossible.
All I need to do is override three methods of VSlider's superclass, named Slider: onThumbPress(), onThumMove(), and onThumbRelease(). My overrides would call the inherited method and then (respectively) start playing the current pitch; change the current pitch; and stop playing the current pitch (then dispatching a custom event indicating the final pitch). That should be no problem, I figured; this kind of "extension by subclassing" is exactly what object-oriented programming is all about. 15 minutes, tops.
But noooooo. Inside of the Slider class, these methods are hidden away in a special "mx_internal" namespace. A Google search on "mx_internal" indicated that any method flagged mx_internal is not to be overridden because Adobe (creators of the Flex framework, of which the Slider class is a part) might change its implementation in future, and therefore wants to be able to say "here there be dragons; use these implementation details at your own risk."
Excuse me? I can't override any of the three methods that uniquely distinguish sliders from all other controls? WTF? When I was napping with Rip van Winkle, did subclassing become passe?
There had to be a way around this.
Google led me to a discussion of how to get past the "mx_internal" barrier: just import and then use the mx_internal namespace. Good enough -- I'd use this work-around in the short run to get the PitchSlider working, so that I could focus my attention on learning how to use try/catch blocks or some other more-pressing concern.
But noooooo. The work-around works when *calling* hidden methods, but not when *over-riding* them. Instead, the compiler barfs an error message that is helpful only as the basis of another Google search. Googling it, I found an official Adobe bug report saying that the work-around didn't work when using it to override methods. This required *another* work-around, to wit, omitting the keyword "override" in the definition of the function.
Fine, OK, whatever. Omitting the "override" keyword did indeed allow the code to compile, thus successfully working around the compiler bug. I figured that I was home free.
But noooooo. Running the application threw up a run-time error, telling me that I had illegally overriden a Flex method.
*&^%$#@er - *&^%$#@ers!
Apparently I was simply not going to be allowed to do the obviously-right thing by overriding these *&^%$#@ing methods.
Deep breath. Om mani padme hum. I am but scum on Buddha's foot; all striving is vanity; and this *&^%$#@ing approach isn't helping me to attain any *&^%$#@ing enlightenment anyway, so forget it and move on (as the Dalai Lama might say).
Time to go to Plan B: have my PitchSlider add event listeners to itself, so that it can "notify itself" when its *&^%$#@ing super-class is done handling thumb press, movement, and release actions.
The right time to add these event listeners is when the PitchSlider's instance is done being created. The Slider class' superclass, UIComponent, has a nice little "creationCompleteHandler()" method that's called at just the right time, too. I figured that I would just override that method to install my event listeners. It's not an "mx_internal" method, either, so that should work.
But noooooo. creationCompleteHandler() is marked as a "private" method, which means that it can't be overriden, either.
*&^%$#@er - *&^%$#@ers!
I was never a great computer programmer. I was good, but never great. And I know that I've been out of the loop for an outrageously long time, so...perhaps there's some new way to accomplish this task that would be patently obvious to anyone lacking grey hair. I'd welcome the opportunity to become enlightened.
But *&^%$#@us *&^%$#@ing *&^%$#@st, I absolutely HATE this cascading series of problems, work-arounds, and work-arounds for work-arounds, with each level delaying the release of my product by hours or even days. I just want things to *&^%$#@ing *work*.
(It's exactly this kind of short-tempered frustration that drove me to create the iGetIt! Music System in the first place, of course. If I'd been more patient, I would have just learned music the way everyone else did...the hard way.)