La dernière fois, j'ai pu jouer de la musique avec Go https://qiita.com/usk81/items/8590172a23bb71e21329
J'ouvre généralement deux onglets de navigateur avec Nico Dou et joue la même chanson en même temps avec différents chanteurs et joue un duo, alors j'ai pensé que je pourrais le faire avec un programme.
// 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
}
// 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)
}
cette?
var (
mu sync.Mutex
mixer beep.Mixer
samples [][2]float64
buf []byte
context *oto.Context
player *oto.Player
done chan struct{}
)
N'est-ce pas juste une question de structure et de méthode?
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
Je ne pouvais pas parce que le contexte de ʻoto` dont dépend le bip ne semble pas prendre en charge la concurrence. Cette fois, je fais quelque chose tous les jours, alors je vais abandonner cette couche.
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)
Ça bouge! !!
En quelque sorte regrettable ... (`; ω; ´)
Il n'est pas possible d'ajuster la position au début du chant, veuillez donc l'ajuster en jouant avec un logiciel d'édition de musique.
Recommended Posts