Monkey言語:サンプル音源の再生
目標
組み込み関数sampleを作成して,引数によって録音された音源を流せるようにしたい.
この記事を参考に書いていく.
テストケースを書く
func TestSampleFunction(t *testing.T){
tests := []struct {
input string
expected interface{}
}{
{`sample("drubase")`, "drubase"},
{`sample()`, "wrong number of arguments. got=0, want=1"},
{`sample("beat", 3)`, "wrong number of arguments. got=2, want=1"},
{`sample(4)`, "argument to `sample` must be STRING, got INTEGER"},
{`sample("foo")`, "There is not foo in SoundSources."},
}
for _, tt := range tests{
evaluated := testEval(tt.input)
switch expected := tt.expected.(type){
case string:
switch evaluated.(type){
case *object.Error:
errObj, ok := evaluated.(*object.Error)
if !ok {
continue
}
if errObj.Message != expected {
t.Errorf("wrong error message. expected=%q, got=%q", expected, errObj.Message)
}
case *object.String:
str, ok := evaluated.(*object.String)
if !ok {
t.Fatalf("object is not String. got=%T (%+v)", evaluated, evaluated)
}
if str.Value != "drum&base.wav"{
t.Errorf("There is not %s in SoundSources.", str.Value)
}
}
}
}
}
ErrorfとすべきところをFatalfとして少し沼ったのでそれについての記事を載せておく.
組み込み関数の認識
再生できる音源の名前を格納するのに,mapの機能を使う.これで後からそのサンプルがあるのかのチェックが容易になる.
音源は以下の箇所から拝借させていただいている.
ひとまず3つほど音源を用意した.
var soundSource = map[string]string{
"drubase": "drum&base.wav",
"distortion": "distortionguitar.wav",
"simple": "simple.wav",
}
関数の中身1
音を鳴らす部分を除いてテストを試せるようにした.
"sample": &object.Builtin{
Fn: func(args ...object.Object) object.Object{
if len(args) != 1{
return newError("wrong number of arguments. got=%d, want=1", len(args))
}
switch args[0].(type){
case *object.String:
if val, ok := soundSource[args[0].Inspect()]; ok{
return &object.String{Value: val}
}else{
return newError("There is not %s in SoundSources.", args[0].Inspect())
}
default:
return newError("argument to `sample` must be STRING, got %s", args[0].Type())
}
},
},
これでテストケースは通るので,あとは記事を参考に.wavを再生させる.
関数の中身2
"sample": &object.Builtin{
Fn: func(args ...object.Object) object.Object{
if len(args) != 1{
return newError("wrong number of arguments. got=%d, want=1", len(args))
}
switch args[0].(type){
case *object.String:
if val, ok := soundSource[args[0].Inspect()]; ok{
f, err := os.Open("sound_source/" + val)
if err != nil {
log.Fatal(err)
}
st, format, err := wav.Decode(f)
if err != nil {
log.Fatal(err)
}
defer st.Close()
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
done := make(chan bool)
speaker.Play(beep.Seq(st, beep.Callback(func() {
done <- true
})))
<-done
return &object.String{Value: val}
}else{
return newError("There is not %s in SoundSources.", args[0].Inspect())
}
default:
return newError("argument to `sample` must be STRING, got %s", args[0].Type())
}
},
},
}
この状態だと,sample関数を使って音を鳴らすことはできるが,鳴らしてる間命令を実行させることができなくなってしまう.
これでは使い道がほとんどないので,どちらも並行してできるようにしたい.
と思ってgoroutineについて調べて次のようにしてみたのだが,結局終わるまで待たせるので使う前と何も変わらなかった.
var wg sync.WaitGroup // WaitGroupの生成
wg.Add(1) // カウンタをインクリメント
done := make(chan bool)
go func(){
speaker.Play(beep.Seq(st, beep.Callback(func() {
done <- true
})))
<-done
wg.Done() // カウンタをデクリメント
}()
wg.Wait() //新規のゴールーチンが完了するのを待つ
なので,sample関数では一旦,この状態のままで完成とさせていく.代わりに,ブラウザ上でファイルを落として,そのファイルの再生をできるようにしようと考えている.
この記事が気に入ったらサポートをしてみませんか?