新楽器「GHI2014」に向けて (技術資料/備忘録)

長嶋 洋一


  1. 概要
    このページは、2013年から2014年にかけて新しく制作した(している)新楽器「GHI2014」に関する技術情報を、講義の中の教材として開発中から公開するとともに、自分で改良していくための備忘録として残すためのものです。内容は適宜、修正・追加されていく予定です。
    2007年に提唱した「GHI」については、先行して制作した新楽器"Cyber Kendang"に関する以下の情報を参照して下さい。海外で説明するのに難儀するのですが(^_^;)、これは今後も続ける予定です。

  2. GHIとは (楽器が光ってもいいじゃないか)

    Cyber Kendang


    ニューヨーク大学での演奏風景

  3. 新楽器「GHI2014」に向けた構想(ニーズ)
    • 「GHIシリーズ」の続編を作りたい
    • 海外公演に持参するために、分解してコンパクト化し、現地で簡単に組み立てたい
    • LEDの増量("Cyber Kendang"の304個よりも多くしたい)
    • 教材としてメイキングを学生に紹介したい
    • セミ・スタンドアロン動作とともに、光り方をMaxから制御したい
    • 味気ない3Dプリンタの時代、20世紀の日本のものづくりを支えた「手作り」「試行錯誤」「現物合わせ」を再確認したい

  4. システム設計(シーズ)
    • 2つの正八面体スケルトンが左右に並ぶ造形のイメージ
    • 2つの正八面体スケルトンの造形はバルサ材で制作し、ジョイント部はネオジム磁石によって連結する
    • 2つの正八面体、計24個の稜(断面は正方形)の4面にLEDテープが並んで光る
    • マイコンとしてPropellerを使用する
    • 多数のLED点灯制御(MIDI入力)用に1個、多数のセンサ(MIDI出力)用に1個、それぞれPropellerを使用する
    • LEDとして、碧風祭で「光るワンピース」を制作した、樺山さんのシステムに使用した「テープLED」(12V高輝度LEDが5cmに3個並ぶテープ)を使用する
      → メイキング  / 展示
    • 正八面体の1辺を15cmとする。1辺あたりLEDテープが3本(9LED)が4面で36LED、これが計24稜あるのでLEDの総数は864個(^_^;)
    • LEDは12V20mA高輝度タイプ、全数を最大輝度で点灯させると864*0.02=17.28Aなので、電源として「12V20A」(240W)のスイッチング電源を使用する
    • 光りモノの中にいつものSHARPの赤外線距離センサ は使えないので、距離センサとして新たにイギリスDevantech社の超音波レーダセンサ「SRF02」を採用、左右それぞれ3方向の計6個(最大拡張10個)を使用する
    • PropellerのMIDI送受信は過去に実績のあるオリジナルライブラリを活用
    • Propellerの24ブロックのLED点灯制御(PWM)は樺山さんシステムで開発したオリジナルライブラリを活用
    • PropellerのSRF02送受制御はオリジナルライブラリを開発
    • 超音波センサからのMIDI出力ラインをLED点灯制御のPropellerにも与えて(両方をモニタ)、スタンドアロンでも超音波センサにより発光制御できるようにする(点灯制御MIDI入力によりソフト的に停止)

    回路図

  5. 主要部品(シーズ)とメイキング

  6. Propellerソースコード

    • ソフトウェアアーカイブ「prop.zip」

    • Propeller[A](LED点灯制御)メインspinプログラム : MIDI_24LED_004.spin
      CON
        _clkmode = xtal1 + pll16x
        _xinfreq = 5_000_000
      
      OBJ
        pwmOut       : "PwmPortOut03"
        midiIn       : "MidiIn03"
        sensorIn     : "MidiIn03"
      
      PUB start | i, dummy, phase, mode, value
        pwmOut.start
        pwmOut.speed_set(0)
        midiIn.start(24)
        sensorIn.start(25)
        LED_all_off
        phase := 0
        repeat
          dummy := sensorIn.event
          if dummy <> -1
            if phase == 0
              if (dummy & $F00000) == $D00000
                mode := (dummy & $0F0000) >> 16 + 100
                value := dummy & $7F
                if value < 26
                  value := 0
                elseif value < 35
                  value := 1
                elseif value < 44
                  value := 2
                elseif value < 53
                  value := 4
                elseif value < 62
                  value := 8
                elseif value < 71
                  value := 16
                elseif value < 80
                  value := 32
                elseif value < 89
                  value := 64
                else
                  value := 127
                LED_control(mode, value)
          dummy := midiIn.event
          if dummy <> -1
            if (dummy & $FF0000) == $AF0000
              phase := 1
              mode := (dummy & $7F00) >> 8
              value := dummy & $7F
              if mode == 26           ' AF 26 dd ---> stand-alone mode
                phase := 0
              else
                LED_control(mode, value)
      
      PUB LED_control(mode,value) | i
        if mode < 24                  ' AF nn dd ---> each LED (nn=0-23) value(0-127)
          LED_data_set(mode, value)
          return
        elseif mode > 99
          if mode < 106
            mode := (mode-100) << 2
            repeat i from 0 to 3
              LED_data_set(mode+i, value)
            return
          case mode
            106:
            107:
            108:
            109:
              return
        case mode
          24:                         ' AF 24 dd ---> all LED value(0-127)
            repeat i from 0 to 23
              LED_data_set(i, value)
            return
          25:
            pwmOut.speed_set(value)   ' AF 25 dd ---> PWM speed (0-6)
            return
      
      PUB LED_all_off | i
        repeat i from 0 to 23
          LED_data_set(i, 0)
      
      PUB LED_data_set(address, data)
        pwmOut.buffer_set(address<<8 + data)
      
      PUB pause1ms(period)
        waitcnt(80_000 * period + cnt)
      

    • Propeller[A](LED点灯制御)サブspinプログラム : PwmPortOut03.spin (LED点灯PWMオリジナルライブラリ)
      VAR
        long speed_Data, pwm_Data[24], stack[10]
      
      PUB start : i
        buffer_address := @pwm_Data
        speed_address := @speed_Data
        speed_Data := %100000
        cognew(@asm_entry, @stack[0])
      
      PUB speed_set(data)
        if data > 6
          data := 6
        speed_Data := 1 << (data+9)
      
      PUB buffer_set(data)
        pwm_Data[data>>8] := data & $7F
      
      DAT
                              org
      asm_entry
                              mov     dira,all_high
                              andn    outa,all_high
                              rdlong  counter1,speed_address
                              mov     counter2,#0
      :start_entry
                              sub     counter1,#1             wz
                    if_nz     jmp     #:start_entry
                              rdlong  counter1,speed_address
                              add     counter2,#1
                              and     counter2,#%01111111
                              mov     work_bit,#0
      :bit_loop
                              mov     bit_mask,#1
                              shl     bit_mask,work_bit
                              mov     t1,work_bit
                              shl     t1,#2
                              add     t1,buffer_address
                              rdlong  pwm_value,t1            wz
                    if_z      jmp     #:led_off
                              cmp     pwm_value,counter2      wz
                    if_z      jmp     #:led_on
                              cmp     pwm_value,counter2      wc
                    if_c      jmp     #:led_off
      :led_on
                              or      outa,bit_mask
                              jmp     #:next_bit
      :led_off
                              andn    outa,bit_mask
      :next_bit
                              add     work_bit,#1
                              cmp     work_bit,#24            wz
                    if_nz     jmp     #:bit_loop
                              jmp     #:start_entry
      
      all_high                long    %00000000111111111111111111111111
      buffer_address          long    0
      counter1                long    0
      counter2                long    0
      speed_address           long    0
      work_bit                long    0
      bit_mask                long    0
      pwm_value               long    0
      t1                      long    0
      
                              fit
      

    • Propeller[A](LED点灯制御)サブspinプログラム : MidiIn03.spin (MIDI入力汎用オリジナルライブラリ)
      VAR
        long rx_Head, rx_Tail, rx_Buff[64]
      
      PUB start(_midiPin) : status
        midiPin := _midiPin
        rx_top := @rx_Head
        rx_end := @rx_Tail
        rx_fifo := @rx_Buff
        bitticks := clkfreq / 31_250
        halfticks := bitticks / 2  
        longfill(@rx_Head,0,66)
        status := cognew(@asm_entry, 0)
      
      PUB event : status
        status := -1
        if rx_Tail <> rx_Head
          status := rx_Buff[rx_Tail]
          rx_Tail := (rx_Tail + 1) & $3F
          
      DAT
                              org
      asm_entry
                              mov     midiMask,#1
                              shl     midiMask,midiPin
      getMidiByte
                              waitpeq midiMask,midiMask
                              mov     bitClk,cnt
                              add     bitClk,halfticks       
                              add     bitClk,bitticks
                              mov     testBits,#9                         
      :check_loop
                              waitcnt bitClk,bitticks
                              test    midiMask,ina            wc
                              rcr     rx_data,#1
                              djnz    testBits,#:check_loop
                              shr     rx_data,#32-9
                              xor     rx_data,#$FF
                              and     rx_data,#$FF
                              test    rx_data,#%10000000      wz
                    if_z      jmp     #:running
                              mov     t1,rx_data
                              and     t1,#%11110000
                              cmp     t1,#%11110000           wz
                    if_z      jmp     #getMidiByte
                              mov     rsb,rx_data
                              mov     dcb,#0
                              jmp     #getMidiByte
      :running
                              mov     t1,rsb
                              and     t1,#%11100000
                              cmp     t1,#%11000000           wz
                    if_z      jmp     #:byte_2
                              tjnz    dcb,#:byte_3
                              add     dcb,#1
                              mov     keyno,rx_data
                              jmp     #getMidiByte
      :byte_2 
                              mov     event_data,rsb
                              shl     event_data,#16                                      
                              or      event_data,rx_data
                              jmp     #:write_event
      :byte_3 
                              mov     dcb,#0
                              mov     event_data,rsb
                              shl     event_data,#16                                      
                              mov     t1,keyno
                              shl     t1,#8
                              or      event_data,t1
                              or      event_data,rx_data
      :write_event
                              rdlong  t1,rx_top
                              mov     rx_pointer,t1
                              shl     rx_pointer,#2
                              add     rx_pointer,rx_fifo
                              wrlong  event_data,rx_pointer
                              add     t1,#1
                              and     t1,#$3F
                              wrlong  t1,rx_top
                              jmp     #getMidiByte
      
      t1                      long    0
      midiMask                long    0
      testBits                long    0
      bitClk                  long    0
      bitticks                long    0
      halfticks               long    0
      midiPin                 long    0
      rx_top                  long    0
      rx_end                  long    0
      rx_fifo                 long    0
      rx_data                 long    0
      rx_pointer              long    0
      event_data              long    0
      rsb                     long    0
      dcb                     long    0
      keyno                   long    0
      
                              fit
      

    • Propeller[B](超音波距離センサ)メインspinプログラム : SRF02_MIDI_001.spin
      CON
        _clkmode = xtal1 + pll16x
        _xinfreq = 5_000_000
      
      OBJ
        midiOut       : "MidiOut01"
        serialOut     : "SRF02_Out01"
        serialIn      : "SRF02_In01"
      
      PUB start | i, num_max, rx_data, old_data[10]
        midiOut.start(26)
        serialIn.start(14)
        serialOut.start(15)
        num_max := 6
        repeat
          repeat i from 1 to num_max
            pause1ms(1)
            rx_data := rx_SF02(i)
            if rx_data < 3700
              rx_data := 127 - ((rx_data>>5) & %01111111)
              if rx_data > 105
                rx_data := 0
              if rx_data <> old_data[i-1]
                old_data[i-1] := rx_data
                midiOut.fifoset(($D0+i-1)<<16 + rx_data)
      
      PUB rx_SF02(channel) | mode, dummy, rx_data
        serialOut.fifoset(channel)
        serialOut.fifoset($55)
        mode := 0
        repeat
          if mode == 1
            dummy := serialIn.event
            if dummy <> -1
              rx_data := rx_data + dummy
              return(rx_data)
          elseif mode == 0
            dummy := serialIn.event
            if dummy <> -1
              rx_data := dummy << 8
              mode := 1
      
      PUB pause1ms(period)
        waitcnt(80_000 * period + cnt)
      

    • Propeller[B](超音波距離センサ)サブspinプログラム : MidiOut01.spin (MIDI出力汎用オリジナルライブラリ)
      VAR
        long tx_Head, tx_Tail, tx_Buff[64]
      
      PUB start(_midiPin) : status
        midiPin := _midiPin
        tx_top := @tx_Head
        tx_end := @tx_Tail
        tx_fifo := @tx_Buff
        bitticks := clkfreq / 31_250
        longfill(@tx_Head,0,66)
        status := cognew(@asm_entry, 0)
      
      PUB fifoset(_tx_data)
        tx_Buff[tx_Head] := _tx_data
        tx_Head := (tx_Head + 1) & $3F
          
      DAT
                              org
      asm_entry
                              mov     midiMask,#1
                              shl     midiMask,midiPin
                              or      dira,midiMask
      :fifo_check
                              rdlong  t1,tx_end
                              rdlong  t2,tx_top
                              cmp     t1,t2                   wz
                    if_z      jmp     #:fifo_check  
                              mov     t2,t1
                              shl     t1,#2
                              add     t1,tx_fifo
                              rdlong  event_data,t1
                              mov     t1,t2
                              add     t1,#1
                              and     t1,#$3F
                              wrlong  t1,tx_end
                              mov     tx_data,event_data
                              shr     tx_data,#16
                              call    #send_event
                              and     tx_data,#%11100000
                              cmp     tx_data,#%11000000      wz
                    if_z      jmp     #:byte_2
                              mov     tx_data,event_data
                              shr     tx_data,#8
                              call    #send_event
      :byte_2
                              mov     tx_data,event_data
                              call    #send_event
                              jmp     #:fifo_check
      
      send_event
                              xor     tx_data,#$FF
                              and     tx_data,#$FF
                              shl     tx_data,#1
                              or      tx_data,#1
                              mov     testBits,#10                        
                              mov     bitClk,cnt
                              add     bitClk,bitticks
      :bit_send
                              shr     tx_data,#1              wc
                              muxc    outa,midiMask
                              waitcnt bitClk,bitticks
                              djnz    testBits,#:bit_send
      send_event_ret          ret
      
      t1                      long    0
      t2                      long    0
      midiMask                long    0
      testBits                long    0
      bitClk                  long    0
      bitticks                long    0
      midiPin                 long    0
      tx_top                  long    0
      tx_end                  long    0
      tx_fifo                 long    0
      tx_data                 long    0
      event_data              long    0
      
                              fit
      

    • Propeller[B](超音波距離センサ)サブspinプログラム : SRF02_Out01.spin (センサ通信[送信]オリジナルライブラリ)
      VAR
        long tx_Head, tx_Tail, tx_Buff[64]
      
      PUB start(_midiPin) : status
        midiPin := _midiPin
        tx_top := @tx_Head
        tx_end := @tx_Tail
        tx_fifo := @tx_Buff
        bitticks := clkfreq / 9600
        longfill(@tx_Head,0,66)
        status := cognew(@asm_entry, 0)
      
      PUB fifoset(_tx_data)
        tx_Buff[tx_Head] := _tx_data
        tx_Head := (tx_Head + 1) & $3F
          
      DAT
                              org
      asm_entry
                              mov     midiMask,#1
                              shl     midiMask,midiPin
                              or      dira,midiMask
      :fifo_check
                              rdlong  t1,tx_end
                              rdlong  t2,tx_top
                              cmp     t1,t2                   wz
                    if_z      jmp     #:fifo_check  
                              mov     t2,t1
                              shl     t1,#2
                              add     t1,tx_fifo
                              rdlong  event_data,t1
                              mov     t1,t2
                              add     t1,#1
                              and     t1,#$3F
                              wrlong  t1,tx_end
                              mov     tx_data,event_data
                              call    #send_event
                              jmp     #:fifo_check
      send_event
                              xor     tx_data,#$FF
                              and     tx_data,#$FF
                              shl     tx_data,#1
                              or      tx_data,#1
                              mov     testBits,#11
                              mov     bitClk,cnt
                              add     bitClk,bitticks
      :bit_send
                              shr     tx_data,#1              wc
                              muxc    outa,midiMask
                              waitcnt bitClk,bitticks
                              djnz    testBits,#:bit_send
      send_event_ret          ret
      
      t1                      long    0
      t2                      long    0
      midiMask                long    0
      testBits                long    0
      bitClk                  long    0
      bitticks                long    0
      midiPin                 long    0
      tx_top                  long    0
      tx_end                  long    0
      tx_fifo                 long    0
      tx_data                 long    0
      status_d                long    0
      event_data              long    0
      
                              fit
      

    • Propeller[B](超音波距離センサ)サブspinプログラム : SRF02_In01.spin (センサ通信[受信]オリジナルライブラリ)
      VAR
        long rx_Head, rx_Tail, rx_Buff[64]
      
      PUB start(_midiPin) : status
        midiPin := _midiPin
        rx_top := @rx_Head
        rx_end := @rx_Tail
        rx_fifo := @rx_Buff
        bitticks := clkfreq / 9600
        halfticks := bitticks / 2  
        longfill(@rx_Head,0,66)
        status := cognew(@asm_entry, 0)
      
      PUB event : status
        status := -1
        if rx_Tail <> rx_Head
          status := rx_Buff[rx_Tail]
          rx_Tail := (rx_Tail + 1) & $3F
          
      DAT
                              org
      asm_entry
                              mov     midiMask,#1
                              shl     midiMask,midiPin
      getMidiByte
                              waitpeq midiMask,midiMask
                              mov     bitClk,cnt
                              add     bitClk,halfticks       
                              add     bitClk,bitticks
                              mov     testBits,#11
      :check_loop
                              waitcnt bitClk,bitticks
                              test    midiMask,ina            wc
                              rcr     rx_data,#1
                              djnz    testBits,#:check_loop
                              shr     rx_data,#32-11
                              mov     t1,#$100
                              test    rx_data,t1
                    if_z      jmp     #getMidiByte
                              shl     t1,#1
                              test    rx_data,t1
                    if_z      jmp     #getMidiByte
                              xor     rx_data,#$FF
                              and     rx_data,#$FF
                              mov     event_data,rx_data
                              rdlong  t1,rx_top
                              mov     rx_pointer,t1
                              shl     rx_pointer,#2
                              add     rx_pointer,rx_fifo
                              wrlong  event_data,rx_pointer
                              add     t1,#1
                              and     t1,#$3F
                              wrlong  t1,rx_top
                              jmp     #getMidiByte
      
      t1                      long    0
      midiMask                long    0
      testBits                long    0
      bitClk                  long    0
      bitticks                long    0
      halfticks               long    0
      midiPin                 long    0
      rx_top                  long    0
      rx_end                  long    0
      rx_fifo                 long    0
      rx_data                 long    0
      rx_pointer              long    0
      event_data              long    0
      rsb                     long    0
      dcb                     long    0
      keyno                   long    0
      
                              fit
      

  7. 「解説とサンプルMaxパッチのライブ制作」パフォーマンスの模様

       →   ここにあります(写真多数)

  8. YouTube

       →   2台のビデオカメラで撮ったそのまま未編集バージョン