みなつ@プチコン

BASICでゲームが作れるWiiU/3DS用ソフト「プチコン」のブログです(*´▽`*)

PiSTARTERをMIDI音源にするテスト Part.1

1.はじめに

PiSTARTERでMIDIメッセージを受信できるようになったので、PiSTARTERのSOUND命令で音を鳴らしてみました!( ゚∀゚)

今のところ、下記のような制限がありますが、一応鳴るようになりました(*´▽`*)

 

2.準備

前回とほぼ同じです。

  • USB-MIDI入力デバイスをRaspberryPiに繋ぎます。
  • PCとRaspberryPiをMIDIケーブルでつなぎます。
  • RaspberryPiのUARTのTX(8番ピン:GPIO14)とRX(10番ピン:GPIO15)をジャンパー線でつなぎます。

3.PiSTARTER(smilebasic)のプログラム

PiSTARTERで下記のプログラムを実行して、PCのMIDI再生ソフトを使ってPCからRaspberryPiにMIDI信号を送ると、PiSTARTER側で音が鳴ります。対応しているMIDIメッセージは以下の通りです。

  • ノートオン/ノートオフ
  • チャンネルボリューム
  • オールサウンドオフ/オールノートオフ
'
'PiSTARTERをMIDI音源にするテスト by みなつ
'
option strict
'画面初期化
acls
var gw=1280,gh=1024    'GRP Width,Height
var sw=1280/4,sh=720/4 'SCREEN Width,Height
xscreen sw,sh

'以前に起動したプロセスがあればkillする
var dq$=chr$(&h22)
?system$("sudo /bin/sh -c "+dq$+"/bin/ps auxww|/bin/grep '/bin/cp /dev/snd/midiC1D0'|/bin/grep -v grep|/usr/bin/awk '{print \$2}'|/usr/bin/xargs -r /bin/kill"+dq$)

'MIDIメッセージをシリアルに出力するプロセスをバックグラウンドで起動
?system$("sudo /bin/sh -c '/usr/bin/nohup /bin/cp /dev/snd/midiC1D0 /dev/serial0 > /dev/null 2>&1 &'")

'シリアルポートをスタート
uartstart 4000000

'変数定義
var i
dim rx[1024] 'UART受信バッファ
var s 'UARTからの受信サイズ
var KR$=chr$(&h1c),KL$=chr$(&h1d),KU$=chr$(&h1e),KD$=chr$(&h1f)

'ピアノロール表示パラメータ
var gw2=gw/4,gh8=gh/8
dim mute[16]
dim chCol[16]
dim chR[16],chG[16],chB[16]
for i=0 to 15
 var br=255-64-((i>>3) and 1)*64
 var r=((i>>2) and 1)*255
 var g=((i>>1) and 1)*255
 var b=((i>>0) and 1)*255
 chR[i]=r+br
 chG[i]=g+br
 chB[i]=b+br
 chCol[i]=rgb(r+br,g+br,b+br)
next
var ox=0,last_cnt
gpage 0,1
sppage 1

'MIDIのパート情報
dim chVol[16]:fill chVol,127
dim sndCh[16]
dim sndVel[16]
dim sndNote[16]

while true
 '0〜9、a〜fで、指定チャネルの表示/非表示切り替え
 var k$=inkey$()
 if k$!="" then
  var m=instr("0123456789abcdef",k$)
  if m!=-1 then
   mute[m]=!mute[m]
  endif
 endif

 'UARTからMIDIメッセージを受信
 uartrecv rx out s
 for i=0 to s-1
  if !(rx[i] and &h80) then continue 'データバイトをスキップ

  var cmd=rx[i] and &hf0
  var ch=rx[i] and &hf
  var note=rx[i+1]
  var vel=rx[i+2]
  var gx=(ch div 8)*gw2+ox
  var gy=(ch mod 8)*gh8

  if cmd==&h90 || cmd==&h80 then
   '&h8n [note] [velocity] (nはチャンネル番号):ノートOFF
   '&h9n [note] [velocity] (nはチャンネル番号):ノートON
   vel=vel*chVol[ch]/127*!!(cmd==&h90)
   if ch!=9 then noteOn ch,note,vel 'ドラムパートはひとまず無視
  elseif cmd==&hb0 then
   if note==&h78 || note==&h7b then
    '&hBn &h78 (nはチャンネル番号):オールサウンドオフ
    '&hBn &h7B (nはチャンネル番号):オールノートオフ
    allOff
   elseif note==&h7 then
    '&hb0 &h07 vol:チャンネルボリューム
    chVol[ch]=vel
   endif
  endif
 next

 '描画位置を1ドット右に変更し、画面を1ドット左にスクロール
 var cnt=maincnt
 if last_cnt!=cnt then
  last_cnt=cnt
  view_scroll (ox+2) mod gw2
  
  gline ox,0,ox,gh-1,0
  gline gw2+ox,0,gw2+ox,gh-1,0
  for i=0 to 15
   note=sndNote[i]
   if !note then continue
   ch=sndCh[i]
   vel=sndVel[i]
   var v=vel/127
   gpset ox,gh8-1-note,rgb(chR[ch]*v,chG[ch]*v,chB[ch]*v)*!!v
  next

  var nx=(ox+1) mod gw2
'  gcopy ox,0,ox,gh-1,nx,0,1
'  gcopy gw2+ox,0,gw2+ox,gh-1,gw2+nx,0,1
  ox=nx
 endif

 soundStep
wend

'画面(スプライト)を指定位置にスクロール
def view_scroll x
 var sclX=sw/gw2
 var ch
 for ch=0 to 0
  var sp=ch*2
  if mute[ch] then
   if spused(sp) then sphide sp
   if spused(sp+1) then sphide sp+1
   continue
  endif
  
  var gx=(ch div 8)*gw2
  var gy=(ch mod 8)*gh8
  spset sp,gx+x,gy,gw2-x,gh8,1+#spadd
  spofs sp,0,0
  spcolor sp,rgb(200,255,255,255)
  spscale sp,sclX,sh/gh8
  if x>0 then
   spset sp+1,gx,gy,x,gh8,1+#spadd
   spofs sp+1,(gw2-x)*sclX,0
   spcolor sp+1,rgb(200,255,255,255)
   spscale sp+1,sclX,sh/gh8
  else
   if spused(sp+1) then sphide sp+1
  endif
 next
end


def noteOn ch,note,vel
 var i
 for i=0 to 15
  if sndNote[i]==note && sndCh[i]==ch then break
 next
' if i==16 then
'  for i=0 to 15
'   if sndNote[i]==0 then break
'  next
' endif
 if i==16 then
  var minVel=sndVel[0],minI=0
  for i=1 to 15
   if sndVel[i]>24) and 255
 sound ch*8+2,(R%>>16) and 255
 sound ch*8+1,(R%>> 8) and 255
 sound ch*8+0,R% and 255
end

def vol ch,v
 sound ch*8+4,v
end

f:id:tksm372:20190111024916p:plain