├── .vscode └── settings.json ├── README.md ├── ac.go ├── cmd └── audioclerk │ └── main.go ├── go.mod ├── go.sum ├── testdata ├── jfk.wav ├── jfk.wav.txt ├── selected.mp3 └── selected.mp3.txt └── vendor ├── github.com ├── go-audio │ ├── audio │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── audio.go │ │ ├── conv.go │ │ ├── doc.go │ │ ├── float_buffer.go │ │ ├── formats.go │ │ ├── int_buffer.go │ │ └── pcm_buffer.go │ ├── riff │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── chunk.go │ │ ├── doc.go │ │ ├── parser.go │ │ └── riff.go │ └── wav │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── cue_chunk.go │ │ ├── decoder.go │ │ ├── encoder.go │ │ ├── list_chunk.go │ │ ├── metadata.go │ │ ├── smpl_chunk.go │ │ └── wav.go └── kardianos │ ├── task │ ├── LICENSE │ ├── README.md │ ├── action.go │ ├── command.go │ ├── fsop │ │ ├── compress.go │ │ └── copy.go │ ├── os.go │ └── start.go │ └── whisper.cpp │ ├── .gitignore │ ├── .gitmodules │ ├── CMakeLists.txt │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── doc.go │ ├── ggml.c │ ├── ggml.h │ ├── params.go │ ├── stt │ ├── consts.go │ ├── context.go │ ├── doc.go │ ├── interface.go │ └── model.go │ ├── whisper.cpp │ ├── whisper.go │ └── whisper.h └── modules.txt /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.tcc": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Audioclerk 2 | 3 | ``` 4 | bash$ export CGO_CFLAGS_ALLOW="-mfma|-mf16c" 5 | fish$ set -x CGO_CFLAGS_ALLOW "-mfma|-mf16c" 6 | $ go install github.com/kardianos/audioclerk@latest 7 | ``` -------------------------------------------------------------------------------- /ac.go: -------------------------------------------------------------------------------- 1 | package audioclerk 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "context" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "runtime" 14 | "strings" 15 | "sync/atomic" 16 | "time" 17 | 18 | "github.com/go-audio/wav" 19 | stt "github.com/kardianos/whisper.cpp/stt" 20 | ) 21 | 22 | type system struct { 23 | model stt.Model 24 | } 25 | 26 | func newSystem(ctx context.Context, modelPath string) (*system, error) { 27 | if ce := ctx.Err(); ce != nil { 28 | return nil, ce 29 | } 30 | model, err := stt.New(modelPath) 31 | if err != nil { 32 | return nil, fmt.Errorf("model load: %w", err) 33 | } 34 | if ce := ctx.Err(); ce != nil { 35 | model.Close() 36 | return nil, ce 37 | } 38 | 39 | return &system{ 40 | model: model, 41 | }, nil 42 | } 43 | 44 | func (s *system) Close() error { 45 | if s == nil { 46 | return nil 47 | } 48 | if s.model == nil { 49 | return nil 50 | } 51 | return s.model.Close() 52 | } 53 | 54 | func (s *system) Transcribe(ctx context.Context, inputPath, outputPath string) error { 55 | var data []float32 56 | 57 | mc, err := s.model.NewContext() 58 | if err != nil { 59 | return err 60 | } 61 | 62 | // Speedups appears limited after 4 threads. 63 | threadCount := runtime.NumCPU() 64 | if threadCount > 4 { 65 | threadCount = 4 66 | } 67 | mc.SetThreads(uint(threadCount)) 68 | 69 | err = mc.SetLanguage("en") 70 | if err != nil { 71 | return err 72 | } 73 | 74 | var tempFile string 75 | if true { 76 | // ffmpeg -loglevel -0 -y -i gb0.ogg -ar 16000 -ac 1 -c:a pcm_s16le gb0.wav 77 | outBuf := &bytes.Buffer{} 78 | errBuf := &bytes.Buffer{} 79 | 80 | // I'd like to just pipe from ffmpeg, but ffmpeg outputs differently if piped. 81 | f, err := os.CreateTemp("", "*.wav") 82 | if err != nil { 83 | return err 84 | } 85 | tempFile = f.Name() 86 | f.Close() 87 | 88 | cmd := exec.CommandContext(ctx, "ffmpeg", 89 | "-hide_banner", 90 | "-loglevel", "error", 91 | "-i", inputPath, 92 | "-ar", "16000", 93 | "-ac", "1", 94 | "-c:a", 95 | "pcm_s16le", 96 | "-y", 97 | "-f", "wav", 98 | tempFile, 99 | ) 100 | cmd.Stdout = outBuf 101 | cmd.Stderr = errBuf 102 | err = cmd.Run() 103 | if err != nil { 104 | if errBuf.Len() > 0 { 105 | return fmt.Errorf("ffmpeg: %w\n%s", err, errBuf.Bytes()) 106 | } 107 | return fmt.Errorf("ffmpeg: %w", err) 108 | } 109 | } 110 | if true { 111 | fh, err := os.Open(tempFile) 112 | if err != nil { 113 | return err 114 | } 115 | dec := wav.NewDecoder(fh) 116 | buf, err := dec.FullPCMBuffer() 117 | fh.Close() 118 | if err != nil { 119 | return err 120 | } 121 | 122 | frames := buf.NumFrames() 123 | if frames == 0 { 124 | return fmt.Errorf("no audio frames") 125 | } 126 | if dec.SampleRate != stt.SampleRate { 127 | return fmt.Errorf("unsupported sample rate: %d", dec.SampleRate) 128 | } 129 | if dec.NumChans != 1 { 130 | return fmt.Errorf("unsupported number of channels: %d", dec.NumChans) 131 | } 132 | 133 | data = buf.AsFloat32Buffer().Data 134 | } 135 | if len(tempFile) > 0 { 136 | os.Remove(tempFile) 137 | } 138 | 139 | if ce := ctx.Err(); ce != nil { 140 | return ce 141 | } 142 | 143 | err = mc.Process(data, nil) 144 | if err != nil { 145 | return err 146 | } 147 | 148 | if len(outputPath) == 0 { 149 | outputPath = inputPath + ".txt" 150 | } 151 | 152 | outf, err := os.Create(outputPath) 153 | if err != nil { 154 | return err 155 | } 156 | defer outf.Close() 157 | 158 | outBuf := bufio.NewWriter(outf) 159 | 160 | out := outBuf 161 | const color = false 162 | const onlyText = true 163 | 164 | for { 165 | if ce := ctx.Err(); ce != nil { 166 | return ce 167 | } 168 | segment, err := mc.NextSegment() 169 | if err != nil { 170 | if errors.Is(err, io.EOF) { 171 | break 172 | } 173 | outBuf.Flush() 174 | return err 175 | } 176 | fmt.Fprintf(out, "%10s: %s\n", segment.Start.Truncate(time.Second/10), segment.Text) 177 | } 178 | err = outBuf.Flush() 179 | if err != nil { 180 | return err 181 | } 182 | return outf.Close() 183 | } 184 | 185 | func Watch(ctx context.Context, modelPath string, dirList []string) error { 186 | for _, fn := range dirList { 187 | fi, err := os.Stat(fn) 188 | if err != nil { 189 | return fmt.Errorf("unable to watch, cannot access %q: %w", fn, err) 190 | } 191 | if !fi.IsDir() { 192 | return fmt.Errorf("path %q is not a directory", fn) 193 | } 194 | } 195 | 196 | s, err := newSystem(ctx, modelPath) 197 | if err != nil { 198 | return err 199 | } 200 | defer s.Close() 201 | 202 | const checkInterval = time.Second * 10 203 | var lastCheckTime time.Time 204 | enqueue := make(chan string, 50) 205 | queue := map[string]bool{} 206 | errorReport := make(chan error, 50) 207 | 208 | inTranscription := &atomic.Bool{} 209 | 210 | tick := time.NewTicker(time.Second * 10) 211 | defer tick.Stop() 212 | 213 | const suffix = ".txt" 214 | ext := map[string]bool{ 215 | ".mp3": true, 216 | ".mp4": true, 217 | ".ogg": true, 218 | ".m4a": true, 219 | ".wav": true, 220 | } 221 | 222 | for { 223 | select { 224 | case <-ctx.Done(): 225 | return nil 226 | case fn := <-enqueue: 227 | queue[fn] = true 228 | case err := <-errorReport: 229 | fmt.Fprintf(os.Stderr, "ERROR: %v\n", err) 230 | case now := <-tick.C: 231 | if inTranscription.Load() { 232 | continue 233 | } 234 | 235 | for fn := range queue { 236 | delete(queue, fn) 237 | 238 | go func(fn string) { 239 | if !inTranscription.CompareAndSwap(false, true) { 240 | return 241 | } 242 | defer inTranscription.Store(false) 243 | 244 | fnText := fn + suffix 245 | _, err := os.Stat(fnText) 246 | if err == nil { 247 | return 248 | } 249 | 250 | start := time.Now() 251 | fmt.Fprintf(os.Stdout, "START %s: %q...", start.Format(time.DateTime), fn) 252 | err = s.Transcribe(ctx, fn, fnText) 253 | dur := time.Since(start) 254 | 255 | if err != nil { 256 | fmt.Fprintf(os.Stdout, "ERROR (%v)\n", dur) 257 | errorReport <- fmt.Errorf("Transcribe %q: %w", fn, err) 258 | } 259 | fmt.Fprintf(os.Stdout, "DONE (%v).\n", dur) 260 | }(fn) 261 | break 262 | } 263 | if now.Sub(lastCheckTime) < checkInterval { 264 | continue 265 | } 266 | lastCheckTime = now 267 | for _, dn := range dirList { 268 | list, err := readDir(dn, ext, suffix) 269 | if err != nil { 270 | errorReport <- fmt.Errorf("read dir %q: %w", dn, err) 271 | } 272 | for _, item := range list { 273 | queue[item] = true 274 | } 275 | } 276 | } 277 | } 278 | } 279 | 280 | func readDir(dn string, allowExt map[string]bool, suffix string) ([]string, error) { 281 | d, err := os.Open(dn) 282 | if err != nil { 283 | return nil, fmt.Errorf("dir open %w", err) 284 | } 285 | defer d.Close() 286 | 287 | var list []string 288 | for { 289 | dl, err := d.ReadDir(50) 290 | if err != nil { 291 | if err == io.EOF { 292 | return list, nil 293 | } 294 | return list, err 295 | } 296 | for _, fi := range dl { 297 | if fi.IsDir() { 298 | continue 299 | } 300 | fn := filepath.Join(dn, fi.Name()) 301 | ext := strings.ToLower(filepath.Ext(fn)) 302 | if !allowExt[ext] { 303 | continue 304 | } 305 | fnText := fn + suffix 306 | if _, err := os.Stat(fnText); err == nil { 307 | continue 308 | } 309 | info, err := fi.Info() 310 | if err != nil { 311 | continue 312 | } 313 | // Ensure a file is fully copied before starting. 314 | mod := info.ModTime() 315 | if time.Since(mod) < time.Minute*10 { 316 | continue 317 | } 318 | list = append(list, fn) 319 | } 320 | } 321 | } 322 | 323 | func Transcribe(ctx context.Context, modelPath, inputPath, outputPath string) error { 324 | s, err := newSystem(ctx, modelPath) 325 | if err != nil { 326 | return err 327 | } 328 | defer s.Close() 329 | 330 | return s.Transcribe(ctx, inputPath, outputPath) 331 | } 332 | -------------------------------------------------------------------------------- /cmd/audioclerk/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "time" 10 | 11 | "github.com/kardianos/audioclerk" 12 | "github.com/kardianos/task" 13 | ) 14 | 15 | func main() { 16 | err := task.Start(context.Background(), time.Second*2, run) 17 | if err != nil { 18 | fmt.Fprintf(os.Stderr, "error: %v\n", err) 19 | os.Exit(1) 20 | } 21 | } 22 | 23 | func run(ctx context.Context) error { 24 | homeDir, err := os.UserHomeDir() 25 | if err != nil { 26 | return err 27 | } 28 | cmd := &task.Command{ 29 | Name: os.Args[0], 30 | Usage: `Transcribe a single file or watch a folder for any applicable files to translate.`, 31 | Flags: []*task.Flag{ 32 | {Name: "model", Usage: `Whisper ggml model.`, Default: filepath.Join(homeDir, ".cache/whisper.cpp/ggml-model.bin")}, 33 | }, 34 | Commands: []*task.Command{{ 35 | Name: "transcribe", 36 | Usage: `Transcribe a single file, provide the input file.`, 37 | Flags: []*task.Flag{ 38 | {Name: "i", Usage: `Input filename`, Default: ""}, 39 | {Name: "o", Usage: `Output filename, if not present, will use the input filename with a suffix appended.`, Default: ""}, 40 | }, 41 | Action: task.ActionFunc(func(ctx context.Context, st *task.State, sc task.Script) error { 42 | return audioclerk.Transcribe(ctx, st.Get("model").(string), st.Get("i").(string), st.Get("o").(string)) 43 | }), 44 | }, { 45 | Name: "watch", 46 | Usage: `Watch a folder to translate.`, 47 | Flags: []*task.Flag{ 48 | {Name: "f", Usage: `Folder name to watch, use ";" to separate out multiple paths.`, Default: ""}, 49 | }, 50 | Action: task.ActionFunc(func(ctx context.Context, st *task.State, sc task.Script) error { 51 | f := st.Get("f").(string) 52 | fl := strings.Split(f, ";") 53 | return audioclerk.Watch(ctx, st.Get("model").(string), fl) 54 | }), 55 | }}, 56 | } 57 | a := cmd.Exec(os.Args[1:]) 58 | st := task.DefaultState() 59 | return task.Run(ctx, st, a) 60 | } 61 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kardianos/audioclerk 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/go-audio/wav v1.1.0 7 | github.com/kardianos/task v0.0.0-20210112221240-c03b31243e29 8 | github.com/kardianos/whisper.cpp v1.1.4 9 | ) 10 | 11 | require ( 12 | github.com/go-audio/audio v1.0.0 // indirect 13 | github.com/go-audio/riff v1.0.0 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/go-audio/audio v1.0.0 h1:zS9vebldgbQqktK4H0lUqWrG8P0NxCJVqcj7ZpNnwd4= 3 | github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs= 4 | github.com/go-audio/riff v1.0.0 h1:d8iCGbDvox9BfLagY94fBynxSPHO80LmZCaOsmKxokA= 5 | github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498= 6 | github.com/go-audio/wav v1.1.0 h1:jQgLtbqBzY7G+BM8fXF7AHUk1uHUviWS4X39d5rsL2g= 7 | github.com/go-audio/wav v1.1.0/go.mod h1:mpe9qfwbScEbkd8uybLuIpTgHyrISw/OTuvjUW2iGtE= 8 | github.com/kardianos/task v0.0.0-20210112221240-c03b31243e29 h1:B/CQUhIw8IYyme3+PCL4+xRBmhfWrOJ5WD9rHZQr60Y= 9 | github.com/kardianos/task v0.0.0-20210112221240-c03b31243e29/go.mod h1:0ca1BtiKGUmiPLOQDzlPyCXNtBeQx9QktdnJNrGOKvA= 10 | github.com/kardianos/whisper.cpp v1.1.2 h1:jAxXWG/nb8SW9L4Bo8zaN4zeRCqWh+59wv83ridZhSM= 11 | github.com/kardianos/whisper.cpp v1.1.2/go.mod h1:Tdb2p3ED92OM59LiUfXDhMDUQM53V9mZH2Nud3FpHG4= 12 | github.com/kardianos/whisper.cpp v1.1.3 h1:F6IzGImVbLjQpZ4mI7J8aEcPVhydwEbPuay/j3DDQ1Q= 13 | github.com/kardianos/whisper.cpp v1.1.3/go.mod h1:Tdb2p3ED92OM59LiUfXDhMDUQM53V9mZH2Nud3FpHG4= 14 | github.com/kardianos/whisper.cpp v1.1.4 h1:QLX0AynVM6t5jmD+VRssKDA9ipKjk7MdwkrbqUQe62M= 15 | github.com/kardianos/whisper.cpp v1.1.4/go.mod h1:Tdb2p3ED92OM59LiUfXDhMDUQM53V9mZH2Nud3FpHG4= 16 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 17 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 18 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 19 | -------------------------------------------------------------------------------- /testdata/jfk.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kardianos/audioclerk/0a3216e8ea569c111789862ff38565559b323f78/testdata/jfk.wav -------------------------------------------------------------------------------- /testdata/jfk.wav.txt: -------------------------------------------------------------------------------- 1 | 0s: And so my fellow Americans, ask not what your country can do for you, ask what you can do for your country. 2 | -------------------------------------------------------------------------------- /testdata/selected.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kardianos/audioclerk/0a3216e8ea569c111789862ff38565559b323f78/testdata/selected.mp3 -------------------------------------------------------------------------------- /testdata/selected.mp3.txt: -------------------------------------------------------------------------------- 1 | 0s: We're going to frame this though in terms of what Klaus Schwab calls "stakeholder capitalism," 2 | 5.2s: which he's been trying to push for a number of years, where shareholders are now out. 3 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.7.6 4 | - 1.8.x 5 | - 1.9.x 6 | - 1.10.x 7 | 8 | sudo: false 9 | 10 | before_install: 11 | - go get -t -v ./... 12 | 13 | script: 14 | - go test -race -coverprofile=coverage.txt -covermode=atomic 15 | 16 | after_success: 17 | - bash <(curl -s https://codecov.io/bash) 18 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Matt Aimonetti 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/README.md: -------------------------------------------------------------------------------- 1 | # audio 2 | 3 | [![GoDoc](http://godoc.org/github.com/go-audio/audio?status.svg)](http://godoc.org/github.com/go-audio/audio) 4 | 5 | `audio` is a generic Go package designed to define a common interface to analyze 6 | and/or process audio data. 7 | 8 | At the heart of the package is the `Buffer` interface and its implementations: 9 | 10 | * `FloatBuffer` 11 | * `Float32Buffer` 12 | * `IntBuffer` 13 | 14 | Decoders, encoders, processors, analyzers and transformers can be written to 15 | accept or return these types and share a common interface. 16 | 17 | The idea is that audio libraries can define this interface or its 18 | implementations as input and return an `audio.Buffer` interface allowing all 19 | audio libraries to be chainable. 20 | 21 | ## Performance 22 | 23 | The buffer implementations are designed so a buffer can be reused and mutated 24 | avoiding allocation penalties. 25 | 26 | It is recommended to avoid using `Float32Buffer` unless performance is critical. 27 | The major drawback of using float32s is that the Go stdlib was designed to work 28 | with float64 and therefore the access to standard packages is limited. 29 | 30 | ## Usage 31 | 32 | Examples of how to use this interface is available under the 33 | [go-audio](https://github.com/go-audio) organization. 34 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/audio.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | // ErrInvalidBuffer is a generic error returned when trying to read/write to an invalid buffer. 9 | ErrInvalidBuffer = errors.New("invalid buffer") 10 | ) 11 | 12 | // Format is a high level representation of the underlying data. 13 | type Format struct { 14 | // NumChannels is the number of channels contained in the data 15 | NumChannels int 16 | // SampleRate is the sampling rate in Hz 17 | SampleRate int 18 | } 19 | 20 | // Buffer is the representation of an audio buffer. 21 | type Buffer interface { 22 | // PCMFormat is the format of buffer (describing the buffer content/format). 23 | PCMFormat() *Format 24 | // NumFrames returns the number of frames contained in the buffer. 25 | NumFrames() int 26 | // AsFloatBuffer returns a float 64 buffer from this buffer. 27 | AsFloatBuffer() *FloatBuffer 28 | // AsFloat32Buffer returns a float 32 buffer from this buffer. 29 | AsFloat32Buffer() *Float32Buffer 30 | // AsIntBuffer returns an int buffer from this buffer. 31 | AsIntBuffer() *IntBuffer 32 | // Clone creates a clean clone that can be modified without 33 | // changing the source buffer. 34 | Clone() Buffer 35 | } 36 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/conv.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import "math" 4 | 5 | // IntMaxSignedValue returns the max value of an integer 6 | // based on its memory size 7 | func IntMaxSignedValue(b int) int { 8 | switch b { 9 | case 8: 10 | return 255 / 2 11 | case 16: 12 | return 65535 / 2 13 | case 24: 14 | return 16777215 / 2 15 | case 32: 16 | return 4294967295 / 2 17 | default: 18 | return 0 19 | } 20 | } 21 | 22 | // IEEEFloatToInt converts a 10 byte IEEE float into an int. 23 | func IEEEFloatToInt(b [10]byte) int { 24 | var i uint32 25 | // Negative number 26 | if (b[0] & 0x80) == 1 { 27 | return 0 28 | } 29 | 30 | // Less than 1 31 | if b[0] <= 0x3F { 32 | return 1 33 | } 34 | 35 | // Too big 36 | if b[0] > 0x40 { 37 | return 67108864 38 | } 39 | 40 | // Still too big 41 | if b[0] == 0x40 && b[1] > 0x1C { 42 | return 800000000 43 | } 44 | 45 | i = (uint32(b[2]) << 23) | (uint32(b[3]) << 15) | (uint32(b[4]) << 7) | (uint32(b[5]) >> 1) 46 | i >>= (29 - uint32(b[1])) 47 | 48 | return int(i) 49 | } 50 | 51 | // IntToIEEEFloat converts an int into a 10 byte IEEE float. 52 | func IntToIEEEFloat(i int) [10]byte { 53 | b := [10]byte{} 54 | num := float64(i) 55 | 56 | var sign int 57 | var expon int 58 | var fMant, fsMant float64 59 | var hiMant, loMant uint 60 | 61 | if num < 0 { 62 | sign = 0x8000 63 | } else { 64 | sign = 0 65 | } 66 | 67 | if num == 0 { 68 | expon = 0 69 | hiMant = 0 70 | loMant = 0 71 | } else { 72 | fMant, expon = math.Frexp(num) 73 | if (expon > 16384) || !(fMant < 1) { /* Infinity or NaN */ 74 | expon = sign | 0x7FFF 75 | hiMant = 0 76 | loMant = 0 /* infinity */ 77 | } else { /* Finite */ 78 | expon += 16382 79 | if expon < 0 { /* denormalized */ 80 | fMant = math.Ldexp(fMant, expon) 81 | expon = 0 82 | } 83 | expon |= sign 84 | fMant = math.Ldexp(fMant, 32) 85 | fsMant = math.Floor(fMant) 86 | hiMant = uint(fsMant) 87 | fMant = math.Ldexp(fMant-fsMant, 32) 88 | fsMant = math.Floor(fMant) 89 | loMant = uint(fsMant) 90 | } 91 | } 92 | 93 | b[0] = byte(expon >> 8) 94 | b[1] = byte(expon) 95 | b[2] = byte(hiMant >> 24) 96 | b[3] = byte(hiMant >> 16) 97 | b[4] = byte(hiMant >> 8) 98 | b[5] = byte(hiMant) 99 | b[6] = byte(loMant >> 24) 100 | b[7] = byte(loMant >> 16) 101 | b[8] = byte(loMant >> 8) 102 | b[9] = byte(loMant) 103 | 104 | return b 105 | } 106 | 107 | // Uint24to32 converts a 3 byte uint23 into a uint32 108 | // BigEndian! 109 | func Uint24to32(bytes []byte) uint32 { 110 | var output uint32 111 | output |= uint32(bytes[2]) << 0 112 | output |= uint32(bytes[1]) << 8 113 | output |= uint32(bytes[0]) << 16 114 | 115 | return output 116 | } 117 | 118 | // Int24BETo32 converts an int24 value from 3 bytes into an int32 value 119 | func Int24BETo32(bytes []byte) int32 { 120 | if len(bytes) < 3 { 121 | return 0 122 | } 123 | ss := int32(0xFF&bytes[0])<<16 | int32(0xFF&bytes[1])<<8 | int32(0xFF&bytes[2]) 124 | if (ss & 0x800000) > 0 { 125 | ss |= ^0xffffff 126 | } 127 | 128 | return ss 129 | } 130 | 131 | // Int24LETo32 converts an int24 value from 3 bytes into an int32 value 132 | func Int24LETo32(bytes []byte) int32 { 133 | if len(bytes) < 3 { 134 | return 0 135 | } 136 | ss := int32(bytes[0]) | int32(bytes[1])<<8 | int32(bytes[2])<<16 137 | if (ss & 0x800000) > 0 { 138 | ss |= ^0xffffff 139 | } 140 | 141 | return ss 142 | } 143 | 144 | // Uint32toUint24Bytes converts a uint32 into a 3 byte uint24 representation 145 | func Uint32toUint24Bytes(n uint32) []byte { 146 | bytes := make([]byte, 3) 147 | bytes[0] = byte(n >> 16) 148 | bytes[1] = byte(n >> 8) 149 | bytes[2] = byte(n >> 0) 150 | 151 | return bytes 152 | } 153 | 154 | // Int32toInt24LEBytes converts an int32 into a little endian 3 byte int24 representation 155 | func Int32toInt24LEBytes(n int32) []byte { 156 | bytes := make([]byte, 3) 157 | if (n & 0x800000) > 0 { 158 | n |= ^0xffffff 159 | } 160 | bytes[2] = byte(n >> 16) 161 | bytes[1] = byte(n >> 8) 162 | bytes[0] = byte(n >> 0) 163 | return bytes 164 | } 165 | 166 | // Int32toInt24BEBytes converts an int32 into a big endian 3 byte int24 representation 167 | func Int32toInt24BEBytes(n int32) []byte { 168 | bytes := make([]byte, 3) 169 | if (n & 0x800000) > 0 { 170 | n |= ^0xffffff 171 | } 172 | bytes[0] = byte(n >> 16) 173 | bytes[1] = byte(n >> 8) 174 | bytes[2] = byte(n >> 0) 175 | 176 | return bytes 177 | } 178 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package audio defines a common 3 | interface to analyze and/or process audio data. 4 | 5 | At the heart of the package is the Buffer interface and its implementations: 6 | FloatBuffer and IntBuffer. 7 | Decoders, encoders, processors, analyzers and transformers can be written to 8 | accept or return these types and share a common interface. 9 | */ 10 | package audio 11 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/float_buffer.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | var _ Buffer = (*FloatBuffer)(nil) 4 | var _ Buffer = (*Float32Buffer)(nil) 5 | 6 | // FloatBuffer is an audio buffer with its PCM data formatted as float64. 7 | type FloatBuffer struct { 8 | // Format is the representation of the underlying data format 9 | Format *Format 10 | // Data is the buffer PCM data as floats 11 | Data []float64 12 | } 13 | 14 | // PCMFormat returns the buffer format information. 15 | func (buf *FloatBuffer) PCMFormat() *Format { return buf.Format } 16 | 17 | // AsFloatBuffer implements the Buffer interface and returns itself. 18 | func (buf *FloatBuffer) AsFloatBuffer() *FloatBuffer { return buf } 19 | 20 | // AsFloat32Buffer implements the Buffer interface and returns a float 32 version of itself. 21 | func (buf *FloatBuffer) AsFloat32Buffer() *Float32Buffer { 22 | newB := &Float32Buffer{} 23 | newB.Data = make([]float32, len(buf.Data)) 24 | for i := 0; i < len(buf.Data); i++ { 25 | newB.Data[i] = float32(buf.Data[i]) 26 | } 27 | newB.Format = &Format{ 28 | NumChannels: buf.Format.NumChannels, 29 | SampleRate: buf.Format.SampleRate, 30 | } 31 | return newB 32 | } 33 | 34 | // AsIntBuffer returns a copy of this buffer but with data truncated to Ints. 35 | func (buf *FloatBuffer) AsIntBuffer() *IntBuffer { 36 | newB := &IntBuffer{} 37 | newB.Data = make([]int, len(buf.Data)) 38 | for i := 0; i < len(buf.Data); i++ { 39 | newB.Data[i] = int(buf.Data[i]) 40 | } 41 | newB.Format = &Format{ 42 | NumChannels: buf.Format.NumChannels, 43 | SampleRate: buf.Format.SampleRate, 44 | } 45 | return newB 46 | } 47 | 48 | // Clone creates a clean clone that can be modified without 49 | // changing the source buffer. 50 | func (buf *FloatBuffer) Clone() Buffer { 51 | if buf == nil { 52 | return nil 53 | } 54 | newB := &FloatBuffer{} 55 | newB.Data = make([]float64, len(buf.Data)) 56 | copy(newB.Data, buf.Data) 57 | newB.Format = &Format{ 58 | NumChannels: buf.Format.NumChannels, 59 | SampleRate: buf.Format.SampleRate, 60 | } 61 | return newB 62 | } 63 | 64 | // NumFrames returns the number of frames contained in the buffer. 65 | func (buf *FloatBuffer) NumFrames() int { 66 | if buf == nil || buf.Format == nil { 67 | return 0 68 | } 69 | numChannels := buf.Format.NumChannels 70 | if numChannels == 0 { 71 | numChannels = 1 72 | } 73 | 74 | return len(buf.Data) / numChannels 75 | } 76 | 77 | // Float32Buffer is an audio buffer with its PCM data formatted as float32. 78 | type Float32Buffer struct { 79 | // Format is the representation of the underlying data format 80 | Format *Format 81 | // Data is the buffer PCM data as floats 82 | Data []float32 83 | // SourceBitDepth helps us know if the source was encoded on 84 | // 8, 16, 24, 32, 64 bits. 85 | SourceBitDepth int 86 | } 87 | 88 | // PCMFormat returns the buffer format information. 89 | func (buf *Float32Buffer) PCMFormat() *Format { return buf.Format } 90 | 91 | // AsFloatBuffer implements the Buffer interface and returns a float64 version of itself. 92 | func (buf *Float32Buffer) AsFloatBuffer() *FloatBuffer { 93 | newB := &FloatBuffer{} 94 | newB.Data = make([]float64, len(buf.Data)) 95 | for i := 0; i < len(buf.Data); i++ { 96 | newB.Data[i] = float64(buf.Data[i]) 97 | } 98 | newB.Format = &Format{ 99 | NumChannels: buf.Format.NumChannels, 100 | SampleRate: buf.Format.SampleRate, 101 | } 102 | return newB 103 | } 104 | 105 | // AsFloat32Buffer implements the Buffer interface and returns itself. 106 | func (buf *Float32Buffer) AsFloat32Buffer() *Float32Buffer { return buf } 107 | 108 | // AsIntBuffer returns a copy of this buffer but with data truncated to Ints. 109 | // It is usually recommended to apply a transforms when going from a 24bit source 110 | // to an int (16bit destination). Look at transforms.PCMScaleF32() for instance 111 | func (buf *Float32Buffer) AsIntBuffer() *IntBuffer { 112 | newB := &IntBuffer{SourceBitDepth: buf.SourceBitDepth} 113 | if newB.SourceBitDepth == 0 { 114 | newB.SourceBitDepth = 16 115 | } 116 | newB.Data = make([]int, len(buf.Data)) 117 | // TODO: we might want to consider checking the min/max values 118 | // and if we are in a normalized float range, apply a denormalization. 119 | for i := 0; i < len(buf.Data); i++ { 120 | newB.Data[i] = int(buf.Data[i]) 121 | } 122 | newB.Format = &Format{ 123 | NumChannels: buf.Format.NumChannels, 124 | SampleRate: buf.Format.SampleRate, 125 | } 126 | return newB 127 | } 128 | 129 | // Clone creates a clean clone that can be modified without 130 | // changing the source buffer. 131 | func (buf *Float32Buffer) Clone() Buffer { 132 | if buf == nil { 133 | return nil 134 | } 135 | newB := &Float32Buffer{} 136 | newB.Data = make([]float32, len(buf.Data)) 137 | copy(newB.Data, buf.Data) 138 | newB.Format = &Format{ 139 | NumChannels: buf.Format.NumChannels, 140 | SampleRate: buf.Format.SampleRate, 141 | } 142 | return newB 143 | } 144 | 145 | // NumFrames returns the number of frames contained in the buffer. 146 | func (buf *Float32Buffer) NumFrames() int { 147 | if buf == nil || buf.Format == nil { 148 | return 0 149 | } 150 | numChannels := buf.Format.NumChannels 151 | if numChannels == 0 { 152 | numChannels = 1 153 | } 154 | 155 | return len(buf.Data) / numChannels 156 | } 157 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/formats.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | var ( 4 | // MONO 5 | 6 | // FormatMono22500 is mono 22.5kHz format. 7 | FormatMono22500 = &Format{ 8 | NumChannels: 1, 9 | SampleRate: 22500, 10 | } 11 | // FormatMono44100 is mono 8bit 44.1kHz format. 12 | FormatMono44100 = &Format{ 13 | NumChannels: 1, 14 | SampleRate: 44100, 15 | } 16 | // FormatMono48000 is mono 48kHz format. 17 | FormatMono48000 = &Format{ 18 | NumChannels: 1, 19 | SampleRate: 48000, 20 | } 21 | // FormatMono96000 is mono 96kHz format. 22 | FormatMono96000 = &Format{ 23 | NumChannels: 1, 24 | SampleRate: 96000, 25 | } 26 | 27 | // STEREO 28 | 29 | // FormatStereo22500 is stereo 22.5kHz format. 30 | FormatStereo22500 = &Format{ 31 | NumChannels: 2, 32 | SampleRate: 22500, 33 | } 34 | // FormatStereo44100 is stereo 8bit 44.1kHz format. 35 | FormatStereo44100 = &Format{ 36 | NumChannels: 2, 37 | SampleRate: 44100, 38 | } 39 | // FormatStereo48000 is stereo 48kHz format. 40 | FormatStereo48000 = &Format{ 41 | NumChannels: 2, 42 | SampleRate: 48000, 43 | } 44 | // FormatStereo96000 is stereo 96kHz format. 45 | FormatStereo96000 = &Format{ 46 | NumChannels: 2, 47 | SampleRate: 96000, 48 | } 49 | ) 50 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/int_buffer.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import "math" 4 | 5 | var _ Buffer = (*IntBuffer)(nil) 6 | 7 | // IntBuffer is an audio buffer with its PCM data formatted as int. 8 | type IntBuffer struct { 9 | // Format is the representation of the underlying data format 10 | Format *Format 11 | // Data is the buffer PCM data as ints 12 | Data []int 13 | // SourceBitDepth helps us know if the source was encoded on 14 | // 8, 16, 24, 32, 64 bits. 15 | SourceBitDepth int 16 | } 17 | 18 | // PCMFormat returns the buffer format information. 19 | func (buf *IntBuffer) PCMFormat() *Format { return buf.Format } 20 | 21 | // AsFloatBuffer returns a copy of this buffer but with data converted to floats. 22 | func (buf *IntBuffer) AsFloatBuffer() *FloatBuffer { 23 | newB := &FloatBuffer{} 24 | newB.Data = make([]float64, len(buf.Data)) 25 | for i := 0; i < len(buf.Data); i++ { 26 | newB.Data[i] = float64(buf.Data[i]) 27 | } 28 | newB.Format = &Format{ 29 | NumChannels: buf.Format.NumChannels, 30 | SampleRate: buf.Format.SampleRate, 31 | } 32 | return newB 33 | } 34 | 35 | // AsFloat32Buffer returns a copy of this buffer but with data converted to float 32. 36 | func (buf *IntBuffer) AsFloat32Buffer() *Float32Buffer { 37 | newB := &Float32Buffer{} 38 | newB.Data = make([]float32, len(buf.Data)) 39 | max := int64(0) 40 | // try to guess the bit depths without knowing the source 41 | if buf.SourceBitDepth == 0 { 42 | for _, s := range buf.Data { 43 | if int64(s) > max { 44 | max = int64(s) 45 | } 46 | } 47 | buf.SourceBitDepth = 8 48 | if max > 127 { 49 | buf.SourceBitDepth = 16 50 | } 51 | // greater than int16, expecting int24 52 | if max > 32767 { 53 | buf.SourceBitDepth = 24 54 | } 55 | // int 32 56 | if max > 8388607 { 57 | buf.SourceBitDepth = 32 58 | } 59 | // int 64 60 | if max > 4294967295 { 61 | buf.SourceBitDepth = 64 62 | } 63 | } 64 | newB.SourceBitDepth = buf.SourceBitDepth 65 | factor := math.Pow(2, float64(buf.SourceBitDepth)-1) 66 | for i := 0; i < len(buf.Data); i++ { 67 | newB.Data[i] = float32(float64(buf.Data[i]) / factor) 68 | } 69 | newB.Format = &Format{ 70 | NumChannels: buf.Format.NumChannels, 71 | SampleRate: buf.Format.SampleRate, 72 | } 73 | return newB 74 | } 75 | 76 | // AsIntBuffer implements the Buffer interface and returns itself. 77 | func (buf *IntBuffer) AsIntBuffer() *IntBuffer { return buf } 78 | 79 | // NumFrames returns the number of frames contained in the buffer. 80 | func (buf *IntBuffer) NumFrames() int { 81 | if buf == nil || buf.Format == nil { 82 | return 0 83 | } 84 | numChannels := buf.Format.NumChannels 85 | if numChannels == 0 { 86 | numChannels = 1 87 | } 88 | 89 | return len(buf.Data) / numChannels 90 | } 91 | 92 | // Clone creates a clean clone that can be modified without 93 | // changing the source buffer. 94 | func (buf *IntBuffer) Clone() Buffer { 95 | if buf == nil { 96 | return nil 97 | } 98 | newB := &IntBuffer{} 99 | newB.Data = make([]int, len(buf.Data)) 100 | copy(newB.Data, buf.Data) 101 | newB.Format = &Format{ 102 | NumChannels: buf.Format.NumChannels, 103 | SampleRate: buf.Format.SampleRate, 104 | } 105 | return newB 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/audio/pcm_buffer.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import "math" 4 | 5 | // PCMDataFormat is an enum type to indicate the underlying data format used. 6 | type PCMDataFormat uint8 7 | 8 | const ( 9 | // DataTypeUnknown refers to an unknown format 10 | DataTypeUnknown PCMDataFormat = iota 11 | // DataTypeI8 indicates that the content of the audio buffer made of 8-bit integers. 12 | DataTypeI8 13 | // DataTypeI16 indicates that the content of the audio buffer made of 16-bit integers. 14 | DataTypeI16 15 | // DataTypeI32 indicates that the content of the audio buffer made of 32-bit integers. 16 | DataTypeI32 17 | // DataTypeF32 indicates that the content of the audio buffer made of 32-bit floats. 18 | DataTypeF32 19 | // DataTypeF64 indicates that the content of the audio buffer made of 64-bit floats. 20 | DataTypeF64 21 | ) 22 | 23 | var _ Buffer = (*PCMBuffer)(nil) 24 | 25 | // PCMBuffer encapsulates uncompressed audio data 26 | // and provides useful methods to read/manipulate this PCM data. 27 | // It's a more flexible buffer type allowing the developer to handle 28 | // different kind of buffer data formats and convert between underlying 29 | // types. 30 | type PCMBuffer struct { 31 | // Format describes the format of the buffer data. 32 | Format *Format 33 | // I8 is a store for audio sample data as integers. 34 | I8 []int8 35 | // I16 is a store for audio sample data as integers. 36 | I16 []int16 37 | // I32 is a store for audio sample data as integers. 38 | I32 []int32 39 | // F32 is a store for audio samples data as float64. 40 | F32 []float32 41 | // F64 is a store for audio samples data as float64. 42 | F64 []float64 43 | // DataType indicates the primary format used for the underlying data. 44 | // The consumer of the buffer might want to look at this value to know what store 45 | // to use to optimaly retrieve data. 46 | DataType PCMDataFormat 47 | // SourceBitDepth helps us know if the source was encoded on 48 | // 1 (int8), 2 (int16), 3(int24), 4(int32), 8(int64) bytes. 49 | SourceBitDepth uint8 50 | } 51 | 52 | // Len returns the length of the underlying data. 53 | func (b *PCMBuffer) Len() int { 54 | if b == nil { 55 | return 0 56 | } 57 | 58 | switch b.DataType { 59 | case DataTypeI8: 60 | return len(b.I8) 61 | case DataTypeI16: 62 | return len(b.I16) 63 | case DataTypeI32: 64 | return len(b.I32) 65 | case DataTypeF32: 66 | return len(b.F32) 67 | case DataTypeF64: 68 | return len(b.F64) 69 | default: 70 | return 0 71 | } 72 | } 73 | 74 | // PCMFormat returns the buffer format information. 75 | func (b *PCMBuffer) PCMFormat() *Format { 76 | if b == nil { 77 | return nil 78 | } 79 | return b.Format 80 | } 81 | 82 | // NumFrames returns the number of frames contained in the buffer. 83 | func (b *PCMBuffer) NumFrames() int { 84 | if b == nil || b.Format == nil { 85 | return 0 86 | } 87 | numChannels := b.Format.NumChannels 88 | if numChannels == 0 { 89 | numChannels = 1 90 | } 91 | 92 | return b.Len() / numChannels 93 | } 94 | 95 | // AsFloatBuffer returns a copy of this buffer but with data converted to floats. 96 | func (b *PCMBuffer) AsFloatBuffer() *FloatBuffer { 97 | newB := &FloatBuffer{} 98 | newB.Data = b.AsF64() 99 | if b.Format != nil { 100 | newB.Format = &Format{ 101 | NumChannels: b.Format.NumChannels, 102 | SampleRate: b.Format.SampleRate, 103 | } 104 | } 105 | return newB 106 | } 107 | 108 | // AsFloat32Buffer implements the Buffer interface and returns a float 32 version of itself. 109 | func (b *PCMBuffer) AsFloat32Buffer() *Float32Buffer { 110 | newB := &Float32Buffer{} 111 | newB.Data = b.AsF32() 112 | if b.Format != nil { 113 | newB.Format = &Format{ 114 | NumChannels: b.Format.NumChannels, 115 | SampleRate: b.Format.SampleRate, 116 | } 117 | } 118 | return newB 119 | } 120 | 121 | // AsIntBuffer returns a copy of this buffer but with data truncated to Ints. 122 | func (b *PCMBuffer) AsIntBuffer() *IntBuffer { 123 | newB := &IntBuffer{} 124 | newB.Data = b.AsInt() 125 | if b.Format != nil { 126 | newB.Format = &Format{ 127 | NumChannels: b.Format.NumChannels, 128 | SampleRate: b.Format.SampleRate, 129 | } 130 | } 131 | return newB 132 | } 133 | 134 | // AsI8 returns the buffer's samples as int8 sample values. 135 | // If the buffer isn't in this format, a copy is created and converted. 136 | // Note that converting might result in loss of resolution. 137 | func (b *PCMBuffer) AsI8() (out []int8) { 138 | if b == nil { 139 | return nil 140 | } 141 | switch b.DataType { 142 | case DataTypeI8: 143 | return b.I8 144 | case DataTypeI16: 145 | out = make([]int8, len(b.I16)) 146 | for i := 0; i < len(b.I16); i++ { 147 | out[i] = int8(b.I16[i]) 148 | } 149 | case DataTypeI32: 150 | out = make([]int8, len(b.I32)) 151 | for i := 0; i < len(b.I32); i++ { 152 | out[i] = int8(b.I32[i]) 153 | } 154 | case DataTypeF32: 155 | out = make([]int8, len(b.F32)) 156 | for i := 0; i < len(b.F32); i++ { 157 | out[i] = int8(b.F32[i]) 158 | } 159 | case DataTypeF64: 160 | out = make([]int8, len(b.F64)) 161 | for i := 0; i < len(b.F64); i++ { 162 | out[i] = int8(b.F64[i]) 163 | } 164 | } 165 | return out 166 | } 167 | 168 | // AsI16 returns the buffer's samples as int16 sample values. 169 | // If the buffer isn't in this format, a copy is created and converted. 170 | // Note that converting might result in loss of resolution. 171 | func (b *PCMBuffer) AsI16() (out []int16) { 172 | if b == nil { 173 | return nil 174 | } 175 | switch b.DataType { 176 | case DataTypeI8: 177 | out = make([]int16, len(b.I8)) 178 | for i := 0; i < len(b.I8); i++ { 179 | out[i] = int16(b.I8[i]) 180 | } 181 | case DataTypeI16: 182 | return b.I16 183 | case DataTypeI32: 184 | out = make([]int16, len(b.I32)) 185 | for i := 0; i < len(b.I32); i++ { 186 | out[i] = int16(b.I32[i]) 187 | } 188 | case DataTypeF32: 189 | out = make([]int16, len(b.F32)) 190 | for i := 0; i < len(b.F32); i++ { 191 | out[i] = int16(b.F32[i]) 192 | } 193 | case DataTypeF64: 194 | out = make([]int16, len(b.F64)) 195 | for i := 0; i < len(b.F64); i++ { 196 | out[i] = int16(b.F64[i]) 197 | } 198 | } 199 | return out 200 | } 201 | 202 | // AsI32 returns the buffer's samples as int32 sample values. 203 | // If the buffer isn't in this format, a copy is created and converted. 204 | // Note that converting a float to an int might result in unexpected truncations. 205 | func (b *PCMBuffer) AsI32() (out []int32) { 206 | if b == nil { 207 | return nil 208 | } 209 | switch b.DataType { 210 | case DataTypeI8: 211 | out = make([]int32, len(b.I8)) 212 | for i := 0; i < len(b.I8); i++ { 213 | out[i] = int32(b.I8[i]) 214 | } 215 | case DataTypeI16: 216 | out = make([]int32, len(b.I16)) 217 | for i := 0; i < len(b.I16); i++ { 218 | out[i] = int32(b.I16[i]) 219 | } 220 | case DataTypeI32: 221 | return b.I32 222 | case DataTypeF32: 223 | out = make([]int32, len(b.F32)) 224 | for i := 0; i < len(b.F32); i++ { 225 | out[i] = int32(b.F32[i]) 226 | } 227 | case DataTypeF64: 228 | out = make([]int32, len(b.F64)) 229 | for i := 0; i < len(b.F64); i++ { 230 | out[i] = int32(b.F64[i]) 231 | } 232 | } 233 | return out 234 | } 235 | 236 | // AsInt returns the buffer content as integers (int32s). 237 | // It's recommended to avoid this method since it creates 238 | // an extra copy of the buffer content. 239 | func (b *PCMBuffer) AsInt() (out []int) { 240 | int32s := b.AsI32() 241 | out = make([]int, len(int32s)) 242 | for i := 0; i < len(int32s); i++ { 243 | out[i] = int(int32s[i]) 244 | } 245 | return out 246 | } 247 | 248 | // AsF32 returns the buffer's samples as float32 sample values. 249 | // If the buffer isn't in this format, a copy is created and converted. 250 | // Note that converting might result in unexpected truncations. 251 | func (b *PCMBuffer) AsF32() (out []float32) { 252 | if b == nil { 253 | return nil 254 | } 255 | switch b.DataType { 256 | case DataTypeI8: 257 | bitDepth := b.calculateIntBitDepth() 258 | factor := math.Pow(2, 8*float64(bitDepth/8)-1) 259 | out = make([]float32, len(b.I8)) 260 | for i := 0; i < len(b.I8); i++ { 261 | out[i] = float32(float64(int64(b.I8[i])) / factor) 262 | } 263 | case DataTypeI16: 264 | bitDepth := b.calculateIntBitDepth() 265 | factor := math.Pow(2, 8*float64(bitDepth/8)-1) 266 | out = make([]float32, len(b.I16)) 267 | for i := 0; i < len(b.I16); i++ { 268 | out[i] = float32(float64(int64(b.I16[i])) / factor) 269 | } 270 | case DataTypeI32: 271 | bitDepth := b.calculateIntBitDepth() 272 | factor := math.Pow(2, 8*float64(bitDepth/8)-1) 273 | out = make([]float32, len(b.I16)) 274 | for i := 0; i < len(b.I16); i++ { 275 | out[i] = float32(float64(int64(b.I16[i])) / factor) 276 | } 277 | case DataTypeF32: 278 | return b.F32 279 | case DataTypeF64: 280 | out = make([]float32, len(b.F64)) 281 | for i := 0; i < len(b.F64); i++ { 282 | out[i] = float32(b.F64[i]) 283 | } 284 | } 285 | return out 286 | } 287 | 288 | // AsF64 returns the buffer's samples as float64 sample values. 289 | // If the buffer isn't in this format, a copy is created and converted. 290 | // Note that converting might result in unexpected truncations. 291 | func (b *PCMBuffer) AsF64() (out []float64) { 292 | if b == nil { 293 | return nil 294 | } 295 | switch b.DataType { 296 | case DataTypeI8: 297 | bitDepth := b.calculateIntBitDepth() 298 | factor := math.Pow(2, 8*float64(bitDepth/8)-1) 299 | out = make([]float64, len(b.I8)) 300 | for i := 0; i < len(b.I8); i++ { 301 | out[i] = float64(int64(b.I8[i])) / factor 302 | } 303 | case DataTypeI16: 304 | bitDepth := b.calculateIntBitDepth() 305 | factor := math.Pow(2, 8*float64(bitDepth/8)-1) 306 | out = make([]float64, len(b.I16)) 307 | for i := 0; i < len(b.I16); i++ { 308 | out[i] = float64(int64(b.I16[i])) / factor 309 | } 310 | case DataTypeI32: 311 | bitDepth := b.calculateIntBitDepth() 312 | factor := math.Pow(2, 8*float64(bitDepth/8)-1) 313 | out = make([]float64, len(b.I16)) 314 | for i := 0; i < len(b.I16); i++ { 315 | out[i] = float64(int64(b.I16[i])) / factor 316 | } 317 | case DataTypeF32: 318 | out = make([]float64, len(b.F32)) 319 | for i := 0; i < len(b.F32); i++ { 320 | out[i] = float64(b.F32[i]) 321 | } 322 | case DataTypeF64: 323 | return b.F64 324 | } 325 | return out 326 | } 327 | 328 | // Clone creates a clean clone that can be modified without 329 | // changing the source buffer. 330 | func (b *PCMBuffer) Clone() Buffer { 331 | if b == nil { 332 | return nil 333 | } 334 | newB := &PCMBuffer{DataType: b.DataType} 335 | switch b.DataType { 336 | case DataTypeI8: 337 | newB.I8 = make([]int8, len(b.I8)) 338 | copy(newB.I8, b.I8) 339 | case DataTypeI16: 340 | newB.I16 = make([]int16, len(b.I16)) 341 | copy(newB.I16, b.I16) 342 | case DataTypeI32: 343 | newB.I32 = make([]int32, len(b.I32)) 344 | copy(newB.I32, b.I32) 345 | case DataTypeF32: 346 | newB.F32 = make([]float32, len(b.F32)) 347 | copy(newB.F32, b.F32) 348 | case DataTypeF64: 349 | newB.F64 = make([]float64, len(b.F64)) 350 | copy(newB.F64, b.F64) 351 | } 352 | 353 | newB.Format = &Format{ 354 | NumChannels: b.Format.NumChannels, 355 | SampleRate: b.Format.SampleRate, 356 | } 357 | return newB 358 | } 359 | 360 | // SwitchPrimaryType is a convenience method to switch the primary data type. 361 | // Use this if you process/swap a different type than the original type. 362 | // Notes that conversion might be lossy if you switch to a lower resolution format. 363 | func (b *PCMBuffer) SwitchPrimaryType(t PCMDataFormat) { 364 | if b == nil || t == b.DataType { 365 | return 366 | } 367 | switch t { 368 | case DataTypeI8: 369 | b.I8 = b.AsI8() 370 | b.I16 = nil 371 | b.I32 = nil 372 | b.F32 = nil 373 | b.F64 = nil 374 | case DataTypeI16: 375 | b.I8 = nil 376 | b.I16 = b.AsI16() 377 | b.I32 = nil 378 | b.F32 = nil 379 | b.F64 = nil 380 | case DataTypeI32: 381 | b.I8 = nil 382 | b.I16 = nil 383 | b.I32 = b.AsI32() 384 | b.F32 = nil 385 | b.F64 = nil 386 | case DataTypeF32: 387 | b.I8 = nil 388 | b.I16 = nil 389 | b.I32 = nil 390 | b.F32 = b.AsF32() 391 | b.F64 = nil 392 | case DataTypeF64: 393 | b.I8 = nil 394 | b.I16 = nil 395 | b.I32 = nil 396 | b.F32 = nil 397 | b.F64 = b.AsF64() 398 | } 399 | 400 | b.DataType = t 401 | } 402 | 403 | // calculateIntBithDepth looks at the int values in the buffer and returns 404 | // the required lowest bit depth. 405 | func (b *PCMBuffer) calculateIntBitDepth() uint8 { 406 | if b == nil { 407 | return 0 408 | } 409 | bitDepth := b.SourceBitDepth 410 | if bitDepth != 0 { 411 | return bitDepth 412 | } 413 | var max int64 414 | switch b.DataType { 415 | case DataTypeI8: 416 | var i8max int8 417 | for _, s := range b.I8 { 418 | if s > i8max { 419 | i8max = s 420 | } 421 | } 422 | max = int64(i8max) 423 | case DataTypeI16: 424 | var i16max int16 425 | for _, s := range b.I16 { 426 | if s > i16max { 427 | i16max = s 428 | } 429 | } 430 | max = int64(i16max) 431 | case DataTypeI32: 432 | var i32max int32 433 | for _, s := range b.I32 { 434 | if s > i32max { 435 | i32max = s 436 | } 437 | } 438 | max = int64(i32max) 439 | default: 440 | // This method is only meant to be used on int buffers. 441 | return bitDepth 442 | } 443 | bitDepth = 8 444 | if max > 127 { 445 | bitDepth = 16 446 | } 447 | // greater than int16, expecting int24 448 | if max > 32767 { 449 | bitDepth = 24 450 | } 451 | // int 32 452 | if max > 8388607 { 453 | bitDepth = 32 454 | } 455 | // int 64 456 | if max > 4294967295 { 457 | bitDepth = 64 458 | } 459 | 460 | return bitDepth 461 | } 462 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/riff/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/settings.json 2 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/riff/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 Matt Aimonetti 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/riff/README.md: -------------------------------------------------------------------------------- 1 | # Resource Interchange File Format parser 2 | 3 | [![GoDoc](http://godoc.org/github.com/go-audio/riff?status.svg)](http://godoc.org/go-audio/riff) 4 | 5 | [![Build 6 | Status](https://travis-ci.org/go-audio/riff.png)](https://travis-ci.org/go-audio/riff) 7 | 8 | The [RIFF](https://en.wikipedia.org/wiki/Resource_Interchange_File_Format) container format is used to store chunks of data in media files, especially in wav files hence this package inside under the go-audio organization. 9 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/riff/chunk.go: -------------------------------------------------------------------------------- 1 | package riff 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "sync" 10 | ) 11 | 12 | // Chunk represents the header and containt of a sub block 13 | // See https://tech.ebu.ch/docs/tech/tech3285.pdf to see how 14 | // audio content is stored in a BWF/WAVE file. 15 | type Chunk struct { 16 | ID [4]byte 17 | Size int 18 | Pos int 19 | R io.Reader 20 | okChan chan bool 21 | Wg *sync.WaitGroup 22 | } 23 | 24 | func (ch *Chunk) DecodeWavHeader(p *Parser) error { 25 | if ch == nil { 26 | return fmt.Errorf("can't decode a nil chunk") 27 | } 28 | if ch.ID == FmtID { 29 | p.wavHeaderSize = uint32(ch.Size) 30 | if err := ch.ReadLE(&p.WavAudioFormat); err != nil { 31 | return err 32 | } 33 | if err := ch.ReadLE(&p.NumChannels); err != nil { 34 | return err 35 | } 36 | if err := ch.ReadLE(&p.SampleRate); err != nil { 37 | return err 38 | } 39 | if err := ch.ReadLE(&p.AvgBytesPerSec); err != nil { 40 | return err 41 | } 42 | if err := ch.ReadLE(&p.BlockAlign); err != nil { 43 | return err 44 | } 45 | if err := ch.ReadLE(&p.BitsPerSample); err != nil { 46 | return err 47 | } 48 | 49 | // if we aren't dealing with a PCM file, we advance to reader to the 50 | // end of the chunck. 51 | if ch.Size > 16 { 52 | extra := make([]byte, ch.Size-16) 53 | ch.ReadLE(&extra) 54 | } 55 | } 56 | return nil 57 | } 58 | 59 | // Done signals the parent parser that we are done reading the chunk 60 | // if the chunk isn't fully read, this code will do so before signaling. 61 | func (ch *Chunk) Done() { 62 | if !ch.IsFullyRead() { 63 | ch.Drain() 64 | } 65 | if ch.Wg != nil { 66 | ch.Wg.Done() 67 | } 68 | } 69 | 70 | // IsFullyRead checks if we're finished reading the chunk 71 | func (ch *Chunk) IsFullyRead() bool { 72 | if ch == nil || ch.R == nil { 73 | return true 74 | } 75 | return ch.Size <= ch.Pos 76 | } 77 | 78 | // Read implements the reader interface 79 | func (ch *Chunk) Read(p []byte) (n int, err error) { 80 | if ch == nil || ch.R == nil { 81 | return 0, errors.New("nil chunk/reader pointer") 82 | } 83 | n, err = ch.R.Read(p) 84 | ch.Pos += n 85 | return n, err 86 | } 87 | 88 | // ReadLE reads the Little Endian chunk data into the passed struct 89 | func (ch *Chunk) ReadLE(dst interface{}) error { 90 | if ch == nil || ch.R == nil { 91 | return errors.New("nil chunk/reader pointer") 92 | } 93 | if ch.IsFullyRead() { 94 | return io.EOF 95 | } 96 | ch.Pos += binary.Size(dst) 97 | return binary.Read(ch.R, binary.LittleEndian, dst) 98 | } 99 | 100 | // ReadBE reads the Big Endian chunk data into the passed struct 101 | func (ch *Chunk) ReadBE(dst interface{}) error { 102 | if ch.IsFullyRead() { 103 | return io.EOF 104 | } 105 | ch.Pos += binary.Size(dst) 106 | return binary.Read(ch.R, binary.LittleEndian, dst) 107 | } 108 | 109 | // ReadByte reads and returns a single byte 110 | func (ch *Chunk) ReadByte() (byte, error) { 111 | if ch.IsFullyRead() { 112 | return 0, io.EOF 113 | } 114 | var r byte 115 | err := ch.ReadLE(&r) 116 | return r, err 117 | } 118 | 119 | // Drain discards the rest of the chunk 120 | func (ch *Chunk) Drain() { 121 | bytesAhead := ch.Size - ch.Pos 122 | for bytesAhead > 0 { 123 | readSize := int64(bytesAhead) 124 | 125 | if _, err := io.CopyN(ioutil.Discard, ch.R, readSize); err != nil { 126 | return 127 | } 128 | bytesAhead -= int(readSize) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/riff/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Package riff is package implementing a simple Resource Interchange File Format 4 | (RIFF) parser with basic support for sub formats such as WAV. 5 | The goal of this package is to give all the tools needed for a developer 6 | to implement parse any kind of file formats using the RIFF container format. 7 | 8 | Support for PCM wav format was added so the headers are parsed, the duration and the raw sound data 9 | of a wav file can be easily accessed (See the examples below) . 10 | 11 | For more information about RIFF: 12 | https://en.wikipedia.org/wiki/Resource_Interchange_File_Format 13 | 14 | */ 15 | package riff 16 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/riff/parser.go: -------------------------------------------------------------------------------- 1 | package riff 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // Parser is a struct containing the overall container information. 13 | type Parser struct { 14 | r io.Reader 15 | // Chan is an Optional channel of chunks that is used to parse chunks 16 | Chan chan *Chunk 17 | // ChunkParserTimeout is the duration after which the main parser keeps going 18 | // if the dev hasn't reported the chunk parsing to be done. 19 | // By default: 2s 20 | ChunkParserTimeout time.Duration 21 | // The waitgroup is used to let the parser that it's ok to continue 22 | // after a chunk was passed to the optional parser channel. 23 | Wg *sync.WaitGroup 24 | 25 | // Must match RIFF 26 | ID [4]byte 27 | // This size is the size of the block 28 | // controlled by the RIFF header. Normally this equals the file size. 29 | Size uint32 30 | // Format name. 31 | // The representation of data in , and the content of the 32 | // of the ‘fmt’ chunk, depend on the format category. 33 | // 0001h => Microsoft Pulse Code Modulation (PCM) format 34 | // 0050h => MPEG-1 Audio (audio only) 35 | Format [4]byte 36 | 37 | // WAV stuff 38 | // size of the wav specific fmt header 39 | wavHeaderSize uint32 40 | // A number indicating the WAVE format category of the file. The content of the 41 | // portion of the ‘fmt’ chunk, and the interpretation of 42 | // the waveform data, depend on this value. 43 | // PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression. 44 | WavAudioFormat uint16 45 | // The number of channels represented in the waveform data: 1 for mono or 2 for stereo. 46 | // Audio: Mono = 1, Stereo = 2, etc. 47 | // The EBU has defined the Multi-channel Broadcast Wave 48 | // Format [4] where more than two channels of audio are required. 49 | NumChannels uint16 50 | // The sampling rate (in sample per second) at which each channel should be played. 51 | // 8000, 44100, etc. 52 | SampleRate uint32 53 | // The average number of bytes per second at which the waveform data should be 54 | // transferred. Playback software can estimate the buffer size using this value. 55 | // SampleRate * NumChannels * BitsPerSample/8 56 | AvgBytesPerSec uint32 57 | // BlockAlign = SignificantBitsPerSample / 8 * NumChannels 58 | // It is the number of bytes per sample slice. This value is not affected by the number of channels and can be calculated with the formula: 59 | // NumChannels * BitsPerSample/8 The number of bytes for one sample including 60 | // all channels. 61 | // The block alignment (in bytes) of the waveform data. Playback software needs 62 | // to process a multiple of bytes of data at a time, so the value of 63 | // can be used for buffer alignment. 64 | BlockAlign uint16 65 | // BitsPerSample 8, 16, 24... 66 | // Only available for PCM 67 | // This value specifies the number of bits used to define each sample. This value is usually 8, 16, 24 or 32. 68 | // If the number of bits is not byte aligned (a multiple of 8) then the number of bytes used per sample is 69 | // rounded up to the nearest byte size and the unused bytes are set to 0 and ignored. 70 | // The field specifies the number of bits of data used to represent each sample of 71 | // each channel. If there are multiple channels, the sample size is the same for each channel. 72 | BitsPerSample uint16 73 | } 74 | 75 | // ParseHeaders reads the header of the passed container and populat the container with parsed info. 76 | // Note that this code advances the container reader. 77 | func (c *Parser) ParseHeaders() error { 78 | id, size, err := c.IDnSize() 79 | if err != nil { 80 | return err 81 | } 82 | c.ID = id 83 | if c.ID != RiffID { 84 | return fmt.Errorf("%s - %s", c.ID, ErrFmtNotSupported) 85 | } 86 | c.Size = size 87 | if err := binary.Read(c.r, binary.BigEndian, &c.Format); err != nil { 88 | return err 89 | } 90 | 91 | return nil 92 | } 93 | 94 | // Duration returns the time duration for the current RIFF container 95 | // based on the sub format (wav etc...) 96 | func (c *Parser) Duration() (time.Duration, error) { 97 | if c == nil { 98 | return 0, errors.New("can't calculate the duration of a nil pointer") 99 | } 100 | if c.ID == [4]byte{} || c.AvgBytesPerSec == 0 { 101 | err := c.Parse() 102 | if err != nil { 103 | return 0, nil 104 | } 105 | } 106 | switch c.Format { 107 | case WavFormatID: 108 | return c.wavDuration() 109 | default: 110 | return 0, ErrFmtNotSupported 111 | } 112 | } 113 | 114 | // String implements the Stringer interface. 115 | func (c *Parser) String() string { 116 | out := fmt.Sprintf("Format: %s - ", c.Format) 117 | if c.Format == WavFormatID { 118 | out += fmt.Sprintf("%d channels @ %d / %d bits - ", c.NumChannels, c.SampleRate, c.BitsPerSample) 119 | d, _ := c.Duration() 120 | out += fmt.Sprintf("Duration: %f seconds", d.Seconds()) 121 | } 122 | return out 123 | } 124 | 125 | // NextChunk returns a convenient structure to parse the next chunk. 126 | // If the container is fully read, io.EOF is returned as an error. 127 | func (c *Parser) NextChunk() (*Chunk, error) { 128 | if c == nil { 129 | return nil, errors.New("can't calculate the duration of a nil pointer") 130 | } 131 | id, size, err := c.IDnSize() 132 | if err != nil { 133 | return nil, err 134 | } 135 | 136 | // all RIFF chunks (including WAVE "data" chunks) must be word aligned. 137 | // If the data uses an odd number of bytes, a padding byte with a value of zero must be placed at the end of the sample data. 138 | // The "data" chunk header's size should not include this byte. 139 | if size%2 == 1 { 140 | size++ 141 | } 142 | 143 | ch := &Chunk{ 144 | ID: id, 145 | Size: int(size), 146 | R: c.r, 147 | } 148 | return ch, nil 149 | } 150 | 151 | // IDnSize returns the next ID + block size 152 | func (c *Parser) IDnSize() ([4]byte, uint32, error) { 153 | var ID [4]byte 154 | var blockSize uint32 155 | if err := binary.Read(c.r, binary.BigEndian, &ID); err != nil { 156 | return ID, blockSize, err 157 | } 158 | if err := binary.Read(c.r, binary.LittleEndian, &blockSize); err != err { 159 | return ID, blockSize, err 160 | } 161 | return ID, blockSize, nil 162 | } 163 | 164 | // Parse parses the content of the file and populate the useful fields. 165 | // If the parser has a chan set, chunks are sent to the channel. 166 | func (p *Parser) Parse() error { 167 | if p == nil { 168 | return errors.New("can't calculate the wav duration of a nil pointer") 169 | } 170 | 171 | if p.Size == 0 { 172 | id, size, err := p.IDnSize() 173 | if err != nil { 174 | return err 175 | } 176 | p.ID = id 177 | if p.ID != RiffID { 178 | return fmt.Errorf("%s - %s", p.ID, ErrFmtNotSupported) 179 | } 180 | p.Size = size 181 | if err := binary.Read(p.r, binary.BigEndian, &p.Format); err != nil { 182 | return err 183 | } 184 | } 185 | 186 | var chunk *Chunk 187 | var err error 188 | for err == nil { 189 | chunk, err = p.NextChunk() 190 | if err != nil { 191 | break 192 | } 193 | 194 | if chunk.ID == FmtID { 195 | chunk.DecodeWavHeader(p) 196 | } else { 197 | if p.Chan != nil { 198 | if chunk.Wg == nil { 199 | chunk.Wg = p.Wg 200 | } 201 | chunk.Wg.Add(1) 202 | p.Chan <- chunk 203 | // the channel has to release otherwise the goroutine is locked 204 | chunk.Wg.Wait() 205 | } 206 | } 207 | 208 | // BFW: bext chunk described here 209 | // https://tech.ebu.ch/docs/tech/tech3285.pdf 210 | 211 | if !chunk.IsFullyRead() { 212 | chunk.Drain() 213 | } 214 | 215 | } 216 | if p.Wg != nil { 217 | p.Wg.Wait() 218 | } 219 | 220 | if p.Chan != nil { 221 | close(p.Chan) 222 | } 223 | 224 | if err == io.EOF { 225 | return nil 226 | } 227 | return err 228 | } 229 | 230 | // WavDuration returns the time duration of a wav container. 231 | func (p *Parser) wavDuration() (time.Duration, error) { 232 | if p.Size == 0 || p.AvgBytesPerSec == 0 { 233 | return 0, fmt.Errorf("can't extract the duration due to the file not properly parsed") 234 | } 235 | duration := time.Duration((float64(p.Size) / float64(p.AvgBytesPerSec)) * float64(time.Second)) 236 | return duration, nil 237 | } 238 | 239 | // jumpTo advances the reader to the amount of bytes provided 240 | func (p *Parser) jumpTo(bytesAhead int) error { 241 | var err error 242 | for bytesAhead > 0 { 243 | readSize := bytesAhead 244 | if readSize > 4000 { 245 | readSize = 4000 246 | } 247 | 248 | buf := make([]byte, readSize) 249 | err = binary.Read(p.r, binary.LittleEndian, &buf) 250 | if err != nil { 251 | return nil 252 | } 253 | bytesAhead -= readSize 254 | } 255 | return nil 256 | } 257 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/riff/riff.go: -------------------------------------------------------------------------------- 1 | package riff 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var ( 11 | RiffID = [4]byte{'R', 'I', 'F', 'F'} 12 | FmtID = [4]byte{'f', 'm', 't', ' '} 13 | // To align RIFF chunks to certain boundaries (i.e. 2048bytes for CD-ROMs) the RIFF specification includes a JUNK chunk. 14 | // Its contents are to be skipped when reading. When writing RIFFs, JUNK chunks should not have odd number as Size. 15 | junkID = [4]byte{'J', 'U', 'N', 'K'} 16 | WavFormatID = [4]byte{'W', 'A', 'V', 'E'} 17 | // DataFormatID is the Wave Data Chunk ID, it contains the digital audio sample data which can be decoded using the format 18 | // and compression method specified in the Wave Format Chunk. If the Compression Code is 1 (uncompressed PCM), then the Wave Data contains raw sample values. 19 | DataFormatID = [4]byte{'d', 'a', 't', 'a'} 20 | rmiFormatID = [4]byte{'R', 'M', 'I', 'D'} 21 | aviFormatID = [4]byte{'A', 'V', 'I', ' '} 22 | // ErrFmtNotSupported is a generic error reporting an unknown format. 23 | ErrFmtNotSupported = errors.New("format not supported") 24 | // ErrUnexpectedData is a generic error reporting that the parser encountered unexpected data. 25 | ErrUnexpectedData = errors.New("unexpected data content") 26 | ) 27 | 28 | // New creates a parser wrapper for a reader. 29 | // Note that the reader doesn't get rewinded as the container is processed. 30 | func New(r io.Reader) *Parser { 31 | return &Parser{r: r, Wg: &sync.WaitGroup{}} 32 | } 33 | 34 | // Duration returns the time duration of the passed reader if the sub format is supported. 35 | func Duration(r io.Reader) (time.Duration, error) { 36 | c := New(r) 37 | if err := c.ParseHeaders(); err != nil { 38 | return 0, err 39 | } 40 | return c.Duration() 41 | } 42 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/.gitignore: -------------------------------------------------------------------------------- 1 | coverage.txt 2 | vendor/* 3 | !vendor/modules.txt 4 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.7.6 4 | - 1.8.x 5 | - 1.9.x 6 | - 1.10.x 7 | 8 | sudo: false 9 | 10 | before_install: 11 | - go get -t -v ./... 12 | 13 | script: 14 | - go test -race -coverprofile=coverage.txt -covermode=atomic 15 | 16 | after_success: 17 | - bash <(curl -s https://codecov.io/bash) 18 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Matt Aimonetti 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/README.md: -------------------------------------------------------------------------------- 1 | # wav codec 2 | 3 | [![GoDoc](https://godoc.org/github.com/go-audio/wav?status.svg)](https://godoc.org/github.com/go-audio/wav) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/go-audio/wav)](https://goreportcard.com/report/github.com/go-audio/wav) 5 | [![Coverage Status](https://codecov.io/gh/go-audio/wav/graph/badge.svg)](https://codecov.io/gh/go-audio/wav) 6 | [![Build Status](https://travis-ci.org/go-audio/wav.svg)](https://travis-ci.org/go-audio/wav) 7 | 8 | Wav audio file encoder and decoder. See [GoDoc](https://godoc.org/github.com/go-audio/wav) for more details. 9 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/cue_chunk.go: -------------------------------------------------------------------------------- 1 | package wav 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/go-audio/riff" 9 | ) 10 | 11 | // CuePoint defines an offset which marks a noteworthy sections of the audio 12 | // content. For example, the beginning and end of a verse in a song may have cue 13 | // points to make them easier to find. 14 | type CuePoint struct { 15 | // ID is the unique identifier of the cue point 16 | ID [4]byte 17 | // Play position specifies the sample offset associated with the cue point 18 | // in terms of the sample's position in the final stream of samples 19 | // generated by the play list. If a play list chunk is 20 | // specified, the position value is equal to the sample number at which this 21 | // cue point will occur during playback of the entire play list as defined 22 | // by the play list's order. If no play list chunk is specified this value 23 | // should be 0. 24 | Position uint32 25 | // DataChunkID - This value specifies the four byte ID used by the chunk 26 | // containing the sample that corresponds to this cue point. A Wave file 27 | // with no play list is always "data". A Wave file with a play list 28 | // containing both sample data and silence may be either "data" or "slnt". 29 | DataChunkID [4]byte 30 | // ChunkStart specifies the byte offset into the Wave List Chunk of the 31 | // chunk containing the sample that corresponds to this cue point. This is 32 | // the same chunk described by the Data Chunk ID value. If no Wave List 33 | // Chunk exists in the Wave file, this value is 0. If a Wave List Chunk 34 | // exists, this is the offset into the "wavl" chunk. The first chunk in the 35 | // Wave List Chunk would be specified with a value of 0. 36 | ChunkStart uint32 37 | // BlockStart specifies the byte offset into the "data" or "slnt" Chunk to 38 | // the start of the block containing the sample. The start of a block is 39 | // defined as the first byte in uncompressed PCM wave data or the last byte 40 | // in compressed wave data where decompression can begin to find the value 41 | // of the corresponding sample value. 42 | BlockStart uint32 43 | // SampleOffset specifies an offset into the block (specified by Block 44 | // Start) for the sample that corresponds to the cue point. In uncompressed 45 | // PCM waveform data, this is simply the byte offset into the "data" chunk. 46 | // In compressed waveform data, this value is equal to the number of samples 47 | // (may or may not be bytes) from the Block Start to the sample that 48 | // corresponds to the cue point. 49 | SampleOffset uint32 50 | } 51 | 52 | // DecodeCueChunk decodes the optional cue chunk and extracts cue points. 53 | func DecodeCueChunk(d *Decoder, ch *riff.Chunk) error { 54 | if ch == nil { 55 | return fmt.Errorf("can't decode a nil chunk") 56 | } 57 | if d == nil { 58 | return fmt.Errorf("nil decoder") 59 | } 60 | if ch.ID == CIDCue { 61 | // read the entire chunk in memory 62 | buf := make([]byte, ch.Size) 63 | var err error 64 | if _, err = ch.Read(buf); err != nil { 65 | return fmt.Errorf("failed to read the CUE chunk - %v", err) 66 | } 67 | r := bytes.NewReader(buf) 68 | var nbrCues uint32 69 | if err := binary.Read(r, binary.LittleEndian, &nbrCues); err != nil { 70 | return fmt.Errorf("failed to read the number of cues - %v", err) 71 | } 72 | if nbrCues > 0 { 73 | if d.Metadata == nil { 74 | d.Metadata = &Metadata{} 75 | } 76 | d.Metadata.CuePoints = []*CuePoint{} 77 | scratch := make([]byte, 4) 78 | for i := uint32(0); i < nbrCues; i++ { 79 | c := &CuePoint{} 80 | if _, err = r.Read(scratch); err != nil { 81 | return fmt.Errorf("failed to read the cue point ID") 82 | } 83 | copy(c.ID[:], scratch[:4]) 84 | if err := binary.Read(r, binary.LittleEndian, &c.Position); err != nil { 85 | return err 86 | } 87 | if _, err = r.Read(scratch); err != nil { 88 | return fmt.Errorf("failed to read the data chunk id") 89 | } 90 | copy(c.DataChunkID[:], scratch[:4]) 91 | if err := binary.Read(r, binary.LittleEndian, &c.ChunkStart); err != nil { 92 | return err 93 | } 94 | if err := binary.Read(r, binary.LittleEndian, &c.BlockStart); err != nil { 95 | return err 96 | } 97 | if err := binary.Read(r, binary.LittleEndian, &c.SampleOffset); err != nil { 98 | return err 99 | } 100 | d.Metadata.CuePoints = append(d.Metadata.CuePoints, c) 101 | } 102 | } 103 | 104 | } 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/decoder.go: -------------------------------------------------------------------------------- 1 | package wav 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "time" 11 | 12 | "github.com/go-audio/audio" 13 | "github.com/go-audio/riff" 14 | ) 15 | 16 | var ( 17 | // CIDList is the chunk ID for a LIST chunk 18 | CIDList = [4]byte{'L', 'I', 'S', 'T'} 19 | // CIDSmpl is the chunk ID for a smpl chunk 20 | CIDSmpl = [4]byte{'s', 'm', 'p', 'l'} 21 | // CIDINFO is the chunk ID for an INFO chunk 22 | CIDInfo = []byte{'I', 'N', 'F', 'O'} 23 | // CIDCue is the chunk ID for the cue chunk 24 | CIDCue = [4]byte{'c', 'u', 'e', 0x20} 25 | ) 26 | 27 | // Decoder handles the decoding of wav files. 28 | type Decoder struct { 29 | r io.ReadSeeker 30 | parser *riff.Parser 31 | 32 | NumChans uint16 33 | BitDepth uint16 34 | SampleRate uint32 35 | 36 | AvgBytesPerSec uint32 37 | WavAudioFormat uint16 38 | 39 | err error 40 | PCMSize int 41 | pcmDataAccessed bool 42 | // pcmChunk is available so we can use the LimitReader 43 | PCMChunk *riff.Chunk 44 | // Metadata for the current file 45 | Metadata *Metadata 46 | } 47 | 48 | // NewDecoder creates a decoder for the passed wav reader. 49 | // Note that the reader doesn't get rewinded as the container is processed. 50 | func NewDecoder(r io.ReadSeeker) *Decoder { 51 | return &Decoder{ 52 | r: r, 53 | parser: riff.New(r), 54 | } 55 | } 56 | 57 | // Seek provides access to the cursor position in the PCM data 58 | func (d *Decoder) Seek(offset int64, whence int) (int64, error) { 59 | return d.r.Seek(offset, whence) 60 | } 61 | 62 | // Rewind allows the decoder to be rewound to the beginning of the PCM data. 63 | // This is useful if you want to keep on decoding the same file in a loop. 64 | func (d *Decoder) Rewind() error { 65 | 66 | _, err := d.r.Seek(0, io.SeekStart) 67 | if err != nil { 68 | return fmt.Errorf("failed to seek back to the start %w", err) 69 | } 70 | // we have to user a new parser since it's read only and can't be seeked 71 | d.parser = riff.New(d.r) 72 | d.pcmDataAccessed = false 73 | d.PCMChunk = nil 74 | d.err = nil 75 | d.NumChans = 0 76 | err = d.FwdToPCM() 77 | if err != nil { 78 | return fmt.Errorf("failed to seek to the PCM data: %w", err) 79 | } 80 | return nil 81 | } 82 | 83 | // SampleBitDepth returns the bit depth encoding of each sample. 84 | func (d *Decoder) SampleBitDepth() int32 { 85 | if d == nil { 86 | return 0 87 | } 88 | return int32(d.BitDepth) 89 | } 90 | 91 | // PCMLen returns the total number of bytes in the PCM data chunk 92 | func (d *Decoder) PCMLen() int64 { 93 | if d == nil { 94 | return 0 95 | } 96 | return int64(d.PCMSize) 97 | } 98 | 99 | // Err returns the first non-EOF error that was encountered by the Decoder. 100 | func (d *Decoder) Err() error { 101 | if d.err == io.EOF { 102 | return nil 103 | } 104 | return d.err 105 | } 106 | 107 | // EOF returns positively if the underlying reader reached the end of file. 108 | func (d *Decoder) EOF() bool { 109 | if d == nil || d.err == io.EOF { 110 | return true 111 | } 112 | return false 113 | } 114 | 115 | // IsValidFile verifies that the file is valid/readable. 116 | func (d *Decoder) IsValidFile() bool { 117 | d.err = d.readHeaders() 118 | if d.err != nil { 119 | return false 120 | } 121 | if d.NumChans < 1 { 122 | return false 123 | } 124 | if d.BitDepth < 8 { 125 | return false 126 | } 127 | if d, err := d.Duration(); err != nil || d <= 0 { 128 | return false 129 | } 130 | 131 | return true 132 | } 133 | 134 | // ReadInfo reads the underlying reader until the comm header is parsed. 135 | // This method is safe to call multiple times. 136 | func (d *Decoder) ReadInfo() { 137 | d.err = d.readHeaders() 138 | } 139 | 140 | // ReadMetadata parses the file for extra metadata such as the INFO list chunk. 141 | // The entire file will be read and should be rewinded if more data must be 142 | // accessed. 143 | func (d *Decoder) ReadMetadata() { 144 | if d.Metadata != nil { 145 | return 146 | } 147 | d.ReadInfo() 148 | if d.Err() != nil || d.Metadata != nil { 149 | return 150 | } 151 | var ( 152 | chunk *riff.Chunk 153 | err error 154 | ) 155 | for err == nil { 156 | chunk, err = d.parser.NextChunk() 157 | if err != nil { 158 | break 159 | } 160 | 161 | switch chunk.ID { 162 | case CIDList: 163 | if err = DecodeListChunk(d, chunk); err != nil { 164 | if err != io.EOF { 165 | d.err = err 166 | } 167 | } 168 | if d.Metadata != nil && d.Metadata.SamplerInfo != nil { 169 | // we got everything we were looking for 170 | break 171 | } 172 | case CIDSmpl: 173 | if err = DecodeSamplerChunk(d, chunk); err != nil { 174 | if err != io.EOF { 175 | d.err = err 176 | } 177 | } 178 | case CIDCue: 179 | if err = DecodeCueChunk(d, chunk); err != nil { 180 | if err != io.EOF { 181 | d.err = err 182 | } 183 | } 184 | default: 185 | // fmt.Println(string(chunk.ID[:])) 186 | chunk.Drain() 187 | } 188 | } 189 | 190 | } 191 | 192 | // FwdToPCM forwards the underlying reader until the start of the PCM chunk. 193 | // If the PCM chunk was already read, no data will be found (you need to rewind). 194 | func (d *Decoder) FwdToPCM() error { 195 | if d == nil { 196 | return fmt.Errorf("PCM data not found") 197 | } 198 | d.err = d.readHeaders() 199 | if d.err != nil { 200 | return nil 201 | } 202 | 203 | var chunk *riff.Chunk 204 | for d.err == nil { 205 | chunk, d.err = d.NextChunk() 206 | if d.err != nil { 207 | return d.err 208 | } 209 | if chunk.ID == riff.DataFormatID { 210 | d.PCMSize = chunk.Size 211 | d.PCMChunk = chunk 212 | break 213 | } 214 | if chunk.ID == CIDList { 215 | DecodeListChunk(d, chunk) 216 | } 217 | chunk.Drain() 218 | } 219 | if chunk == nil { 220 | return fmt.Errorf("PCM data not found") 221 | } 222 | d.pcmDataAccessed = true 223 | 224 | return nil 225 | } 226 | 227 | // WasPCMAccessed returns positively if the PCM data was previously accessed. 228 | func (d *Decoder) WasPCMAccessed() bool { 229 | if d == nil { 230 | return false 231 | } 232 | return d.pcmDataAccessed 233 | } 234 | 235 | // FullPCMBuffer is an inefficient way to access all the PCM data contained in the 236 | // audio container. The entire PCM data is held in memory. 237 | // Consider using PCMBuffer() instead. 238 | func (d *Decoder) FullPCMBuffer() (*audio.IntBuffer, error) { 239 | if !d.WasPCMAccessed() { 240 | err := d.FwdToPCM() 241 | if err != nil { 242 | return nil, d.err 243 | } 244 | } 245 | if d.PCMChunk == nil { 246 | return nil, errors.New("PCM chunk not found") 247 | } 248 | format := &audio.Format{ 249 | NumChannels: int(d.NumChans), 250 | SampleRate: int(d.SampleRate), 251 | } 252 | 253 | buf := &audio.IntBuffer{Data: make([]int, 4096), Format: format, SourceBitDepth: int(d.BitDepth)} 254 | bytesPerSample := (d.BitDepth-1)/8 + 1 255 | sampleBufData := make([]byte, bytesPerSample) 256 | decodeF, err := sampleDecodeFunc(int(d.BitDepth)) 257 | if err != nil { 258 | return nil, fmt.Errorf("could not get sample decode func %v", err) 259 | } 260 | 261 | i := 0 262 | for err == nil { 263 | buf.Data[i], err = decodeF(d.PCMChunk, sampleBufData) 264 | if err != nil { 265 | break 266 | } 267 | i++ 268 | // grow the underlying slice if needed 269 | if i == len(buf.Data) { 270 | buf.Data = append(buf.Data, make([]int, 4096)...) 271 | } 272 | } 273 | buf.Data = buf.Data[:i] 274 | 275 | if err == io.EOF { 276 | err = nil 277 | } 278 | 279 | return buf, err 280 | } 281 | 282 | // PCMBuffer populates the passed PCM buffer 283 | func (d *Decoder) PCMBuffer(buf *audio.IntBuffer) (n int, err error) { 284 | if buf == nil { 285 | return 0, nil 286 | } 287 | 288 | if !d.pcmDataAccessed { 289 | err := d.FwdToPCM() 290 | if err != nil { 291 | return 0, d.err 292 | } 293 | } 294 | if d.PCMChunk == nil { 295 | return 0, ErrPCMChunkNotFound 296 | } 297 | 298 | format := &audio.Format{ 299 | NumChannels: int(d.NumChans), 300 | SampleRate: int(d.SampleRate), 301 | } 302 | 303 | buf.SourceBitDepth = int(d.BitDepth) 304 | decodeF, err := sampleDecodeFunc(int(d.BitDepth)) 305 | if err != nil { 306 | return 0, fmt.Errorf("could not get sample decode func %v", err) 307 | } 308 | 309 | bPerSample := bytesPerSample(int(d.BitDepth)) 310 | // populate a file buffer to avoid multiple very small reads 311 | // we need to cap the buffer size to not be bigger than the pcm chunk. 312 | size := len(buf.Data) * bPerSample 313 | tmpBuf := make([]byte, size) 314 | var m int 315 | m, err = d.PCMChunk.R.Read(tmpBuf) 316 | if err != nil { 317 | if err == io.EOF { 318 | return m, nil 319 | } 320 | return m, err 321 | } 322 | if m == 0 { 323 | return m, nil 324 | } 325 | bufR := bytes.NewReader(tmpBuf[:m]) 326 | sampleBuf := make([]byte, bPerSample, bPerSample) 327 | var misaligned bool 328 | if m%bPerSample > 0 { 329 | misaligned = true 330 | } 331 | 332 | // Note that we populate the buffer even if the 333 | // size of the buffer doesn't fit an even number of frames. 334 | for n = 0; n < len(buf.Data); n++ { 335 | buf.Data[n], err = decodeF(bufR, sampleBuf) 336 | if err != nil { 337 | // the last sample isn't a full sample but just padding. 338 | if misaligned { 339 | n-- 340 | } 341 | break 342 | } 343 | } 344 | buf.Format = format 345 | if err == io.EOF { 346 | err = nil 347 | } 348 | 349 | return n, err 350 | } 351 | 352 | // Format returns the audio format of the decoded content. 353 | func (d *Decoder) Format() *audio.Format { 354 | if d == nil { 355 | return nil 356 | } 357 | return &audio.Format{ 358 | NumChannels: int(d.NumChans), 359 | SampleRate: int(d.SampleRate), 360 | } 361 | } 362 | 363 | // NextChunk returns the next available chunk 364 | func (d *Decoder) NextChunk() (*riff.Chunk, error) { 365 | if d.err = d.readHeaders(); d.err != nil { 366 | d.err = fmt.Errorf("failed to read header - %v", d.err) 367 | return nil, d.err 368 | } 369 | 370 | var ( 371 | id [4]byte 372 | size uint32 373 | ) 374 | 375 | id, size, d.err = d.parser.IDnSize() 376 | if d.err != nil { 377 | d.err = fmt.Errorf("error reading chunk header - %v", d.err) 378 | return nil, d.err 379 | } 380 | 381 | // TODO: any reason we don't use d.parser.NextChunk (riff.NextChunk) here? 382 | // It correctly handles the misaligned chunk. 383 | 384 | // TODO: copied over from riff.parser.NextChunk 385 | // all RIFF chunks (including WAVE "data" chunks) must be word aligned. 386 | // If the data uses an odd number of bytes, a padding byte with a value of zero must be placed at the end of the sample data. 387 | // The "data" chunk header's size should not include this byte. 388 | if size%2 == 1 { 389 | size++ 390 | } 391 | 392 | c := &riff.Chunk{ 393 | ID: id, 394 | Size: int(size), 395 | R: io.LimitReader(d.r, int64(size)), 396 | } 397 | return c, d.err 398 | } 399 | 400 | // Duration returns the time duration for the current audio container 401 | func (d *Decoder) Duration() (time.Duration, error) { 402 | if d == nil || d.parser == nil { 403 | return 0, errors.New("can't calculate the duration of a nil pointer") 404 | } 405 | return d.parser.Duration() 406 | } 407 | 408 | // String implements the Stringer interface. 409 | func (d *Decoder) String() string { 410 | return d.parser.String() 411 | } 412 | 413 | // readHeaders is safe to call multiple times 414 | func (d *Decoder) readHeaders() error { 415 | if d == nil || d.NumChans > 0 { 416 | return nil 417 | } 418 | 419 | id, size, err := d.parser.IDnSize() 420 | if err != nil { 421 | return err 422 | } 423 | d.parser.ID = id 424 | if d.parser.ID != riff.RiffID { 425 | return fmt.Errorf("%s - %s", d.parser.ID, riff.ErrFmtNotSupported) 426 | } 427 | d.parser.Size = size 428 | if err := binary.Read(d.r, binary.BigEndian, &d.parser.Format); err != nil { 429 | return err 430 | } 431 | 432 | var chunk *riff.Chunk 433 | var rewindBytes int64 434 | 435 | for err == nil { 436 | chunk, err = d.parser.NextChunk() 437 | if err != nil { 438 | break 439 | } 440 | 441 | if chunk.ID == riff.FmtID { 442 | chunk.DecodeWavHeader(d.parser) 443 | d.NumChans = d.parser.NumChannels 444 | d.BitDepth = d.parser.BitsPerSample 445 | d.SampleRate = d.parser.SampleRate 446 | d.WavAudioFormat = d.parser.WavAudioFormat 447 | d.AvgBytesPerSec = d.parser.AvgBytesPerSec 448 | 449 | if rewindBytes > 0 { 450 | d.r.Seek(-(rewindBytes + int64(chunk.Size) + 8), 1) 451 | } 452 | break 453 | } else if chunk.ID == CIDList { 454 | // The list chunk can be in the header or footer 455 | // because so many players don't support that chunk properly 456 | // it is recommended to have it at the end of the file. 457 | DecodeListChunk(d, chunk) 458 | // unexpected chunk order, might be a bext chunk 459 | rewindBytes += int64(chunk.Size) + 8 460 | } else if chunk.ID == CIDSmpl { 461 | DecodeSamplerChunk(d, chunk) 462 | rewindBytes += int64(chunk.Size) + 8 463 | } else { 464 | // unexpected chunk order, might be a bext chunk 465 | rewindBytes += int64(chunk.Size) + 8 466 | // drain the chunk 467 | io.CopyN(ioutil.Discard, d.r, int64(chunk.Size)) 468 | } 469 | } 470 | 471 | return d.err 472 | } 473 | 474 | func bytesPerSample(bitDepth int) int { 475 | return bitDepth / 8 476 | } 477 | 478 | // sampleDecodeFunc returns a function that can be used to convert 479 | // a byte range into an int value based on the amount of bits used per sample. 480 | // Note that 8bit samples are unsigned, all other values are signed. 481 | func sampleDecodeFunc(bitsPerSample int) (func(io.Reader, []byte) (int, error), error) { 482 | // NOTE: WAV PCM data is stored using little-endian 483 | switch bitsPerSample { 484 | case 8: 485 | // 8bit values are unsigned 486 | return func(r io.Reader, buf []byte) (int, error) { 487 | _, err := r.Read(buf[:1]) 488 | return int(buf[0]), err 489 | }, nil 490 | case 16: 491 | return func(r io.Reader, buf []byte) (int, error) { 492 | _, err := r.Read(buf[:2]) 493 | return int(int16(binary.LittleEndian.Uint16(buf[:2]))), err 494 | }, nil 495 | case 24: 496 | // -34,359,738,367 (0x7FFFFF) to 34,359,738,368 (0x800000) 497 | return func(r io.Reader, buf []byte) (int, error) { 498 | _, err := r.Read(buf[:3]) 499 | if err != nil { 500 | return 0, err 501 | } 502 | return int(audio.Int24LETo32(buf[:3])), nil 503 | }, nil 504 | case 32: 505 | return func(r io.Reader, buf []byte) (int, error) { 506 | _, err := r.Read(buf[:4]) 507 | return int(int32(binary.LittleEndian.Uint32(buf[:4]))), err 508 | }, nil 509 | default: 510 | return nil, fmt.Errorf("unhandled byte depth:%d", bitsPerSample) 511 | } 512 | } 513 | 514 | // sampleDecodeFloat64Func returns a function that can be used to convert 515 | // a byte range into a float64 value based on the amount of bits used per sample. 516 | func sampleFloat64DecodeFunc(bitsPerSample int) (func([]byte) float64, error) { 517 | bytesPerSample := bitsPerSample / 8 518 | switch bytesPerSample { 519 | case 1: 520 | // 8bit values are unsigned 521 | return func(s []byte) float64 { 522 | return float64(uint8(s[0])) 523 | }, nil 524 | case 2: 525 | return func(s []byte) float64 { 526 | return float64(int(s[0]) + int(s[1])<<8) 527 | }, nil 528 | case 3: 529 | return func(s []byte) float64 { 530 | var output int32 531 | output |= int32(s[2]) << 0 532 | output |= int32(s[1]) << 8 533 | output |= int32(s[0]) << 16 534 | return float64(output) 535 | }, nil 536 | case 4: 537 | // TODO: fix the float64 conversion (current int implementation) 538 | return func(s []byte) float64 { 539 | return float64(int(s[0]) + int(s[1])<<8 + int(s[2])<<16 + int(s[3])<<24) 540 | }, nil 541 | default: 542 | return nil, fmt.Errorf("unhandled byte depth:%d", bitsPerSample) 543 | } 544 | } 545 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/encoder.go: -------------------------------------------------------------------------------- 1 | package wav 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "os" 10 | "time" 11 | 12 | "github.com/go-audio/audio" 13 | "github.com/go-audio/riff" 14 | ) 15 | 16 | // Encoder encodes LPCM data into a wav containter. 17 | type Encoder struct { 18 | w io.WriteSeeker 19 | buf *bytes.Buffer 20 | 21 | SampleRate int 22 | BitDepth int 23 | NumChans int 24 | 25 | // A number indicating the WAVE format category of the file. The content of 26 | // the portion of the ‘fmt’ chunk, and the 27 | // interpretation of the waveform data, depend on this value. PCM = 1 (i.e. 28 | // Linear quantization) Values other than 1 indicate some form of 29 | // compression. 30 | WavAudioFormat int 31 | 32 | // Metadata contains metadata to inject in the file. 33 | Metadata *Metadata 34 | 35 | WrittenBytes int 36 | frames int 37 | pcmChunkStarted bool 38 | pcmChunkSizePos int 39 | wroteHeader bool // true if we've written the header out 40 | } 41 | 42 | // NewEncoder creates a new encoder to create a new wav file. 43 | // Don't forget to add Frames to the encoder before writing. 44 | func NewEncoder(w io.WriteSeeker, sampleRate, bitDepth, numChans, audioFormat int) *Encoder { 45 | return &Encoder{ 46 | w: w, 47 | buf: bytes.NewBuffer(make([]byte, 0, bytesNumFromDuration(time.Minute, sampleRate, bitDepth)*numChans)), 48 | SampleRate: sampleRate, 49 | BitDepth: bitDepth, 50 | NumChans: numChans, 51 | WavAudioFormat: audioFormat, 52 | } 53 | } 54 | 55 | // AddLE serializes and adds the passed value using little endian 56 | func (e *Encoder) AddLE(src interface{}) error { 57 | e.WrittenBytes += binary.Size(src) 58 | return binary.Write(e.w, binary.LittleEndian, src) 59 | } 60 | 61 | // AddBE serializes and adds the passed value using big endian 62 | func (e *Encoder) AddBE(src interface{}) error { 63 | e.WrittenBytes += binary.Size(src) 64 | return binary.Write(e.w, binary.BigEndian, src) 65 | } 66 | 67 | func (e *Encoder) addBuffer(buf *audio.IntBuffer) error { 68 | if buf == nil { 69 | return fmt.Errorf("can't add a nil buffer") 70 | } 71 | 72 | frameCount := buf.NumFrames() 73 | // performance tweak: setup a buffer so we don't do too many writes 74 | var err error 75 | for i := 0; i < frameCount; i++ { 76 | for j := 0; j < buf.Format.NumChannels; j++ { 77 | v := buf.Data[i*buf.Format.NumChannels+j] 78 | switch e.BitDepth { 79 | case 8: 80 | if err = binary.Write(e.buf, binary.LittleEndian, uint8(v)); err != nil { 81 | return err 82 | } 83 | case 16: 84 | if err = binary.Write(e.buf, binary.LittleEndian, int16(v)); err != nil { 85 | return err 86 | } 87 | case 24: 88 | if err = binary.Write(e.buf, binary.LittleEndian, audio.Int32toInt24LEBytes(int32(v))); err != nil { 89 | return err 90 | } 91 | case 32: 92 | if err = binary.Write(e.buf, binary.LittleEndian, int32(v)); err != nil { 93 | return err 94 | } 95 | default: 96 | return fmt.Errorf("can't add frames of bit size %d", e.BitDepth) 97 | } 98 | } 99 | e.frames++ 100 | } 101 | if n, err := e.w.Write(e.buf.Bytes()); err != nil { 102 | e.WrittenBytes += n 103 | return err 104 | } 105 | e.WrittenBytes += e.buf.Len() 106 | e.buf.Reset() 107 | 108 | return nil 109 | } 110 | 111 | func (e *Encoder) writeHeader() error { 112 | if e.wroteHeader { 113 | return errors.New("already wrote header") 114 | } 115 | e.wroteHeader = true 116 | if e == nil { 117 | return fmt.Errorf("can't write a nil encoder") 118 | } 119 | if e.w == nil { 120 | return fmt.Errorf("can't write to a nil writer") 121 | } 122 | 123 | if e.WrittenBytes > 0 { 124 | return nil 125 | } 126 | 127 | // riff ID 128 | if err := e.AddLE(riff.RiffID); err != nil { 129 | return err 130 | } 131 | // file size uint32, to update later on. 132 | if err := e.AddLE(uint32(42)); err != nil { 133 | return err 134 | } 135 | // wave headers 136 | if err := e.AddLE(riff.WavFormatID); err != nil { 137 | return err 138 | } 139 | // form 140 | if err := e.AddLE(riff.FmtID); err != nil { 141 | return err 142 | } 143 | // chunk size 144 | if err := e.AddLE(uint32(16)); err != nil { 145 | return err 146 | } 147 | // wave format 148 | if err := e.AddLE(uint16(e.WavAudioFormat)); err != nil { 149 | return err 150 | } 151 | // num channels 152 | if err := e.AddLE(uint16(e.NumChans)); err != nil { 153 | return fmt.Errorf("error encoding the number of channels - %v", err) 154 | } 155 | // samplerate 156 | if err := e.AddLE(uint32(e.SampleRate)); err != nil { 157 | return fmt.Errorf("error encoding the sample rate - %v", err) 158 | } 159 | blockAlign := e.NumChans * e.BitDepth / 8 160 | // avg bytes per sec 161 | if err := e.AddLE(uint32(e.SampleRate * blockAlign)); err != nil { 162 | return fmt.Errorf("error encoding the avg bytes per sec - %v", err) 163 | } 164 | // block align 165 | if err := e.AddLE(uint16(blockAlign)); err != nil { 166 | return err 167 | } 168 | // bits per sample 169 | if err := e.AddLE(uint16(e.BitDepth)); err != nil { 170 | return fmt.Errorf("error encoding bits per sample - %v", err) 171 | } 172 | 173 | return nil 174 | } 175 | 176 | // Write encodes and writes the passed buffer to the underlying writer. 177 | // Don't forget to Close() the encoder or the file won't be valid. 178 | func (e *Encoder) Write(buf *audio.IntBuffer) error { 179 | if !e.wroteHeader { 180 | if err := e.writeHeader(); err != nil { 181 | return err 182 | } 183 | } 184 | 185 | if !e.pcmChunkStarted { 186 | // sound header 187 | if err := e.AddLE(riff.DataFormatID); err != nil { 188 | return fmt.Errorf("error encoding sound header %v", err) 189 | } 190 | e.pcmChunkStarted = true 191 | 192 | // write a temporary chunksize 193 | e.pcmChunkSizePos = e.WrittenBytes 194 | if err := e.AddLE(uint32(42)); err != nil { 195 | return fmt.Errorf("%v when writing wav data chunk size header", err) 196 | } 197 | } 198 | 199 | return e.addBuffer(buf) 200 | } 201 | 202 | // WriteFrame writes a single frame of data to the underlying writer. 203 | func (e *Encoder) WriteFrame(value interface{}) error { 204 | if !e.wroteHeader { 205 | e.writeHeader() 206 | } 207 | if !e.pcmChunkStarted { 208 | // sound header 209 | if err := e.AddLE(riff.DataFormatID); err != nil { 210 | return fmt.Errorf("error encoding sound header %v", err) 211 | } 212 | e.pcmChunkStarted = true 213 | 214 | // write a temporary chunksize 215 | e.pcmChunkSizePos = e.WrittenBytes 216 | if err := e.AddLE(uint32(42)); err != nil { 217 | return fmt.Errorf("%v when writing wav data chunk size header", err) 218 | } 219 | } 220 | 221 | e.frames++ 222 | return e.AddLE(value) 223 | } 224 | 225 | func (e *Encoder) writeMetadata() error { 226 | chunkData := encodeInfoChunk(e) 227 | if err := e.AddBE(CIDList); err != nil { 228 | return fmt.Errorf("failed to write the LIST chunk ID: %s", err) 229 | } 230 | if err := e.AddLE(uint32(len(chunkData))); err != nil { 231 | return fmt.Errorf("failed to write the LIST chunk size: %s", err) 232 | } 233 | return e.AddBE(chunkData) 234 | } 235 | 236 | // Close flushes the content to disk, make sure the headers are up to date 237 | // Note that the underlying writer is NOT being closed. 238 | func (e *Encoder) Close() error { 239 | if e == nil || e.w == nil { 240 | return nil 241 | } 242 | 243 | // inject metadata at the end to not trip implementation not supporting 244 | // metadata chunks 245 | if e.Metadata != nil { 246 | if err := e.writeMetadata(); err != nil { 247 | return fmt.Errorf("failed to write metadata - %v", err) 248 | } 249 | } 250 | 251 | // go back and write total size in header 252 | if _, err := e.w.Seek(4, 0); err != nil { 253 | return err 254 | } 255 | if err := e.AddLE(uint32(e.WrittenBytes) - 8); err != nil { 256 | return fmt.Errorf("%v when writing the total written bytes", err) 257 | } 258 | 259 | // rewrite the audio chunk length header 260 | if e.pcmChunkSizePos > 0 { 261 | if _, err := e.w.Seek(int64(e.pcmChunkSizePos), 0); err != nil { 262 | return err 263 | } 264 | chunksize := uint32((int(e.BitDepth) / 8) * int(e.NumChans) * e.frames) 265 | if err := e.AddLE(uint32(chunksize)); err != nil { 266 | return fmt.Errorf("%v when writing wav data chunk size header", err) 267 | } 268 | } 269 | 270 | // jump back to the end of the file. 271 | if _, err := e.w.Seek(0, 2); err != nil { 272 | return err 273 | } 274 | switch e.w.(type) { 275 | case *os.File: 276 | return e.w.(*os.File).Sync() 277 | } 278 | return nil 279 | } 280 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/list_chunk.go: -------------------------------------------------------------------------------- 1 | package wav 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "io" 8 | 9 | "github.com/go-audio/riff" 10 | ) 11 | 12 | var ( 13 | // See http://bwfmetaedit.sourceforge.net/listinfo.html 14 | markerIART = [4]byte{'I', 'A', 'R', 'T'} 15 | markerISFT = [4]byte{'I', 'S', 'F', 'T'} 16 | markerICRD = [4]byte{'I', 'C', 'R', 'D'} 17 | markerICOP = [4]byte{'I', 'C', 'O', 'P'} 18 | markerIARL = [4]byte{'I', 'A', 'R', 'L'} 19 | markerINAM = [4]byte{'I', 'N', 'A', 'M'} 20 | markerIENG = [4]byte{'I', 'E', 'N', 'G'} 21 | markerIGNR = [4]byte{'I', 'G', 'N', 'R'} 22 | markerIPRD = [4]byte{'I', 'P', 'R', 'D'} 23 | markerISRC = [4]byte{'I', 'S', 'R', 'C'} 24 | markerISBJ = [4]byte{'I', 'S', 'B', 'J'} 25 | markerICMT = [4]byte{'I', 'C', 'M', 'T'} 26 | markerITRK = [4]byte{'I', 'T', 'R', 'K'} 27 | markerITRKBug = [4]byte{'i', 't', 'r', 'k'} 28 | markerITCH = [4]byte{'I', 'T', 'C', 'H'} 29 | markerIKEY = [4]byte{'I', 'K', 'E', 'Y'} 30 | markerIMED = [4]byte{'I', 'M', 'E', 'D'} 31 | ) 32 | 33 | // DecodeListChunk decodes a LIST chunk 34 | func DecodeListChunk(d *Decoder, ch *riff.Chunk) error { 35 | if ch == nil { 36 | return fmt.Errorf("can't decode a nil chunk") 37 | } 38 | if d == nil { 39 | return fmt.Errorf("nil decoder") 40 | } 41 | if ch.ID == CIDList { 42 | // read the entire chunk in memory 43 | buf := make([]byte, ch.Size) 44 | var err error 45 | if _, err = ch.Read(buf); err != nil { 46 | return fmt.Errorf("failed to read the LIST chunk - %v", err) 47 | } 48 | r := bytes.NewReader(buf) 49 | // INFO subchunk 50 | scratch := make([]byte, 4) 51 | if _, err = r.Read(scratch); err != nil { 52 | return fmt.Errorf("failed to read the INFO subchunk - %v", err) 53 | } 54 | if !bytes.Equal(scratch, CIDInfo[:]) { 55 | // "expected an INFO subchunk but got %s", string(scratch) 56 | // TODO: support adtl subchunks 57 | ch.Drain() 58 | return nil 59 | } 60 | if d.Metadata == nil { 61 | d.Metadata = &Metadata{} 62 | } 63 | 64 | // the rest is a list of string entries 65 | var ( 66 | id [4]byte 67 | size uint32 68 | ) 69 | readSubHeader := func() error { 70 | if err := binary.Read(r, binary.BigEndian, &id); err != nil { 71 | return err 72 | } 73 | return binary.Read(r, binary.LittleEndian, &size) 74 | } 75 | 76 | for err == nil { 77 | err = readSubHeader() 78 | if err != nil { 79 | break 80 | } 81 | scratch = make([]byte, size) 82 | r.Read(scratch) 83 | switch id { 84 | case markerIARL: 85 | d.Metadata.Location = nullTermStr(scratch) 86 | case markerIART: 87 | d.Metadata.Artist = nullTermStr(scratch) 88 | case markerISFT: 89 | d.Metadata.Software = nullTermStr(scratch) 90 | case markerICRD: 91 | d.Metadata.CreationDate = nullTermStr(scratch) 92 | case markerICOP: 93 | d.Metadata.Copyright = nullTermStr(scratch) 94 | case markerINAM: 95 | d.Metadata.Title = nullTermStr(scratch) 96 | case markerIENG: 97 | d.Metadata.Engineer = nullTermStr(scratch) 98 | case markerIGNR: 99 | d.Metadata.Genre = nullTermStr(scratch) 100 | case markerIPRD: 101 | d.Metadata.Product = nullTermStr(scratch) 102 | case markerISRC: 103 | d.Metadata.Source = nullTermStr(scratch) 104 | case markerISBJ: 105 | d.Metadata.Subject = nullTermStr(scratch) 106 | case markerICMT: 107 | d.Metadata.Comments = nullTermStr(scratch) 108 | case markerITRK, markerITRKBug: 109 | d.Metadata.TrackNbr = nullTermStr(scratch) 110 | case markerITCH: 111 | d.Metadata.Technician = nullTermStr(scratch) 112 | case markerIKEY: 113 | d.Metadata.Keywords = nullTermStr(scratch) 114 | case markerIMED: 115 | d.Metadata.Medium = nullTermStr(scratch) 116 | } 117 | 118 | if size%2 == 1 { 119 | r.Seek(1, io.SeekCurrent) 120 | } 121 | } 122 | } 123 | ch.Drain() 124 | return nil 125 | } 126 | 127 | func encodeInfoChunk(e *Encoder) []byte { 128 | if e == nil || e.Metadata == nil { 129 | return nil 130 | } 131 | buf := bytes.NewBuffer(nil) 132 | 133 | writeSection := func(id [4]byte, val string) { 134 | buf.Write(id[:]) 135 | binary.Write(buf, binary.LittleEndian, uint32(len(val)+1)) 136 | buf.Write(append([]byte(val), 0x00)) 137 | } 138 | if e.Metadata.Artist != "" { 139 | writeSection(markerIART, e.Metadata.Artist) 140 | } 141 | if e.Metadata.Comments != "" { 142 | writeSection(markerICMT, e.Metadata.Comments) 143 | } 144 | if e.Metadata.Copyright != "" { 145 | writeSection(markerICOP, e.Metadata.Copyright) 146 | } 147 | if e.Metadata.CreationDate != "" { 148 | writeSection(markerICRD, e.Metadata.CreationDate) 149 | } 150 | if e.Metadata.Engineer != "" { 151 | writeSection(markerIENG, e.Metadata.Engineer) 152 | } 153 | if e.Metadata.Technician != "" { 154 | writeSection(markerITCH, e.Metadata.Technician) 155 | } 156 | if e.Metadata.Genre != "" { 157 | writeSection(markerIGNR, e.Metadata.Genre) 158 | } 159 | if e.Metadata.Keywords != "" { 160 | writeSection(markerIKEY, e.Metadata.Keywords) 161 | } 162 | if e.Metadata.Medium != "" { 163 | writeSection(markerIMED, e.Metadata.Medium) 164 | } 165 | if e.Metadata.Title != "" { 166 | writeSection(markerINAM, e.Metadata.Title) 167 | } 168 | if e.Metadata.Product != "" { 169 | writeSection(markerIPRD, e.Metadata.Product) 170 | } 171 | if e.Metadata.Subject != "" { 172 | writeSection(markerISBJ, e.Metadata.Subject) 173 | } 174 | if e.Metadata.Software != "" { 175 | writeSection(markerISFT, e.Metadata.Software) 176 | } 177 | if e.Metadata.Source != "" { 178 | writeSection(markerISRC, e.Metadata.Source) 179 | } 180 | if e.Metadata.Location != "" { 181 | writeSection(markerIARL, e.Metadata.Location) 182 | } 183 | if e.Metadata.TrackNbr != "" { 184 | writeSection(markerITRK, e.Metadata.TrackNbr) 185 | } 186 | 187 | return append(CIDInfo, buf.Bytes()...) 188 | } 189 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/metadata.go: -------------------------------------------------------------------------------- 1 | package wav 2 | 3 | // Metadata represents optional metadata added to the wav file. 4 | type Metadata struct { 5 | SamplerInfo *SamplerInfo 6 | // Artist of the original subject of the file. For example, Michaelangelo. 7 | Artist string 8 | // Comments provides general comments about the file or the subject of the 9 | // file. If the comment is several sentences long, end each sentence with a 10 | // period. Do not include newline characters. 11 | Comments string 12 | // Copyright records the copyright information for the file. 13 | Copyright string 14 | // CreationDate specifies the date the subject of the file was created. List 15 | // dates in year-month-day format, padding one-digit months and days with a 16 | // zero on the left. For example: 1553-05-03 for May 3, 1553. The year 17 | // should always be given using four digits. 18 | CreationDate string 19 | // Engineer stores the name of the engineer who worked on the file. If there 20 | // are multiple engineers, separate the names by a semicolon and a blank. 21 | // For example: Smith, John; Adams, Joe. 22 | Engineer string 23 | // Technician identifies the technician who sampled the subject file. For 24 | // example: Smith, John. 25 | Technician string 26 | // Genre describes the original work, such as jazz, classical, rock, etc. 27 | Genre string 28 | // Keywords provides a list of keywords that refer to the file or subject of 29 | // the file. Separate multiple keywords with a semicolon and a blank. For 30 | // example, Seattle; zoology; The Civil War. 31 | Keywords string 32 | // Medium describes the original subject of the file, such as record, CD and so forth. 33 | Medium string 34 | // Title stores the title of the subject of the file, such as bohemian rhapsody. 35 | Title string 36 | // Product AKA album specifies the name of the title the file was originally 37 | // intended for: A Night at the Opera 38 | Product string 39 | // Subject describes the contents of the file, such as Metadata Management. 40 | Subject string 41 | // Software identifies the name of the software package used to create the 42 | // file, such as go-audio. 43 | Software string 44 | // Source identifies the name of the person or organization who supplied the 45 | // original subject of the file. For example: Splice. 46 | Source string 47 | // Location or Archival Location - Indicates where the subject of the file is archived. 48 | Location string 49 | // TrackNbr is the track number 50 | TrackNbr string 51 | // CuePoints is a list of cue points in the wav file. 52 | CuePoints []*CuePoint 53 | } 54 | 55 | // SamplerInfo is extra metadata pertinent to a sampler type usage. 56 | type SamplerInfo struct { 57 | // Manufacturer field specifies the MIDI Manufacturer's Association 58 | // (MMA) Manufacturer code for the sampler intended to receive this file's 59 | // waveform. Each manufacturer of a MIDI product is assigned a unique ID 60 | // which identifies the company. If no particular manufacturer is to be 61 | // specified, a value of 0 should be used. The value is stored with some 62 | // extra information to enable translation to the value used in a MIDI 63 | // System Exclusive transmission to the sampler. The high byte indicates the 64 | // number of low order bytes (1 or 3) that are valid for the manufacturer 65 | // code. For example, the value for Digidesign will be 0x01000013 (0x13) and 66 | // the value for Microsoft will be 0x30000041 (0x00, 0x00, 0x41). 67 | Manufacturer [4]byte 68 | // Product field specifies the MIDI model ID defined by the manufacturer 69 | // corresponding to the Manufacturer field. Contact the manufacturer of the 70 | // sampler to get the model ID. If no particular manufacturer's product is 71 | // to be specified, a value of 0 should be used. 72 | Product [4]byte 73 | // SamplePeriod The sample period specifies the duration of time that passes 74 | // during the playback of one sample in nanoseconds (normally equal to 1 / 75 | // Samplers Per Second, where Samples Per Second is the value found in the 76 | // format chunk). 77 | SamplePeriod uint32 78 | // MIDIUnityNote The MIDI unity note value has the same meaning as the instrument chunk's 79 | // MIDI Unshifted Note field which specifies the musical note at which the 80 | // sample will be played at it's original sample rate (the sample rate 81 | // specified in the format chunk). 82 | MIDIUnityNote uint32 83 | // MIDIPitchFraction The MIDI pitch fraction specifies the fraction of a 84 | // semitone up from the specified MIDI unity note field. A value of 85 | // 0x80000000 means 1/2 semitone (50 cents) and a value of 0x00000000 means 86 | // no fine tuning between semitones. 87 | MIDIPitchFraction uint32 88 | // SMPTEFormat The SMPTE format specifies the Society of Motion Pictures and 89 | // Television E time format used in the following SMPTE Offset field. If a 90 | // value of 0 is set, SMPTE Offset should also be set to 0. (0, 24, 25, 29, 30) 91 | SMPTEFormat uint32 92 | // SMPTEOffset The SMPTE Offset value specifies the time offset to be used 93 | // for the synchronization / calibration to the first sample in the 94 | // waveform. This value uses a format of 0xhhmmssff where hh is a signed 95 | // value that specifies the number of hours (-23 to 23), mm is an unsigned 96 | // value that specifies the number of minutes (0 to 59), ss is an unsigned 97 | // value that specifies the number of seconds (0 to 59) and ff is an 98 | // unsigned value that specifies the number of frames (0 to -1). 99 | SMPTEOffset uint32 100 | // NumSampleLoops The sample loops field specifies the number Sample Loop 101 | // definitions in the following list. This value may be set to 0 meaning 102 | // that no sample loops follow. 103 | NumSampleLoops uint32 104 | // Loops A list of sample loops is simply a set of consecutive loop 105 | // descriptions. The sample loops do not have to be in any particular order 106 | // because each sample loop associated cue point position is used to 107 | // determine the play order. 108 | Loops []*SampleLoop 109 | } 110 | 111 | // SampleLoop indicates a loop and its properties within the audio file 112 | type SampleLoop struct { 113 | // CuePointID - The Cue Point ID specifies the unique ID that corresponds to one of the 114 | // defined cue points in the cue point list. Furthermore, this ID 115 | // corresponds to any labels defined in the associated data list chunk which 116 | // allows text labels to be assigned to the various sample loops. 117 | CuePointID [4]byte 118 | // Type - The type field defines how the waveform samples will be looped. 119 | // 0 Loop forward (normal) 120 | // 1 Alternating loop (forward/backward, also known as Ping Pong) 121 | // 2 Loop backward (reverse) 122 | // 3 Reserved for future standard types 123 | // 32 - 0xFFFFFFFF Sampler specific types (defined by manufacturer) 124 | Type uint32 125 | // Start - The start value specifies the byte offset into the waveform data 126 | // of the first sample to be played in the loop. 127 | Start uint32 128 | // End - The end value specifies the byte offset into the waveform data of 129 | // the last sample to be played in the loop. 130 | End uint32 131 | // Fraction - The fractional value specifies a fraction of a sample at which 132 | // to loop. This allows a loop to be fine tuned at a resolution greater than 133 | // one sample. The value can range from 0x00000000 to 0xFFFFFFFF. A value of 134 | // 0 means no fraction, a value of 0x80000000 means 1/2 of a sample length. 135 | // 0xFFFFFFFF is the smallest fraction of a sample that can be represented. 136 | Fraction uint32 137 | // PlayCount - The play count value determines the number of times to play 138 | // the loop. A value of 0 specifies an infinite sustain loop. An infinite 139 | // sustain loop will continue looping until some external force interrupts 140 | // playback, such as the musician releasing the key that triggered the 141 | // wave's playback. All other values specify an absolute number of times to 142 | // loop. 143 | PlayCount uint32 144 | } 145 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/smpl_chunk.go: -------------------------------------------------------------------------------- 1 | package wav 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/go-audio/riff" 9 | ) 10 | 11 | // smpl chunk is documented here: 12 | // https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl 13 | 14 | // DecodeSamplerChunk decodes a smpl chunk and put the data in Decoder.Metadata.SamplerInfo 15 | func DecodeSamplerChunk(d *Decoder, ch *riff.Chunk) error { 16 | if ch == nil { 17 | return fmt.Errorf("can't decode a nil chunk") 18 | } 19 | if d == nil { 20 | return fmt.Errorf("nil decoder") 21 | } 22 | if ch.ID == CIDSmpl { 23 | // read the entire chunk in memory 24 | buf := make([]byte, ch.Size) 25 | var err error 26 | if _, err = ch.Read(buf); err != nil { 27 | return fmt.Errorf("failed to read the smpl chunk - %v", err) 28 | } 29 | if d.Metadata == nil { 30 | d.Metadata = &Metadata{} 31 | } 32 | 33 | d.Metadata.SamplerInfo = &SamplerInfo{} 34 | 35 | r := bytes.NewReader(buf) 36 | 37 | scratch := make([]byte, 4) 38 | if _, err = r.Read(scratch); err != nil { 39 | return fmt.Errorf("failed to read the smpl Manufacturer") 40 | } 41 | copy(d.Metadata.SamplerInfo.Manufacturer[:], scratch[:4]) 42 | if _, err = r.Read(scratch); err != nil { 43 | return fmt.Errorf("failed to read the smpl Product") 44 | } 45 | copy(d.Metadata.SamplerInfo.Product[:], scratch[:4]) 46 | 47 | if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.SamplePeriod); err != nil { 48 | return err 49 | } 50 | if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.MIDIUnityNote); err != nil { 51 | return err 52 | } 53 | if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.MIDIPitchFraction); err != nil { 54 | return err 55 | } 56 | if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.SMPTEFormat); err != nil { 57 | return err 58 | } 59 | if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.SMPTEOffset); err != nil { 60 | return err 61 | } 62 | if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.NumSampleLoops); err != nil { 63 | return err 64 | } 65 | var remaining uint32 66 | // sampler data 67 | if err := binary.Read(r, binary.BigEndian, &remaining); err != nil { 68 | return err 69 | } 70 | if d.Metadata.SamplerInfo.NumSampleLoops > 0 { 71 | d.Metadata.SamplerInfo.Loops = []*SampleLoop{} 72 | for i := uint32(0); i < d.Metadata.SamplerInfo.NumSampleLoops; i++ { 73 | sl := &SampleLoop{} 74 | if _, err = r.Read(scratch); err != nil { 75 | return fmt.Errorf("failed to read the sample loop cue point id") 76 | } 77 | copy(sl.CuePointID[:], scratch[:4]) 78 | if err := binary.Read(r, binary.LittleEndian, &sl.Type); err != nil { 79 | return err 80 | } 81 | if err := binary.Read(r, binary.LittleEndian, &sl.Start); err != nil { 82 | return err 83 | } 84 | if err := binary.Read(r, binary.LittleEndian, &sl.End); err != nil { 85 | return err 86 | } 87 | if err := binary.Read(r, binary.LittleEndian, &sl.Fraction); err != nil { 88 | return err 89 | } 90 | if err := binary.Read(r, binary.LittleEndian, &sl.PlayCount); err != nil { 91 | return err 92 | } 93 | 94 | d.Metadata.SamplerInfo.Loops = append(d.Metadata.SamplerInfo.Loops, sl) 95 | } 96 | } 97 | } 98 | ch.Drain() 99 | return nil 100 | } 101 | -------------------------------------------------------------------------------- /vendor/github.com/go-audio/wav/wav.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Package wav is a package allowing developers to decode and encode audio PCM 4 | data using the Waveform Audio File Format https://en.wikipedia.org/wiki/WAV 5 | 6 | */ 7 | package wav 8 | 9 | import ( 10 | "errors" 11 | "math" 12 | "time" 13 | ) 14 | 15 | var ( 16 | // ErrPCMChunkNotFound indicates a bad audio file without data 17 | ErrPCMChunkNotFound = errors.New("PCM Chunk not found in audio file") 18 | ) 19 | 20 | func nullTermStr(b []byte) string { 21 | return string(b[:clen(b)]) 22 | } 23 | 24 | func clen(n []byte) int { 25 | for i := 0; i < len(n); i++ { 26 | if n[i] == 0 { 27 | return i 28 | } 29 | } 30 | return len(n) 31 | } 32 | 33 | func bytesNumFromDuration(dur time.Duration, sampleRate, bitDepth int) int { 34 | k := bitDepth / 8 35 | return samplesNumFromDuration(dur, sampleRate) * k 36 | } 37 | 38 | func samplesNumFromDuration(dur time.Duration, sampleRate int) int { 39 | return int(math.Floor(float64(dur / sampleDuration(sampleRate)))) 40 | } 41 | 42 | func sampleDuration(sampleRate int) time.Duration { 43 | if sampleRate == 0 { 44 | return 0 45 | } 46 | return time.Second / time.Duration(math.Abs(float64(sampleRate))) 47 | } -------------------------------------------------------------------------------- /vendor/github.com/kardianos/task/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Daniel Theophanes. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Solid Core Data nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/task/README.md: -------------------------------------------------------------------------------- 1 | # Task 2 | 3 | Task runs a sequence of actions. Errors and failures are handled centerally. 4 | Rollback actions can be defined and added as the task progresses. 5 | 6 | Commands and flags may be defined to begin the correct task. 7 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/task/action.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Daniel Theophanes. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package task handles running a sequence of tasks. State context 6 | // is separated from script actions. Native context support. 7 | package task 8 | 9 | import ( 10 | "context" 11 | "fmt" 12 | "io" 13 | "os" 14 | "path/filepath" 15 | "strings" 16 | ) 17 | 18 | // Action is a unit of work that gets run. 19 | type Action interface { 20 | Run(ctx context.Context, st *State, sc Script) error 21 | } 22 | 23 | // ActionFunc runs a function like an Action. 24 | type ActionFunc func(ctx context.Context, st *State, sc Script) error 25 | 26 | // Run the ActionFunc. 27 | func (f ActionFunc) Run(ctx context.Context, st *State, sc Script) error { 28 | return f(ctx, st, sc) 29 | } 30 | 31 | // Script is a list of actions. If an action 32 | type Script interface { 33 | Add(a ...Action) 34 | AddRollback(a ...Action) 35 | RunAction(ctx context.Context, st *State, a Action) error 36 | Run(ctx context.Context, st *State, parent Script) error 37 | } 38 | 39 | // Run is the entry point for actions. It is a short-cut 40 | // for ScriptAdd and Run. 41 | func Run(ctx context.Context, st *State, a Action) error { 42 | sc := ScriptAdd(a) 43 | return sc.Run(ctx, st, nil) 44 | } 45 | 46 | type script struct { 47 | at int 48 | list []Action 49 | 50 | rollback *script 51 | } 52 | 53 | // ScriptAdd creates a script and appends the given actions to it. 54 | func ScriptAdd(a ...Action) Script { 55 | sc := &script{} 56 | sc.list = append(sc.list, a...) 57 | return sc 58 | } 59 | 60 | // Add creates a script if nil and appends the given actions to it. 61 | func (sc *script) Add(a ...Action) { 62 | sc.list = append(sc.list, a...) 63 | } 64 | 65 | // AddRollback adds actions to be done on failure. 66 | func (sc *script) AddRollback(a ...Action) { 67 | if sc.rollback == nil { 68 | sc.rollback = &script{} 69 | } 70 | sc.rollback.Add(a...) 71 | } 72 | 73 | // Branch represents a branch condition used in Switch. 74 | type Branch int64 75 | 76 | // Typical branch values. 77 | const ( 78 | BranchUnset Branch = iota 79 | BranchTrue 80 | BranchFalse 81 | BranchCommit 82 | BranchRollback 83 | 84 | // BranchCustom is the smallest custom branch value that may be used. 85 | BranchCustom Branch = 1024 86 | ) 87 | 88 | // Policy describes the current error policy. 89 | type Policy byte 90 | 91 | // Policies may be combined together. The default policy is to fail on error 92 | // and run any rollback acitions. If Continue is selected then normal execution 93 | // will proceed and a rollback will not be triggered. If Log is selected 94 | // any error will be logged to the ErrorLogger. If SkipRollback is selected 95 | // then a failure will not trigger the rollback actions. If both Continue 96 | // and SkipRollbackk are selected, execution will continue and SkipRollback 97 | // will be ignored. 98 | const ( 99 | PolicyFail Policy = 0 100 | PolicyContinue Policy = 1 << iota 101 | PolicyLog 102 | PolicySkipRollback 103 | 104 | // Fail 105 | // Fail + Log 106 | // Fail + Log + SkipRollback 107 | // Fail + SkipRollback 108 | // Continue 109 | // Continue + Log 110 | // 111 | // Continue + SkipRollback will ignore skip rollback. 112 | ) 113 | 114 | // State of the current task. 115 | type State struct { 116 | Env map[string]string 117 | Dir string // Current working directory. 118 | Stdout io.Writer 119 | Stderr io.Writer 120 | Branch Branch 121 | Policy Policy 122 | 123 | ErrorLogger func(err error) 124 | MsgLogger func(msg string) 125 | 126 | bucket map[string]interface{} 127 | } 128 | 129 | // Environ calls os.Environ and maps it to key value pairs. 130 | func Environ() map[string]string { 131 | envList := os.Environ() 132 | envMap := make(map[string]string, len(envList)) 133 | for _, env := range envList { 134 | ss := strings.SplitN(env, "=", 2) 135 | if len(ss) != 2 { 136 | continue 137 | } 138 | envMap[ss[0]] = ss[1] 139 | } 140 | return envMap 141 | } 142 | 143 | // DefaultState creates a new states with the current OS env. 144 | func DefaultState() *State { 145 | wd, _ := os.Getwd() 146 | return &State{ 147 | Env: Environ(), 148 | Dir: wd, 149 | Stdout: os.Stdout, 150 | Stderr: os.Stderr, 151 | ErrorLogger: func(err error) { 152 | fmt.Fprint(os.Stderr, err, "\n") 153 | }, 154 | MsgLogger: func(msg string) { 155 | fmt.Fprint(os.Stdout, msg, "\n") 156 | }, 157 | } 158 | } 159 | 160 | // Log a message to the MsgLogger if present. 161 | func (st *State) Log(msg string) { 162 | if st.MsgLogger == nil { 163 | return 164 | } 165 | st.MsgLogger(msg) 166 | } 167 | 168 | // Logf logs a formatted message to the MsgLogger if present. 169 | func (st *State) Logf(f string, v ...interface{}) { 170 | st.Log(fmt.Sprintf(f, v...)) 171 | } 172 | 173 | // Error reports an error to the ErrorLogger if present. 174 | func (st *State) Error(err error) { 175 | if st.ErrorLogger == nil { 176 | return 177 | } 178 | st.ErrorLogger(err) 179 | } 180 | 181 | // Filepath returns filename if absolute, or State.Dir + filename if not. 182 | func (st *State) Filepath(filename string) string { 183 | if filepath.IsAbs(filename) { 184 | return filename 185 | } 186 | return filepath.Join(st.Dir, filename) 187 | } 188 | 189 | func (st *State) init() { 190 | if st.bucket == nil { 191 | st.bucket = make(map[string]interface{}) 192 | } 193 | } 194 | 195 | // Get the variable called name from the state bucket. 196 | func (st *State) Get(name string) interface{} { 197 | st.init() 198 | return st.bucket[name] 199 | } 200 | 201 | // Default gets the variable called name from the state bucket. If 202 | // no value is present, return v. 203 | func (st *State) Default(name string, v interface{}) interface{} { 204 | st.init() 205 | if got, found := st.bucket[name]; found { 206 | return got 207 | } 208 | return v 209 | } 210 | 211 | // Set the variable v to the name. 212 | func (st *State) Set(name string, v interface{}) { 213 | st.init() 214 | st.bucket[name] = v 215 | } 216 | 217 | // Delete the variable called name. 218 | func (st *State) Delete(name string) { 219 | st.init() 220 | delete(st.bucket, name) 221 | } 222 | 223 | // RunAction runs the given action in the current script's context. 224 | func (sc *script) RunAction(ctx context.Context, st *State, a Action) error { 225 | if sc == nil { 226 | return nil 227 | } 228 | select { 229 | default: 230 | case <-ctx.Done(): 231 | return ctx.Err() 232 | } 233 | err := a.Run(ctx, st, sc) 234 | if err == nil { 235 | return nil 236 | } 237 | if st.Policy&PolicyLog != 0 { 238 | st.Error(err) 239 | } 240 | if st.Policy&PolicyContinue != 0 { 241 | err = nil 242 | } 243 | if st.Policy&PolicySkipRollback != 0 { 244 | return err 245 | } 246 | if err == nil { 247 | return err 248 | } 249 | rberr := sc.rollback.Run(context.Background(), st, sc) 250 | if rberr == nil { 251 | return err 252 | } 253 | return fmt.Errorf("%v, rollback failed: %v", err, rberr) 254 | } 255 | 256 | func (sc *script) runNext(ctx context.Context, st *State) error { 257 | if sc.at >= len(sc.list) { 258 | return io.EOF 259 | } 260 | a := sc.list[sc.at] 261 | sc.at++ 262 | return sc.RunAction(ctx, st, a) 263 | } 264 | 265 | // Run the items in the method script. The parent script is ignored. 266 | func (sc *script) Run(ctx context.Context, st *State, parent Script) error { 267 | if sc == nil { 268 | return nil 269 | } 270 | var err error 271 | for { 272 | err = sc.runNext(ctx, st) 273 | if err == io.EOF { 274 | return nil 275 | } 276 | if err != nil { 277 | return err 278 | } 279 | } 280 | } 281 | 282 | // AddRollback adds rollback actions to the current Script. Rollback actions 283 | // are only executed on failure under non-Continue policies. 284 | func AddRollback(a ...Action) Action { 285 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 286 | sc.AddRollback(a...) 287 | return nil 288 | }) 289 | } 290 | 291 | // Switch will run the f action, read the state branch value, and then 292 | // execute the given action in sw. 293 | func Switch(f Action, sw map[Branch]Action) Action { 294 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 295 | err := sc.RunAction(ctx, st, f) 296 | if err != nil { 297 | return err 298 | } 299 | br := st.Branch 300 | st.Branch = BranchUnset 301 | if next, ok := sw[br]; ok { 302 | return sc.RunAction(ctx, st, next) 303 | } 304 | return nil 305 | }) 306 | } 307 | 308 | // WithPolicy sets the state policy for a single action. 309 | func WithPolicy(p Policy, a Action) Action { 310 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 311 | orig := st.Policy 312 | st.Policy = p 313 | err := sc.RunAction(ctx, st, a) 314 | st.Policy = orig 315 | return err 316 | }) 317 | } 318 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/task/command.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Daniel Theophanes. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package task 6 | 7 | import ( 8 | "context" 9 | "errors" 10 | "fmt" 11 | "strconv" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | // Command represents an Action that may be invoked with a name. 17 | // Flags will be mapped to State bucket values. 18 | // Extra arguments at the end of a command chain will be passed to the state as 19 | // "args []string". To pass arguments to a command that has sub-commands, first 20 | // pass in "--" then pass in the arguments. 21 | // 22 | // exec cmd arg1 arg2 # cmd has no sub-commands. 23 | // exec cmd -- arg1 arg2 # cmd has one or more sub-commands. 24 | type Command struct { 25 | Name string 26 | Usage string 27 | Flags []*Flag 28 | Commands []*Command 29 | Action Action 30 | } 31 | 32 | // Flag represents values that may be set on comments. 33 | // Values will be mapped to State bucket values. 34 | type Flag struct { 35 | Name string 36 | Usage string 37 | Default interface{} 38 | Type FlagType 39 | } 40 | 41 | // FlagType is set in Flag and determins how the value is parsed. 42 | type FlagType byte 43 | 44 | // FlagType options. If a default is present the flag type may be left as 45 | // Auto to choose the parse type based on the default type. 46 | const ( 47 | FlagAuto FlagType = iota 48 | FlagString 49 | FlagBool 50 | FlagInt64 51 | FlagFloat64 52 | FlagDuration 53 | ) 54 | 55 | func (ft FlagType) spaceValue() bool { 56 | switch ft { 57 | default: 58 | return true 59 | case FlagBool: 60 | return false 61 | } 62 | } 63 | 64 | type flagStatus struct { 65 | used bool 66 | flag *Flag 67 | } 68 | 69 | func (fs *flagStatus) init() error { 70 | fl := fs.flag 71 | if fl.Default == nil { 72 | return nil 73 | } 74 | // If a default value is set, automatically set the flag type. 75 | if fl.Type == FlagAuto { 76 | switch fl.Default.(type) { 77 | case string: 78 | fl.Type = FlagString 79 | case bool: 80 | fl.Type = FlagBool 81 | case int64: 82 | fl.Type = FlagInt64 83 | case int32: 84 | fl.Type = FlagInt64 85 | case int: 86 | fl.Type = FlagInt64 87 | case float64: 88 | fl.Type = FlagFloat64 89 | case float32: 90 | fl.Type = FlagFloat64 91 | case time.Duration: 92 | fl.Type = FlagDuration 93 | } 94 | } 95 | var ok bool 96 | switch fl.Type { 97 | default: 98 | return fmt.Errorf("unknown flag type %v", fl.Type) 99 | case FlagString: 100 | _, ok = fl.Default.(string) 101 | case FlagBool: 102 | _, ok = fl.Default.(bool) 103 | case FlagInt64: 104 | switch v := fl.Default.(type) { 105 | case int32: 106 | fl.Default = int64(v) 107 | ok = true 108 | case int: 109 | fl.Default = int64(v) 110 | ok = true 111 | case int64: 112 | ok = true 113 | } 114 | case FlagFloat64: 115 | switch v := fl.Default.(type) { 116 | case float32: 117 | fl.Default = float64(v) 118 | ok = true 119 | case float64: 120 | ok = true 121 | } 122 | case FlagDuration: 123 | _, ok = fl.Default.(time.Duration) 124 | } 125 | if !ok { 126 | return fmt.Errorf("invalid default flag value %[1]v (%[1]T) for -%[2]s", fl.Default, fl.Name) 127 | } 128 | return nil 129 | } 130 | 131 | func (fs *flagStatus) set(st *State, vs string) error { 132 | fl := fs.flag 133 | if fs.used { 134 | return fmt.Errorf("flag -%s already declared", fl.Name) 135 | } 136 | fs.used = true 137 | switch fl.Type { 138 | default: 139 | return fmt.Errorf("unknown flag type %v", fl.Type) 140 | case FlagString, FlagAuto: 141 | st.Set(fl.Name, vs) 142 | case FlagBool: 143 | if vs == "" { 144 | st.Set(fl.Name, true) 145 | } else { 146 | v, err := strconv.ParseBool(vs) 147 | if err != nil { 148 | return err 149 | } 150 | st.Set(fl.Name, v) 151 | } 152 | case FlagInt64: 153 | v, err := strconv.ParseInt(vs, 10, 64) 154 | if err != nil { 155 | return err 156 | } 157 | st.Set(fl.Name, v) 158 | case FlagFloat64: 159 | v, err := strconv.ParseFloat(vs, 64) 160 | if err != nil { 161 | return err 162 | } 163 | st.Set(fl.Name, v) 164 | case FlagDuration: 165 | v, err := time.ParseDuration(vs) 166 | if err != nil { 167 | return err 168 | } 169 | st.Set(fl.Name, v) 170 | } 171 | return nil 172 | } 173 | 174 | func (fs *flagStatus) setDefault(st *State) { 175 | fl := fs.flag 176 | if fl.Default == nil { 177 | return 178 | } 179 | st.Set(fl.Name, fl.Default) 180 | } 181 | 182 | // Exec takes a command arguments and returns an Action, ready to be run. 183 | func (c *Command) Exec(args []string) Action { 184 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 185 | if sc == nil { 186 | return errors.New("missing Script") 187 | } 188 | flagLookup := make(map[string]*flagStatus) 189 | cmdLookup := make(map[string]*Command) 190 | for _, cmd := range c.Commands { 191 | cmdLookup[cmd.Name] = cmd 192 | } 193 | for _, fl := range c.Flags { 194 | fs := &flagStatus{flag: fl} 195 | if err := fs.init(); err != nil { 196 | return err 197 | } 198 | flagLookup[fl.Name] = fs 199 | } 200 | 201 | // First parse any flags. 202 | // The first non-flag seen is a sub-command, stop after the cmd is found. 203 | var nextFlag *flagStatus 204 | for len(args) > 0 { 205 | a := args[0] 206 | prevArgs := args 207 | args = args[1:] 208 | 209 | if nextFlag != nil { 210 | if err := nextFlag.set(st, a); err != nil { 211 | return err 212 | } 213 | nextFlag.used = true 214 | nextFlag = nil 215 | continue 216 | } 217 | 218 | if len(a) == 0 { 219 | continue 220 | } 221 | if a[0] != '-' { 222 | if len(cmdLookup) == 0 { 223 | // This is an argument. 224 | st.Set("args", prevArgs) 225 | break 226 | } 227 | // This is a subcommand. 228 | for _, fs := range flagLookup { 229 | if fs.used { 230 | continue 231 | } 232 | fs.setDefault(st) 233 | } 234 | cmd, ok := cmdLookup[a] 235 | if !ok { 236 | return c.helpError("invalid command %q", a) 237 | } 238 | sc.Add(cmd.Exec(args)) 239 | return nil 240 | } 241 | a = a[1:] 242 | if a == "-" { // "--" 243 | st.Set("args", args) 244 | break 245 | } 246 | // This is a flag. 247 | nameValue := strings.SplitN(a, "=", 2) 248 | fl, ok := flagLookup[nameValue[0]] 249 | if !ok { 250 | return c.helpError("invalid flag -%s", nameValue[0]) 251 | } 252 | val := "" 253 | if len(nameValue) == 1 { 254 | if fl.flag.Type.spaceValue() { 255 | nextFlag = fl 256 | continue 257 | } 258 | } else { 259 | val = nameValue[1] 260 | } 261 | if err := fl.set(st, val); err != nil { 262 | return err 263 | } 264 | } 265 | for _, fs := range flagLookup { 266 | if fs.used { 267 | continue 268 | } 269 | fs.setDefault(st) 270 | } 271 | if nextFlag != nil { 272 | return fmt.Errorf("expected value after flag %q", nextFlag.flag.Name) 273 | } 274 | if c.Action == nil { 275 | return c.helpError("incorrect command") 276 | } 277 | sc.Add(c.Action) 278 | return nil 279 | }) 280 | } 281 | 282 | // ErrUsage signals that the error returned is not a runtime error 283 | // but a usage message. 284 | type ErrUsage string 285 | 286 | func (err ErrUsage) Error() string { 287 | return string(err) 288 | } 289 | 290 | func (c *Command) helpError(f string, v ...interface{}) error { 291 | msg := &strings.Builder{} 292 | if len(f) > 0 { 293 | fmt.Fprintf(msg, f, v...) 294 | msg.WriteRune('\n') 295 | } 296 | msg.WriteString(c.Name) 297 | if len(c.Usage) > 0 { 298 | msg.WriteString(" - ") 299 | msg.WriteString(c.Usage) 300 | } 301 | msg.WriteString("\n") 302 | for _, fl := range c.Flags { 303 | msg.WriteString("\t") 304 | msg.WriteRune('-') 305 | msg.WriteString(fl.Name) 306 | if len(fl.Usage) > 0 { 307 | msg.WriteString(" - ") 308 | msg.WriteString(fl.Usage) 309 | } 310 | if fl.Default != nil { 311 | fmt.Fprintf(msg, " (%v)", fl.Default) 312 | } 313 | msg.WriteString("\n") 314 | } 315 | msg.WriteString("\n") 316 | for _, sub := range c.Commands { 317 | msg.WriteString("\t") 318 | msg.WriteString(sub.Name) 319 | if len(sub.Usage) > 0 { 320 | msg.WriteString(" - ") 321 | msg.WriteString(sub.Usage) 322 | } 323 | msg.WriteString("\n") 324 | } 325 | return ErrUsage(msg.String()) 326 | } 327 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/task/fsop/compress.go: -------------------------------------------------------------------------------- 1 | package fsop 2 | 3 | import ( 4 | "archive/zip" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | // Compress will create and zip archive of the file(s) and folder(s) in fileOrDir. 14 | // fileOrDir may be a single file or a directory containing many files. 15 | // The returned bytes is the content of the zip archive. 16 | func Compress(fileOrDir string, only Only) ([]byte, error) { 17 | buf := &bytes.Buffer{} 18 | w := zip.NewWriter(buf) 19 | 20 | baseStat, err := os.Stat(fileOrDir) 21 | if err != nil { 22 | return nil, err 23 | } 24 | if baseStat.IsDir() { 25 | err = compressDir(fileOrDir, w, only) 26 | } else { 27 | filename := fileOrDir 28 | fileOrDir, _ = filepath.Split(fileOrDir) 29 | err = compressFile(filename, fileOrDir, w, baseStat) 30 | } 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | // Close the zip writer. 36 | err = w.Close() 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return buf.Bytes(), nil 42 | } 43 | 44 | var slashReplace = strings.NewReplacer(`\`, `/`) 45 | 46 | func compressFile(path, baseDir string, w *zip.Writer, info os.FileInfo) error { 47 | // Make sure the contents of the file can be read before 48 | // adding it to the zip archive. 49 | f, err := os.Open(path) 50 | if err != nil { 51 | return fmt.Errorf("failed to read file %q: %v", path, err) 52 | } 53 | defer f.Close() 54 | 55 | // Create the file location in the zip archive 56 | fh := &zip.FileHeader{ 57 | Name: slashReplace.Replace(strings.TrimPrefix(path, baseDir)), 58 | Method: zip.Deflate, 59 | Modified: info.ModTime(), 60 | } 61 | fh.SetMode(info.Mode()) 62 | zf, err := w.CreateHeader(fh) 63 | if err != nil { 64 | return fmt.Errorf("failed to create file %q in archive: %v", path, err) 65 | } 66 | 67 | // Write the contents of the file to the zip archive 68 | _, err = io.Copy(zf, f) 69 | if err != nil { 70 | return fmt.Errorf("failed to write contents of file %q to archive: %v", path, err) 71 | } 72 | return nil 73 | } 74 | 75 | // compressDir will create and zip archive of the file(s) and folder(s) in baseDir 76 | // The returned bytes is the content of the zip archive. 77 | func compressDir(baseDir string, w *zip.Writer, only Only) error { 78 | return filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error { 79 | if err != nil { 80 | return fmt.Errorf("failure access path %q: %v", path, err) 81 | } 82 | if only != nil && !only(path) { 83 | return nil 84 | } 85 | 86 | // No need to process diretories. They will be created in the archive 87 | // relative a files location on disk. 88 | if info.IsDir() { 89 | return nil 90 | } 91 | return compressFile(path, baseDir, w, info) 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/task/fsop/copy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Daniel Theophanes. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package fsop has common file system operations. 6 | package fsop 7 | 8 | import ( 9 | "io" 10 | "io/ioutil" 11 | "os" 12 | "path/filepath" 13 | ) 14 | 15 | // Only takes a path and returns true to include the file or folder. 16 | type Only func(p string) bool 17 | 18 | // Copy the the oldpath to the newpath. If only is not nil, only copy the 19 | // files and folders where only returns true. 20 | func Copy(oldpath, newpath string, only Only) error { 21 | if only != nil && !only(oldpath) { 22 | return nil 23 | } 24 | fi, err := os.Stat(oldpath) 25 | if err != nil { 26 | return err 27 | } 28 | if fi.IsDir() { 29 | return copyFolder(fi, oldpath, newpath, only) 30 | } 31 | return copyFile(fi, oldpath, newpath) 32 | } 33 | 34 | func copyFile(fi os.FileInfo, oldpath, newpath string) error { 35 | old, err := os.Open(oldpath) 36 | if err != nil { 37 | return err 38 | } 39 | defer old.Close() 40 | 41 | err = os.MkdirAll(filepath.Dir(newpath), fi.Mode()|0700) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | new, err := os.OpenFile(newpath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode()) 47 | if err != nil { 48 | return err 49 | } 50 | _, err = io.Copy(new, old) 51 | cerr := new.Close() 52 | if cerr != nil { 53 | return cerr 54 | } 55 | 56 | return err 57 | } 58 | 59 | func copyFolder(fi os.FileInfo, oldpath, newpath string, only Only) error { 60 | err := os.MkdirAll(newpath, fi.Mode()) 61 | if err != nil { 62 | return err 63 | } 64 | list, err := ioutil.ReadDir(oldpath) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | for _, item := range list { 70 | err = Copy(filepath.Join(oldpath, item.Name()), filepath.Join(newpath, item.Name()), only) 71 | if err != nil { 72 | return err 73 | } 74 | } 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/task/os.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Daniel Theophanes. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package task 6 | 7 | import ( 8 | "bytes" 9 | "context" 10 | "fmt" 11 | "io/ioutil" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "strings" 16 | 17 | "github.com/kardianos/task/fsop" 18 | ) 19 | 20 | // Env sets one or more environment variables. 21 | // To delete an environment variable just include the key, no equals. 22 | // Env("GOOS=linux", "GOARCH=arm64") 23 | func Env(env ...string) Action { 24 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 25 | if st.Env == nil { 26 | st.Env = make(map[string]string, len(env)) 27 | } 28 | for i, e := range env { 29 | env[i] = ExpandEnv(e, st) 30 | } 31 | for _, e := range env { 32 | ss := strings.SplitN(e, "=", 2) 33 | if len(ss) != 2 { 34 | delete(st.Env, ss[0]) 35 | continue 36 | } 37 | st.Env[ss[0]] = ss[1] 38 | } 39 | return nil 40 | }) 41 | } 42 | 43 | // ExpandEnv will expand env vars from s and return the combined string. 44 | // Var names may take the form of "text${var}suffix". 45 | // The source of the value will first look for current state bucket, 46 | // then in the state Env. 47 | func ExpandEnv(s string, st *State) string { 48 | return os.Expand(s, func(key string) string { 49 | if st.bucket != nil { 50 | if v, ok := st.bucket[key]; ok { 51 | switch x := v.(type) { 52 | case string: 53 | return x 54 | case nil: 55 | // Nothing. 56 | default: 57 | return fmt.Sprint(x) 58 | } 59 | } 60 | } 61 | return st.Env[key] 62 | }) 63 | } 64 | 65 | // ExecFunc is the standard executable function type. 66 | type ExecFunc func(executable string, args ...string) Action 67 | 68 | // Exec runs an executable. Sets the "stdout" bucket variable as a []byte. 69 | func Exec(executable string, args ...string) Action { 70 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 71 | executable = ExpandEnv(executable, st) 72 | for i, a := range args { 73 | args[i] = ExpandEnv(a, st) 74 | } 75 | cmd := exec.CommandContext(ctx, executable, args...) 76 | envList := make([]string, 0, len(st.Env)) 77 | for key, value := range st.Env { 78 | envList = append(envList, key+"="+value) 79 | } 80 | cmd.Env = envList 81 | cmd.Dir = st.Dir 82 | stdin, _ := st.Default("stdin", []byte{}).([]byte) 83 | if len(stdin) > 0 { 84 | cmd.Stdin = bytes.NewReader(stdin) 85 | } 86 | out, err := cmd.Output() 87 | st.Set("stdout", out) 88 | if err != nil { 89 | st.Set("success", false) 90 | if ec, ok := err.(*exec.ExitError); ok { 91 | return fmt.Errorf("%s %q failed: %v\n%s", executable, args, err, ec.Stderr) 92 | } 93 | return err 94 | } 95 | st.Set("success", true) 96 | return nil 97 | }) 98 | } 99 | 100 | // Pipe sets stdin to the value of stdout. The stdout is removed. 101 | var Pipe = ActionFunc(pipe) 102 | 103 | func pipe(ctx context.Context, st *State, sc Script) error { 104 | stdin := []byte{} 105 | if stdout, is := st.Default("stdout", []byte{}).([]byte); is { 106 | stdin = stdout 107 | } 108 | st.Set("stdin", stdin) 109 | st.Delete("stdout") 110 | return nil 111 | } 112 | 113 | // ExecStreamOut runs an executable but streams the output to stderr and stdout. 114 | func ExecStreamOut(executable string, args ...string) Action { 115 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 116 | executable = ExpandEnv(executable, st) 117 | for i, a := range args { 118 | args[i] = ExpandEnv(a, st) 119 | } 120 | cmd := exec.CommandContext(ctx, executable, args...) 121 | envList := make([]string, 0, len(st.Env)) 122 | for key, value := range st.Env { 123 | envList = append(envList, key+"="+value) 124 | } 125 | cmd.Env = envList 126 | cmd.Dir = st.Dir 127 | stdin, _ := st.Default("stdin", []byte{}).([]byte) 128 | if len(stdin) > 0 { 129 | cmd.Stdin = bytes.NewReader(stdin) 130 | } 131 | cmd.Stdout = st.Stdout 132 | cmd.Stderr = st.Stderr 133 | err := cmd.Run() 134 | if err != nil { 135 | st.Set("success", false) 136 | if ec, ok := err.(*exec.ExitError); ok { 137 | return fmt.Errorf("%s %q failed: %v\n%s", executable, args, err, ec.Stderr) 138 | } 139 | return err 140 | } 141 | st.Set("success", true) 142 | return nil 143 | }) 144 | } 145 | 146 | // WriteFileStdout writes the given file from the "stdout" bucket variable assuming it is a []byte. 147 | func WriteFileStdout(filename string, mode os.FileMode) Action { 148 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 149 | filename = ExpandEnv(filename, st) 150 | return ioutil.WriteFile(st.Filepath(filename), st.Default("stdout", []byte{}).([]byte), mode) 151 | }) 152 | } 153 | 154 | // ReadFileStdin reads the given file into the stdin bucket variable as a []byte. 155 | func ReadFileStdin(filename string, mode os.FileMode) Action { 156 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 157 | filename = ExpandEnv(filename, st) 158 | b, err := ioutil.ReadFile(st.Filepath(filename)) 159 | if err != nil { 160 | return err 161 | } 162 | st.Set("stdin", b) 163 | return nil 164 | }) 165 | } 166 | 167 | // Delete file. 168 | func Delete(filename string) Action { 169 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 170 | filename = ExpandEnv(filename, st) 171 | return os.RemoveAll(st.Filepath(filename)) 172 | }) 173 | } 174 | 175 | // Move file. 176 | func Move(old, new string) Action { 177 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 178 | old = ExpandEnv(old, st) 179 | new = ExpandEnv(new, st) 180 | np := st.Filepath(new) 181 | err := os.MkdirAll(filepath.Dir(np), 0700) 182 | if err != nil { 183 | return err 184 | } 185 | return os.Rename(st.Filepath(old), np) 186 | }) 187 | } 188 | 189 | // Copy file or folder recursively. If only is present, only copy path 190 | // if only returns true. 191 | func Copy(old, new string, only func(p string, st *State) bool) Action { 192 | return ActionFunc(func(ctx context.Context, st *State, sc Script) error { 193 | old = ExpandEnv(old, st) 194 | new = ExpandEnv(new, st) 195 | return fsop.Copy(st.Filepath(old), st.Filepath(new), func(p string) bool { 196 | if only == nil { 197 | return true 198 | } 199 | return only(p, st) 200 | }) 201 | }) 202 | } 203 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/task/start.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/signal" 7 | "sync" 8 | "sync/atomic" 9 | "time" 10 | ) 11 | 12 | // StartFunc is called during start-up, but should watch the 13 | // context to check if it should shutdown. 14 | type StartFunc func(ctx context.Context) error 15 | 16 | // Start listens for an interrupt signal, and cancels the context if 17 | // interrupted. It starts run in a new goroutine. If it takes more then 18 | // stopTimeout before run returns after the ctx is canceled, then 19 | // it returns regardless. 20 | func Start(ctx context.Context, stopTimeout time.Duration, run StartFunc) error { 21 | notify := make(chan os.Signal, 3) 22 | signal.Notify(notify, os.Interrupt) 23 | ctx, cancel := context.WithCancel(ctx) 24 | once := &sync.Once{} 25 | fin := make(chan bool) 26 | unlock := func() { 27 | close(fin) 28 | } 29 | unlockOnce := func() { 30 | once.Do(unlock) 31 | } 32 | runErr := atomic.Value{} 33 | go func() { 34 | err := run(ctx) 35 | if err != nil { 36 | runErr.Store(err) 37 | } 38 | unlockOnce() 39 | }() 40 | select { 41 | case <-notify: 42 | case <-fin: 43 | } 44 | cancel() 45 | go func() { 46 | <-time.After(stopTimeout) 47 | unlockOnce() 48 | }() 49 | <-fin 50 | if err, ok := runErr.Load().(error); ok { 51 | return err 52 | } 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | .cache/ 4 | .vs/ 5 | .vscode/ 6 | .DS_Store 7 | 8 | build/ 9 | build-em/ 10 | build-debug/ 11 | build-release/ 12 | build-static/ 13 | build-sanitize-addr/ 14 | build-sanitize-thread/ 15 | 16 | /main 17 | /stream 18 | /command 19 | /talk 20 | /bench 21 | 22 | arm_neon.h 23 | sync.sh 24 | libwhisper.a 25 | libwhisper.so 26 | compile_commands.json 27 | 28 | examples/arm_neon.h 29 | examples/whisper.objc/whisper.objc.xcodeproj/xcshareddata 30 | examples/whisper.objc/whisper.objc.xcodeproj/xcuserdata/ 31 | examples/whisper.objc/whisper.objc.xcodeproj/project.xcworkspace/xcuserdata 32 | 33 | extra/bench-gg.txt 34 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bindings/ios"] 2 | path = bindings/ios 3 | url = https://github.com/ggerganov/whisper.spm 4 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | 3 | project(whisper.cpp VERSION 1.1.1) 4 | 5 | # Add path to modules 6 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") 7 | 8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 9 | 10 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 11 | set(WHISPER_STANDALONE ON) 12 | include(GitVars) 13 | include(BuildTypes) 14 | 15 | # configure project version 16 | if (EXISTS "${CMAKE_SOURCE_DIR}/bindings/ios/Makefile-tmpl") 17 | configure_file(${CMAKE_SOURCE_DIR}/bindings/ios/Makefile-tmpl ${CMAKE_SOURCE_DIR}/bindings/ios/Makefile @ONLY) 18 | endif() 19 | configure_file(${CMAKE_SOURCE_DIR}/bindings/javascript/package-tmpl.json ${CMAKE_SOURCE_DIR}/bindings/javascript/package.json @ONLY) 20 | else() 21 | set(WHISPER_STANDALONE OFF) 22 | endif() 23 | 24 | if (EMSCRIPTEN) 25 | set(BUILD_SHARED_LIBS_DEFAULT OFF) 26 | 27 | option(WHISPER_WASM_SINGLE_FILE "whisper: embed WASM inside the generated whisper.js" ON) 28 | else() 29 | if (MINGW) 30 | set(BUILD_SHARED_LIBS_DEFAULT OFF) 31 | else() 32 | set(BUILD_SHARED_LIBS_DEFAULT ON) 33 | endif() 34 | endif() 35 | 36 | # options 37 | 38 | option(BUILD_SHARED_LIBS "whisper: build shared libs" ${BUILD_SHARED_LIBS_DEFAULT}) 39 | 40 | option(WHISPER_ALL_WARNINGS "whisper: enable all compiler warnings" ON) 41 | option(WHISPER_ALL_WARNINGS_3RD_PARTY "whisper: enable all compiler warnings in 3rd party libs" OFF) 42 | 43 | option(WHISPER_SANITIZE_THREAD "whisper: enable thread sanitizer" OFF) 44 | option(WHISPER_SANITIZE_ADDRESS "whisper: enable address sanitizer" OFF) 45 | option(WHISPER_SANITIZE_UNDEFINED "whisper: enable undefined sanitizer" OFF) 46 | 47 | option(WHISPER_BUILD_TESTS "whisper: build tests" ${WHISPER_STANDALONE}) 48 | option(WHISPER_BUILD_EXAMPLES "whisper: build examples" ${WHISPER_STANDALONE}) 49 | 50 | option(WHISPER_SUPPORT_SDL2 "whisper: support for libSDL2" OFF) 51 | 52 | if (APPLE) 53 | option(WHISPER_NO_ACCELERATE "whisper: disable Accelerate framework" OFF) 54 | option(WHISPER_NO_AVX "whisper: disable AVX" OFF) 55 | option(WHISPER_NO_AVX2 "whisper: disable AVX2" OFF) 56 | option(WHISPER_NO_FMA "whisper: disable FMA" OFF) 57 | else() 58 | option(WHISPER_SUPPORT_OPENBLAS "whisper: support for OpenBLAS" OFF) 59 | endif() 60 | 61 | option(WHISPER_PERF "whisper: enable perf timings" OFF) 62 | 63 | # sanitizers 64 | 65 | if (NOT MSVC) 66 | if (WHISPER_SANITIZE_THREAD) 67 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") 68 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") 69 | endif() 70 | 71 | if (WHISPER_SANITIZE_ADDRESS) 72 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") 73 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") 74 | endif() 75 | 76 | if (WHISPER_SANITIZE_UNDEFINED) 77 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined") 78 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") 79 | endif() 80 | endif() 81 | 82 | #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffast-math") 83 | #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") 84 | 85 | # dependencies 86 | 87 | find_package(Threads REQUIRED) 88 | 89 | # on APPLE - include Accelerate framework 90 | if (APPLE AND NOT WHISPER_NO_ACCELERATE) 91 | find_library(ACCELERATE_FRAMEWORK Accelerate) 92 | if (ACCELERATE_FRAMEWORK) 93 | message(STATUS "Accelerate framework found") 94 | 95 | set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${ACCELERATE_FRAMEWORK}) 96 | set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_ACCELERATE) 97 | else() 98 | message(WARNING "Accelerate framework not found") 99 | endif() 100 | endif() 101 | 102 | if (WHISPER_SUPPORT_OPENBLAS) 103 | find_library(OPENBLAS_LIB 104 | NAMES openblas libopenblas 105 | ) 106 | if (OPENBLAS_LIB) 107 | message(STATUS "OpenBLAS found") 108 | 109 | set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${OPENBLAS_LIB}) 110 | set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_OPENBLAS) 111 | else() 112 | message(WARNING "OpenBLAS not found") 113 | endif() 114 | endif() 115 | 116 | # compiler flags 117 | 118 | if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 119 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) 120 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo") 121 | endif () 122 | 123 | if (WHISPER_ALL_WARNINGS) 124 | if (NOT MSVC) 125 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \ 126 | -Wall \ 127 | -Wextra \ 128 | -Wpedantic \ 129 | -Wshadow \ 130 | -Wcast-qual \ 131 | -Wstrict-prototypes \ 132 | -Wpointer-arith \ 133 | -Wno-unused-function \ 134 | ") 135 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ 136 | -Wall \ 137 | -Wextra \ 138 | -Wpedantic \ 139 | -Wcast-qual \ 140 | ") 141 | else() 142 | # todo : msvc 143 | endif() 144 | endif() 145 | 146 | if (NOT MSVC) 147 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=vla") 148 | #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-math-errno -ffinite-math-only -funsafe-math-optimizations") 149 | endif() 150 | 151 | message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") 152 | 153 | if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") 154 | message(STATUS "ARM detected") 155 | else() 156 | message(STATUS "x86 detected") 157 | if (MSVC) 158 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2") 159 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:AVX2") 160 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /arch:AVX2") 161 | else() 162 | if (EMSCRIPTEN) 163 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") 164 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") 165 | else() 166 | if(NOT WHISPER_NO_AVX) 167 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx") 168 | endif() 169 | if(NOT WHISPER_NO_AVX2) 170 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx2") 171 | endif() 172 | if(NOT WHISPER_NO_FMA) 173 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma") 174 | endif() 175 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mf16c") 176 | endif() 177 | endif() 178 | endif() 179 | 180 | if (WHISPER_PERF) 181 | set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_PERF) 182 | endif() 183 | 184 | # 185 | # whisper - this is the main library of the project 186 | # 187 | 188 | set(TARGET whisper) 189 | 190 | add_library(${TARGET} 191 | ggml.h 192 | ggml.c 193 | whisper.h 194 | whisper.cpp 195 | ) 196 | 197 | include(DefaultTargetOptions) 198 | 199 | target_include_directories(${TARGET} PUBLIC 200 | . 201 | ) 202 | 203 | if (MSVC) 204 | target_link_libraries(${TARGET} PRIVATE ${WHISPER_EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT}) 205 | 206 | set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -D_CRT_SECURE_NO_WARNINGS) 207 | else() 208 | target_link_libraries(${TARGET} PRIVATE m ${WHISPER_EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT}) 209 | endif() 210 | 211 | if (BUILD_SHARED_LIBS) 212 | target_link_libraries(${TARGET} PUBLIC 213 | ${CMAKE_DL_LIBS} 214 | ) 215 | 216 | target_compile_definitions(${TARGET} PUBLIC 217 | WHISPER_SHARED 218 | ) 219 | endif() 220 | 221 | if (EMSCRIPTEN) 222 | set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS "-msimd128") 223 | endif() 224 | 225 | target_compile_definitions(${TARGET} PUBLIC 226 | ${WHISPER_EXTRA_FLAGS} 227 | ) 228 | 229 | install(TARGETS ${TARGET} 230 | LIBRARY DESTINATION lib 231 | ARCHIVE DESTINATION lib/static 232 | RUNTIME DESTINATION bin 233 | ) 234 | 235 | # 236 | # bindings 237 | # 238 | 239 | add_subdirectory(bindings) 240 | 241 | # 242 | # programs, examples and tests 243 | # 244 | 245 | if (WHISPER_BUILD_TESTS) 246 | enable_testing() 247 | add_subdirectory(tests) 248 | endif () 249 | 250 | if (WHISPER_BUILD_EXAMPLES) 251 | add_subdirectory(examples) 252 | endif() 253 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Georgi Gerganov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/Makefile: -------------------------------------------------------------------------------- 1 | ifndef UNAME_S 2 | UNAME_S := $(shell uname -s) 3 | endif 4 | 5 | ifndef UNAME_P 6 | UNAME_P := $(shell uname -p) 7 | endif 8 | 9 | ifndef UNAME_M 10 | UNAME_M := $(shell uname -m) 11 | endif 12 | 13 | CCV := $(shell $(CC) --version | head -n 1) 14 | CXXV := $(shell $(CXX) --version | head -n 1) 15 | 16 | # Mac OS + Arm can report x86_64 17 | # ref: https://github.com/ggerganov/whisper.cpp/issues/66#issuecomment-1282546789 18 | ifeq ($(UNAME_S),Darwin) 19 | ifneq ($(UNAME_P),arm) 20 | SYSCTL_M := $(shell sysctl -n hw.optional.arm64) 21 | ifeq ($(SYSCTL_M),1) 22 | # UNAME_P := arm 23 | # UNAME_M := arm64 24 | warn := $(warning Your arch is announced as x86_64, but it seems to actually be ARM64. Not fixing that can lead to bad performance. For more info see: https://github.com/ggerganov/whisper.cpp/issues/66\#issuecomment-1282546789) 25 | endif 26 | endif 27 | endif 28 | 29 | # 30 | # Compile flags 31 | # 32 | 33 | CFLAGS = -I. -O3 -std=c11 -fPIC 34 | CXXFLAGS = -I. -I./examples -O3 -std=c++11 -fPIC 35 | LDFLAGS = 36 | 37 | # OS specific 38 | # TODO: support Windows 39 | ifeq ($(UNAME_S),Linux) 40 | CFLAGS += -pthread 41 | CXXFLAGS += -pthread 42 | endif 43 | ifeq ($(UNAME_S),Darwin) 44 | CFLAGS += -pthread 45 | CXXFLAGS += -pthread 46 | endif 47 | ifeq ($(UNAME_S),FreeBSD) 48 | CFLAGS += -pthread 49 | CXXFLAGS += -pthread 50 | endif 51 | ifeq ($(UNAME_S),Haiku) 52 | CFLAGS += -pthread 53 | CXXFLAGS += -pthread 54 | endif 55 | 56 | # Architecture specific 57 | # TODO: probably these flags need to be tweaked on some architectures 58 | # feel free to update the Makefile for your architecture and send a pull request or issue 59 | ifeq ($(UNAME_M),$(filter $(UNAME_M),x86_64 i686)) 60 | ifeq ($(UNAME_S),Darwin) 61 | CFLAGS += -mf16c 62 | AVX1_M := $(shell sysctl machdep.cpu.features) 63 | ifneq (,$(findstring FMA,$(AVX1_M))) 64 | CFLAGS += -mfma 65 | endif 66 | ifneq (,$(findstring AVX1.0,$(AVX1_M))) 67 | CFLAGS += -mavx 68 | endif 69 | AVX2_M := $(shell sysctl machdep.cpu.leaf7_features) 70 | ifneq (,$(findstring AVX2,$(AVX2_M))) 71 | CFLAGS += -mavx2 72 | endif 73 | else ifeq ($(UNAME_S),Linux) 74 | AVX1_M := $(shell grep "avx " /proc/cpuinfo) 75 | ifneq (,$(findstring avx,$(AVX1_M))) 76 | CFLAGS += -mavx 77 | endif 78 | AVX2_M := $(shell grep "avx2 " /proc/cpuinfo) 79 | ifneq (,$(findstring avx2,$(AVX2_M))) 80 | CFLAGS += -mavx2 81 | endif 82 | FMA_M := $(shell grep "fma " /proc/cpuinfo) 83 | ifneq (,$(findstring fma,$(FMA_M))) 84 | CFLAGS += -mfma 85 | endif 86 | F16C_M := $(shell grep "f16c " /proc/cpuinfo) 87 | ifneq (,$(findstring f16c,$(F16C_M))) 88 | CFLAGS += -mf16c 89 | endif 90 | SSE3_M := $(shell grep "sse3 " /proc/cpuinfo) 91 | ifneq (,$(findstring sse3,$(SSE3_M))) 92 | CFLAGS += -msse3 93 | endif 94 | else ifeq ($(UNAME_S),Haiku) 95 | AVX1_M := $(shell sysinfo -cpu | grep "AVX ") 96 | ifneq (,$(findstring avx,$(AVX1_M))) 97 | CFLAGS += -mavx 98 | endif 99 | AVX2_M := $(shell sysinfo -cpu | grep "AVX2 ") 100 | ifneq (,$(findstring avx2,$(AVX2_M))) 101 | CFLAGS += -mavx2 102 | endif 103 | FMA_M := $(shell sysinfo -cpu | grep "FMA ") 104 | ifneq (,$(findstring fma,$(FMA_M))) 105 | CFLAGS += -mfma 106 | endif 107 | F16C_M := $(shell sysinfo -cpu | grep "F16C ") 108 | ifneq (,$(findstring f16c,$(F16C_M))) 109 | CFLAGS += -mf16c 110 | endif 111 | else 112 | CFLAGS += -mfma -mf16c -mavx -mavx2 113 | endif 114 | endif 115 | ifeq ($(UNAME_M),amd64) 116 | CFLAGS += -mavx -mavx2 -mfma -mf16c 117 | endif 118 | ifneq ($(filter ppc64%,$(UNAME_M)),) 119 | POWER9_M := $(shell grep "POWER9" /proc/cpuinfo) 120 | ifneq (,$(findstring POWER9,$(POWER9_M))) 121 | CFLAGS += -mpower9-vector 122 | endif 123 | # Require c++23's std::byteswap for big-endian support. 124 | ifeq ($(UNAME_M),ppc64) 125 | CXXFLAGS += -std=c++23 -DGGML_BIG_ENDIAN 126 | endif 127 | endif 128 | ifndef WHISPER_NO_ACCELERATE 129 | # Mac M1 - include Accelerate framework 130 | ifeq ($(UNAME_S),Darwin) 131 | CFLAGS += -DGGML_USE_ACCELERATE 132 | LDFLAGS += -framework Accelerate 133 | endif 134 | endif 135 | ifdef WHISPER_OPENBLAS 136 | CFLAGS += -DGGML_USE_OPENBLAS -I/usr/local/include/openblas 137 | LDFLAGS += -lopenblas 138 | endif 139 | ifdef WHISPER_GPROF 140 | CFLAGS += -pg 141 | CXXFLAGS += -pg 142 | endif 143 | ifneq ($(filter aarch64%,$(UNAME_M)),) 144 | endif 145 | ifneq ($(filter armv6%,$(UNAME_M)),) 146 | # Raspberry Pi 1, 2, 3 147 | CFLAGS += -mfpu=neon-fp-armv8 -mfp16-format=ieee -mno-unaligned-access 148 | endif 149 | ifneq ($(filter armv7%,$(UNAME_M)),) 150 | # Raspberry Pi 4 151 | CFLAGS += -mfpu=neon-fp-armv8 -mfp16-format=ieee -mno-unaligned-access -funsafe-math-optimizations 152 | endif 153 | ifneq ($(filter armv8%,$(UNAME_M)),) 154 | # Raspberry Pi 4 155 | CFLAGS += -mfp16-format=ieee -mno-unaligned-access 156 | endif 157 | 158 | # 159 | # Print build information 160 | # 161 | 162 | $(info I whisper.cpp build info: ) 163 | $(info I UNAME_S: $(UNAME_S)) 164 | $(info I UNAME_P: $(UNAME_P)) 165 | $(info I UNAME_M: $(UNAME_M)) 166 | $(info I CFLAGS: $(CFLAGS)) 167 | $(info I CXXFLAGS: $(CXXFLAGS)) 168 | $(info I LDFLAGS: $(LDFLAGS)) 169 | $(info I CC: $(CCV)) 170 | $(info I CXX: $(CXXV)) 171 | $(info ) 172 | 173 | default: main 174 | 175 | # 176 | # Build library 177 | # 178 | 179 | ggml.o: ggml.c ggml.h 180 | $(CC) $(CFLAGS) -c ggml.c -o ggml.o 181 | 182 | whisper.o: whisper.cpp whisper.h 183 | $(CXX) $(CXXFLAGS) -c whisper.cpp -o whisper.o 184 | 185 | libwhisper.a: ggml.o whisper.o 186 | $(AR) rcs libwhisper.a ggml.o whisper.o 187 | 188 | libwhisper.so: ggml.o whisper.o 189 | $(CXX) $(CXXFLAGS) -shared -o libwhisper.so ggml.o whisper.o $(LDFLAGS) 190 | 191 | clean: 192 | rm -f *.o main stream command talk bench libwhisper.a libwhisper.so 193 | 194 | # 195 | # Examples 196 | # 197 | 198 | CC_SDL=`sdl2-config --cflags --libs` 199 | 200 | main: examples/main/main.cpp ggml.o whisper.o 201 | $(CXX) $(CXXFLAGS) examples/main/main.cpp ggml.o whisper.o -o main $(LDFLAGS) 202 | ./main -h 203 | 204 | stream: examples/stream/stream.cpp ggml.o whisper.o 205 | $(CXX) $(CXXFLAGS) examples/stream/stream.cpp ggml.o whisper.o -o stream $(CC_SDL) $(LDFLAGS) 206 | 207 | command: examples/command/command.cpp ggml.o whisper.o 208 | $(CXX) $(CXXFLAGS) examples/command/command.cpp ggml.o whisper.o -o command $(CC_SDL) $(LDFLAGS) 209 | 210 | talk: examples/talk/talk.cpp examples/talk/gpt-2.cpp ggml.o whisper.o 211 | $(CXX) $(CXXFLAGS) examples/talk/talk.cpp examples/talk/gpt-2.cpp ggml.o whisper.o -o talk $(CC_SDL) $(LDFLAGS) 212 | 213 | bench: examples/bench/bench.cpp ggml.o whisper.o 214 | $(CXX) $(CXXFLAGS) examples/bench/bench.cpp ggml.o whisper.o -o bench $(LDFLAGS) 215 | 216 | # 217 | # Audio samples 218 | # 219 | 220 | # download a few audio samples into folder "./samples": 221 | .PHONY: samples 222 | samples: 223 | @echo "Downloading samples..." 224 | @mkdir -p samples 225 | @wget --quiet --show-progress -O samples/gb0.ogg https://upload.wikimedia.org/wikipedia/commons/2/22/George_W._Bush%27s_weekly_radio_address_%28November_1%2C_2008%29.oga 226 | @wget --quiet --show-progress -O samples/gb1.ogg https://upload.wikimedia.org/wikipedia/commons/1/1f/George_W_Bush_Columbia_FINAL.ogg 227 | @wget --quiet --show-progress -O samples/hp0.ogg https://upload.wikimedia.org/wikipedia/en/d/d4/En.henryfphillips.ogg 228 | @wget --quiet --show-progress -O samples/mm1.wav https://cdn.openai.com/whisper/draft-20220913a/micro-machines.wav 229 | @echo "Converting to 16-bit WAV ..." 230 | @ffmpeg -loglevel -0 -y -i samples/gb0.ogg -ar 16000 -ac 1 -c:a pcm_s16le samples/gb0.wav 231 | @ffmpeg -loglevel -0 -y -i samples/gb1.ogg -ar 16000 -ac 1 -c:a pcm_s16le samples/gb1.wav 232 | @ffmpeg -loglevel -0 -y -i samples/hp0.ogg -ar 16000 -ac 1 -c:a pcm_s16le samples/hp0.wav 233 | @ffmpeg -loglevel -0 -y -i samples/mm1.wav -ar 16000 -ac 1 -c:a pcm_s16le samples/mm0.wav 234 | @rm samples/mm1.wav 235 | 236 | # 237 | # Models 238 | # 239 | 240 | # if not already downloaded, the following targets download the specified model and 241 | # runs it on all samples in the folder "./samples": 242 | 243 | .PHONY: tiny.en 244 | .PHONY: tiny 245 | .PHONY: base.en 246 | .PHONY: base 247 | .PHONY: small.en 248 | .PHONY: small 249 | .PHONY: medium.en 250 | .PHONY: medium 251 | .PHONY: large-v1 252 | .PHONY: large 253 | 254 | tiny.en tiny base.en base small.en small medium.en medium large-v1 large: main 255 | bash ./models/download-ggml-model.sh $@ 256 | @echo "" 257 | @echo "===============================================" 258 | @echo "Running $@ on all samples in ./samples ..." 259 | @echo "===============================================" 260 | @echo "" 261 | @for f in samples/*.wav; do \ 262 | echo "----------------------------------------------" ; \ 263 | echo "[+] Running $@ on $$f ... (run 'ffplay $$f' to listen)" ; \ 264 | echo "----------------------------------------------" ; \ 265 | echo "" ; \ 266 | ./main -m models/ggml-$@.bin -f $$f ; \ 267 | echo "" ; \ 268 | done 269 | 270 | # 271 | # Tests 272 | # 273 | 274 | .PHONY: tests 275 | tests: 276 | bash ./tests/run-tests.sh 277 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | github.com/ggerganov/whisper.cpp/bindings/go 3 | provides a speech-to-text service bindings for the Go programming language. 4 | */ 5 | package whisper 6 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/params.go: -------------------------------------------------------------------------------- 1 | package whisper 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // CGO 9 | 10 | /* 11 | #include "whisper.h" 12 | */ 13 | import "C" 14 | 15 | func (p *Params) SetTranslate(v bool) { 16 | p.translate = toBool(v) 17 | } 18 | 19 | func (p *Params) SetNoContext(v bool) { 20 | p.no_context = toBool(v) 21 | } 22 | 23 | func (p *Params) SetSingleSegment(v bool) { 24 | p.single_segment = toBool(v) 25 | } 26 | 27 | func (p *Params) SetPrintSpecial(v bool) { 28 | p.print_special = toBool(v) 29 | } 30 | 31 | func (p *Params) SetPrintProgress(v bool) { 32 | p.print_progress = toBool(v) 33 | } 34 | 35 | func (p *Params) SetPrintRealtime(v bool) { 36 | p.print_realtime = toBool(v) 37 | } 38 | 39 | func (p *Params) SetPrintTimestamps(v bool) { 40 | p.print_timestamps = toBool(v) 41 | } 42 | 43 | func (p *Params) SetSpeedup(v bool) { 44 | p.speed_up = toBool(v) 45 | } 46 | 47 | // Set language id 48 | func (p *Params) SetLanguage(lang int) error { 49 | str := C.whisper_lang_str(C.int(lang)) 50 | if str == nil { 51 | return ErrInvalidLanguage 52 | } else { 53 | p.language = str 54 | } 55 | return nil 56 | } 57 | 58 | // Get language id 59 | func (p *Params) Language() int { 60 | if p.language == nil { 61 | return -1 62 | } 63 | return int(C.whisper_lang_id(p.language)) 64 | } 65 | 66 | // Set number of threads to use 67 | func (p *Params) SetThreads(threads int) { 68 | p.n_threads = C.int(threads) 69 | } 70 | 71 | // Set start offset in ms 72 | func (p *Params) SetOffset(offset_ms int) { 73 | p.offset_ms = C.int(offset_ms) 74 | } 75 | 76 | // Set audio duration to process in ms 77 | func (p *Params) SetDuration(duration_ms int) { 78 | p.duration_ms = C.int(duration_ms) 79 | } 80 | 81 | // Set timestamp token probability threshold (~0.01) 82 | func (p *Params) SetTokenThreshold(t float32) { 83 | p.thold_pt = C.float(t) 84 | } 85 | 86 | // Set timestamp token sum probability threshold (~0.01) 87 | func (p *Params) SetTokenSumThreshold(t float32) { 88 | p.thold_ptsum = C.float(t) 89 | } 90 | 91 | // Set max segment length in characters 92 | func (p *Params) SetMaxSegmentLength(n int) { 93 | p.max_len = C.int(n) 94 | } 95 | 96 | // Set max tokens per segment (0 = no limit) 97 | func (p *Params) SetMaxTokensPerSegment(n int) { 98 | p.max_tokens = C.int(n) 99 | } 100 | 101 | /////////////////////////////////////////////////////////////////////////////// 102 | // PRIVATE METHODS 103 | 104 | func toBool(v bool) C.bool { 105 | if v { 106 | return C.bool(true) 107 | } 108 | return C.bool(false) 109 | } 110 | 111 | /////////////////////////////////////////////////////////////////////////////// 112 | // STRINGIFY 113 | 114 | func (p *Params) String() string { 115 | str := "" 153 | } 154 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/stt/consts.go: -------------------------------------------------------------------------------- 1 | package tts 2 | 3 | import ( 4 | "errors" 5 | 6 | whisper "github.com/kardianos/whisper.cpp" 7 | ) 8 | 9 | var ( 10 | ErrUnableToLoadModel = errors.New("unable to load model") 11 | ErrInternalAppError = errors.New("internal application error") 12 | ErrProcessingFailed = errors.New("processing failed") 13 | ErrUnsupportedLanguage = errors.New("unsupported language") 14 | ErrModelNotMultilingual = errors.New("model is not multilingual") 15 | ) 16 | 17 | // SampleRate is the sample rate of the audio data. 18 | const SampleRate = whisper.SampleRate 19 | 20 | // SampleBits is the number of bytes per sample. 21 | const SampleBits = whisper.SampleBits 22 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/stt/context.go: -------------------------------------------------------------------------------- 1 | package tts 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "time" 7 | 8 | whisper "github.com/kardianos/whisper.cpp" 9 | ) 10 | 11 | /////////////////////////////////////////////////////////////////////////////// 12 | // TYPES 13 | 14 | type context struct { 15 | n int 16 | model *model 17 | params whisper.Params 18 | } 19 | 20 | // Make sure context adheres to the interface 21 | var _ Context = (*context)(nil) 22 | 23 | /////////////////////////////////////////////////////////////////////////////// 24 | // LIFECYCLE 25 | 26 | func newContext(model *model, params whisper.Params) (Context, error) { 27 | context := new(context) 28 | context.model = model 29 | context.params = params 30 | 31 | // Return success 32 | return context, nil 33 | } 34 | 35 | /////////////////////////////////////////////////////////////////////////////// 36 | // PUBLIC METHODS 37 | 38 | // Set the language to use for speech recognition. 39 | func (context *context) SetLanguage(lang string) error { 40 | if context.model.ctx == nil { 41 | return ErrInternalAppError 42 | } 43 | if !context.model.IsMultilingual() { 44 | return ErrModelNotMultilingual 45 | } 46 | if id := context.model.ctx.Whisper_lang_id(lang); id < 0 { 47 | return ErrUnsupportedLanguage 48 | } else if err := context.params.SetLanguage(id); err != nil { 49 | return err 50 | } 51 | // Return success 52 | return nil 53 | } 54 | 55 | func (context *context) IsMultilingual() bool { 56 | return context.model.IsMultilingual() 57 | } 58 | 59 | // Get language 60 | func (context *context) Language() string { 61 | return whisper.Whisper_lang_str(context.params.Language()) 62 | } 63 | 64 | // Set translate flag 65 | func (context *context) SetTranslate(v bool) { 66 | context.params.SetTranslate(v) 67 | } 68 | 69 | // Set speedup flag 70 | func (context *context) SetSpeedup(v bool) { 71 | context.params.SetSpeedup(v) 72 | } 73 | 74 | // Set number of threads to use 75 | func (context *context) SetThreads(v uint) { 76 | context.params.SetThreads(int(v)) 77 | } 78 | 79 | // Set time offset 80 | func (context *context) SetOffset(v time.Duration) { 81 | context.params.SetOffset(int(v.Milliseconds())) 82 | } 83 | 84 | // Set duration of audio to process 85 | func (context *context) SetDuration(v time.Duration) { 86 | context.params.SetOffset(int(v.Milliseconds())) 87 | } 88 | 89 | // Set timestamp token probability threshold (~0.01) 90 | func (context *context) SetTokenThreshold(t float32) { 91 | context.params.SetTokenThreshold(t) 92 | } 93 | 94 | // Set timestamp token sum probability threshold (~0.01) 95 | func (context *context) SetTokenSumThreshold(t float32) { 96 | context.params.SetTokenSumThreshold(t) 97 | } 98 | 99 | // Set max segment length in characters 100 | func (context *context) SetMaxSegmentLength(n uint) { 101 | context.params.SetMaxSegmentLength(int(n)) 102 | } 103 | 104 | // Set max tokens per segment (0 = no limit) 105 | func (context *context) SetMaxTokensPerSegment(n uint) { 106 | context.params.SetMaxTokensPerSegment(int(n)) 107 | } 108 | 109 | // ResetTimings resets the mode timings. Should be called before processing 110 | func (context *context) ResetTimings() { 111 | context.model.ctx.Whisper_reset_timings() 112 | } 113 | 114 | // PrintTimings prints the model timings to stdout. 115 | func (context *context) PrintTimings() { 116 | context.model.ctx.Whisper_print_timings() 117 | } 118 | 119 | // Use mel data at offset_ms to try and auto-detect the spoken language 120 | // Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first. 121 | // Returns the probabilities of all languages. 122 | func (context *context) WhisperLangAutoDetect(offset_ms int, n_threads int) ([]float32, error) { 123 | langProbs, err := context.model.ctx.Whisper_lang_auto_detect(offset_ms, n_threads) 124 | if err != nil { 125 | return nil, err 126 | } 127 | return langProbs, nil 128 | } 129 | 130 | // Process new sample data and return any errors 131 | func (context *context) Process(data []float32, cb SegmentCallback) error { 132 | if context.model.ctx == nil { 133 | return ErrInternalAppError 134 | } 135 | // If the callback is defined then we force on single_segment mode 136 | if cb != nil { 137 | context.params.SetSingleSegment(true) 138 | } 139 | 140 | // We don't do parallel processing at the moment 141 | processors := 0 142 | if processors > 1 { 143 | if err := context.model.ctx.Whisper_full_parallel(context.params, data, processors, nil, func(new int) { 144 | if cb != nil { 145 | num_segments := context.model.ctx.Whisper_full_n_segments() 146 | s0 := num_segments - new 147 | for i := s0; i < num_segments; i++ { 148 | cb(toSegment(context.model.ctx, i)) 149 | } 150 | } 151 | }); err != nil { 152 | return err 153 | } 154 | } else if err := context.model.ctx.Whisper_full(context.params, data, nil, func(new int) { 155 | if cb != nil { 156 | num_segments := context.model.ctx.Whisper_full_n_segments() 157 | s0 := num_segments - new 158 | for i := s0; i < num_segments; i++ { 159 | cb(toSegment(context.model.ctx, i)) 160 | } 161 | } 162 | }); err != nil { 163 | return err 164 | } 165 | 166 | // Return success 167 | return nil 168 | } 169 | 170 | // Return the next segment of tokens 171 | func (context *context) NextSegment() (Segment, error) { 172 | if context.model.ctx == nil { 173 | return Segment{}, ErrInternalAppError 174 | } 175 | if context.n >= context.model.ctx.Whisper_full_n_segments() { 176 | return Segment{}, io.EOF 177 | } 178 | 179 | // Populate result 180 | result := toSegment(context.model.ctx, context.n) 181 | 182 | // Increment the cursor 183 | context.n++ 184 | 185 | // Return success 186 | return result, nil 187 | } 188 | 189 | // Test for text tokens 190 | func (context *context) IsText(t Token) bool { 191 | switch { 192 | case context.IsBEG(t): 193 | return false 194 | case context.IsSOT(t): 195 | return false 196 | case whisper.Token(t.Id) >= context.model.ctx.Whisper_token_eot(): 197 | return false 198 | case context.IsPREV(t): 199 | return false 200 | case context.IsSOLM(t): 201 | return false 202 | case context.IsNOT(t): 203 | return false 204 | default: 205 | return true 206 | } 207 | } 208 | 209 | // Test for "begin" token 210 | func (context *context) IsBEG(t Token) bool { 211 | return whisper.Token(t.Id) == context.model.ctx.Whisper_token_beg() 212 | } 213 | 214 | // Test for "start of transcription" token 215 | func (context *context) IsSOT(t Token) bool { 216 | return whisper.Token(t.Id) == context.model.ctx.Whisper_token_sot() 217 | } 218 | 219 | // Test for "end of transcription" token 220 | func (context *context) IsEOT(t Token) bool { 221 | return whisper.Token(t.Id) == context.model.ctx.Whisper_token_eot() 222 | } 223 | 224 | // Test for "start of prev" token 225 | func (context *context) IsPREV(t Token) bool { 226 | return whisper.Token(t.Id) == context.model.ctx.Whisper_token_prev() 227 | } 228 | 229 | // Test for "start of lm" token 230 | func (context *context) IsSOLM(t Token) bool { 231 | return whisper.Token(t.Id) == context.model.ctx.Whisper_token_solm() 232 | } 233 | 234 | // Test for "No timestamps" token 235 | func (context *context) IsNOT(t Token) bool { 236 | return whisper.Token(t.Id) == context.model.ctx.Whisper_token_not() 237 | } 238 | 239 | // Test for token associated with a specific language 240 | func (context *context) IsLANG(t Token, lang string) bool { 241 | if id := context.model.ctx.Whisper_lang_id(lang); id >= 0 { 242 | return whisper.Token(t.Id) == context.model.ctx.Whisper_token_lang(id) 243 | } else { 244 | return false 245 | } 246 | } 247 | 248 | /////////////////////////////////////////////////////////////////////////////// 249 | // PRIVATE METHODS 250 | 251 | func toSegment(ctx *whisper.Context, n int) Segment { 252 | return Segment{ 253 | Num: n, 254 | Text: strings.TrimSpace(ctx.Whisper_full_get_segment_text(n)), 255 | Start: time.Duration(ctx.Whisper_full_get_segment_t0(n)) * time.Millisecond * 10, 256 | End: time.Duration(ctx.Whisper_full_get_segment_t1(n)) * time.Millisecond * 10, 257 | Tokens: toTokens(ctx, n), 258 | } 259 | } 260 | 261 | func toTokens(ctx *whisper.Context, n int) []Token { 262 | result := make([]Token, ctx.Whisper_full_n_tokens(n)) 263 | for i := 0; i < len(result); i++ { 264 | result[i] = Token{ 265 | Id: int(ctx.Whisper_full_get_token_id(n, i)), 266 | Text: strings.TrimSpace(ctx.Whisper_full_get_token_text(n, i)), 267 | P: ctx.Whisper_full_get_token_p(n, i), 268 | } 269 | } 270 | return result 271 | } 272 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/stt/doc.go: -------------------------------------------------------------------------------- 1 | // tts is a text to speech package for whisper.cpp. 2 | package tts 3 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/stt/interface.go: -------------------------------------------------------------------------------- 1 | package tts 2 | 3 | import ( 4 | "io" 5 | "time" 6 | ) 7 | 8 | // SegmentCallback is the callback function for processing segments in real 9 | // time. It is called during the Process function 10 | type SegmentCallback func(Segment) 11 | 12 | // Model is the interface to a whisper model. Create a new model with the 13 | // function whisper.New(string) 14 | type Model interface { 15 | io.Closer 16 | 17 | // Return a new speech-to-text context. 18 | NewContext() (Context, error) 19 | 20 | // Return true if the model is multilingual. 21 | IsMultilingual() bool 22 | 23 | // Return all languages supported. 24 | Languages() []string 25 | } 26 | 27 | // Context is the speach recognition context. 28 | type Context interface { 29 | SetLanguage(string) error // Set the language to use for speech recognition. 30 | SetTranslate(bool) // Set translate flag 31 | IsMultilingual() bool // Return true if the model is multilingual. 32 | Language() string // Get language 33 | 34 | SetOffset(time.Duration) // Set offset 35 | SetDuration(time.Duration) // Set duration 36 | SetThreads(uint) // Set number of threads to use 37 | SetSpeedup(bool) // Set speedup flag 38 | SetTokenThreshold(float32) // Set timestamp token probability threshold 39 | SetTokenSumThreshold(float32) // Set timestamp token sum probability threshold 40 | SetMaxSegmentLength(uint) // Set max segment length in characters 41 | SetMaxTokensPerSegment(uint) // Set max tokens per segment (0 = no limit) 42 | 43 | // Process mono audio data and return any errors. 44 | // If defined, newly generated segments are passed to the 45 | // callback function during processing. 46 | Process([]float32, SegmentCallback) error 47 | 48 | // After process is called, return segments until the end of the stream 49 | // is reached, when io.EOF is returned. 50 | NextSegment() (Segment, error) 51 | 52 | IsBEG(Token) bool // Test for "begin" token 53 | IsSOT(Token) bool // Test for "start of transcription" token 54 | IsEOT(Token) bool // Test for "end of transcription" token 55 | IsPREV(Token) bool // Test for "start of prev" token 56 | IsSOLM(Token) bool // Test for "start of lm" token 57 | IsNOT(Token) bool // Test for "No timestamps" token 58 | IsLANG(Token, string) bool // Test for token associated with a specific language 59 | IsText(Token) bool // Test for text token 60 | 61 | PrintTimings() 62 | ResetTimings() 63 | } 64 | 65 | // Segment is the text result of a speech recognition. 66 | type Segment struct { 67 | // Segment Number 68 | Num int 69 | 70 | // Time beginning and end timestamps for the segment. 71 | Start, End time.Duration 72 | 73 | // The text of the segment. 74 | Text string 75 | 76 | // The tokens of the segment. 77 | Tokens []Token 78 | } 79 | 80 | // Token is a text or special token 81 | type Token struct { 82 | Id int 83 | Text string 84 | P float32 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/kardianos/whisper.cpp/stt/model.go: -------------------------------------------------------------------------------- 1 | package tts 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | 8 | whisper "github.com/kardianos/whisper.cpp" 9 | ) 10 | 11 | /////////////////////////////////////////////////////////////////////////////// 12 | // TYPES 13 | 14 | type model struct { 15 | path string 16 | ctx *whisper.Context 17 | } 18 | 19 | // Make sure model adheres to the interface 20 | var _ Model = (*model)(nil) 21 | 22 | /////////////////////////////////////////////////////////////////////////////// 23 | // LIFECYCLE 24 | 25 | func New(path string) (Model, error) { 26 | model := new(model) 27 | if _, err := os.Stat(path); err != nil { 28 | return nil, err 29 | } else if ctx := whisper.Whisper_init(path); ctx == nil { 30 | return nil, ErrUnableToLoadModel 31 | } else { 32 | model.ctx = ctx 33 | model.path = path 34 | } 35 | 36 | // Return success 37 | return model, nil 38 | } 39 | 40 | func (model *model) Close() error { 41 | if model.ctx != nil { 42 | model.ctx.Whisper_free() 43 | } 44 | 45 | // Release resources 46 | model.ctx = nil 47 | 48 | // Return success 49 | return nil 50 | } 51 | 52 | /////////////////////////////////////////////////////////////////////////////// 53 | // STRINGIFY 54 | 55 | func (model *model) String() string { 56 | str := "" 61 | } 62 | 63 | /////////////////////////////////////////////////////////////////////////////// 64 | // PUBLIC METHODS 65 | 66 | // Return true if model is multilingual (language and translation options are supported) 67 | func (model *model) IsMultilingual() bool { 68 | return model.ctx.Whisper_is_multilingual() != 0 69 | } 70 | 71 | // Return all recognized languages. Initially it is set to auto-detect 72 | func (model *model) Languages() []string { 73 | result := make([]string, 0, whisper.Whisper_lang_max_id()) 74 | for i := 0; i < whisper.Whisper_lang_max_id(); i++ { 75 | str := whisper.Whisper_lang_str(i) 76 | if model.ctx.Whisper_lang_id(str) >= 0 { 77 | result = append(result, str) 78 | } 79 | } 80 | return result 81 | } 82 | 83 | func (model *model) NewContext() (Context, error) { 84 | if model.ctx == nil { 85 | return nil, ErrInternalAppError 86 | } 87 | 88 | // Create new context 89 | params := model.ctx.Whisper_full_default_params(whisper.SAMPLING_GREEDY) 90 | params.SetTranslate(false) 91 | params.SetPrintSpecial(false) 92 | params.SetPrintProgress(false) 93 | params.SetPrintRealtime(false) 94 | params.SetPrintTimestamps(false) 95 | params.SetThreads(runtime.NumCPU()) 96 | 97 | // Return new context 98 | return newContext(model, params) 99 | } 100 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/go-audio/audio v1.0.0 2 | ## explicit 3 | github.com/go-audio/audio 4 | # github.com/go-audio/riff v1.0.0 5 | ## explicit; go 1.12 6 | github.com/go-audio/riff 7 | # github.com/go-audio/wav v1.1.0 8 | ## explicit; go 1.13 9 | github.com/go-audio/wav 10 | # github.com/kardianos/task v0.0.0-20210112221240-c03b31243e29 11 | ## explicit; go 1.12 12 | github.com/kardianos/task 13 | github.com/kardianos/task/fsop 14 | # github.com/kardianos/whisper.cpp v1.1.4 15 | ## explicit; go 1.19 16 | github.com/kardianos/whisper.cpp 17 | github.com/kardianos/whisper.cpp/stt 18 | --------------------------------------------------------------------------------