[GO] Ich möchte gleichzeitig einen Musik-Player erstellen und Musik ablegen

Letztes Mal konnte ich mit Go Musik spielen https://qiita.com/usk81/items/8590172a23bb71e21329

Normalerweise öffne ich mit Nico Dou zwei Browser-Registerkarten und spiele das gleiche Lied zur gleichen Zeit mit verschiedenen Sängern und spiele ein Duett, also dachte ich, ich könnte es mit einem Programm machen.

Originalcode

// 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
}

Untersuchen Sie den Lautsprechercode

// 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)
}

Das?

var (
	mu      sync.Mutex
	mixer   beep.Mixer
	samples [][2]float64
	buf     []byte
	context *oto.Context
	player  *oto.Player
	done    chan struct{}
)

Geht es nicht nur darum, eine Struktur und eine Methode zu erstellen?

Versuchen Sie, eine geänderte Version zu erstellen

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)
}

Ich habe versucht zu implementieren

// 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
}

Ja, das funktioniert nicht

oto: NewContext can be called only once

Der "oto" -Kontext, von dem der Piepton abhängt, scheint keine Parallelität zu unterstützen, daher konnte ich das nicht. Dieses Mal mache ich jeden Tag etwas, also werde ich diese Schicht aufgeben.

Ich habe es vorerst mit Python versucht.

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)

Es bewegte sich! !!

Irgendwie bedauerlich ... (`; ω; ´)

Bemerkungen

Es ist nicht möglich, die Position zu Beginn des Gesangs anzupassen. Bitte passen Sie sie an, indem Sie mit einer Musikbearbeitungssoftware spielen.

Recommended Posts

Ich möchte gleichzeitig einen Musik-Player erstellen und Musik ablegen
Ich möchte Tag-Informationen (Titel und Künstler) einer Musikdatei (flac, wav) extrahieren.
Ich möchte vorerst eine Docker-Datei erstellen.
Ich möchte Informationen von fstab am ssh-Verbindungsziel abrufen und den Befehl ausführen
Ich möchte die Verarbeitung zwischen Testzeit und Produktionsumgebung trennen
Python: Ich möchte die Verarbeitungszeit einer Funktion genau messen
Ich möchte eine Webanwendung mit React und Python Flask erstellen
Ich möchte Matplotlib zu einem dunklen Thema machen
Ich möchte ein Spiel mit Python machen
Ich möchte mit Python in eine Datei schreiben
Ich möchte die Einstellungsdatei erhalten und prüfen, ob die von jinja2 generierte JSON-Datei eine gültige JSON ist
[Hi Py (Teil 1)] Ich möchte vorerst etwas machen, also setze zuerst ein Ziel.
Die Geschichte der IPv6-Adresse, die ich auf ein Minimum beschränken möchte
Ich möchte eine Datei auf tkinter ablegen und ihren Pfad abrufen [Tkinter DnD2]
Ich möchte ein Element mit numpy in eine Datei schreiben und es überprüfen.
Stellen Sie sicher, dass die Vorverarbeitung zum Zeitpunkt der Erstellung und Vorhersage des Vorhersagemodells ausgerichtet ist
Ich möchte dem Anfang einer WAV-Datei 1 Sekunde lang Stille hinzufügen
Ich möchte den Dateinamen, die Zeilennummer und den Funktionsnamen in Python 3.4 erhalten
Ich möchte den Dateinamen von DataLoader sehen
Visualisieren Sie Daten und erfassen Sie gleichzeitig die Korrelation
Ich möchte eine Datei mit Python zufällig testen
[Python] Ich möchte aus einer verschachtelten Liste einen Taple machen
Ich möchte das Produkt zu den niedrigsten Kosten veröffentlichen
Ich möchte die Variablen in der Python-Vorlagendatei ersetzen und in einer anderen Datei in Massenproduktion herstellen
Ich möchte zum ersten Mal eine Django-Studie zur Mittagsdatenbank [EP1] erstellen
Ich möchte zum ersten Mal eine Django-Studie zum Mittagessen [EP1-4] erstellen
wxPython: Gleichzeitiges Zeichnen von Animationen und Grafiken
Ich möchte vorerst Selen bewegen [für Mac]
Ich möchte einen Blog-Editor mit dem Administrator von Django erstellen
Ich möchte ein Klickmakro mit pyautogui (Wunsch) erstellen.
Ich möchte ein Klickmakro mit pyautogui (Outlook) erstellen.
Ich habe versucht, die Zeit und die Zeit der C-Sprache zu veranschaulichen
Ich habe versucht, die Uhrzeit und das heutige Wetter anzuzeigen
Ich möchte die Natur von Python und Pip kennenlernen
Ich möchte den Wörterbuchtyp in der Liste eindeutig machen
Ich möchte den EDINET-Code und die Wertpapiernummer zuordnen
Ich möchte eine schöne Ergänzung zu input () in Python hinzufügen
Ich habe versucht, zum Zeitpunkt der Bereitstellung mit Fabric und ChatWork Api automatisch in ChatWork zu posten
So starten Sie den PC jeden Morgen zu einer festgelegten Zeit und führen das Python-Programm aus
Verfahren zum gleichzeitigen Ändern des Tabellennamens und des Spaltennamens des Django-Modells
Ich habe die gleiche Datenanalyse mit kaggle notebook (python) und PowerBI gleichzeitig versucht ①
Ich möchte die Grafik in 3D sehen! Ich kann einen solchen Traum wahr werden lassen.
Ich möchte den Schnittpunkt einer Bezier-Kurve und einer geraden Linie finden (Bezier-Clipping-Methode)
Ich möchte einen Sprachwechsler mit Python und SPTK in Bezug auf eine berühmte Site erstellen
Ich habe versucht, mit Python einen regulären Ausdruck von "Zeit" zu erstellen
So erstellen Sie einen Befehl zum Lesen der Einstellungsdatei mit Pyramide
Ich möchte ein System erstellen, um zu verhindern, dass vergessen wird, den Schlüssel 1 festzuziehen
Ich habe versucht, mit Selenium und Python einen regelmäßigen Ausführungsprozess durchzuführen
Holz kratzen und essen - ich möchte ein gutes Restaurant finden! ~ (Arbeit)
[Einführung] Ich möchte mit Python einen Mastodon-Bot erstellen! 【Anfänger】
Ich möchte eine Pipfile erstellen und im Docker wiedergeben
Ich möchte eine Parameterliste aus CloudFormation-Code (yaml) erstellen.
Ich möchte die zweite Zeile zum Spaltennamen in Pandas machen
Vorerst möchte ich jede Datei mit ffmpeg konvertieren !!
Durchsuchen Sie .loc und .iloc gleichzeitig in pandas DataFrame
Ich möchte Robomaster S1 ① Rooting und Dateikonfigurationsprüfung hacken
Ich möchte ein Histogramm erstellen und die Normalverteilungskurve darauf überlagern. matplotlib edition
Ich habe versucht, den Unterschied zwischen A + = B und A = A + B in Python herauszufinden
[Twitter] Ich möchte die heruntergeladenen vergangenen Tweets (meines Kontos) in eine schöne CSV verwandeln
Ich möchte die Frage nach der Methode "__init__" und dem Argument "self" der Python-Klasse klären.