7 Commits

Author SHA1 Message Date
3482b6952a fixed stacktrack, crashing issue 2022-10-25 18:16:23 +02:00
92a6b4b1d3 fixed small thing 2022-10-13 18:01:06 +02:00
22edd2ea9c added additional info 2022-10-13 17:58:09 +02:00
39a5d0b0cd - small fixes 2022-10-13 17:22:01 +02:00
8b198040bd - add a new remaining endpoint 2022-10-13 16:04:51 +02:00
1af34dfcf3 - add predictable / vanity ids
- add / route
2022-10-13 12:47:44 +02:00
f3e07d3f4d - add error handeling
- add loop parameter
- improve documentation
2022-05-26 17:46:03 +02:00
5 changed files with 135 additions and 18 deletions

View File

@ -21,7 +21,9 @@ This is the basic json configuration file layout:
{
"Port": 8082
}
```
``
> All sounds should be in the same bitrate. It will assume the bitrate of the first loaded sample.
# Usage
Drop your sounds into the /sounds. You can play them by sending a GET request to the /v1/play endpoint.

View File

@ -14,6 +14,11 @@ paths:
description: A base64 encoded version of the file name
schema:
type: string
- in: query
name: loop
description: Defaults to false; if true, will loop the sound until stopped
schema:
type: boolean
responses:
'200':
description: OK

View File

@ -16,6 +16,8 @@ import (
"github.com/h2non/filetype"
)
var firstLoad = true
func BufferSound(file string) bool {
_, ok := streamMap[file]
if !ok {
@ -26,9 +28,18 @@ func BufferSound(file string) bool {
}
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)
var streamer beep.StreamSeekCloser
@ -45,8 +56,10 @@ func BufferSound(file string) bool {
fmt.Println("!!!!! Unsupported file type for " + file)
return false
}
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
if firstLoad {
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
firstLoad = false
}
fmt.Println("Decoded file")
buffer := beep.NewBuffer(format)
@ -66,37 +79,45 @@ func BufferSound(file string) bool {
}
}
func PlaySound(file string, index int) int {
func PlaySound(file string, index int, loop bool) int {
playbacks[index] = playback{
File: file,
IsLoaded: false,
Streamer: nil,
Control: nil,
Loop: loop,
Format: streamMap[file].Format,
}
fmt.Println("Playing sound: " + file)
var buffer *beep.Buffer
BufferSound(file)
buffer = streamMap[file].Buffer
streamer := streamMap[file].Streamer
// streamer := streamMap[file].Streamer
fmt.Println("Trying to play sound")
shot := buffer.Streamer(0, buffer.Len())
amountOfLoops := 1
if loop {
amountOfLoops = -1
}
shot := buffer.Streamer(amountOfLoops, buffer.Len())
done := make(chan bool)
ctrl := &beep.Ctrl{Streamer: beep.Seq(shot, beep.Callback(func() {
done <- true
})), Paused: false}
ctrl := &beep.Ctrl{Streamer: shot, Paused: false}
playbacks[index] = playback{
File: file,
IsLoaded: true,
Streamer: streamer,
Streamer: shot,
Control: ctrl,
Loop: loop,
Format: streamMap[file].Format,
Done: done,
}
speaker.Play(ctrl)
<-done
fmt.Println("Finished playing sound: " + file)
delete(playbacks, index)
return 1
}

View File

@ -14,8 +14,11 @@ import (
type playback struct {
File string
IsLoaded bool
Streamer beep.Streamer
Streamer beep.StreamSeeker
Control *beep.Ctrl
Loop bool
Format beep.Format
Done chan bool
}
type playbackWebReturn struct {
@ -89,6 +92,8 @@ func main() {
http.HandleFunc("/v1/stopAll", handleStopAll)
http.HandleFunc("/v1/current", handleCurrent)
http.HandleFunc("/v1/list", handleListing)
http.HandleFunc("/v1/remaining", handleRemaining)
http.HandleFunc("/", handleRoot)
fmt.Println("Listening on port " + fmt.Sprint(configuration.Port))
log.Fatal(http.ListenAndServe(":"+fmt.Sprint(configuration.Port), nil))

View File

@ -10,6 +10,7 @@ import (
"net/http"
"os"
"strconv"
"time"
"github.com/faiface/beep/speaker"
"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
bytArr, err := base64.StdEncoding.DecodeString(cnt) // Decode the base64 string
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
if errors.Is(err, os.ErrNotExist) {
@ -54,10 +63,24 @@ func handlePlay(w http.ResponseWriter, r *http.Request) {
return
}
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
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
files, err := ioutil.ReadDir("./sounds/") // Read the directory
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
// 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())
go BufferSound(f.Name())
time.Sleep(200 * time.Millisecond) // Wait a bit to not overload the system
}
// Return the amount of files buffered
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
bytArr, err := base64.StdEncoding.DecodeString(cnt) // Decode the base64 string
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
@ -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
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
// Iterate through the playbacks map and add important information to the tempResultSet map
for index, element := range playbacks {
@ -259,3 +302,44 @@ func handleListing(w http.ResponseWriter, r *http.Request) {
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.")
}