ebiten/examples/pcm/main.go

168 lines
3.2 KiB
Go
Raw Normal View History

2015-01-10 17:23:43 +01:00
// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
2016-02-10 18:04:23 +01:00
"bytes"
2015-01-10 17:23:43 +01:00
"fmt"
"log"
"math"
2015-01-10 17:23:43 +01:00
"github.com/hajimehoshi/ebiten"
2016-04-18 18:24:44 +02:00
"github.com/hajimehoshi/ebiten/audio"
2015-01-10 17:23:43 +01:00
"github.com/hajimehoshi/ebiten/ebitenutil"
)
const (
screenWidth = 320
screenHeight = 240
2016-02-09 15:04:00 +01:00
sampleRate = 44100
2015-01-10 17:23:43 +01:00
)
2016-03-02 16:48:59 +01:00
var audioContext *audio.Context
func init() {
var err error
audioContext, err = audio.NewContext(sampleRate)
if err != nil {
log.Fatal(err)
}
2016-03-02 16:48:59 +01:00
}
2015-01-10 17:23:43 +01:00
var frames = 0
const (
2015-01-11 11:52:11 +01:00
freqA = 440.0
freqAS = 466.2
freqB = 493.9
freqC = 523.3
freqCS = 554.4
freqD = 587.3
freqDS = 622.3
freqE = 659.3
freqF = 698.5
freqFS = 740.0
freqG = 784.0
freqGS = 830.6
2015-01-10 17:23:43 +01:00
)
// Twinkle, Twinkle, Little Star
2015-01-11 11:52:11 +01:00
const score = `CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR`
var scoreIndex = 0
func square(out []int16, volume float64, freq float64, sequence float64) {
2015-01-23 02:58:18 +01:00
if freq == 0 {
for i := 0; i < len(out); i++ {
out[i] = 0
}
return
}
2016-02-09 15:04:00 +01:00
length := int(float64(sampleRate) / freq)
2015-01-10 17:23:43 +01:00
if length == 0 {
2015-01-11 11:52:11 +01:00
panic("invalid freq")
2015-01-10 17:23:43 +01:00
}
for i := 0; i < len(out); i++ {
a := int16(volume * math.MaxInt16)
2015-01-10 17:23:43 +01:00
if i%length < int(float64(length)*sequence) {
2015-01-24 13:46:30 +01:00
a = -a
2015-01-10 17:23:43 +01:00
}
out[i] = a
}
}
func toBytes(l, r []int16) []byte {
if len(l) != len(r) {
panic("len(l) must equal to len(r)")
}
2015-06-13 20:27:02 +02:00
b := make([]byte, len(l)*4)
for i, _ := range l {
b[4*i] = byte(l[i])
b[4*i+1] = byte(l[i] >> 8)
b[4*i+2] = byte(r[i])
b[4*i+3] = byte(r[i] >> 8)
}
2015-06-13 20:27:02 +02:00
return b
}
2016-03-28 20:37:28 +02:00
type srcStream struct {
*bytes.Reader
}
func (s *srcStream) Close() error {
s.Reader = nil
return nil
}
func addNote() error {
2016-03-12 20:48:13 +01:00
size := sampleRate / ebiten.FPS
2015-01-11 11:52:11 +01:00
notes := []float64{freqC, freqD, freqE, freqF, freqG, freqA * 2, freqB * 2}
defer func() {
scoreIndex++
scoreIndex %= len(score)
}()
l := make([]int16, size*30)
r := make([]int16, size*30)
2015-01-11 11:52:11 +01:00
note := score[scoreIndex]
for note == ' ' {
scoreIndex++
scoreIndex %= len(score)
note = score[scoreIndex]
}
freq := 0.0
switch {
case note == 'R':
2015-01-23 02:58:18 +01:00
freq = 0
2015-01-11 11:52:11 +01:00
case note <= 'B':
freq = notes[int(note)+len(notes)-int('C')]
default:
freq = notes[note-'C']
}
2015-01-23 15:04:56 +01:00
vol := 1.0 / 16.0
square(l, vol, freq, 0.25)
square(r, vol, freq, 0.25)
2016-02-10 18:04:23 +01:00
b := toBytes(l, r)
2016-03-28 20:37:28 +02:00
s := &srcStream{bytes.NewReader(b)}
p, err := audio.NewPlayer(audioContext, s)
if err != nil {
return err
}
2016-02-10 18:18:39 +01:00
p.Play()
return nil
2015-01-11 11:52:11 +01:00
}
2015-01-10 17:23:43 +01:00
func update(screen *ebiten.Image) error {
defer func() {
frames++
}()
if frames%30 == 0 {
if err := addNote(); err != nil {
return err
}
2015-01-10 17:23:43 +01:00
}
2015-01-11 11:52:11 +01:00
ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f", ebiten.CurrentFPS()))
if err := audioContext.Update(); err != nil {
return err
}
2015-01-10 17:23:43 +01:00
return nil
}
func main() {
2016-03-06 14:05:56 +01:00
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "PCM (Ebiten Demo)"); err != nil {
2015-01-10 17:23:43 +01:00
log.Fatal(err)
}
}