mieki256's diary



2018/11/23(金) [n年前の日記]

#2 [tic80] TIC-80の波形メモリ音源について試行錯誤中

TIC-80のサウンド機能は、いわゆる波形メモリ音源タイプとして実装されているのだけれど。

この、波形メモリ音源、一見すると波形を描いていけばいいだけだから簡単そうに思えてくるけれど、実際にやってみるとイイ感じの音がさっぱり鳴らないというアレな感じの音源タイプで。

もうちょっとどうにかならんのか、と思えてきたので、試しに、sin値等で一周期分の波形データを作って、ソレを波形メモリに書き込んで鳴らすプログラムを書いてみたり。

wavetest_ss1.png

ブラウザでプレイするなら以下。

_wavetest.tic.html
cart(.tic)は以下。

_wavetest.tic.zip

ソース。 :

ソースは以下。無駄に長いけど、makeWave() の中だけ弄れば波形が変わるので…。長さ32個の配列さえ作れば、中の各値を音量16段階(4bit)に切り詰めて波形メモリに書き込んでくれる。

_wavetest.lua
-- title:  wavetest
-- author: mieki256
-- desc:   short description
-- script: lua

waveid=4

WAVADR=0x0ffe4

function makeWave()
 dt={}
 for i=0,31 do
  local v=0
  -- multiplier, speed, shift
  v=v+getSin(i, 1.0, 1.0, 0)
  v=v+getSin(i, 1.5, 0.5, 0)
  v=v+getSin(i, 1.5, 2.0, 0)
  v=v+getSin(i, 0.5, 3.0, 90)
  --[[
  v=v+getSin(i, 0.5, 3.0, 0)
  v=v+getSin(i, 0.25, 4.0, 0)
  --]]
  table.insert(dt,v)
 end
 return dt
end

function getSin(i,m,spd,sft)
 return m*math.sin(math.rad(spd*i*360/32+sft))
end

function writeWave(id,dt)
 -- adjust
 if id<0 or id>15 then return end
 if #dt>32 then return end
 local vmax,vmin
 vmax=0
 vmin=0
 for i=1,#dt do
  if dt[i]>vmax then vmax=dt[i] end
  if dt[i]<vmin then vmin=dt[i] end
 end
 local range=vmax-vmin
 for i=1,#dt do
  dt[i]=math.floor(15*(dt[i]-vmin)/range)
 end

 -- write wave 
 local addr=WAVADR+id*16
 for i=1,#dt,2 do
  local v=0
  v = v | ((dt[i+1] & 0x0f)<<4)
  v = v | (dt[i] & 0x0f)
  poke(addr+((i-1)//2),v)
 end
end

function drawGuide(bx,by,w,h)
 local x0,y0,x1,y1
 local c=3
 x0,x1=bx,bx+w*32-2
 y0=by+h*8-1
 line(x0,y0,x1,y0,c)
 y0=by+h*4-1
 line(x0,y0,x1,y0,c)
 y0=by+h*12-1
 line(x0,y0,x1,y0,c)
 x0=bx+w*16-1
 y0,y1=by,by+h*16-2
 line(x0,y0,x0,y1,c)
 x0=bx+w*8-1
 line(x0,y0,x0,y1,c)
 x0=bx+w*24-1
 line(x0,y0,x0,y1,c)
end

function drawWave(id)
 -- read wave
 local addr=WAVADR+id*16
 local dt={}
 for i=0,15 do
  local v=peek(addr+i)
  local v1=v&0x0f
  local v2=(v>>4)&0x0f
  table.insert(dt,v1)
  table.insert(dt,v2)
 end

 -- draw wave
 local bx,by=24,12
 local w,h=6,6
 local x,y
 for ix=0,31 do
  x = ix * w + bx
  for iy=0,15 do
   y = iy * h + by
   rect(x,y,w,h,0)
   rect(x,y,w-1,h-1,1)
  end
  y=(15-dt[ix+1])*h+by
  rect(x,y,w-1,h-1,6)
 end
 drawGuide(bx,by,w,h)
end

dt=makeWave()
writeWave(waveid,dt)
t=0

function TIC()

 if keyp(14) then sfx(0,"A-4",30,0) end
 if keyp(25) then sfx(1,"A-4",30,0) end
 if btnp(5) then
  -- write to cart
  sync(1<<3,0,true)
 end

 cls(0)
 drawWave(waveid)
 print("WAVE ID: "..waveid, 2,120)
 print("Play: N or Y / Write: X", 2,128)
 t=t+1
end

少し解説。 :

TIC-80上で波形データがどこに記録されているかは、以下のページで説明されてる。

_RAM - nesbox/TIC-80 Wiki

| 0FFE4 | WAVEFORMS | 256 | 16 waveforms, each 32 x 4bit values
というのがソレ。
  • 0x0FFE4番地から、256byteほど確保してあって、16種類の波形が記録されてる。
  • 1つの波形につき、4bitの音量が32個並んでる。つまり、16byteで一つの波形になる。
このアドレス領域に、poke(アドレス,値) を使って 16byte を書き込んでやれば、波形メモリ1つ分の内容を書き換えられる。

_poke - nesbox/TIC-80 Wiki

書き込む1byteのうち、下位4bitが0番目の音量、上位4bitが1番目の音量、となっている模様。
bytes : 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, ...

wave memory : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, ...

せっかく波形メモリを書き換えても、プログラムを終了してしまうと、波形メモリの内容はデフォルト値でリセットされてしまう。できれば cart.tic に保存・記録しておきたい。そんな時は、sync() を使う。

_sync - nesbox/TIC-80 Wiki

とりあえず、sync(1<<3, 0, true) を呼び出せば、現在のSFXデータ(効果音データ)を、cart.tic に記憶させられる。と言っても、その段階ではファイルとして保存されてない気配がするので、更に Ctrl+S を叩いてファイルとして上書き保存したほうがいいのかもしれず。

課題。 :

一々ソースを書き換えて波形を変えていくのは面倒なので、実行画面上でパラメータを弄ればその場で変化するようにしたいのだけど。しかし、どういった波形ソース、及び、パラメータを用意しておけばいいのかで悩んだり。

sin波をいくつか合成するだけでではそんなに変化が出ないだろうし…。矩形波、三角波、ノコギリ波も加算できるようにしたほうがいいのかな…。

以上です。

過去ログ表示

Prev - 2018/11 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project