199 lines
3.7 KiB
Go
199 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"math"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/faiface/beep"
|
|
"github.com/faiface/beep/effects"
|
|
"github.com/faiface/beep/flac"
|
|
"github.com/faiface/beep/mp3"
|
|
"github.com/faiface/beep/speaker"
|
|
"github.com/faiface/beep/vorbis"
|
|
"github.com/faiface/beep/wav"
|
|
)
|
|
|
|
var (
|
|
playingFileName string
|
|
playingFileInfo string
|
|
playingFileID int64
|
|
playingStreamer beep.StreamSeekCloser
|
|
playingFormat beep.Format
|
|
playingSampleRate beep.SampleRate
|
|
|
|
nextStreamer beep.StreamSeekCloser
|
|
nextFormat beep.Format
|
|
nextFileName string
|
|
|
|
volume *effects.Volume
|
|
ctrl *beep.Ctrl
|
|
|
|
audioLock = new(sync.Mutex)
|
|
)
|
|
|
|
type AudioFile struct {
|
|
File *os.File
|
|
Streamer beep.StreamSeekCloser
|
|
Format beep.Format
|
|
Metadata *Metadata
|
|
}
|
|
|
|
func openFile(filePath string) (*AudioFile, error) {
|
|
f, err := os.Open(filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
metadata := readMetadata(f)
|
|
_, err = f.Seek(0, io.SeekStart)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
var (
|
|
streamer beep.StreamSeekCloser
|
|
format beep.Format
|
|
)
|
|
switch strings.ToLower(path.Ext(filePath)) {
|
|
case ".wav":
|
|
streamer, format, err = wav.Decode(f)
|
|
case ".mp3":
|
|
streamer, format, err = mp3.Decode(f)
|
|
case ".ogg", ".weba", ".webm":
|
|
streamer, format, err = vorbis.Decode(f)
|
|
case ".flac":
|
|
streamer, format, err = flac.Decode(f)
|
|
default:
|
|
err = fmt.Errorf("unsupported file format")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
a := AudioFile{File: f, Streamer: streamer, Format: format, Metadata: metadata}
|
|
return &a, nil
|
|
}
|
|
|
|
func play(audioFile *AudioFile) {
|
|
audioLock.Lock()
|
|
defer audioLock.Unlock()
|
|
|
|
if playingStreamer != nil {
|
|
playingStreamer.Close()
|
|
}
|
|
|
|
thisFileID := time.Now().UnixNano()
|
|
|
|
playingFileID = thisFileID
|
|
playingStreamer = audioFile.Streamer
|
|
playingFormat = audioFile.Format
|
|
playingFileName = audioFile.File.Name()
|
|
|
|
playingFileInfo = ""
|
|
if audioFile.Metadata.Title != "" {
|
|
playingFileInfo = audioFile.Metadata.Title
|
|
|
|
if audioFile.Metadata.Artist != "" {
|
|
playingFileInfo = audioFile.Metadata.Artist + " - " + playingFileInfo
|
|
}
|
|
}
|
|
|
|
if audioFile.Format.SampleRate != playingSampleRate {
|
|
err := speaker.Init(audioFile.Format.SampleRate, audioFile.Format.SampleRate.N(time.Second/2))
|
|
if err != nil {
|
|
log.Fatalf("failed to initialize audio device: %s", err)
|
|
}
|
|
}
|
|
|
|
var (
|
|
vol float64
|
|
silent bool
|
|
)
|
|
speaker.Lock()
|
|
if volume != nil {
|
|
vol = volume.Volume
|
|
silent = volume.Silent
|
|
}
|
|
speaker.Unlock()
|
|
|
|
volume = &effects.Volume{
|
|
Streamer: beep.Seq(audioFile.Streamer, beep.Callback(func() {
|
|
if playingFileID != thisFileID {
|
|
return
|
|
}
|
|
|
|
go nextTrack()
|
|
})),
|
|
Base: volumeBase,
|
|
Volume: vol,
|
|
Silent: silent,
|
|
}
|
|
|
|
ctrl = &beep.Ctrl{
|
|
Streamer: volume,
|
|
Paused: false,
|
|
}
|
|
|
|
speaker.Clear()
|
|
speaker.Play(ctrl)
|
|
app.QueueUpdateDraw(func() {
|
|
updateMain()
|
|
updateQueue()
|
|
updateStatus()
|
|
})
|
|
}
|
|
|
|
func nextTrack() {
|
|
if mainBufferCursor-1 < len(mainBufferFiles)-1 {
|
|
mainBufferCursor++
|
|
|
|
audioFile, err := openFile(path.Join(mainBufferDirectory, selectedEntry().File.Name()))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
play(audioFile)
|
|
app.QueueUpdateDraw(updateMain)
|
|
}
|
|
}
|
|
|
|
func roundUnit(x, unit float64) float64 {
|
|
return math.Round(x/unit) * unit
|
|
}
|
|
|
|
func supportedFormat(filePath string) bool {
|
|
switch strings.ToLower(path.Ext(filePath)) {
|
|
case ".wav":
|
|
return true
|
|
case ".mp3":
|
|
return true
|
|
case ".ogg", ".weba", ".webm":
|
|
return true
|
|
case ".flac":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func fileFormat(fileName string) string {
|
|
switch strings.ToLower(path.Ext(fileName)) {
|
|
case ".wav":
|
|
return "WAV"
|
|
case ".mp3":
|
|
return "MP3"
|
|
case ".ogg", ".weba", ".webm":
|
|
return "OGG"
|
|
case ".flac":
|
|
return "FLAC"
|
|
default:
|
|
return "?"
|
|
}
|
|
}
|