前回、Goで音楽再生まではできました
https://qiita.com/usk81/items/8590172a23bb71e21329
普段、ニコ動でブラウザのタブ2つ開いて違う歌い手さんで同じ曲を同時再生してデュエットする遊びをしているので、プログラムでできないかと思ってやってみました。
元のコード
// package main plays two audio file
package main
import (
"log"
"os"
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/mp3"
"github.com/faiface/beep/speaker"
)
func main() {
f, err := os.Open("test.mp3")
if err != nil {
log.Fatal(err)
}
st, format, err := mp3.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
}
speakerのコードを調べてみる
// Package speaker implements playback of beep.Streamer values through physical speakers.
package speaker
import (
"sync"
"github.com/faiface/beep"
"github.com/hajimehoshi/oto"
"github.com/pkg/errors"
)
var (
mu sync.Mutex
mixer beep.Mixer
samples [][2]float64
buf []byte
context *oto.Context
player *oto.Player
done chan struct{}
)
// Init initializes audio playback through speaker. Must be called before using this package.
//
// The bufferSize argument specifies the number of samples of the speaker's buffer. Bigger
// bufferSize means lower CPU usage and more reliable playback. Lower bufferSize means better
// responsiveness and less delay.
func Init(sampleRate beep.SampleRate, bufferSize int) error {
mu.Lock()
defer mu.Unlock()
Close()
mixer = beep.Mixer{}
numBytes := bufferSize * 4
samples = make([][2]float64, bufferSize)
buf = make([]byte, numBytes)
var err error
context, err = oto.NewContext(int(sampleRate), 2, 2, numBytes)
if err != nil {
return errors.Wrap(err, "failed to initialize speaker")
}
player = context.NewPlayer()
done = make(chan struct{})
go func() {
for {
select {
default:
update()
case <-done:
return
}
}
}()
return nil
}
// Close closes the playback and the driver. In most cases, there is certainly no need to call Close
// even when the program doesn't play anymore, because in properly set systems, the default mixer
// handles multiple concurrent processes. It's only when the default device is not a virtual but hardware
// device, that you'll probably want to manually manage the device from your application.
func Close() {
if player != nil {
if done != nil {
done <- struct{}{}
done = nil
}
player.Close()
context.Close()
player = nil
}
}
// Lock locks the speaker. While locked, speaker won't pull new data from the playing Stramers. Lock
// if you want to modify any currently playing Streamers to avoid race conditions.
//
// Always lock speaker for as little time as possible, to avoid playback glitches.
func Lock() {
mu.Lock()
}
// Unlock unlocks the speaker. Call after modifying any currently playing Streamer.
func Unlock() {
mu.Unlock()
}
// Play starts playing all provided Streamers through the speaker.
func Play(s ...beep.Streamer) {
mu.Lock()
mixer.Add(s...)
mu.Unlock()
}
// Clear removes all currently playing Streamers from the speaker.
func Clear() {
mu.Lock()
mixer.Clear()
mu.Unlock()
}
// update pulls new data from the playing Streamers and sends it to the speaker. Blocks until the
// data is sent and started playing.
func update() {
mu.Lock()
mixer.Stream(samples)
mu.Unlock()
for i := range samples {
for c := range samples[i] {
val := samples[i][c]
if val < -1 {
val = -1
}
if val > +1 {
val = +1
}
valInt16 := int16(val * (1<<15 - 1))
low := byte(valInt16)
high := byte(valInt16 >> 8)
buf[i*4+c*2+0] = low
buf[i*4+c*2+1] = high
}
}
player.Write(buf)
}
あれ?
var (
mu sync.Mutex
mixer beep.Mixer
samples [][2]float64
buf []byte
context *oto.Context
player *oto.Player
done chan struct{}
)
structure作ってメソッドはやすだけでいいんじゃね?
修正版を作ってみる
package speaker
import (
"log"
"sync"
"github.com/faiface/beep"
"github.com/hajimehoshi/oto"
"github.com/pkg/errors"
)
type Player struct {
mu sync.Mutex
mixer beep.Mixer
samples [][2]float64
buf []byte
context *oto.Context
player *oto.Player
done chan struct{}
}
// Init initializes audio playback through speaker. Must be called before using this package.
//
// The bufferSize argument specifies the number of samples of the speaker's buffer. Bigger
// bufferSize means lower CPU usage and more reliable playback. Lower bufferSize means better
// responsiveness and less delay.
func Init(sampleRate beep.SampleRate, bufferSize int) (p *Player, err error) {
p = &Player{}
p.mu.Lock()
defer p.mu.Unlock()
p.Close()
p.mixer = beep.Mixer{}
numBytes := bufferSize * 4
p.samples = make([][2]float64, bufferSize)
p.buf = make([]byte, numBytes)
p.context, err = oto.NewContext(int(sampleRate), 2, 2, numBytes)
if err != nil {
return nil, errors.Wrap(err, "failed to initialize speaker")
}
log.Print("before NewPlayer")
p.player = p.context.NewPlayer()
log.Print("before NewPlayer")
p.done = make(chan struct{})
log.Print("make channel")
go func() {
for {
select {
default:
p.update()
case <-p.done:
return
}
}
}()
return p, nil
}
// Close closes the playback and the driver. In most cases, there is certainly no need to call Close
// even when the program doesn't play anymore, because in properly set systems, the default mixer
// handles multiple concurrent processes. It's only when the default device is not a virtual but hardware
// device, that you'll probably want to manually manage the device from your application.
func (p *Player) Close() {
if p.player != nil {
if p.done != nil {
p.done <- struct{}{}
p.done = nil
}
p.player.Close()
p.context.Close()
p.player = nil
}
}
// Lock locks the speaker. While locked, speaker won't pull new data from the playing Stramers. Lock
// if you want to modify any currently playing Streamers to avoid race conditions.
//
// Always lock speaker for as little time as possible, to avoid playback glitches.
func (p *Player) Lock() {
p.mu.Lock()
}
// Unlock unlocks the speaker. Call after modifying any currently playing Streamer.
func (p *Player) Unlock() {
p.mu.Unlock()
}
// Play starts playing all provided Streamers through the speaker.
func (p *Player) Play(s ...beep.Streamer) {
p.mu.Lock()
p.mixer.Add(s...)
p.mu.Unlock()
}
// Clear removes all currently playing Streamers from the speaker.
func (p *Player) Clear() {
p.mu.Lock()
p.mixer.Clear()
p.mu.Unlock()
}
// update pulls new data from the playing Streamers and sends it to the speaker. Blocks until the
// data is sent and started playing.
func (p *Player) update() {
p.mu.Lock()
p.mixer.Stream(p.samples)
p.mu.Unlock()
// buf := p.buf
for i := range p.samples {
for c := range p.samples[i] {
val := p.samples[i][c]
if val < -1 {
val = -1
}
if val > +1 {
val = +1
}
valInt16 := int16(val * (1<<15 - 1))
low := byte(valInt16)
high := byte(valInt16 >> 8)
p.buf[i*4+c*2+0] = low
p.buf[i*4+c*2+1] = high
}
}
p.player.Write(p.buf)
}
実装してみた
// package main plays two audio file
// failure: oto.NewContext can be called only once
package main
import (
"log"
"os"
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/mp3"
"github.com/usk81/til/go-duet-player/speaker"
)
func main() {
f1, err := os.Open("test1.mp3")
if err != nil {
log.Fatal(err)
}
st1, format, err := mp3.Decode(f1)
if err != nil {
log.Fatal(err)
}
defer st1.Close()
sp1, err := speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
if err != nil {
log.Fatal(err)
}
done1 := make(chan bool)
f2, err := os.Open("test2.mp3")
if err != nil {
log.Fatal(err)
}
st2, format, err := mp3.Decode(f2)
if err != nil {
log.Fatal(err)
}
defer st2.Close()
sp2, err := speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
if err != nil {
log.Fatal(err)
}
done2 := make(chan bool)
sp1.Play(beep.Seq(st1, beep.Callback(func() {
done1 <- true
})))
sp2.Play(beep.Seq(st2, beep.Callback(func() {
done2 <- true
})))
<-done1
<-done2
}
うっ、動かないだと
oto: NewContext can be called only once
beepが依存している oto
のContextが同時実行に対応していないようで、できませんでした。
今回は毎日なんか作るをやっているので、そのレイヤーに手を出すのは諦めます。
とりあえずPythonでしてみた。
from pydub import AudioSegment
from pydub.playback import play
#Load an audio file
audio1 = "test1.mp3"
audio2 = "test2.mp3"
sound1 = AudioSegment.from_mp3(audio1)
sound2 = AudioSegment.from_mp3(audio2)
combined = sound1.overlay(sound2)
play(combined)
うごいた!!
なんか悔しい... (`;ω;´)
備考
歌いはじめの位置調整とかはできてないので、音楽編集ソフトでいじっていじって調整してください