| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- package app
- import (
- "encoding/binary"
- "math"
- "net/http"
- "strconv"
- "strings"
- "git.monoexp.com/bajdev/mpckit/internal/project"
- "git.monoexp.com/bajdev/mpckit/internal/wav"
- )
- // AudioPrefix is the URL path the preview loads samples from:
- // /audio/<slot> serves the pad's source WAV as it is, and
- // /audio/<slot>?pcm=1 serves it decoded, for formats the webview cannot
- // decode (build plan 4.5 and section 9).
- const AudioPrefix = "/audio/"
- // AudioMiddleware serves AudioPrefix and passes every other request on.
- // It is for main.go's asset server, not the frontend.
- //
- //wails:ignore
- func (s *Service) AudioMiddleware(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if !strings.HasPrefix(r.URL.Path, AudioPrefix) {
- next.ServeHTTP(w, r)
- return
- }
- s.serveAudio(w, r)
- })
- }
- func (s *Service) serveAudio(w http.ResponseWriter, r *http.Request) {
- slot, err := strconv.Atoi(strings.TrimPrefix(r.URL.Path, AudioPrefix))
- if err != nil || slot < 0 || slot >= project.Pads {
- http.NotFound(w, r)
- return
- }
- // Unlock by defer, so a panic cannot leave the lock held.
- v, src, err := func() (project.Pad, string, error) {
- s.mu.Lock()
- defer s.mu.Unlock()
- v, err := s.p.Pad(slot)
- src, _ := s.p.Source(v.File)
- return v, src, err
- }()
- if err != nil || v.Empty() || src == "" {
- http.NotFound(w, r)
- return
- }
- w.Header().Set("Cache-Control", "no-store")
- if r.URL.Query().Get("pcm") == "" {
- w.Header().Set("Content-Type", "audio/wav")
- http.ServeFile(w, r, src)
- return
- }
- // Decoded: a 12-byte header (sample rate, channels, frames as uint32
- // little-endian), then each channel's frames as float32 little-endian.
- info, chans, err := wav.DecodeFile(src)
- if err != nil {
- http.Error(w, err.Error(), http.StatusUnprocessableEntity)
- return
- }
- frames := len(chans[0])
- buf := make([]byte, 12+4*frames*len(chans))
- binary.LittleEndian.PutUint32(buf[0:], uint32(info.SampleRate))
- binary.LittleEndian.PutUint32(buf[4:], uint32(len(chans)))
- binary.LittleEndian.PutUint32(buf[8:], uint32(frames))
- off := 12
- for _, ch := range chans {
- for _, v := range ch {
- binary.LittleEndian.PutUint32(buf[off:], math.Float32bits(v))
- off += 4
- }
- }
- w.Header().Set("Content-Type", "application/octet-stream")
- w.Write(buf)
- }
|