みなつ@プチコン

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

PiSTARTERでUARTをループバックさせて、MIDIメッセージを入力するテスト Part.2(生データ高速版)

1.はじめに

前回の改良として、生データのままSmileBasicへ送信するように変更したところ、遅延がかなり改善されましたヾ(*´∀`*)ノ

あと、コマンドプロンプトを使わず、SmileBasicのSYSTEM$()コマンドで、MIDI-INからのメッセージをUARTに転送するコマンドを自動起動するようにしたので、ソフトウェア的な前準備が要らなくなりました(≧∇≦)b

 

 

2.準備

下記の通り、ハードウェアの準備だけでOKです。

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

f:id:tksm372:20190103024456p:plain

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

以下のプログラムを実行すると、MIDI-INから入力したノートオン/オフメッセージに従い、ピアノロールが表示されます。なお、キーボードで0~9/a~fを押すと、指定したチャネルの表示/非表示を変更可能です。

'
'UARTから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

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*!!(cmd==&h90)
   var v=vel/127
   gpset gx,gy+gh8-1-note,rgb(chR[ch]*v,chG[ch]*v,chB[ch]*v)*!!v
  elseif cmd==&hb0 && (note==&h78 || note==&h7b) then
   '&hBn &h78 (nはチャンネル番号):オールサウンドオフ
   '&hBn &h7B (nはチャンネル番号):オールノートオフ
   gline gx,gy,gx,gy+gh8-1,0
  endif
 next

 '描画位置を1ドット右に変更し、画面を1ドット左にスクロール
 var cnt=maincnt
 if last_cnt!=cnt then
  last_cnt=cnt
  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
  view_scroll (ox+2) mod gw2
  ox=nx
 endif
wend

'画面(スプライト)を指定位置にスクロール
def view_scroll x
 var sclX=sw/gw2
 var ch
 for ch=0 to 15
  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

f:id:tksm372:20190110001517p:plain