<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/halo"
               xmlns:d="http://ns.adobe.com/fxg/2008/dt"
               xmlns:ai="http://ns.adobe.com/ai/2009" 
               xmlns:igimc="com.igetitmusic.components.*"
               xmlns:i="com.igetitmusic.components.intervalSlider.*"
               xmlns:k="com.igetitmusic.components.keyboard.*"
               xmlns:j="com.igetitmusic.JIMS_Sonoflash.*"
               xmlns:audio="assets.audio.*"
               minWidth="640" minHeight="480"
               width="640" height="480" 
               viewSourceURL="srcview/index.html"
               preinitialize="appPreinitializeHandler(event)"
               applicationComplete="appCompleteHandler(event)"
               currentStateChange="{ctlRe0Interval.value = 0}">    
    
    
    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
    <!-- Visual Elements                                                               -->
    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
    
    <!-- Re0's Pitch Slider -->
    <s:Group id="grpRe0"
             left="0" verticalCenter="{0}"
             visible="true"
             enabled="true"
             includeInLayout="true">
        
        <s:VSlider id="ctlRe0Interval"
                   horizontalCenter="0" verticalCenter="0" height="80%"
                   minimum="{MIN_RE0}" maximum="{MAX_RE0}"
                   value="0"
                   snapInterval="100"
                   liveDragging="true"
                   change="ctlRe0Interval_changeHandler(event);"
                   valueCommit="ctlRe0Interval_valueCommitHandler(event);"/>
        
        <s:Label horizontalCenter="0" top="0" text="High" fontSize="12" textAlign="center"/>
        <s:Label horizontalCenter="0" bottom="0" text="Low" fontSize="12" textAlign="center"/>
    </s:Group>
    
    <!-- Tuning Slider -->
    <s:Group id="grpTuning"
             top="10"
             right="0"
             bottom="10"
             width="103">
        
        <s:VSlider id="ctl5thWidth"
                   left="22"
                   top="15"
                   height="430"
                   minimum="{MIN_5TH_WIDTH}" maximum="{MAX_5TH_WIDTH}"
                   value="{Model.dataModel.beta}"
                   liveDragging="true"
                   valueCommit="ctl5thWidth_valueCommitHandler(event);"/>
        
        <s:Label left="9" top="0" text="Widest" fontSize="12" textAlign="center"/>
        <s:Label left="0" bottom="0" text="Narrowest" fontSize="12" textAlign="center"/>
        
        <s:Label x="33.5" y="16" text="5-tet"/>
        <s:Label x="33.5" y="186" text="17-tet"/>
        <s:Label x="33.5" y="235" text="Pythagorean"/>
        <s:Label x="33.5" y="260" text="12-tet"/>
        <s:Label x="33.5" y="297" text="31-tet"/>
        <s:Label x="33.5" y="304" text="1/4-comma"/>
        <s:Label x="33.5" y="320" text="19-tet"/>
        <s:Label x="37.5" y="434.5" text="7-tet"/>
    </s:Group>
    
    
    <mx:HRule id="ctlRule" 
              visible="{grpRe0.visible}"
              verticalCenter="{_re0sThumbHeight}" height="1" 
              left="{grpRe0.width + 5}"
              width="20%"/>
    
    <k:KeyboardGroup id="grpKeyboard"
         verticalCenter="{_re0sThumbHeight}"
         scaleX="0.9" scaleY="0.9"
         horizontalCenter="0"
         clipAndEnableScrolling="true"
         keyboardLayout="{kblQWERTY}">
        
        <k:layout>
            <k:WickiNoteLayout octaves="2" cardinality="19" margin="5"/>
        </k:layout>
        
        <!-- Se0 row (top row of KB) -->
        <k:NoteButton id="Se0" alphas="5"  betas="-8" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Le0" alphas="4"  betas="-6" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Te0" alphas="3"  betas="-4" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Do1" alphas="2"  betas="-2" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>        
        <k:NoteButton id="Re1" alphas="1"  betas="0"  keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Mi1" alphas="0"  betas="2"  keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Fi1" alphas="-1" betas="4" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Si1" alphas="-2" betas="6" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Li1" alphas="-3" betas="8" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        
        <!-- De_1 row (next-to-top KB row) -->
        <k:NoteButton id="De_1" alphas="5" betas="-9" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Ra0" alphas="4" betas="-7" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Me0" alphas="3" betas="-5" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Fa0" alphas="2" betas="-3" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>        
        <k:NoteButton id="So0" alphas="1" betas="-1" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="La0" alphas="0" betas="1"  keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Ti0" alphas="-1" betas="3" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Di1" alphas="-2" betas="5" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Ri1" alphas="-3" betas="7" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="My1" alphas="-4" betas="9" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        
        <!-- Se_1 row (next-to-bottom row of KB) -->
        <k:NoteButton id="Se_1" alphas="4" betas="-8" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Le_1" alphas="3" betas="-6" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Te_1" alphas="2" betas="-4" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Do0" alphas="1"  betas="-2" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>        
        <k:NoteButton id="Re0" alphas="0"  betas="0"  keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Mi0" alphas="-1" betas="2"  keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Fi0" alphas="-2" betas="4" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Si0" alphas="-3" betas="6" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Li0" alphas="-4" betas="8" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        
        <!-- De_2 row (bottom KB row) -->
        <k:NoteButton id="De_2" alphas="4" betas="-9" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Ra_1" alphas="3" betas="-7" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Me_1" alphas="2" betas="-5" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Fa_1" alphas="1" betas="-3" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>        
        <k:NoteButton id="So_1" alphas="0" betas="-1" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="La_1" alphas="-1" betas="1"  keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Ti_1" alphas="-2" betas="3" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Di0" alphas="-3" betas="5" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="Ri0" alphas="-4" betas="7" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
        <k:NoteButton id="My0" alphas="-5" betas="9" keyboardLayout="{kblQWERTY}" skinClass="MacNoteButtonSkin"/>
    </k:KeyboardGroup>
    
    
    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
    <!-- Code                                                                          -->
    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
    <fx:Script>
        <![CDATA[
            import mx.events.MoveEvent;
            import com.igetitmusic.JIMS_Sonoflash.*;
            import com.igetitmusic.assert.Assert;
            import com.igetitmusic.components.*;
            import com.igetitmusic.components.intervalSlider.*;
            import com.igetitmusic.components.keyboard.*;
            import com.igetitmusic.music.Model;
            import com.igetitmusic.music.Note;
            import com.igetitmusic.synth.PingNoteSound;
            import com.igetitmusic.synth.Synth;
            
            import flash.media.*;
            
            import mx.events.FlexEvent;
            
            
            //--------------------------------------------------------------------------
            //
            //  Constants
            //
            //--------------------------------------------------------------------------
            
            private const CENTS_PER_OCTAVE:Number =  1200;            
            private const MAX_RE0:Number          =  CENTS_PER_OCTAVE;
            private const MIN_RE0:Number          = -MAX_RE0;
            
            private const MAX_5TH_WIDTH:Number      =  720;
            private const FIVE_TET:Number          =  720;
            private const SEVENTEEN_TET:Number      =  705.9;
            private const PYTHAGOREAN:Number      =  702;
            private const TWELVE_TET:Number          =  700;
            private const THIRTY_ONE_TET:Number      =  696.8;
            private const QC_MEANTONE:Number      =  696.6; 
            private const NINETEEN_TET:Number      =  694.7;
            private const SEVEN_TET:Number          =  685.7;
            private const MIN_5TH_WIDTH:Number      =  685.7;
            
            //----------------------------------
            //  synth
            //----------------------------------
            
            public const synth:Synth = new Synth(Model.dataModel, PingNoteSound);            
            
            //--------------------------------------------------------------------------
            //
            //  Properties
            //
            //--------------------------------------------------------------------------
            
            //----------------------------------
            //  baseFrequency
            //----------------------------------
            
            private var baseFrequency:Number      =  440;        // 440Hz -- the A above middle C
            
            //----------------------------------
            //  re0Sound
            //----------------------------------
            
            [Bindable]public var _re0Sound:PingSound = new PingSound(440);
            public function get re0Sound():PingSound { return _re0Sound; }
            public function set re0Sound(v:PingSound):void { _re0Sound = v; }
            
            //----------------------------------
            //  re0sThumbHeight
            //----------------------------------
            
            // Suitable for use in MXML statement: verticalCenter="{_re0sThumbHeight}"
            [Bindable]private var _re0sThumbHeight:Number = NaN;            
            public function get re0sThumbHeight():Number { return _re0sThumbHeight; }
            public function set re0sThumbHeight(v:Number):void { _re0sThumbHeight = v; }
            
            //----------------------------------
            //  re0sThumbsCenterY
            //----------------------------------
            
            // Suitable for use in MXML statement: y="{_re0sThumbsCenterY - ctlID.height}"
            [Bindable]private var _re0sThumbsCenterY:Number = NaN;            
            public function get re0sThumbsCenterY():Number { return _re0sThumbsCenterY; }
            public function set re0sThumbsCenterY(v:Number):void { _re0sThumbsCenterY = v; }
            
            //----------------------------------
            //  fifthWidth
            //----------------------------------
            
            [Bindable]private var _fifthWidth:Number = NaN;            
            public function get fifthWidth():Number { return _fifthWidth; }
            public function set fifthWidth(v:Number):void { _fifthWidth = v; }
            
            //--------------------------------------------------------------------------
            //
            //  Methods
            //
            //--------------------------------------------------------------------------
            
            //----------------------------------
            //  centsToHz
            //----------------------------------
            
            public function centsToHz(c:Number):Number {
                return (baseFrequency * Math.pow(2, (c/CENTS_PER_OCTAVE))); 
            }
            
            //----------------------------------
            //  updateRe0sThumbHeight
            //----------------------------------
            
            // Called on any change to Re0's value (VALUE_COMMIT).
            public function updateRe0sThumbHeight():void {
                CONFIG::DEBUG {Assert.assertTrue("ctlRe0Interval != null", ctlRe0Interval != null);}
                CONFIG::DEBUG {Assert.assertTrue("ctlRe0Interval.track != null", ctlRe0Interval.track != null);}
                CONFIG::DEBUG {Assert.assertTrue("ctlRe0Interval.thumb != null", ctlRe0Interval.thumb != null);}
                CONFIG::DEBUG {Assert.assertTrue("grpRe0 != null", grpRe0 != null);}
                
                var trackRange:Number    = ctlRe0Interval.maximum - ctlRe0Interval.minimum;
                var normalized:Number    = (ctlRe0Interval.value  - ctlRe0Interval.minimum) / trackRange;
                
                re0sThumbsCenterY        = ((normalized - 0.0) * (ctlRe0Interval.thumb.height - ctlRe0Interval.track.height)) + grpRe0.y;
                re0sThumbHeight            =  (normalized - 0.5) * (ctlRe0Interval.thumb.height - ctlRe0Interval.track.height);
            }
            
            //--------------------------------------------------------------------------
            //
            //  Event Handlers
            //
            //--------------------------------------------------------------------------
            
            //----------------------------------
            //  appPreinitializeHandler
            //----------------------------------    
            
            protected function appPreinitializeHandler(event:FlexEvent):void {
                // initialize internal data structures from external data
                // KeyboardLayout.initialize(); // always fails due to a security error -- use kludge for now
            }
            
            //----------------------------------
            //  appCompleteHandler
            //----------------------------------
            
            protected function appCompleteHandler(event:FlexEvent):void    {
                stage.addEventListener(KeyboardEvent.KEY_DOWN, globalKeyHandler);
                stage.addEventListener(KeyboardEvent.KEY_UP, globalKeyHandler);
                
                updateRe0sThumbHeight();
            }
            
            //----------------------------------
            //  globalKeyHandler
            //----------------------------------
            
            public function globalKeyHandler(e:KeyboardEvent):void {
                var b:NoteButton = (grpKeyboard as KeyboardGroup).getChildByKeyCode(e.keyCode);
                if ( (b == null) || (b.enabled == false) || (b.visible == false) ) return;
                
                var n:Note = b.note;
                
                if (n == null) {
                    // not sure what to do here
                } else {
                    // b is a note-controlling button
                    var modelState:Boolean = Model.dataModel.getNoteOn(n);
                    var keyboardState:Boolean = (e.type == KeyboardEvent.KEY_DOWN);
                    
                    if (modelState != keyboardState) {                      
                        // Change the state of the dataModel, which will 
                        // notify its listeners of the change.
                        Model.dataModel.setNoteOn(n, keyboardState);
                    }
                }              
            }
            
            //----------------------------------
            //  ctlRe0Interval_valueCommitHandler
            //----------------------------------
            
            // called whenever Re0's value changes for any reason (animation, user action, whatever)
            protected function ctlRe0Interval_valueCommitHandler(event:Event):void {
                CONFIG::DEBUG {Assert.assertTrue("ctlRe0Interval != null", ctlRe0Interval != null);}
                CONFIG::DEBUG {Assert.assertTrue("re0Sound != null", re0Sound != null);}
                
                updateRe0sThumbHeight();
                if (isNaN(ctlRe0Interval.value)) return;
                
                // keep model's refFreq in sync w/ Re0's pitch, so that keyboard plays the right pitches
                re0Sound.frequency = centsToHz(ctlRe0Interval.value);
                Model.dataModel.refFreq = this.centsToHz(ctlRe0Interval.value);
                
                // play re0Sound()                
            }
            
            //----------------------------------
            //  ctlRe0Interval_changeHandler
            //----------------------------------
            
            protected function ctlRe0Interval_changeHandler(event:Event):void {
                CONFIG::DEBUG {Assert.assertTrue("ctlRe0Interval != null", ctlRe0Interval != null);}
                CONFIG::DEBUG {Assert.assertTrue("re0Sound != null", re0Sound != null);}
                
                updateRe0sThumbHeight();
                if (isNaN(ctlRe0Interval.value)) return;
                
                re0Sound.frequency = centsToHz(ctlRe0Interval.value);
            }
            
            //----------------------------------
            //  ctl5thWidth_valueCommitHandler
            //----------------------------------            
            
            protected function ctl5thWidth_valueCommitHandler(event:FlexEvent):void {
                Model.dataModel.beta = ctl5thWidth.value;
            }
            
            //--------------------------------------------------------------------------
            //
            //  Embedded Assets
            //
            //--------------------------------------------------------------------------
            
            // Define a class name for each embedded asset, so that it can be referred to by name            
            //[Embed(source="assets/audio/XXX.mp3")]
            //public const XXX_MP3:Class;

        ]]>
    </fx:Script>
    
    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
    <!-- Declarations                                                                -->
    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
    
    <fx:Declarations>
        <mx:Sort id="compareNotes" compareFunction="{Key.compare}"/>
        
        <s:ArrayCollection id="acQWERTY" sort="{compareNotes}">
            <!-- top row -->
            <k:Key name="3" alphas="5" betas="-8" code="51"/>
            <k:Key name="4" alphas="4" betas="-6" code="52"/>
            <k:Key name="5" alphas="3" betas="-4" code="53"/>
            <k:Key name="6" alphas="2" betas="-2" code="54"/>
            <k:Key name="7" alphas="1" betas="0" code="55"/>
            <k:Key name="8" alphas="0" betas="2" code="56"/>
            <k:Key name="9" alphas="-1" betas="4" code="57"/>
            <k:Key name="0" alphas="-2" betas="6" code="48"/>
            <k:Key name="-" alphas="-3" betas="8" code="189"/>
            
            <!-- next-to-top row -->
            <k:Key name="W" alphas="5" betas="-9" code="87"/>
            <k:Key name="E" alphas="4" betas="-7" code="69"/>
            <k:Key name="R" alphas="3" betas="-5" code="82"/>
            <k:Key name="T" alphas="2" betas="-3" code="84"/>
            <k:Key name="Y" alphas="1" betas="-1" code="89"/>
            <k:Key name="U" alphas="0" betas="1" code="85"/>
            <k:Key name="I" alphas="-1" betas="3" code="73"/>
            <k:Key name="O" alphas="-2" betas="5" code="79"/>
            <k:Key name="P" alphas="-3" betas="7" code="80"/>
            <k:Key name="[" alphas="-4" betas="9" code="219"/>
            
            <!-- next-to-bottom row -->
            <k:Key name="S" alphas="4" betas="-8" code="83"/>
            <k:Key name="D" alphas="3" betas="-6" code="68"/>
            <k:Key name="F" alphas="2" betas="-4" code="70"/>
            <k:Key name="G" alphas="1" betas="-2" code="71"/>
            <k:Key name="H" alphas="0" betas="0" code="72"/>
            <k:Key name="J" alphas="-1" betas="2" code="74"/>
            <k:Key name="K" alphas="-2" betas="4" code="75"/>
            <k:Key name="L" alphas="-3" betas="6" code="76"/>
            <k:Key name=";" alphas="-4" betas="8" code="186"/>
            
            <!-- bottom row -->
            <k:Key name="Z" alphas="4" betas="-9" code="90"/>
            <k:Key name="X" alphas="3" betas="-7" code="88"/>
            <k:Key name="C" alphas="2" betas="-5" code="67"/>
            <k:Key name="V" alphas="1" betas="-3" code="86"/>
            <k:Key name="B" alphas="0" betas="-1" code="66"/>
            <k:Key name="N" alphas="-1" betas="1" code="78"/>
            <k:Key name="M" alphas="-2" betas="3" code="77"/>
            <k:Key name="," alphas="-3" betas="5" code="188"/>
            <k:Key name="." alphas="-4" betas="7" code="190"/>
            <k:Key name="/" alphas="-5" betas="9" code="191"/>
        </s:ArrayCollection>
        
        <k:KeyboardLayout id="kblQWERTY" name="{KeyboardLayout.QWERTY}" keyData="{acQWERTY}"/>
    </fx:Declarations>
</s:Application>