2014年12月28日日曜日

RixMini.java の ソースコード



/*  

▼RixMini 仕様

再生・停止のPlayButtonと、T,S,Dの order を入力するCadenceButtonを実装。
CadenceButtonを操作して和声進行を演奏する。
何も入力せずに次の小節に行くと、HarmonyBalancerが作動して、最適なバランス値をもたらす和声を演奏する。

和声進行のバランス値に応じてグラデーションで色が変わる楕円 class Graph を実装。

和声進行のバランス値と、4声コードの内容(12音表記)と、入力した cadenceOrderList を、リアルタイムで表示するパネル class Display を実装。

実装クラス一覧:
{ RixMini, Graph, Display, ButtonPanel, Conductor, Harmony, Key, EqualKey, Scale, HarmonyBalancer, Rhythm, Dynamics, Meter, Sound, Sine, PercussiveNoise }

*/




class RixMini extends JFrame {   //  BAK  2014.11.10


    int frameWidth = 320;
    int frameHeight = 480;
    Color backColor = new Color(245, 150, 20);

    static Display display = new Display();
    static Graph graph = new Graph();
    static ButtonPanel buttonPanel = new ButtonPanel();
   
    int marginLeft = 5;  // setResizable(true)のときと(false)のときの左余白の差の調整

   // static boolean statePlaying = false;
   
   
   
        public static void main(String[] args) {

           // Sine s = new Sine(800,1000,800);
           // Thread t = new Thread(s);
           // t.start();
           
        Conductor.init();
       
        new RixMini().setVisible(true);

    }

   

    public RixMini() {
        super();
        setTitle("RixMini");
        setSize(frameWidth, frameHeight);  // スマホ用サイズに変更させる
        setLocationRelativeTo(null); // 画面中央に表示
        setLayout(null);
        setResizable(false);
        getContentPane().setBackground(backColor);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

       
        // logo
        JLabel logo = new JLabel("RixMini");
        logo.setBounds(10,10,120,24);
            //  logo.setBorder(new LineBorder(Color.BLACK));
        logo.setFont(new Font("Serif",Font.PLAIN,22));
        logo.setForeground(new Color(120,20,20));
        this.add(logo);
       
       
        // graph
        graph.setBounds(20+marginLeft, 46, 262, 146);
         //  graph.setBorder(new LineBorder(Color.RED));
        this.add(graph);

        // display
        display.setBounds(40+marginLeft, 190, 262, 100);
      //  display.setBorder(new LineBorder(new Color(120,20,20)));
        display.setBackground(backColor);
        this.add(display);

       
        //  buttonPanel
        buttonPanel.setBounds(46+marginLeft, 304, 208, 128);
        this.add(buttonPanel);

    }


   

} // class RixMini
   




   class Graph extends JPanel {   // BAK  2014.11.10

        /*
         class Graph  説明
         Balancerの値を利用してユーザーに視覚効果を与えて楽しませるクラス。
         機能
         ・Balancer.harmonyRecord の値によって背景色がグラデーションで変わる
         */
        static int[] h = new int[4];

        /*  以下の3つの配列は、試験的に、前から2つの要素までしか使わない。  */
        static  int[] hR = new int[4];  // h から赤色成分だけ抽出 Dominantへの近さ
        static  int[] hG = new int[4];  // h から緑色成分だけ抽出 Tonic(0)への近さ
         static int[] hB = new int[4];  // h から青色成分だけ抽出   Subdominantへの近さ

        static int nowR;
        static  int nowG;
         static int nowB;
        static  int frame = 24;

        public Graph(){
           
           
        }
       
       
        public  void update() {

            for (int i = 0; i < HarmonyBalancer.harmonyRecord.length; i++) {

  //  System.out.println("Graph.update() harmonyRecord : " +Conductor.conductor.harmony.balancer.harmonyRecord[i] * 255 / 1000 );
                h[i] = Conductor.conductor.harmony.balancer.harmonyRecord[i] * 255 / 1000 ; //色の比率に変更.

                if (h[i] >= 0) {
                    //System.out.println("ThroughFlag : Graph.update() ifBlock");
                    hR[i] = h[i];
                    hG[i] = 255 - h[i];
                } else {
                    //System.out.println("ThroughFlag : Graph.update() elseBlock");
                    hB[i] = h[i] * -1;
                    hG[i] = 255 + h[i];
                }

            }

            ColorMotion cm = new ColorMotion();
            Thread colorThread = new Thread(cm);
            colorThread.start();
           

        }

        @Override
        public void paint(Graphics g) {    // paint()はJPanel描画の度に呼ばれるメソッド.

               // super.paint(g);
            // g.drawRect(0, 0, 862, 526);
            Graphics2D g2 = (Graphics2D) g;    // 機能が豊富なGraphics2D にキャスト

          //  System.out.println(" hR = "+hR[1] + "  hG = "+ hG[1] + "  hB = "+hB[1]);
           
            GradientPaint gp = new GradientPaint(0, 0, new Color(hR[1], hG[1], hB[1]), 100, 10, new Color(nowR, nowG, nowB), true);
            g2.setPaint(gp);

            Ellipse2D ellipse = new Ellipse2D.Double(10, 0, 242, 130);  // Ellipse2Dは楕円描画.
            g2.fill(ellipse);

            g.setColor(new Color(151, 151, 111));

        }

           class ColorMotion implements Runnable {

            public void run() {
                try {
                    for (int i = 0; i < frame; i++) {

                        nowR = hR[1] + (hR[0] - hR[1]) / frame * i;
                        nowG = hG[1] + (hG[0] - hG[1]) / frame * i;
                        nowB = hB[1] + (hB[0] - hB[1]) / frame * i;
                        repaint();  // 描画の更新
                    }
                    Thread.sleep(10);
                } catch (InterruptedException ex) {
                    Logger.getLogger(RixMini.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }
    }  // class Graph




    class Display extends JPanel {    // BAK  2014.11.10

        /*  class Display

         RixMiniの内部情報をテキストで表示するPanel。

         機能
         ・Conductor.cadenceOrderListを表示
         ・Harmony.chord[] を表示
         ・HarmonyBalancer.harmonyRecord を表示

         */

        String chord;
        String harmonyBalance;

        JLabel orderLabel = new JLabel("order | ");
        JLabel chordLabel = new JLabel("chord | ");
        JLabel harmonyBalanceLabel = new JLabel("balance | ");


        public Display() {

            setLayout(new GridLayout(3, 1));


            //  harmonyBalanceLabel.setBounds(0, 40, 100, 10);
           harmonyBalanceLabel.setFont(new Font("Serif", Font.PLAIN, 16));
           harmonyBalanceLabel.setForeground(new Color(30, 20, 50));
            this.add(harmonyBalanceLabel);
           
           
            // chordLabel.setBounds(0, 20, 100, 10);
            chordLabel.setFont(new Font("Serif", Font.PLAIN, 16));
            chordLabel.setForeground(new Color(20, 40, 20));
            this.add(chordLabel);
           
           
            orderLabel.setFont(new Font("Serif", Font.PLAIN, 18));
            orderLabel.setForeground(new Color(120, 20, 20));
            // orderLabel.setBorder(new LineBorder(new Color(120,20,20)));
           // orderLabel.setBounds(0, 0, 100, 10);
            this.add(orderLabel);


        }

       
        public void updateOrder() {


            orderLabel.setText("order |  "+Conductor.conductor.cadenceOrderList.toString());
           

        }

        public void updateHarmonyInfo() {

            chord = "chord |  ";
           
            for (int c : Conductor.conductor.harmony.chord) {
                chord += Integer.toString(c) + " ";
            }

            chordLabel.setText(chord);


            harmonyBalance = "balance |  ";
           
            for (int b : Conductor.conductor.harmony.balancer.harmonyRecord) {
                harmonyBalance += Integer.toString(b) + "  ";
            }
           
           
            harmonyBalanceLabel.setText(harmonyBalance);




        }

    } // class Display

   


    class ButtonPanel extends JPanel {   // BAK  2014.11.10

        PlayButton playBtn = new PlayButton();
        CadenceButton tBtn = new CadenceButton("T");
        CadenceButton dBtn = new CadenceButton("D");
        CadenceButton sBtn = new CadenceButton("S");
       
        public ButtonPanel() {
            setLayout(new GridLayout(2, 2));

            add(tBtn);
            add(dBtn);
            add(playBtn);
            add(sBtn);

        }


        class PlayButton extends JButton {  

           
            String btnMark[] = {" > ", " || "};

            public PlayButton() {

                setText(btnMark[0]);
                setFont(new Font("Serif",Font.PLAIN,22));
                this.addActionListener(new ClickReaction());

            }

            public class ClickReaction implements ActionListener {

                @Override
                public void actionPerformed(ActionEvent ev) {
                    try {

                        if (Conductor.statePlaying == false) {
                           
                            Thread conductorThread = new Thread(Conductor.conductor);
                            conductorThread.start();
                           
                            setText(btnMark[1]);

                        } else {

                            Conductor.conductor.stop();
                           
                            setText(btnMark[0]);
                           
                        }

                    } catch (Exception ex) {
                        //System.out.println(ex);
                       // ex.printStackTrace();
                        ex.printStackTrace(System.out);
                    }
                }
            }

           
            public void changeMark() {
               
                if (Conductor.statePlaying) {
                    //System.out.println("Through Flag : playBtn.changeMark() ifBlock");
                    setText(btnMark[1]);
                } else {
                    //System.out.println("Through Flag : playBtn.changeMark() elseBlock");
                    setText(btnMark[0]);
                }
            }
           

        } // class PlayButton




        class CadenceButton extends JButton {

            boolean statePlaying = false;
            String btnMark[] = {"T", "S", "D"};

            String btnName;

            public CadenceButton(String funcName) {

                this.btnName = funcName;
               
                setText(btnName);
                setFont(new Font("Serif", Font.PLAIN, 22));
                this.addActionListener(new ClickReaction());

            }

            public class ClickReaction implements ActionListener {

                public void actionPerformed(ActionEvent ev) {

                    try {

                        Conductor.cadenceOrderList.append(btnName);
                        RixMini.display.updateOrder();

                    } catch (Exception ex) {
                        System.out.println("myERROR: ClickBtn.actionPerformed ");
                    }

                }

            }

        } // class CadenceButton



    } // class ButtonPanel

   




public class Conductor implements Runnable {    //  BAK 2014.11.10


    static Conductor conductor;
   
      public static boolean statePlaying;

      public static StringBuilder cadenceOrderList;

      static Harmony harmony;
      static Rhythm rhythm;
     
      static Score score;
   

    public Conductor() {
        statePlaying = false;
        cadenceOrderList = new StringBuilder();
       
       
        harmony = new Harmony();
        rhythm = new Rhythm();
    }

    public static void init() {

        conductor = new Conductor();

    }

    public void run(){
       
        score = new Score(8); // 引数は演奏小節数の総数
        start();
       
    }
   
    public void start() {

            statePlaying = true;
           
//System.out.println("statePlaying  CHECK at start() : "+statePlaying);
            conduct();
       

    }

    public void stop() {
 //System.out.println("Through Flag : Conductor.stop()");
        statePlaying = false;

        RixMini.buttonPanel.playBtn.changeMark();
       
      }


    void orderReset(){
       
        cadenceOrderList.delete(0,cadenceOrderList.length());
        RixMini.display.updateOrder();
       
    }

     
    private void conduct()  {
        try {
            Thread threadHarmony = new Thread((Runnable) harmony);
            Thread threadRhythm = new Thread((Runnable) rhythm);
           
            threadHarmony.start();
            threadRhythm.start();
           
            RixMini.display.updateHarmonyInfo();
            RixMini.graph.update();
           
            threadHarmony.join();
            threadRhythm.join();
           
            orderReset();
           
            score.look();
           
          //  System.out.println("CHECK AT conduct()  :"+statePlaying);
           
        } catch (InterruptedException ex) {
            Logger.getLogger(Conductor.class.getName()).log(Level.SEVERE, null, ex);
        }
           

    }




       
       

static class Score {


       static int unplayBar;
   
      boolean musicIsEnd;


     public Score(int bar){

        unplayBar = bar;

     }




       public void look(){
         
         
            unplayBar--;
           
             if(unplayBar > 0 ) {

//System.out.println("Through Flag : Score.look() ifBlock  "+ unplayBar);
               
                 Conductor.conductor.conduct();

             } else {
//System.out.println("Through Flag : Score.look() elseBlock  "+ unplayBar);
                   musicIsEnd = true;
                   conductor.stop();
              }


       }





}  // class Score



}  //  class Conductor







public class Harmony implements Runnable{  // BAK  2014.11.10


    static Key key;
    static Scale scale ;
    static HarmonyBalancer balancer;
   
    static int tonicPitch = 300;
    static int tonicNum = 0;  //機能和声番号は、配列と適合させるため -1 した番号とする。
    static int pastFuncNum = 0;
    static int[] chord;
    static int chordOct = 4;   // chord の音程のオクターブ数
    static float chordVol = 500;  // playChord の音量


      public Harmony(){
         
          key = new EqualKey(tonicPitch);
          scale = new Scale(tonicNum, Scale.major);
          chord = new int[]{0, 4, 7, 0xB};
          balancer = new HarmonyBalancer();   // balancerの初期化はScaleの初期化を要するので
                                              // 初期化の順序に注意すること。

       
      }


    @Override
      public void run(){
         
         
          symbol2Chord(Conductor.cadenceOrderList.toString());
          playChord(chord);
         
          RixMini.display.updateHarmonyInfo();
          RixMini.graph.update();
          Conductor.conductor.orderReset();
      }
     
     
      void init(){
         
          scale.change(tonicNum, Scale.major);
          chord = getChord(0);
          HarmonyBalancer.harmonyRecord = new int[]{0,0,0,0};
         
          pastFuncNum = 0;
          tonicNum = 0;
      }
     
     

     public static  int[] getChord ( int funcNum){
     
            pastFuncNum = funcNum;
           
           int[] c = new int[4];
           
            int[] s = scale.getScale();

             for( int i =0; i<4; i++){     // i<4 なのは 4声だから。

                 c[i] =  s[ ( funcNum + i*2 ) % 7 ];
       
            }

            return c;

      }



   public void symbol2Chord ( String symbol ){


       if(symbol.length() == 0){
           symbol = symbol +"A";
       }else {
           Conductor.conductor.score.unplayBar +=1;
       }
     

     
       symbol = symbol + "E";  // 処理されない最終文字 E を追加
       char[] symbolArray = symbol.toCharArray();
       int funcNum = pastFuncNum;

     
     
     
      /*  Balancer が算出する値に応じて、switch文の内容を変化させる案もある。  */
      for ( int i=0 ; i < symbolArray.length -1; i++ ) {

      if(symbolArray[i]== symbolArray[i+1]){

          switch (symbolArray[i]) {
              case 'S':
                  if (scale.interval == Scale.major) {
                      scale.change(tonicNum += 9, Scale.minor);  // la minor scale へ。
                  } else if (scale.interval == Scale.minor) {
                      scale.change(tonicNum += 3, Scale.major);  // do major scale へ。
                  }
                  ;
                  break;
              case 'T':
                  scale.change(tonicNum += 6);
                  break;
              case 'D':
                  scale.change(tonicNum += 7);
                  funcNum += 4;
                  break;
              default:
                  System.out.println("Invalid kadenzSymbol at Harmony.symbol2Chord");
          }

          i++;
         
      }else{
           
         
          switch(symbolArray[i]){
          case 'T':      funcNum = 0 ;  break;  // TS、TDと入力することで、presentScale のSとDをいつでも呼び出せる。
          case 'S':      funcNum += 3;  break;
          case 'D':      funcNum +=4;  break;
          case 'A':   funcNum = balancer.getNextHarmony(pastFuncNum); break;   // 何も入力されていないとき、Aが入る
          default:  System.out.println("Invalid kadenzSymbol at Harmony.symbol2Chord");
           }
         
         
       }

      /*  オクターブ移動のアルゴリズムは、未定なので、すべてのオクターブ番号を中心の4とする。
         ボイシングを考えつつ機能拡張する必要あり。     */
           // chordOct += funcNum / 7;
        funcNum %= 7;

       }
     

         

           chord = getChord(funcNum);

           HarmonyBalancer.recordHarmony(chord);  // Balancerの変数に1小節前の和声として記録しておく。
     
         

   }

   
    public void playChord ( int[] chord ){
       
        float freq;

     for(int c : chord ){
     
      freq = key.num2Hz(chordOct, c) ;

     
     //System.out.println("CHECK playChord()  " +  freq +"  "+ Rhythm.Meter.oneBar+"   "+ chordVol );

       Sound s = new Sine ( freq , Rhythm.Meter.oneBar, chordVol );

       Thread t = new Thread(s);
       t.start();
     
      }



   }




   
}  // class Harmony







abstract class Key {    // OK


    int centerPitch ;
   
    scaleArray [] = {  } ;
    octaveArray [] = { }  ;  



    public Key (int c){

      this.centerPitch = c ;
      tuning();

    }


   public void tuning (){


    }



    public int num2Hz ( int oct , int num ) {


    }



}  // class Key





public class EqualKey extends Key {    //OK


    int centerPitch ;
   
    scaleArray [] = {  } ;
    octaveArray [] = { }  ;  


    public Key (int c){

      this.centerPitch = c ;
      tuning();

    }


   public void tuning (){

  for  (int i = 0; i<12 ; i++){
      scaleArray[i] = tonicPitch * Math.pow(2.0 , i / 12.0 );
    }

     for ( int i = 0; i<8; i++ ){
          octaveArray[i] =  Math.pow( 2 , (i - 4 ) ) ;
     }

    }



    public int num2Hz ( int oct , int num ) {


    int hz = scaleArray[num] * octaveArray[oct];
    return hz;


    }



}  // class EqualKey




class Scale {              // OK

        int[] presentScale ;
        public int[] major  = { 0 , 2 , 4, 5, 7, 9, B };
        public int[] minor  = { 0, 2 , 3 , 5 , 7 , 8 , A };
        public int[] interval ;


       int tonicNum ;



       public Scale(int tonicNum , int[] interval ){
 
               change( tonicNum, interval);

        }


        public int[] getScale(){

           return presentScale;

        }




        public void change(int newTonicNum , int[] interval ){

                 for( int i=0; i<7; i++ ) {

                       presentScale[i] = (newTonicNum + interval[i] ) % 12 ;

                }

         }
        public void change(int newTonic  ){

              this.change(newTonic, this.interval );

         }
        public void change(int[] interval  ){

              this.change(this.tonic, interval );

         }


} //class Scale





class HarmonyBalancer {     // OK

    int[] harmonyRecord = new int[4] ;  // 過去4小節分の cadenceValue が入る配列
    int[] pastChord = new int[4];  //  1小節前の Chord 配列

    int[][]  cadenceValueTable;  // Scale.presentScaleの和声進行が持つvalue値の表


     /*   音程 0_0, 0_1, 0_2 ... の順で12音程分の value を含む配列   */
    int[] intervalValueD_SD = { 0 , -50, 0, +50, 0, -100, 0, +100, 0, -50, 0, +50 };



   public HarmonyBalancer(){

                 setCadenceValue();
               

   }




    public void recordHarmony(int[] chord){

          int diffNum;
          int cadenceValue;

         if(pastChord == null ){
                pastChord = chord;
         }

           for(int i =0; i<4; i++){
              for(int j = 0; j<4 ; j++ ){
                      diffNum = chord[i] - pastChord[j] ;
                      cadenceValue  += intervalValueD_SD[diffNum];
              }
           }

       

           for( int i =0; i< harmonyRecord.length - 1 ; i++ ) {
                harmonyRecord[i] = harmonyRecord[i+1]
            }
   
          harmonyRecord[harmonyRecord.length -1 ] =  cadenceValue;

    }



   public int getNextHarmony(int pastFuncNum){   // 機能和声の番号を返す

              int lean = 0;
              int nextValue;
              int optinumFuncNum;
              int remainder;
              int minDiff = 2000;  // 2000 は cadenceValue の差の最大値(上下1000ずつ)
             
              for(int i =0; i< harmonyRecord.length; i++ ){

                  lean += harmonyRecord[i];                  

              }

               nextValue = lean * -1 ;  // harmonyRecord の value の偏りを揺り戻す    
             
               for(int i = 0; i< cadenceValueTable[pastFuncNum].length; i++){

                     remainder = cadenceValueTable[pastFuncNum][i] - nextValue;

                     if(remainder < 0 ) {
                          remainder *= -1;   // 差を絶対値化
                      }

                      /*
                         まったく同じ値の remainder が算出されたときには、
                         適した和声が2つ以上あるということなので、
                         その中からランダムで optinumFuncNum を決定するという方法もある。
                      */

                      if(remainder < minDiff ) {
                            minDiff = remainder;
                            optinumFuncNum = i ;
                      }

               }  // for
               
               return optinumFuncNum;                

   }



    public void setCadenceValue(){

       
        int[] standardChord = new int[4];

        for(int i =0; i < Scale.presentScale.length; i++) {

              standardChord = Harmony.getChord(i+1); // +1は機能番号にするため。
             
              for(int j = 0; j < Scale.presentScale.length; j++ ){
                 
                   cadenceValueTable[i][j] = getChordProgressionValue(standardChord, Harmony.getChord(j+1) );

                  /*
                            ・ i == j のとき必ず0が入る
                             ・ cadenceValueTableの [i][j] と [j][i] は+- が反転した値である
              以上のことを踏まえて、Code を簡略化できる。
                            ※ただしBalancerのシステム変更の際、不適切になる可能性がある。
                  */

              }
        }


   }




     public int getChordProgressionValue( int[] chord1, int[] chord2 ){

              int interval;
              int value;
         
for( int i =0; i < chord1.length ; i++){  

                   for(int j = 0; j< chord2.length; j++){

                        interval = chord2[j] - chord1[i];
                        if(diff < 0 ){
                            diff += 12 ; //マイナス値をプラスの音程数に直す。
                         }
                        value += intervalValueD_SD[interval];                        
                                                   
                   }

}

              return value;

    }




} // class HarmonyBalancer







class Rhythm {     // OK

      int[] presentBeat;

      int[][] 4beat = { 4, 4, 4, 4 }, { 6,4,6,4  };

      Meter meter;  
      Dynamics dynamics;


    public Rhythm() {

        presentBeat = 4beat;
       
        meter = new Meter(120);
        dynamics = new Dynamics();
    }
   
   

   
    public void playRhythm(){
       
            int t;
            int v;

          for(int i=0; i< presentBeat.length; i++){
         
             t = meter.num2Time(presentBeat[0][i]);
             v = dynamics.num2Volume(presentBeat[1][i]);
           
              Sound s = new Sound( t, v );        

         }
         
       
    }
   
   

   
}   // class Rhythm






class Dynamics {   // OK

       int[] presentLevels;
       int[] nineLevels = { 0, 100,200,300,400, 500, 600,700,800,900 }



        public Dynamics (){

            presentLevels = nineLevels;


        }



       public int num2Volume(int num){

           num = presentLevels[num];
           return num;

       }



} // class Dynamics




public class Meter {    // OK


    int bpm;
    int oneBeat;
    int oneBar;
   


    public Meter (int bpm){

            this.bpm = bpm;
            oneBeat = 1000 / ( bpm / 60 ) ;    // 1000 (= 1秒)
            oneBar = oneBeat  * 4;

    }



    public int num2Time ( int num ) {

            num = oneBar / num ;
            return num;

    }



}  // class Meter







public class Sound  implements Runnable {  //OK

    static final int bit = 16;
    static final int ch = 1;
    static int frame = 2;
    static final int SR = 44100;  //SampleRate
    static final AudioFormat af = new AudioFormat(SR, bit, ch, true, true);
    static final DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
    static SourceDataLine line;
    static final int BUFFER = 10000;

    float fadeTime = 30;   // fadeIn/Out は 10msec でも可能なはず。
    int fadeSR = Math.round(SR * fadeTime/1000);

    float freq;
    float vol;
    int time;
    int[] soundArray;
    byte[] playableArray;
   


    public Sound (float freq, int time,  float vol ) {

        this.freq = freq;
        this.vol =(float)( vol * (Math.pow(2, bit - 1) / 1000 ));  // 最大値の1000分割単位
        this.time = time * ( SR / 1000 );   // 1000分の1秒(mSec)単位
        this.soundArray = new int[this.time];
        this.playableArray = new byte[ ch * frame * this.time];
               

        this.soundArray = writeWave();
       
        this.soundArray = fadeProcess(this.soundArray);
       
        this.playableArray = getPlayableArray(this.soundArray);

       
    }



    public void run() {

        int restLength = playableArray.length;
        int offset = 0;

        try {
            SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info); // AudioをInitialize
            line.open(af);
            line.start();

            if (!RixUI.stopped) {

                while (BUFFER < restLength && !RixUI.stopped) {
                    line.write(playableArray, offset, BUFFER);
                    restLength -= BUFFER;
                    offset += BUFFER;
                }

                line.write(playArray, offset, restLength);

            }

            line.drain();

            //System.out.println("AudioFormat: " + af.toString());
        } catch (LineUnavailableException ex) {
            Logger.getLogger(Oscillator.class.getName()).log(Level.SEVERE, null, ex);

        }

    }




    public int[] writeWave(){      
       
        return s;
       
    }
   
   

    public int[] fadeProcess(int[] s) {    

        float turnDown;
       
        for (int i = 0; i < fadeSR; i++) {
            turnDown = (float)Math.pow((i / (float)fadeSR),2);
            s[i] =Math.round( s[i] * turnDown);
            s[s.length -1 - i] =Math.round(s[s.length -1 - i] * turnDown );
            //System.out.println("turnDown= "+ turnDown);
        }

        return s;

    }


   
    public byte[] getPlayableArray(int[] s){
       
        int nBAdd;
       
        for (int i = 0; i < soundArray.length; i++) {
            nBAdd = i * frame * ch;
            playableArray[nBAdd + 0] = (byte) ((soundArray[i] >>> 8) & 0xFF);
            playableArray[nBAdd + 1] = (byte) (soundArray[i] & 0xFF);

        }

        return playableArray;
       
    }
   
 }  // class Sound







public class Sine extends Sound {     // OK




     public Sine (){

          super();

     }


   
    public int[] writeWave(int[] s, float f, float v){
       
        int waveOneSR = Math.round(SR / f);  // harmE 0x03 で f=55000 SRを超過
        int[] waveOne = new int[waveOneSR];

        float waveXPos;
        float waveY;

        for (int i = 0; i < waveOneSR; i++) {
            waveXPos = (float) i / (float) waveOneSR;
            waveY = (float) Math.sin(waveXPos * 2.0 * Math.PI);
            waveOne[i] = Math.round(waveY * v);
            //System.out.println("TEST-1 "+ waveOne[i]);
        }


        int startPos = 0;
        int setPos = 0;
        int waveLap = soundArray.length;
        int nWave;

        while (waveLap > 0) {
           
           
            nWave = Math.min((waveOne.length - startPos), waveLap);
           
            try {
                System.arraycopy(waveOne, startPos, s, setPos, nWave);
               
            } catch (Exception e) {
                System.out.println("myERROR:Oscillator2.writeWave | System.arraycopy("+waveOne.length +
                        ", "+startPos+", "+s.length+", "+setPos+", "+ nWave+");" );
            }

            waveLap -= nWave;
            setPos += nWave;
            startPos = (startPos + nWave) % waveOne.length;
            // soundForm=0x030303 により waveOne.length = 0 の Error
        }

       
        return s;
       
    }
   
   


}  // class Sine






class PercussiveNoise extends Sound{    // BAK  2014.11.08

   int vibration;

     public PercussiveNoise (float f, int t,  float v, int vibration ){

          super(f,t,v);

         this.vibration = vibration * ( SR / 1000 );  // 1000分の1秒単位


       this.soundArray = writeWave(soundArray, freq, vol, vibration);
       
        this.soundArray = fadeProcess(this.soundArray);
       
        this.playableArray = getPlayableArray(this.soundArray);
         

     }


   
    public int[] writeWave(int[] s, float f, float v, int vibration){

        float v2;
       
        for ( int i =0; i < vibration ; i++ ){

                v2 =   v -  i *  ( v / vibration );
                s[i] =  (int) Math.floor(Math.random() * v2);
        }

       
        int rest = s.length - vibration ;
       
        for ( int i =0; i< rest; i++ ){

              s[vibration + i] = 0;

        }



        return s;
       
    }
   

}  // class PercussiveNoise




// EOF