Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
7e6cc8cd1b | |||
0b40953cf5 | |||
2eb3b0a089 | |||
3482b6952a | |||
92a6b4b1d3 | |||
22edd2ea9c | |||
39a5d0b0cd | |||
8b198040bd | |||
1af34dfcf3 | |||
f3e07d3f4d |
@ -23,6 +23,8 @@ This is the basic json configuration file layout:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> All sounds should be in the same bitrate. It will assume the bitrate of the first loaded sample.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
Drop your sounds into the /sounds. You can play them by sending a GET request to the /v1/play endpoint.
|
Drop your sounds into the /sounds. You can play them by sending a GET request to the /v1/play endpoint.
|
||||||
You need to know the base64 encoded file name of the sound you want to play. You can get started by querying /v1/list. It will return a list of all sounds with their respective base64 encoded file name.
|
You need to know the base64 encoded file name of the sound you want to play. You can get started by querying /v1/list. It will return a list of all sounds with their respective base64 encoded file name.
|
||||||
|
@ -14,6 +14,11 @@ paths:
|
|||||||
description: A base64 encoded version of the file name
|
description: A base64 encoded version of the file name
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: loop
|
||||||
|
description: Defaults to false; if true, will loop the sound until stopped
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: OK
|
description: OK
|
||||||
|
@ -16,6 +16,8 @@ import (
|
|||||||
"github.com/h2non/filetype"
|
"github.com/h2non/filetype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var firstLoad = true
|
||||||
|
|
||||||
func BufferSound(file string) bool {
|
func BufferSound(file string) bool {
|
||||||
_, ok := streamMap[file]
|
_, ok := streamMap[file]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -26,9 +28,18 @@ func BufferSound(file string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Opened file")
|
fmt.Println("Opened file")
|
||||||
buf, _ := ioutil.ReadFile("sounds/" + string(file))
|
buf, err := ioutil.ReadFile("sounds/" + string(file))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Fatal error while opening: " + err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
kind, _ := filetype.Match(buf)
|
kind, err := filetype.Match(buf)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Fatal error while detecting file type: " + err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("File type: " + kind.MIME.Subtype)
|
fmt.Println("File type: " + kind.MIME.Subtype)
|
||||||
var streamer beep.StreamSeekCloser
|
var streamer beep.StreamSeekCloser
|
||||||
@ -45,8 +56,10 @@ func BufferSound(file string) bool {
|
|||||||
fmt.Println("!!!!! Unsupported file type for " + file)
|
fmt.Println("!!!!! Unsupported file type for " + file)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if firstLoad {
|
||||||
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
|
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
|
||||||
|
firstLoad = false
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("Decoded file")
|
fmt.Println("Decoded file")
|
||||||
buffer := beep.NewBuffer(format)
|
buffer := beep.NewBuffer(format)
|
||||||
@ -66,37 +79,46 @@ func BufferSound(file string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PlaySound(file string, index int) int {
|
func PlaySound(file string, index int, loop bool) int {
|
||||||
playbacks[index] = playback{
|
playbacks[index] = playback{
|
||||||
File: file,
|
File: file,
|
||||||
IsLoaded: false,
|
IsLoaded: false,
|
||||||
Streamer: nil,
|
Streamer: nil,
|
||||||
Control: nil,
|
Control: nil,
|
||||||
|
Loop: loop,
|
||||||
|
Format: streamMap[file].Format,
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Playing sound: " + file)
|
fmt.Println("Playing sound: " + file)
|
||||||
var buffer *beep.Buffer
|
var buffer *beep.Buffer
|
||||||
BufferSound(file)
|
BufferSound(file)
|
||||||
buffer = streamMap[file].Buffer
|
buffer = streamMap[file].Buffer
|
||||||
streamer := streamMap[file].Streamer
|
// streamer := streamMap[file].Streamer
|
||||||
|
|
||||||
fmt.Println("Trying to play sound")
|
fmt.Println("Trying to play sound")
|
||||||
|
amountOfLoops := 1
|
||||||
|
if loop {
|
||||||
|
amountOfLoops = -1
|
||||||
|
fmt.Println("Looping sound: " + file)
|
||||||
|
}
|
||||||
shot := buffer.Streamer(0, buffer.Len())
|
shot := buffer.Streamer(0, buffer.Len())
|
||||||
|
|
||||||
done := make(chan bool)
|
done := make(chan bool)
|
||||||
ctrl := &beep.Ctrl{Streamer: beep.Seq(shot, beep.Callback(func() {
|
ctrl := &beep.Ctrl{Streamer: beep.Loop(amountOfLoops, shot), Paused: false}
|
||||||
done <- true
|
|
||||||
})), Paused: false}
|
|
||||||
|
|
||||||
playbacks[index] = playback{
|
playbacks[index] = playback{
|
||||||
File: file,
|
File: file,
|
||||||
IsLoaded: true,
|
IsLoaded: true,
|
||||||
Streamer: streamer,
|
Streamer: shot,
|
||||||
Control: ctrl,
|
Control: ctrl,
|
||||||
|
Loop: loop,
|
||||||
|
Format: streamMap[file].Format,
|
||||||
|
Done: done,
|
||||||
}
|
}
|
||||||
speaker.Play(ctrl)
|
speaker.Play(ctrl)
|
||||||
<-done
|
<-done
|
||||||
fmt.Println("Finished playing sound: " + file)
|
fmt.Println("Finished playing sound: " + file)
|
||||||
delete(playbacks, index)
|
delete(playbacks, index)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,11 @@ import (
|
|||||||
type playback struct {
|
type playback struct {
|
||||||
File string
|
File string
|
||||||
IsLoaded bool
|
IsLoaded bool
|
||||||
Streamer beep.Streamer
|
Streamer beep.StreamSeeker
|
||||||
Control *beep.Ctrl
|
Control *beep.Ctrl
|
||||||
|
Loop bool
|
||||||
|
Format beep.Format
|
||||||
|
Done chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type playbackWebReturn struct {
|
type playbackWebReturn struct {
|
||||||
@ -89,6 +92,8 @@ func main() {
|
|||||||
http.HandleFunc("/v1/stopAll", handleStopAll)
|
http.HandleFunc("/v1/stopAll", handleStopAll)
|
||||||
http.HandleFunc("/v1/current", handleCurrent)
|
http.HandleFunc("/v1/current", handleCurrent)
|
||||||
http.HandleFunc("/v1/list", handleListing)
|
http.HandleFunc("/v1/list", handleListing)
|
||||||
|
http.HandleFunc("/v1/remaining", handleRemaining)
|
||||||
|
http.HandleFunc("/", handleRoot)
|
||||||
|
|
||||||
fmt.Println("Listening on port " + fmt.Sprint(configuration.Port))
|
fmt.Println("Listening on port " + fmt.Sprint(configuration.Port))
|
||||||
log.Fatal(http.ListenAndServe(":"+fmt.Sprint(configuration.Port), nil))
|
log.Fatal(http.ListenAndServe(":"+fmt.Sprint(configuration.Port), nil))
|
||||||
|
92
webRoutes.go
92
webRoutes.go
@ -10,6 +10,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/faiface/beep/speaker"
|
"github.com/faiface/beep/speaker"
|
||||||
"github.com/h2non/filetype"
|
"github.com/h2non/filetype"
|
||||||
@ -25,8 +26,16 @@ func handlePlay(w http.ResponseWriter, r *http.Request) {
|
|||||||
var cnt = r.URL.Query().Get("file") // Retrieve the file name from the query string
|
var cnt = r.URL.Query().Get("file") // Retrieve the file name from the query string
|
||||||
bytArr, err := base64.StdEncoding.DecodeString(cnt) // Decode the base64 string
|
bytArr, err := base64.StdEncoding.DecodeString(cnt) // Decode the base64 string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\""+err.Error()+"\"}")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
loop := r.URL.Query().Get("loop") // Retrieve the loop value from the query string
|
||||||
|
loopBool := false
|
||||||
|
if loop == "true" {
|
||||||
|
loopBool = true
|
||||||
|
}
|
||||||
|
|
||||||
|
wantedId := r.URL.Query().Get("id") // Retrieve the id value from the query string, it's optional
|
||||||
|
|
||||||
t, err := os.Stat("./sounds/" + string(bytArr[:])) // Check if the file exists
|
t, err := os.Stat("./sounds/" + string(bytArr[:])) // Check if the file exists
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
@ -55,9 +64,23 @@ func handlePlay(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var currIndex = len(playbacks) // Create a new index for the playback
|
var currIndex = len(playbacks) // Create a new index for the playback
|
||||||
|
|
||||||
|
if len(wantedId) > 0 { // If the id is set, check if it is already in use
|
||||||
|
id, err := strconv.Atoi(wantedId)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"id is not a number\"}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := playbacks[id]; ok {
|
||||||
|
fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"id is already in use\"}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currIndex = id
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Fprintf(w, "{\"status\":\"ok\", \"id\":%d}", currIndex) // Return a JSON object to the user
|
fmt.Fprintf(w, "{\"status\":\"ok\", \"id\":%d}", currIndex) // Return a JSON object to the user
|
||||||
|
|
||||||
go PlaySound(string(bytArr[:]), currIndex) // Play the sound
|
go PlaySound(string(bytArr[:]), currIndex, loopBool) // Play the sound
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +97,8 @@ func handleBufferAll(w http.ResponseWriter, r *http.Request) {
|
|||||||
var temp []string
|
var temp []string
|
||||||
files, err := ioutil.ReadDir("./sounds/") // Read the directory
|
files, err := ioutil.ReadDir("./sounds/") // Read the directory
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\""+err.Error()+"\"}")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Loop through the files and add the file name to the temp array
|
// Loop through the files and add the file name to the temp array
|
||||||
// Also triggers the buffer process for the file
|
// Also triggers the buffer process for the file
|
||||||
@ -93,6 +117,7 @@ func handleBufferAll(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
temp = append(temp, f.Name())
|
temp = append(temp, f.Name())
|
||||||
go BufferSound(f.Name())
|
go BufferSound(f.Name())
|
||||||
|
time.Sleep(200 * time.Millisecond) // Wait a bit to not overload the system
|
||||||
}
|
}
|
||||||
// Return the amount of files buffered
|
// Return the amount of files buffered
|
||||||
fmt.Fprintf(w, "{\"status\":\"ok\", \"amount\":%d}", len(temp))
|
fmt.Fprintf(w, "{\"status\":\"ok\", \"amount\":%d}", len(temp))
|
||||||
@ -109,7 +134,8 @@ func handleBuffer(w http.ResponseWriter, r *http.Request) {
|
|||||||
var cnt = r.URL.Query().Get("file") // Retrieve the file name from the query string
|
var cnt = r.URL.Query().Get("file") // Retrieve the file name from the query string
|
||||||
bytArr, err := base64.StdEncoding.DecodeString(cnt) // Decode the base64 string
|
bytArr, err := base64.StdEncoding.DecodeString(cnt) // Decode the base64 string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\""+err.Error()+"\"}")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := os.Stat("./sounds/" + string(bytArr[:])) // Check if the file exists
|
t, err := os.Stat("./sounds/" + string(bytArr[:])) // Check if the file exists
|
||||||
@ -199,6 +225,23 @@ func handleCurrent(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json") // Set the content type to json
|
w.Header().Set("Content-Type", "application/json") // Set the content type to json
|
||||||
|
|
||||||
|
for i := 0; i < len(playbacks); i++ {
|
||||||
|
plyB := playbacks[i]
|
||||||
|
seeker := plyB.Streamer
|
||||||
|
format := plyB.Format
|
||||||
|
|
||||||
|
if seeker != nil {
|
||||||
|
fmt.Println(format.SampleRate)
|
||||||
|
// fmt.Println(plyB.Seeker.)
|
||||||
|
position := plyB.Format.SampleRate.D(seeker.Position())
|
||||||
|
length := plyB.Format.SampleRate.D(seeker.Len())
|
||||||
|
remaining := length - position
|
||||||
|
if remaining == 0 {
|
||||||
|
plyB.Done <- true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var tempResultSet map[int]playbackWebReturn = make(map[int]playbackWebReturn) // Create a new map to store the results
|
var tempResultSet map[int]playbackWebReturn = make(map[int]playbackWebReturn) // Create a new map to store the results
|
||||||
// Iterate through the playbacks map and add important information to the tempResultSet map
|
// Iterate through the playbacks map and add important information to the tempResultSet map
|
||||||
for index, element := range playbacks {
|
for index, element := range playbacks {
|
||||||
@ -259,3 +302,44 @@ func handleListing(w http.ResponseWriter, r *http.Request) {
|
|||||||
fmt.Fprintf(w, string(j))
|
fmt.Fprintf(w, string(j))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleRemaining(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Rejct everything else then GET requests
|
||||||
|
if r.Method != "GET" {
|
||||||
|
http.Error(w, "Method is not supported.", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json") // Set the content type to json
|
||||||
|
var cnt, err = strconv.Atoi(r.URL.Query().Get("id")) // Retrieve the id, first convert it to an int
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"invalid id\"}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(cnt)
|
||||||
|
plyB := playbacks[cnt]
|
||||||
|
// fmt.Println(beep.SampleRate.D(plyB.Streamer.Stream().Len()))
|
||||||
|
seeker := plyB.Streamer
|
||||||
|
format := plyB.Format
|
||||||
|
n := plyB.Format.SampleRate // Streamer.Stream() // .At(beep.SampleRate.D(plyB.Streamer.Stream().Len()))
|
||||||
|
|
||||||
|
if seeker != nil {
|
||||||
|
fmt.Println(format.SampleRate)
|
||||||
|
// fmt.Println(plyB.Seeker.)
|
||||||
|
position := plyB.Format.SampleRate.D(seeker.Position())
|
||||||
|
length := plyB.Format.SampleRate.D(seeker.Len())
|
||||||
|
remaining := length - position
|
||||||
|
if remaining == 0 {
|
||||||
|
plyB.Done <- true
|
||||||
|
}
|
||||||
|
fmt.Println(position)
|
||||||
|
fmt.Fprintf(w, "{\"status\":\"ok\", \"id\":%d, \"SampleRate\":%d, \"Length\":%d, \"Position\":%d, \"Remaining\": %d, \"LengthSec\":\"%v\", \"PosSec\":\"%v\", \"RemaningSec\":\"%v\"}", cnt, n, length, position, remaining, length, position, remaining)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Seeker is nil")
|
||||||
|
fmt.Fprintf(w, "{\"status\":\"ok\", \"SampleRate\":%d}", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRoot(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, "Soundr is running.")
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user