From 3847e616405d79c2d2b0f9d70e66b245bfcc42f5 Mon Sep 17 00:00:00 2001 From: Eva Fineberg Date: Thu, 3 May 2018 15:42:21 +0200 Subject: [PATCH] use non sc specific paths, add vendoring --- cmd/wavelet/points.go | 4 +- cmd/wavelet/waveform.go | 6 +- vendor/github.com/go-audio/audio/LICENSE | 201 +++++++ vendor/github.com/go-audio/audio/README.md | 33 ++ vendor/github.com/go-audio/audio/audio.go | 35 ++ vendor/github.com/go-audio/audio/conv.go | 177 ++++++ vendor/github.com/go-audio/audio/doc.go | 10 + .../github.com/go-audio/audio/float_buffer.go | 156 ++++++ vendor/github.com/go-audio/audio/formats.go | 49 ++ .../github.com/go-audio/audio/int_buffer.go | 106 ++++ .../github.com/go-audio/audio/pcm_buffer.go | 461 +++++++++++++++ vendor/github.com/go-audio/wav/LICENSE | 201 +++++++ vendor/github.com/go-audio/wav/README.md | 8 + vendor/github.com/go-audio/wav/cue_chunk.go | 106 ++++ vendor/github.com/go-audio/wav/decoder.go | 523 ++++++++++++++++++ vendor/github.com/go-audio/wav/encoder.go | 245 ++++++++ vendor/github.com/go-audio/wav/list_chunk.go | 183 ++++++ vendor/github.com/go-audio/wav/metadata.go | 144 +++++ vendor/github.com/go-audio/wav/smpl_chunk.go | 100 ++++ vendor/github.com/go-audio/wav/wav.go | 27 + vendor/github.com/mattetti/audio/LICENSE | 22 + .../github.com/mattetti/audio/riff/README.md | 6 + .../github.com/mattetti/audio/riff/chunk.go | 130 +++++ vendor/github.com/mattetti/audio/riff/doc.go | 15 + .../github.com/mattetti/audio/riff/parser.go | 256 +++++++++ vendor/github.com/mattetti/audio/riff/riff.go | 41 ++ vendor/github.com/pkg/errors/LICENSE | 23 + vendor/github.com/pkg/errors/README.md | 52 ++ vendor/github.com/pkg/errors/appveyor.yml | 32 ++ vendor/github.com/pkg/errors/errors.go | 269 +++++++++ vendor/github.com/pkg/errors/stack.go | 147 +++++ vendor/vendor.json | 31 ++ 32 files changed, 3794 insertions(+), 5 deletions(-) create mode 100644 vendor/github.com/go-audio/audio/LICENSE create mode 100644 vendor/github.com/go-audio/audio/README.md create mode 100644 vendor/github.com/go-audio/audio/audio.go create mode 100644 vendor/github.com/go-audio/audio/conv.go create mode 100644 vendor/github.com/go-audio/audio/doc.go create mode 100644 vendor/github.com/go-audio/audio/float_buffer.go create mode 100644 vendor/github.com/go-audio/audio/formats.go create mode 100644 vendor/github.com/go-audio/audio/int_buffer.go create mode 100644 vendor/github.com/go-audio/audio/pcm_buffer.go create mode 100644 vendor/github.com/go-audio/wav/LICENSE create mode 100644 vendor/github.com/go-audio/wav/README.md create mode 100644 vendor/github.com/go-audio/wav/cue_chunk.go create mode 100644 vendor/github.com/go-audio/wav/decoder.go create mode 100644 vendor/github.com/go-audio/wav/encoder.go create mode 100644 vendor/github.com/go-audio/wav/list_chunk.go create mode 100644 vendor/github.com/go-audio/wav/metadata.go create mode 100644 vendor/github.com/go-audio/wav/smpl_chunk.go create mode 100644 vendor/github.com/go-audio/wav/wav.go create mode 100644 vendor/github.com/mattetti/audio/LICENSE create mode 100644 vendor/github.com/mattetti/audio/riff/README.md create mode 100644 vendor/github.com/mattetti/audio/riff/chunk.go create mode 100644 vendor/github.com/mattetti/audio/riff/doc.go create mode 100644 vendor/github.com/mattetti/audio/riff/parser.go create mode 100644 vendor/github.com/mattetti/audio/riff/riff.go create mode 100644 vendor/github.com/pkg/errors/LICENSE create mode 100644 vendor/github.com/pkg/errors/README.md create mode 100644 vendor/github.com/pkg/errors/appveyor.yml create mode 100644 vendor/github.com/pkg/errors/errors.go create mode 100644 vendor/github.com/pkg/errors/stack.go create mode 100644 vendor/vendor.json diff --git a/cmd/wavelet/points.go b/cmd/wavelet/points.go index 4c01e10..84a03bf 100644 --- a/cmd/wavelet/points.go +++ b/cmd/wavelet/points.go @@ -5,8 +5,8 @@ import ( "net/http" "os" - "github.com/soundcloud/wavelet/lib/audio" - "github.com/soundcloud/wavelet/lib/sampler" + "github.com/wwgberlin/go-funk/lib/audio" + "github.com/wwgberlin/go-funk/lib/sampler" ) func points(w http.ResponseWriter, req *http.Request) { diff --git a/cmd/wavelet/waveform.go b/cmd/wavelet/waveform.go index dca2939..a25a240 100644 --- a/cmd/wavelet/waveform.go +++ b/cmd/wavelet/waveform.go @@ -10,9 +10,9 @@ import ( "os" "strconv" - "github.com/soundcloud/wavelet/lib/audio" - "github.com/soundcloud/wavelet/lib/sampler" - "github.com/soundcloud/wavelet/lib/utils" + "github.com/wwgberlin/go-funk/lib/audio" + "github.com/wwgberlin/go-funk/lib/sampler" + "github.com/wwgberlin/go-funk/lib/utils" ) var sampling = map[string]sampler.SamplerFunc{ diff --git a/vendor/github.com/go-audio/audio/LICENSE b/vendor/github.com/go-audio/audio/LICENSE new file mode 100644 index 0000000..54790e8 --- /dev/null +++ b/vendor/github.com/go-audio/audio/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 Matt Aimonetti + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-audio/audio/README.md b/vendor/github.com/go-audio/audio/README.md new file mode 100644 index 0000000..44bdfe1 --- /dev/null +++ b/vendor/github.com/go-audio/audio/README.md @@ -0,0 +1,33 @@ +# audio + +[![GoDoc](http://godoc.org/github.com/go-audio/audio?status.svg)](http://godoc.org/github.com/go-audio/audio) + +`audio` is a generic Go package designed to define a common interface to analyze +and/or process audio data. + +At the heart of the package is the `Buffer` interface and its implementations: + +* `FloatBuffer` +* `Float32Buffer` +* `IntBuffer` + +Decoders, encoders, processors, analyzers and transformers can be written to +accept or return these types and share a common interface. + +The idea is that audio libraries can define this interface or its +implementations as input and return an `audio.Buffer` interface allowing all +audio libraries to be chainable. + +## Performance + +The buffer implementations are designed so a buffer can be reused and mutated +avoiding allocation penalties. + +It is recommended to avoid using `Float32Buffer` unless performance is critical. +The major drawback of using float32s is that the Go stdlib was designed to work +with float64 and therefore the access to standard packages is limited. + +## Usage + +Examples of how to use this interface is available under the +[go-audio](https://github.com/go-audio) organization. diff --git a/vendor/github.com/go-audio/audio/audio.go b/vendor/github.com/go-audio/audio/audio.go new file mode 100644 index 0000000..531fc89 --- /dev/null +++ b/vendor/github.com/go-audio/audio/audio.go @@ -0,0 +1,35 @@ +package audio + +import ( + "errors" +) + +var ( + // ErrInvalidBuffer is a generic error returned when trying to read/write to an invalid buffer. + ErrInvalidBuffer = errors.New("invalid buffer") +) + +// Format is a high level representation of the underlying data. +type Format struct { + // NumChannels is the number of channels contained in the data + NumChannels int + // SampleRate is the sampling rate in Hz + SampleRate int +} + +// Buffer is the representation of an audio buffer. +type Buffer interface { + // PCMFormat is the format of buffer (describing the buffer content/format). + PCMFormat() *Format + // NumFrames returns the number of frames contained in the buffer. + NumFrames() int + // AsFloatBuffer returns a float 64 buffer from this buffer. + AsFloatBuffer() *FloatBuffer + // AsFloat32Buffer returns a float 32 buffer from this buffer. + AsFloat32Buffer() *Float32Buffer + // AsIntBuffer returns an int buffer from this buffer. + AsIntBuffer() *IntBuffer + // Clone creates a clean clone that can be modified without + // changing the source buffer. + Clone() Buffer +} diff --git a/vendor/github.com/go-audio/audio/conv.go b/vendor/github.com/go-audio/audio/conv.go new file mode 100644 index 0000000..e6aadb5 --- /dev/null +++ b/vendor/github.com/go-audio/audio/conv.go @@ -0,0 +1,177 @@ +package audio + +import "math" + +// IntMaxSignedValue returns the max value of an integer +// based on its memory size +func IntMaxSignedValue(b int) int { + switch b { + case 8: + return 255 / 2 + case 16: + return 65535 / 2 + case 24: + return 16777215 / 2 + case 32: + return 4294967295 / 2 + default: + return 0 + } +} + +// IEEEFloatToInt converts a 10 byte IEEE float into an int. +func IEEEFloatToInt(b [10]byte) int { + var i uint32 + // Negative number + if (b[0] & 0x80) == 1 { + return 0 + } + + // Less than 1 + if b[0] <= 0x3F { + return 1 + } + + // Too big + if b[0] > 0x40 { + return 67108864 + } + + // Still too big + if b[0] == 0x40 && b[1] > 0x1C { + return 800000000 + } + + i = (uint32(b[2]) << 23) | (uint32(b[3]) << 15) | (uint32(b[4]) << 7) | (uint32(b[5]) >> 1) + i >>= (29 - uint32(b[1])) + + return int(i) +} + +// IntToIEEEFloat converts an int into a 10 byte IEEE float. +func IntToIEEEFloat(i int) [10]byte { + b := [10]byte{} + num := float64(i) + + var sign int + var expon int + var fMant, fsMant float64 + var hiMant, loMant uint + + if num < 0 { + sign = 0x8000 + } else { + sign = 0 + } + + if num == 0 { + expon = 0 + hiMant = 0 + loMant = 0 + } else { + fMant, expon = math.Frexp(num) + if (expon > 16384) || !(fMant < 1) { /* Infinity or NaN */ + expon = sign | 0x7FFF + hiMant = 0 + loMant = 0 /* infinity */ + } else { /* Finite */ + expon += 16382 + if expon < 0 { /* denormalized */ + fMant = math.Ldexp(fMant, expon) + expon = 0 + } + expon |= sign + fMant = math.Ldexp(fMant, 32) + fsMant = math.Floor(fMant) + hiMant = uint(fsMant) + fMant = math.Ldexp(fMant-fsMant, 32) + fsMant = math.Floor(fMant) + loMant = uint(fsMant) + } + } + + b[0] = byte(expon >> 8) + b[1] = byte(expon) + b[2] = byte(hiMant >> 24) + b[3] = byte(hiMant >> 16) + b[4] = byte(hiMant >> 8) + b[5] = byte(hiMant) + b[6] = byte(loMant >> 24) + b[7] = byte(loMant >> 16) + b[8] = byte(loMant >> 8) + b[9] = byte(loMant) + + return b +} + +// Uint24to32 converts a 3 byte uint23 into a uint32 +// BigEndian! +func Uint24to32(bytes []byte) uint32 { + var output uint32 + output |= uint32(bytes[2]) << 0 + output |= uint32(bytes[1]) << 8 + output |= uint32(bytes[0]) << 16 + + return output +} + +// Int24BETo32 converts an int24 value from 3 bytes into an int32 value +func Int24BETo32(bytes []byte) int32 { + if len(bytes) < 3 { + return 0 + } + ss := int32(0xFF&bytes[0])<<16 | int32(0xFF&bytes[1])<<8 | int32(0xFF&bytes[2]) + if (ss & 0x800000) > 0 { + ss |= ^0xffffff + } + + return ss +} + +// Int24LETo32 converts an int24 value from 3 bytes into an int32 value +func Int24LETo32(bytes []byte) int32 { + if len(bytes) < 3 { + return 0 + } + ss := int32(bytes[0]) | int32(bytes[1])<<8 | int32(bytes[2])<<16 + if (ss & 0x800000) > 0 { + ss |= ^0xffffff + } + + return ss +} + +// Uint32toUint24Bytes converts a uint32 into a 3 byte uint24 representation +func Uint32toUint24Bytes(n uint32) []byte { + bytes := make([]byte, 3) + bytes[0] = byte(n >> 16) + bytes[1] = byte(n >> 8) + bytes[2] = byte(n >> 0) + + return bytes +} + +// Int32toInt24LEBytes converts an int32 into a little endian 3 byte int24 representation +func Int32toInt24LEBytes(n int32) []byte { + bytes := make([]byte, 3) + if (n & 0x800000) > 0 { + n |= ^0xffffff + } + bytes[2] = byte(n >> 16) + bytes[1] = byte(n >> 8) + bytes[0] = byte(n >> 0) + return bytes +} + +// Int32toInt24BEBytes converts an int32 into a big endian 3 byte int24 representation +func Int32toInt24BEBytes(n int32) []byte { + bytes := make([]byte, 3) + if (n & 0x800000) > 0 { + n |= ^0xffffff + } + bytes[0] = byte(n >> 16) + bytes[1] = byte(n >> 8) + bytes[2] = byte(n >> 0) + + return bytes +} diff --git a/vendor/github.com/go-audio/audio/doc.go b/vendor/github.com/go-audio/audio/doc.go new file mode 100644 index 0000000..2fd77bf --- /dev/null +++ b/vendor/github.com/go-audio/audio/doc.go @@ -0,0 +1,10 @@ +/* +Package audio defines a common +interface to analyze and/or process audio data. + +At the heart of the package is the Buffer interface and its implementations: +FloatBuffer and IntBuffer. +Decoders, encoders, processors, analyzers and transformers can be written to +accept or return these types and share a common interface. +*/ +package audio diff --git a/vendor/github.com/go-audio/audio/float_buffer.go b/vendor/github.com/go-audio/audio/float_buffer.go new file mode 100644 index 0000000..8698810 --- /dev/null +++ b/vendor/github.com/go-audio/audio/float_buffer.go @@ -0,0 +1,156 @@ +package audio + +var _ Buffer = (*FloatBuffer)(nil) +var _ Buffer = (*Float32Buffer)(nil) + +// FloatBuffer is an audio buffer with its PCM data formatted as float64. +type FloatBuffer struct { + // Format is the representation of the underlying data format + Format *Format + // Data is the buffer PCM data as floats + Data []float64 +} + +// PCMFormat returns the buffer format information. +func (buf *FloatBuffer) PCMFormat() *Format { return buf.Format } + +// AsFloatBuffer implements the Buffer interface and returns itself. +func (buf *FloatBuffer) AsFloatBuffer() *FloatBuffer { return buf } + +// AsFloat32Buffer implements the Buffer interface and returns a float 32 version of itself. +func (buf *FloatBuffer) AsFloat32Buffer() *Float32Buffer { + newB := &Float32Buffer{} + newB.Data = make([]float32, len(buf.Data)) + for i := 0; i < len(buf.Data); i++ { + newB.Data[i] = float32(buf.Data[i]) + } + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} + +// AsIntBuffer returns a copy of this buffer but with data truncated to Ints. +func (buf *FloatBuffer) AsIntBuffer() *IntBuffer { + newB := &IntBuffer{} + newB.Data = make([]int, len(buf.Data)) + for i := 0; i < len(buf.Data); i++ { + newB.Data[i] = int(buf.Data[i]) + } + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} + +// Clone creates a clean clone that can be modified without +// changing the source buffer. +func (buf *FloatBuffer) Clone() Buffer { + if buf == nil { + return nil + } + newB := &FloatBuffer{} + newB.Data = make([]float64, len(buf.Data)) + copy(newB.Data, buf.Data) + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} + +// NumFrames returns the number of frames contained in the buffer. +func (buf *FloatBuffer) NumFrames() int { + if buf == nil || buf.Format == nil { + return 0 + } + numChannels := buf.Format.NumChannels + if numChannels == 0 { + numChannels = 1 + } + + return len(buf.Data) / numChannels +} + +// Float32Buffer is an audio buffer with its PCM data formatted as float32. +type Float32Buffer struct { + // Format is the representation of the underlying data format + Format *Format + // Data is the buffer PCM data as floats + Data []float32 + // SourceBitDepth helps us know if the source was encoded on + // 8, 16, 24, 32, 64 bits. + SourceBitDepth int +} + +// PCMFormat returns the buffer format information. +func (buf *Float32Buffer) PCMFormat() *Format { return buf.Format } + +// AsFloatBuffer implements the Buffer interface and returns a float64 version of itself. +func (buf *Float32Buffer) AsFloatBuffer() *FloatBuffer { + newB := &FloatBuffer{} + newB.Data = make([]float64, len(buf.Data)) + for i := 0; i < len(buf.Data); i++ { + newB.Data[i] = float64(buf.Data[i]) + } + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} + +// AsFloat32Buffer implements the Buffer interface and returns itself. +func (buf *Float32Buffer) AsFloat32Buffer() *Float32Buffer { return buf } + +// AsIntBuffer returns a copy of this buffer but with data truncated to Ints. +// It is usually recommended to apply a transforms when going from a 24bit source +// to an int (16bit destination). Look at transforms.PCMScaleF32() for instance +func (buf *Float32Buffer) AsIntBuffer() *IntBuffer { + newB := &IntBuffer{SourceBitDepth: buf.SourceBitDepth} + if newB.SourceBitDepth == 0 { + newB.SourceBitDepth = 16 + } + newB.Data = make([]int, len(buf.Data)) + // TODO: we might want to consider checking the min/max values + // and if we are in a normalized float range, apply a denormalization. + for i := 0; i < len(buf.Data); i++ { + newB.Data[i] = int(buf.Data[i]) + } + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} + +// Clone creates a clean clone that can be modified without +// changing the source buffer. +func (buf *Float32Buffer) Clone() Buffer { + if buf == nil { + return nil + } + newB := &Float32Buffer{} + newB.Data = make([]float32, len(buf.Data)) + copy(newB.Data, buf.Data) + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} + +// NumFrames returns the number of frames contained in the buffer. +func (buf *Float32Buffer) NumFrames() int { + if buf == nil || buf.Format == nil { + return 0 + } + numChannels := buf.Format.NumChannels + if numChannels == 0 { + numChannels = 1 + } + + return len(buf.Data) / numChannels +} diff --git a/vendor/github.com/go-audio/audio/formats.go b/vendor/github.com/go-audio/audio/formats.go new file mode 100644 index 0000000..215bbb8 --- /dev/null +++ b/vendor/github.com/go-audio/audio/formats.go @@ -0,0 +1,49 @@ +package audio + +var ( + // MONO + + // FormatMono22500 is mono 22.5kHz format. + FormatMono22500 = &Format{ + NumChannels: 1, + SampleRate: 22500, + } + // FormatMono44100 is mono 8bit 44.1kHz format. + FormatMono44100 = &Format{ + NumChannels: 1, + SampleRate: 44100, + } + // FormatMono48000 is mono 48kHz format. + FormatMono48000 = &Format{ + NumChannels: 1, + SampleRate: 48000, + } + // FormatMono96000 is mono 96kHz format. + FormatMono96000 = &Format{ + NumChannels: 1, + SampleRate: 96000, + } + + // STEREO + + // FormatStereo22500 is stereo 22.5kHz format. + FormatStereo22500 = &Format{ + NumChannels: 2, + SampleRate: 22500, + } + // FormatStereo44100 is stereo 8bit 44.1kHz format. + FormatStereo44100 = &Format{ + NumChannels: 2, + SampleRate: 44100, + } + // FormatStereo48000 is stereo 48kHz format. + FormatStereo48000 = &Format{ + NumChannels: 2, + SampleRate: 48000, + } + // FormatStereo96000 is stereo 96kHz format. + FormatStereo96000 = &Format{ + NumChannels: 2, + SampleRate: 96000, + } +) diff --git a/vendor/github.com/go-audio/audio/int_buffer.go b/vendor/github.com/go-audio/audio/int_buffer.go new file mode 100644 index 0000000..fccd1de --- /dev/null +++ b/vendor/github.com/go-audio/audio/int_buffer.go @@ -0,0 +1,106 @@ +package audio + +import "math" + +var _ Buffer = (*IntBuffer)(nil) + +// IntBuffer is an audio buffer with its PCM data formatted as int. +type IntBuffer struct { + // Format is the representation of the underlying data format + Format *Format + // Data is the buffer PCM data as ints + Data []int + // SourceBitDepth helps us know if the source was encoded on + // 8, 16, 24, 32, 64 bits. + SourceBitDepth int +} + +// PCMFormat returns the buffer format information. +func (buf *IntBuffer) PCMFormat() *Format { return buf.Format } + +// AsFloatBuffer returns a copy of this buffer but with data converted to floats. +func (buf *IntBuffer) AsFloatBuffer() *FloatBuffer { + newB := &FloatBuffer{} + newB.Data = make([]float64, len(buf.Data)) + for i := 0; i < len(buf.Data); i++ { + newB.Data[i] = float64(buf.Data[i]) + } + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} + +// AsFloat32Buffer returns a copy of this buffer but with data converted to float 32. +func (buf *IntBuffer) AsFloat32Buffer() *Float32Buffer { + newB := &Float32Buffer{} + newB.Data = make([]float32, len(buf.Data)) + max := int64(0) + // try to guess the bit depths without knowing the source + if buf.SourceBitDepth == 0 { + for _, s := range buf.Data { + if int64(s) > max { + max = int64(s) + } + } + buf.SourceBitDepth = 8 + if max > 127 { + buf.SourceBitDepth = 16 + } + // greater than int16, expecting int24 + if max > 32767 { + buf.SourceBitDepth = 24 + } + // int 32 + if max > 8388607 { + buf.SourceBitDepth = 32 + } + // int 64 + if max > 4294967295 { + buf.SourceBitDepth = 64 + } + } + newB.SourceBitDepth = buf.SourceBitDepth + factor := math.Pow(2, float64(buf.SourceBitDepth)-1) + for i := 0; i < len(buf.Data); i++ { + newB.Data[i] = float32(float64(buf.Data[i]) / factor) + } + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} + +// AsIntBuffer implements the Buffer interface and returns itself. +func (buf *IntBuffer) AsIntBuffer() *IntBuffer { return buf } + +// NumFrames returns the number of frames contained in the buffer. +func (buf *IntBuffer) NumFrames() int { + if buf == nil || buf.Format == nil { + return 0 + } + numChannels := buf.Format.NumChannels + if numChannels == 0 { + numChannels = 1 + } + + return len(buf.Data) / numChannels +} + +// Clone creates a clean clone that can be modified without +// changing the source buffer. +func (buf *IntBuffer) Clone() Buffer { + if buf == nil { + return nil + } + newB := &IntBuffer{} + newB.Data = make([]int, len(buf.Data)) + copy(newB.Data, buf.Data) + newB.Format = &Format{ + NumChannels: buf.Format.NumChannels, + SampleRate: buf.Format.SampleRate, + } + return newB +} diff --git a/vendor/github.com/go-audio/audio/pcm_buffer.go b/vendor/github.com/go-audio/audio/pcm_buffer.go new file mode 100644 index 0000000..dae22ef --- /dev/null +++ b/vendor/github.com/go-audio/audio/pcm_buffer.go @@ -0,0 +1,461 @@ +package audio + +import "math" + +// PCMDataFormat is an enum type to indicate the underlying data format used. +type PCMDataFormat uint8 + +const ( + // DataTypeUnknown refers to an unknown format + DataTypeUnknown PCMDataFormat = iota + // DataTypeI8 indicates that the content of the audio buffer made of 8-bit integers. + DataTypeI8 + // DataTypeI16 indicates that the content of the audio buffer made of 16-bit integers. + DataTypeI16 + // DataTypeI32 indicates that the content of the audio buffer made of 32-bit integers. + DataTypeI32 + // DataTypeF32 indicates that the content of the audio buffer made of 32-bit floats. + DataTypeF32 + // DataTypeF64 indicates that the content of the audio buffer made of 64-bit floats. + DataTypeF64 +) + +var _ Buffer = (*PCMBuffer)(nil) + +// PCMBuffer encapsulates uncompressed audio data +// and provides useful methods to read/manipulate this PCM data. +// It's a more flexible buffer type allowing the developer to handle +// different kind of buffer data formats and convert between underlying +// types. +type PCMBuffer struct { + // Format describes the format of the buffer data. + Format *Format + // I8 is a store for audio sample data as integers. + I8 []int8 + // I16 is a store for audio sample data as integers. + I16 []int16 + // I32 is a store for audio sample data as integers. + I32 []int32 + // F32 is a store for audio samples data as float64. + F32 []float32 + // F64 is a store for audio samples data as float64. + F64 []float64 + // DataType indicates the primary format used for the underlying data. + // The consumer of the buffer might want to look at this value to know what store + // to use to optimaly retrieve data. + DataType PCMDataFormat + // SourceBitDepth helps us know if the source was encoded on + // 1 (int8), 2 (int16), 3(int24), 4(int32), 8(int64) bytes. + SourceBitDepth uint8 +} + +// Len returns the length of the underlying data. +func (b *PCMBuffer) Len() int { + if b == nil { + return 0 + } + + switch b.DataType { + case DataTypeI8: + return len(b.I8) + case DataTypeI16: + return len(b.I16) + case DataTypeI32: + return len(b.I32) + case DataTypeF32: + return len(b.F32) + case DataTypeF64: + return len(b.F64) + default: + return 0 + } +} + +// PCMFormat returns the buffer format information. +func (b *PCMBuffer) PCMFormat() *Format { + if b == nil { + return nil + } + return b.Format +} + +// NumFrames returns the number of frames contained in the buffer. +func (b *PCMBuffer) NumFrames() int { + if b == nil || b.Format == nil { + return 0 + } + numChannels := b.Format.NumChannels + if numChannels == 0 { + numChannels = 1 + } + + return b.Len() / numChannels +} + +// AsFloatBuffer returns a copy of this buffer but with data converted to floats. +func (b *PCMBuffer) AsFloatBuffer() *FloatBuffer { + newB := &FloatBuffer{} + newB.Data = b.AsF64() + if b.Format != nil { + newB.Format = &Format{ + NumChannels: b.Format.NumChannels, + SampleRate: b.Format.SampleRate, + } + } + return newB +} + +// AsFloat32Buffer implements the Buffer interface and returns a float 32 version of itself. +func (b *PCMBuffer) AsFloat32Buffer() *Float32Buffer { + newB := &Float32Buffer{} + newB.Data = b.AsF32() + if b.Format != nil { + newB.Format = &Format{ + NumChannels: b.Format.NumChannels, + SampleRate: b.Format.SampleRate, + } + } + return newB +} + +// AsIntBuffer returns a copy of this buffer but with data truncated to Ints. +func (b *PCMBuffer) AsIntBuffer() *IntBuffer { + newB := &IntBuffer{} + newB.Data = b.AsInt() + if b.Format != nil { + newB.Format = &Format{ + NumChannels: b.Format.NumChannels, + SampleRate: b.Format.SampleRate, + } + } + return newB +} + +// AsI8 returns the buffer's samples as int8 sample values. +// If the buffer isn't in this format, a copy is created and converted. +// Note that converting might result in loss of resolution. +func (b *PCMBuffer) AsI8() (out []int8) { + if b == nil { + return nil + } + switch b.DataType { + case DataTypeI8: + return b.I8 + case DataTypeI16: + out = make([]int8, len(b.I16)) + for i := 0; i < len(b.I16); i++ { + out[i] = int8(b.I16[i]) + } + case DataTypeI32: + out = make([]int8, len(b.I32)) + for i := 0; i < len(b.I32); i++ { + out[i] = int8(b.I32[i]) + } + case DataTypeF32: + out = make([]int8, len(b.F32)) + for i := 0; i < len(b.F32); i++ { + out[i] = int8(b.F32[i]) + } + case DataTypeF64: + out = make([]int8, len(b.F64)) + for i := 0; i < len(b.F64); i++ { + out[i] = int8(b.F64[i]) + } + } + return out +} + +// AsI16 returns the buffer's samples as int16 sample values. +// If the buffer isn't in this format, a copy is created and converted. +// Note that converting might result in loss of resolution. +func (b *PCMBuffer) AsI16() (out []int16) { + if b == nil { + return nil + } + switch b.DataType { + case DataTypeI8: + out = make([]int16, len(b.I8)) + for i := 0; i < len(b.I8); i++ { + out[i] = int16(b.I8[i]) + } + case DataTypeI16: + return b.I16 + case DataTypeI32: + out = make([]int16, len(b.I32)) + for i := 0; i < len(b.I32); i++ { + out[i] = int16(b.I32[i]) + } + case DataTypeF32: + out = make([]int16, len(b.F32)) + for i := 0; i < len(b.F32); i++ { + out[i] = int16(b.F32[i]) + } + case DataTypeF64: + out = make([]int16, len(b.F64)) + for i := 0; i < len(b.F64); i++ { + out[i] = int16(b.F64[i]) + } + } + return out +} + +// AsI32 returns the buffer's samples as int32 sample values. +// If the buffer isn't in this format, a copy is created and converted. +// Note that converting a float to an int might result in unexpected truncations. +func (b *PCMBuffer) AsI32() (out []int32) { + if b == nil { + return nil + } + switch b.DataType { + case DataTypeI8: + out = make([]int32, len(b.I8)) + for i := 0; i < len(b.I8); i++ { + out[i] = int32(b.I8[i]) + } + case DataTypeI16: + out = make([]int32, len(b.I16)) + for i := 0; i < len(b.I16); i++ { + out[i] = int32(b.I16[i]) + } + case DataTypeI32: + return b.I32 + case DataTypeF32: + out = make([]int32, len(b.F32)) + for i := 0; i < len(b.F32); i++ { + out[i] = int32(b.F32[i]) + } + case DataTypeF64: + out = make([]int32, len(b.F64)) + for i := 0; i < len(b.F64); i++ { + out[i] = int32(b.F64[i]) + } + } + return out +} + +// AsInt returns the buffer content as integers (int32s). +// It's recommended to avoid this method since it creates +// an extra copy of the buffer content. +func (b *PCMBuffer) AsInt() (out []int) { + int32s := b.AsI32() + out = make([]int, len(int32s)) + for i := 0; i < len(int32s); i++ { + out[i] = int(int32s[i]) + } + return out +} + +// AsF32 returns the buffer's samples as float32 sample values. +// If the buffer isn't in this format, a copy is created and converted. +// Note that converting might result in unexpected truncations. +func (b *PCMBuffer) AsF32() (out []float32) { + if b == nil { + return nil + } + switch b.DataType { + case DataTypeI8: + bitDepth := b.calculateIntBitDepth() + factor := math.Pow(2, 8*float64(bitDepth/8)-1) + out = make([]float32, len(b.I8)) + for i := 0; i < len(b.I8); i++ { + out[i] = float32(float64(int64(b.I8[i])) / factor) + } + case DataTypeI16: + bitDepth := b.calculateIntBitDepth() + factor := math.Pow(2, 8*float64(bitDepth/8)-1) + out = make([]float32, len(b.I16)) + for i := 0; i < len(b.I16); i++ { + out[i] = float32(float64(int64(b.I16[i])) / factor) + } + case DataTypeI32: + bitDepth := b.calculateIntBitDepth() + factor := math.Pow(2, 8*float64(bitDepth/8)-1) + out = make([]float32, len(b.I16)) + for i := 0; i < len(b.I16); i++ { + out[i] = float32(float64(int64(b.I16[i])) / factor) + } + case DataTypeF32: + return b.F32 + case DataTypeF64: + out = make([]float32, len(b.F64)) + for i := 0; i < len(b.F64); i++ { + out[i] = float32(b.F64[i]) + } + } + return out +} + +// AsF64 returns the buffer's samples as float64 sample values. +// If the buffer isn't in this format, a copy is created and converted. +// Note that converting might result in unexpected truncations. +func (b *PCMBuffer) AsF64() (out []float64) { + if b == nil { + return nil + } + switch b.DataType { + case DataTypeI8: + bitDepth := b.calculateIntBitDepth() + factor := math.Pow(2, 8*float64(bitDepth/8)-1) + out = make([]float64, len(b.I8)) + for i := 0; i < len(b.I8); i++ { + out[i] = float64(int64(b.I8[i])) / factor + } + case DataTypeI16: + bitDepth := b.calculateIntBitDepth() + factor := math.Pow(2, 8*float64(bitDepth/8)-1) + out = make([]float64, len(b.I16)) + for i := 0; i < len(b.I16); i++ { + out[i] = float64(int64(b.I16[i])) / factor + } + case DataTypeI32: + bitDepth := b.calculateIntBitDepth() + factor := math.Pow(2, 8*float64(bitDepth/8)-1) + out = make([]float64, len(b.I16)) + for i := 0; i < len(b.I16); i++ { + out[i] = float64(int64(b.I16[i])) / factor + } + case DataTypeF32: + out = make([]float64, len(b.F32)) + for i := 0; i < len(b.F32); i++ { + out[i] = float64(b.F32[i]) + } + case DataTypeF64: + return b.F64 + } + return out +} + +// Clone creates a clean clone that can be modified without +// changing the source buffer. +func (b *PCMBuffer) Clone() Buffer { + if b == nil { + return nil + } + newB := &PCMBuffer{DataType: b.DataType} + switch b.DataType { + case DataTypeI8: + newB.I8 = make([]int8, len(b.I8)) + copy(newB.I8, b.I8) + case DataTypeI16: + newB.I16 = make([]int16, len(b.I16)) + copy(newB.I16, b.I16) + case DataTypeI32: + newB.I32 = make([]int32, len(b.I32)) + copy(newB.I32, b.I32) + case DataTypeF32: + newB.F32 = make([]float32, len(b.F32)) + copy(newB.F32, b.F32) + case DataTypeF64: + newB.F64 = make([]float64, len(b.F64)) + copy(newB.F64, b.F64) + } + + newB.Format = &Format{ + NumChannels: b.Format.NumChannels, + SampleRate: b.Format.SampleRate, + } + return newB +} + +// SwitchPrimaryType is a convenience method to switch the primary data type. +// Use this if you process/swap a different type than the original type. +// Notes that conversion might be lossy if you switch to a lower resolution format. +func (b *PCMBuffer) SwitchPrimaryType(t PCMDataFormat) { + if b == nil || t == b.DataType { + return + } + switch t { + case DataTypeI8: + b.I8 = b.AsI8() + b.I16 = nil + b.I32 = nil + b.F32 = nil + b.F64 = nil + case DataTypeI16: + b.I8 = nil + b.I16 = b.AsI16() + b.I32 = nil + b.F32 = nil + b.F64 = nil + case DataTypeI32: + b.I8 = nil + b.I16 = nil + b.I32 = b.AsI32() + b.F32 = nil + b.F64 = nil + case DataTypeF32: + b.I8 = nil + b.I16 = nil + b.I32 = nil + b.F32 = b.AsF32() + b.F64 = nil + case DataTypeF64: + b.I8 = nil + b.I16 = nil + b.I32 = nil + b.F32 = nil + b.F64 = b.AsF64() + } + + b.DataType = t +} + +// calculateIntBithDepth looks at the int values in the buffer and returns +// the required lowest bit depth. +func (b *PCMBuffer) calculateIntBitDepth() uint8 { + if b == nil { + return 0 + } + bitDepth := b.SourceBitDepth + if bitDepth != 0 { + return bitDepth + } + var max int64 + switch b.DataType { + case DataTypeI8: + var i8max int8 + for _, s := range b.I8 { + if s > i8max { + i8max = s + } + } + max = int64(i8max) + case DataTypeI16: + var i16max int16 + for _, s := range b.I16 { + if s > i16max { + i16max = s + } + } + max = int64(i16max) + case DataTypeI32: + var i32max int32 + for _, s := range b.I32 { + if s > i32max { + i32max = s + } + } + max = int64(i32max) + default: + // This method is only meant to be used on int buffers. + return bitDepth + } + bitDepth = 8 + if max > 127 { + bitDepth = 16 + } + // greater than int16, expecting int24 + if max > 32767 { + bitDepth = 24 + } + // int 32 + if max > 8388607 { + bitDepth = 32 + } + // int 64 + if max > 4294967295 { + bitDepth = 64 + } + + return bitDepth +} diff --git a/vendor/github.com/go-audio/wav/LICENSE b/vendor/github.com/go-audio/wav/LICENSE new file mode 100644 index 0000000..54790e8 --- /dev/null +++ b/vendor/github.com/go-audio/wav/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 Matt Aimonetti + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-audio/wav/README.md b/vendor/github.com/go-audio/wav/README.md new file mode 100644 index 0000000..b484744 --- /dev/null +++ b/vendor/github.com/go-audio/wav/README.md @@ -0,0 +1,8 @@ +# wav codec + +[![GoDoc](https://godoc.org/github.com/go-audio/wav?status.svg)](https://godoc.org/github.com/go-audio/wav) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-audio/wav)](https://goreportcard.com/report/github.com/go-audio/wav) +[![Coverage Status](https://codecov.io/gh/go-audio/wav/graph/badge.svg)](https://codecov.io/gh/go-audio/wav) +[![Build Status](https://travis-ci.org/go-audio/wav.svg)](https://travis-ci.org/go-audio/wav) + +Wav audio file encoder and decoder. See [GoDoc](https://godoc.org/github.com/go-audio/wav) for more details. diff --git a/vendor/github.com/go-audio/wav/cue_chunk.go b/vendor/github.com/go-audio/wav/cue_chunk.go new file mode 100644 index 0000000..a54422c --- /dev/null +++ b/vendor/github.com/go-audio/wav/cue_chunk.go @@ -0,0 +1,106 @@ +package wav + +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/mattetti/audio/riff" +) + +// CuePoint defines an offset which marks a noteworthy sections of the audio +// content. For example, the beginning and end of a verse in a song may have cue +// points to make them easier to find. +type CuePoint struct { + // ID is the unique identifier of the cue point + ID [4]byte + // Play position specifies the sample offset associated with the cue point + // in terms of the sample's position in the final stream of samples + // generated by the play list. If a play list chunk is + // specified, the position value is equal to the sample number at which this + // cue point will occur during playback of the entire play list as defined + // by the play list's order. If no play list chunk is specified this value + // should be 0. + Position uint32 + // DataChunkID - This value specifies the four byte ID used by the chunk + // containing the sample that corresponds to this cue point. A Wave file + // with no play list is always "data". A Wave file with a play list + // containing both sample data and silence may be either "data" or "slnt". + DataChunkID [4]byte + // ChunkStart specifies the byte offset into the Wave List Chunk of the + // chunk containing the sample that corresponds to this cue point. This is + // the same chunk described by the Data Chunk ID value. If no Wave List + // Chunk exists in the Wave file, this value is 0. If a Wave List Chunk + // exists, this is the offset into the "wavl" chunk. The first chunk in the + // Wave List Chunk would be specified with a value of 0. + ChunkStart uint32 + // BlockStart specifies the byte offset into the "data" or "slnt" Chunk to + // the start of the block containing the sample. The start of a block is + // defined as the first byte in uncompressed PCM wave data or the last byte + // in compressed wave data where decompression can begin to find the value + // of the corresponding sample value. + BlockStart uint32 + // SampleOffset specifies an offset into the block (specified by Block + // Start) for the sample that corresponds to the cue point. In uncompressed + // PCM waveform data, this is simply the byte offset into the "data" chunk. + // In compressed waveform data, this value is equal to the number of samples + // (may or may not be bytes) from the Block Start to the sample that + // corresponds to the cue point. + SampleOffset uint32 +} + +// DecodeCueChunk decodes the optional cue chunk and extracts cue points. +func DecodeCueChunk(d *Decoder, ch *riff.Chunk) error { + if ch == nil { + return fmt.Errorf("can't decode a nil chunk") + } + if d == nil { + return fmt.Errorf("nil decoder") + } + if ch.ID == CIDCue { + // read the entire chunk in memory + buf := make([]byte, ch.Size) + var err error + if _, err = ch.Read(buf); err != nil { + return fmt.Errorf("failed to read the CUE chunk - %v", err) + } + r := bytes.NewReader(buf) + var nbrCues uint32 + if err := binary.Read(r, binary.LittleEndian, &nbrCues); err != nil { + return fmt.Errorf("failed to read the number of cues - %v", err) + } + if nbrCues > 0 { + if d.Metadata == nil { + d.Metadata = &Metadata{} + } + d.Metadata.CuePoints = []*CuePoint{} + scratch := make([]byte, 4) + for i := uint32(0); i < nbrCues; i++ { + c := &CuePoint{} + if _, err = r.Read(scratch); err != nil { + return fmt.Errorf("failed to read the cue point ID") + } + copy(c.ID[:], scratch[:4]) + if err := binary.Read(r, binary.LittleEndian, &c.Position); err != nil { + return err + } + if _, err = r.Read(scratch); err != nil { + return fmt.Errorf("failed to read the data chunk id") + } + copy(c.DataChunkID[:], scratch[:4]) + if err := binary.Read(r, binary.LittleEndian, &c.ChunkStart); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &c.BlockStart); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &c.SampleOffset); err != nil { + return err + } + d.Metadata.CuePoints = append(d.Metadata.CuePoints, c) + } + } + + } + return nil +} diff --git a/vendor/github.com/go-audio/wav/decoder.go b/vendor/github.com/go-audio/wav/decoder.go new file mode 100644 index 0000000..dfe4e75 --- /dev/null +++ b/vendor/github.com/go-audio/wav/decoder.go @@ -0,0 +1,523 @@ +package wav + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "time" + + "github.com/go-audio/audio" + "github.com/mattetti/audio/riff" +) + +var ( + // CIDList is the chunk ID for a LIST chunk + CIDList = [4]byte{'L', 'I', 'S', 'T'} + // CIDSmpl is the chunk ID for a smpl chunk + CIDSmpl = [4]byte{'s', 'm', 'p', 'l'} + // CIDINFO is the chunk ID for an INFO chunk + CIDInfo = []byte{'I', 'N', 'F', 'O'} + // CIDCue is the chunk ID for the cue chunk + CIDCue = [4]byte{'c', 'u', 'e', 0x20} +) + +// Decoder handles the decoding of wav files. +type Decoder struct { + r io.ReadSeeker + parser *riff.Parser + + NumChans uint16 + BitDepth uint16 + SampleRate uint32 + + AvgBytesPerSec uint32 + WavAudioFormat uint16 + + err error + PCMSize int + pcmDataAccessed bool + // pcmChunk is available so we can use the LimitReader + PCMChunk *riff.Chunk + // Metadata for the current file + Metadata *Metadata +} + +// NewDecoder creates a decoder for the passed wav reader. +// Note that the reader doesn't get rewinded as the container is processed. +func NewDecoder(r io.ReadSeeker) *Decoder { + return &Decoder{ + r: r, + parser: riff.New(r), + } +} + +// Seek provides access to the curosr position in the PCM data +func (d *Decoder) Seek(offset int64, whence int) (int64, error) { + return d.r.Seek(offset, whence) +} + +// SampleBitDepth returns the bit depth encoding of each sample. +func (d *Decoder) SampleBitDepth() int32 { + if d == nil { + return 0 + } + return int32(d.BitDepth) +} + +// PCMLen returns the total number of bytes in the PCM data chunk +func (d *Decoder) PCMLen() int64 { + if d == nil { + return 0 + } + return int64(d.PCMSize) +} + +// Err returns the first non-EOF error that was encountered by the Decoder. +func (d *Decoder) Err() error { + if d.err == io.EOF { + return nil + } + return d.err +} + +// EOF returns positively if the underlying reader reached the end of file. +func (d *Decoder) EOF() bool { + if d == nil || d.err == io.EOF { + return true + } + return false +} + +// IsValidFile verifies that the file is valid/readable. +func (d *Decoder) IsValidFile() bool { + d.err = d.readHeaders() + if d.err != nil { + return false + } + if d.NumChans < 1 { + return false + } + if d.BitDepth < 8 { + return false + } + if d, err := d.Duration(); err != nil || d <= 0 { + return false + } + + return true +} + +// ReadInfo reads the underlying reader until the comm header is parsed. +// This method is safe to call multiple times. +func (d *Decoder) ReadInfo() { + d.err = d.readHeaders() +} + +// ReadMetadata parses the file for extra metadata such as the INFO list chunk. +// The entire file will be read and should be rewinded if more data must be +// accessed. +func (d *Decoder) ReadMetadata() { + if d.Metadata != nil { + return + } + d.ReadInfo() + if d.Err() != nil || d.Metadata != nil { + return + } + var ( + chunk *riff.Chunk + err error + ) + for err == nil { + chunk, err = d.parser.NextChunk() + if err != nil { + break + } + + switch chunk.ID { + case CIDList: + if err = DecodeListChunk(d, chunk); err != nil { + if err != io.EOF { + d.err = err + } + } + if d.Metadata != nil && d.Metadata.SamplerInfo != nil { + // we got everything we were looking for + break + } + case CIDSmpl: + if err = DecodeSamplerChunk(d, chunk); err != nil { + if err != io.EOF { + d.err = err + } + } + case CIDCue: + if err = DecodeCueChunk(d, chunk); err != nil { + if err != io.EOF { + d.err = err + } + } + default: + // fmt.Println(string(chunk.ID[:])) + chunk.Drain() + } + } + +} + +// FwdToPCM forwards the underlying reader until the start of the PCM chunk. +// If the PCM chunk was already read, no data will be found (you need to rewind). +func (d *Decoder) FwdToPCM() error { + if d == nil { + return fmt.Errorf("PCM data not found") + } + d.err = d.readHeaders() + if d.err != nil { + return nil + } + + var chunk *riff.Chunk + for d.err == nil { + chunk, d.err = d.NextChunk() + if d.err != nil { + return d.err + } + if chunk.ID == riff.DataFormatID { + d.PCMSize = chunk.Size + d.PCMChunk = chunk + break + } + if chunk.ID == CIDList { + DecodeListChunk(d, chunk) + } + chunk.Drain() + } + if chunk == nil { + return fmt.Errorf("PCM data not found") + } + d.pcmDataAccessed = true + + return nil +} + +// WasPCMAccessed returns positively if the PCM data was previously accessed. +func (d *Decoder) WasPCMAccessed() bool { + if d == nil { + return false + } + return d.pcmDataAccessed +} + +// FullPCMBuffer is an inneficient way to access all the PCM data contained in the +// audio container. The entire PCM data is held in memory. +// Consider using Buffer() instead. +func (d *Decoder) FullPCMBuffer() (*audio.IntBuffer, error) { + if !d.WasPCMAccessed() { + err := d.FwdToPCM() + if err != nil { + return nil, d.err + } + } + if d.PCMChunk == nil { + return nil, errors.New("PCM chunk not found") + } + format := &audio.Format{ + NumChannels: int(d.NumChans), + SampleRate: int(d.SampleRate), + } + + buf := &audio.IntBuffer{Data: make([]int, 4096), Format: format, SourceBitDepth: int(d.BitDepth)} + bytesPerSample := (d.BitDepth-1)/8 + 1 + sampleBufData := make([]byte, bytesPerSample) + decodeF, err := sampleDecodeFunc(int(d.BitDepth)) + if err != nil { + return nil, fmt.Errorf("could not get sample decode func %v", err) + } + + i := 0 + for err == nil { + buf.Data[i], err = decodeF(d.PCMChunk, sampleBufData) + if err != nil { + break + } + i++ + // grow the underlying slice if needed + if i == len(buf.Data) { + buf.Data = append(buf.Data, make([]int, 4096)...) + } + } + buf.Data = buf.Data[:i] + + if err == io.EOF { + err = nil + } + + return buf, err +} + +// PCMBuffer populates the passed PCM buffer +func (d *Decoder) PCMBuffer(buf *audio.IntBuffer) (n int, err error) { + if buf == nil { + return 0, nil + } + + if !d.pcmDataAccessed { + err := d.FwdToPCM() + if err != nil { + return 0, d.err + } + } + if d.PCMChunk == nil { + return 0, ErrPCMChunkNotFound + } + + format := &audio.Format{ + NumChannels: int(d.NumChans), + SampleRate: int(d.SampleRate), + } + + buf.SourceBitDepth = int(d.BitDepth) + decodeF, err := sampleDecodeFunc(int(d.BitDepth)) + if err != nil { + return 0, fmt.Errorf("could not get sample decode func %v", err) + } + + bPerSample := bytesPerSample(int(d.BitDepth)) + // populate a file buffer to avoid multiple very small reads + // we need to cap the buffer size to not be bigger than the pcm chunk. + size := len(buf.Data) * bPerSample + tmpBuf := make([]byte, size) + var m int + m, err = d.PCMChunk.R.Read(tmpBuf) + if err != nil { + if err == io.EOF { + return m, nil + } + return m, err + } + if m == 0 { + return m, nil + } + bufR := bytes.NewReader(tmpBuf[:m]) + sampleBuf := make([]byte, bPerSample, bPerSample) + var misaligned bool + if m%bPerSample > 0 { + misaligned = true + } + + // Note that we populate the buffer even if the + // size of the buffer doesn't fit an even number of frames. + for n = 0; n < len(buf.Data); n++ { + buf.Data[n], err = decodeF(bufR, sampleBuf) + if err != nil { + // the last sample isn't a full sample but just padding. + if misaligned { + n-- + } + break + } + } + buf.Format = format + if err == io.EOF { + err = nil + } + + return n, err +} + +// Format returns the audio format of the decoded content. +func (d *Decoder) Format() *audio.Format { + if d == nil { + return nil + } + return &audio.Format{ + NumChannels: int(d.NumChans), + SampleRate: int(d.SampleRate), + } +} + +// NextChunk returns the next available chunk +func (d *Decoder) NextChunk() (*riff.Chunk, error) { + if d.err = d.readHeaders(); d.err != nil { + d.err = fmt.Errorf("failed to read header - %v", d.err) + return nil, d.err + } + + var ( + id [4]byte + size uint32 + ) + + id, size, d.err = d.parser.IDnSize() + if d.err != nil { + d.err = fmt.Errorf("error reading chunk header - %v", d.err) + return nil, d.err + } + + // TODO: any reason we don't use d.parser.NextChunk (riff.NextChunk) here? + // It correctly handles the misaligned chunk. + + // TODO: copied over from riff.parser.NextChunk + // all RIFF chunks (including WAVE "data" chunks) must be word aligned. + // 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. + // The "data" chunk header's size should not include this byte. + if size%2 == 1 { + size++ + } + + c := &riff.Chunk{ + ID: id, + Size: int(size), + R: io.LimitReader(d.r, int64(size)), + } + return c, d.err +} + +// Duration returns the time duration for the current audio container +func (d *Decoder) Duration() (time.Duration, error) { + if d == nil || d.parser == nil { + return 0, errors.New("can't calculate the duration of a nil pointer") + } + return d.parser.Duration() +} + +// String implements the Stringer interface. +func (d *Decoder) String() string { + return d.parser.String() +} + +// readHeaders is safe to call multiple times +func (d *Decoder) readHeaders() error { + if d == nil || d.NumChans > 0 { + return nil + } + + id, size, err := d.parser.IDnSize() + if err != nil { + return err + } + d.parser.ID = id + if d.parser.ID != riff.RiffID { + return fmt.Errorf("%s - %s", d.parser.ID, riff.ErrFmtNotSupported) + } + d.parser.Size = size + if err := binary.Read(d.r, binary.BigEndian, &d.parser.Format); err != nil { + return err + } + + var chunk *riff.Chunk + var rewindBytes int64 + + for err == nil { + chunk, err = d.parser.NextChunk() + if err != nil { + break + } + + if chunk.ID == riff.FmtID { + chunk.DecodeWavHeader(d.parser) + d.NumChans = d.parser.NumChannels + d.BitDepth = d.parser.BitsPerSample + d.SampleRate = d.parser.SampleRate + d.WavAudioFormat = d.parser.WavAudioFormat + d.AvgBytesPerSec = d.parser.AvgBytesPerSec + + if rewindBytes > 0 { + d.r.Seek(-(rewindBytes + int64(chunk.Size) + 8), 1) + } + break + } else if chunk.ID == CIDList { + // The list chunk can be in the header or footer + // because so many players don't support that chunk properly + // it is recommended to have it at the end of the file. + DecodeListChunk(d, chunk) + // unexpected chunk order, might be a bext chunk + rewindBytes += int64(chunk.Size) + 8 + } else if chunk.ID == CIDSmpl { + DecodeSamplerChunk(d, chunk) + rewindBytes += int64(chunk.Size) + 8 + } else { + // unexpected chunk order, might be a bext chunk + rewindBytes += int64(chunk.Size) + 8 + // drain the chunk + io.CopyN(ioutil.Discard, d.r, int64(chunk.Size)) + } + } + + return d.err +} + +func bytesPerSample(bitDepth int) int { + return bitDepth / 8 +} + +// sampleDecodeFunc returns a function that can be used to convert +// a byte range into an int value based on the amount of bits used per sample. +// Note that 8bit samples are unsigned, all other values are signed. +func sampleDecodeFunc(bitsPerSample int) (func(io.Reader, []byte) (int, error), error) { + // NOTE: WAV PCM data is stored using little-endian + switch bitsPerSample { + case 8: + // 8bit values are unsigned + return func(r io.Reader, buf []byte) (int, error) { + _, err := r.Read(buf[:1]) + return int(buf[0]), err + }, nil + case 16: + return func(r io.Reader, buf []byte) (int, error) { + _, err := r.Read(buf[:2]) + return int(int16(binary.LittleEndian.Uint16(buf[:2]))), err + }, nil + case 24: + // -34,359,738,367 (0x7FFFFF) to 34,359,738,368 (0x800000) + return func(r io.Reader, buf []byte) (int, error) { + _, err := r.Read(buf[:3]) + if err != nil { + return 0, err + } + return int(audio.Int24LETo32(buf[:3])), nil + }, nil + case 32: + return func(r io.Reader, buf []byte) (int, error) { + _, err := r.Read(buf[:4]) + return int(int32(binary.LittleEndian.Uint32(buf[:4]))), err + }, nil + default: + return nil, fmt.Errorf("unhandled byte depth:%d", bitsPerSample) + } +} + +// sampleDecodeFloat64Func returns a function that can be used to convert +// a byte range into a float64 value based on the amount of bits used per sample. +func sampleFloat64DecodeFunc(bitsPerSample int) (func([]byte) float64, error) { + bytesPerSample := bitsPerSample / 8 + switch bytesPerSample { + case 1: + // 8bit values are unsigned + return func(s []byte) float64 { + return float64(uint8(s[0])) + }, nil + case 2: + return func(s []byte) float64 { + return float64(int(s[0]) + int(s[1])<<8) + }, nil + case 3: + return func(s []byte) float64 { + var output int32 + output |= int32(s[2]) << 0 + output |= int32(s[1]) << 8 + output |= int32(s[0]) << 16 + return float64(output) + }, nil + case 4: + // TODO: fix the float64 conversion (current int implementation) + return func(s []byte) float64 { + return float64(int(s[0]) + int(s[1])<<8 + int(s[2])<<16 + int(s[3])<<24) + }, nil + default: + return nil, fmt.Errorf("unhandled byte depth:%d", bitsPerSample) + } +} diff --git a/vendor/github.com/go-audio/wav/encoder.go b/vendor/github.com/go-audio/wav/encoder.go new file mode 100644 index 0000000..c7451eb --- /dev/null +++ b/vendor/github.com/go-audio/wav/encoder.go @@ -0,0 +1,245 @@ +package wav + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "os" + + "github.com/go-audio/audio" + + "github.com/mattetti/audio/riff" +) + +// Encoder encodes LPCM data into a wav containter. +type Encoder struct { + w io.WriteSeeker + SampleRate int + BitDepth int + NumChans int + + // A number indicating the WAVE format category of the file. The content of + // the portion of the ‘fmt’ chunk, and the + // interpretation of the waveform data, depend on this value. PCM = 1 (i.e. + // Linear quantization) Values other than 1 indicate some form of + // compression. + WavAudioFormat int + + // Metadata contains metadata to inject in the file. + Metadata *Metadata + + WrittenBytes int + frames int + pcmChunkStarted bool + pcmChunkSizePos int +} + +// NewEncoder creates a new encoder to create a new wav file. +// Don't forget to add Frames to the encoder before writing. +func NewEncoder(w io.WriteSeeker, sampleRate, bitDepth, numChans, audioFormat int) *Encoder { + return &Encoder{ + w: w, + SampleRate: sampleRate, + BitDepth: bitDepth, + NumChans: numChans, + WavAudioFormat: audioFormat, + } +} + +// AddLE serializes and adds the passed value using little endian +func (e *Encoder) AddLE(src interface{}) error { + e.WrittenBytes += binary.Size(src) + return binary.Write(e.w, binary.LittleEndian, src) +} + +// AddBE serializes and adds the passed value using big endian +func (e *Encoder) AddBE(src interface{}) error { + e.WrittenBytes += binary.Size(src) + return binary.Write(e.w, binary.BigEndian, src) +} + +func (e *Encoder) addBuffer(buf *audio.IntBuffer) error { + if buf == nil { + return fmt.Errorf("can't add a nil buffer") + } + + frameCount := buf.NumFrames() + // performance tweak: setup a buffer so we don't do too many writes + bb := bytes.NewBuffer(nil) + var err error + for i := 0; i < frameCount; i++ { + for j := 0; j < buf.Format.NumChannels; j++ { + v := buf.Data[i*buf.Format.NumChannels+j] + switch e.BitDepth { + case 8: + if err = binary.Write(bb, binary.LittleEndian, uint8(v)); err != nil { + return err + } + case 16: + if err = binary.Write(bb, binary.LittleEndian, int16(v)); err != nil { + return err + } + case 24: + if err = binary.Write(bb, binary.LittleEndian, audio.Int32toInt24LEBytes(int32(v))); err != nil { + return err + } + case 32: + if err = binary.Write(bb, binary.LittleEndian, int32(v)); err != nil { + return err + } + default: + return fmt.Errorf("can't add frames of bit size %d", e.BitDepth) + } + } + e.frames++ + } + if n, err := e.w.Write(bb.Bytes()); err != nil { + e.WrittenBytes += n + return err + } + e.WrittenBytes += bb.Len() + + return nil +} + +func (e *Encoder) writeHeader() error { + if e == nil { + return fmt.Errorf("can't write a nil encoder") + } + if e.w == nil { + return fmt.Errorf("can't write to a nil writer") + } + + if e.WrittenBytes > 0 { + return nil + } + + // riff ID + if err := e.AddLE(riff.RiffID); err != nil { + return err + } + // file size uint32, to update later on. + if err := e.AddLE(uint32(42)); err != nil { + return err + } + // wave headers + if err := e.AddLE(riff.WavFormatID); err != nil { + return err + } + // form + if err := e.AddLE(riff.FmtID); err != nil { + return err + } + // chunk size + if err := e.AddLE(uint32(16)); err != nil { + return err + } + // wave format + if err := e.AddLE(uint16(e.WavAudioFormat)); err != nil { + return err + } + // num channels + if err := e.AddLE(uint16(e.NumChans)); err != nil { + return fmt.Errorf("error encoding the number of channels - %v", err) + } + // samplerate + if err := e.AddLE(uint32(e.SampleRate)); err != nil { + return fmt.Errorf("error encoding the sample rate - %v", err) + } + blockAlign := e.NumChans * e.BitDepth / 8 + // avg bytes per sec + if err := e.AddLE(uint32(e.SampleRate * blockAlign)); err != nil { + return fmt.Errorf("error encoding the avg bytes per sec - %v", err) + } + // block align + if err := e.AddLE(uint16(blockAlign)); err != nil { + return err + } + // bits per sample + if err := e.AddLE(uint16(e.BitDepth)); err != nil { + return fmt.Errorf("error encoding bits per sample - %v", err) + } + + return nil +} + +// Write encodes and writes the passed buffer to the underlying writer. +// Don't forger to Close() the encoder or the file won't be valid. +func (e *Encoder) Write(buf *audio.IntBuffer) error { + if err := e.writeHeader(); err != nil { + return err + } + + if !e.pcmChunkStarted { + // sound header + if err := e.AddLE(riff.DataFormatID); err != nil { + return fmt.Errorf("error encoding sound header %v", err) + } + e.pcmChunkStarted = true + + // write a temporary chunksize + e.pcmChunkSizePos = e.WrittenBytes + if err := e.AddLE(uint32(42)); err != nil { + return fmt.Errorf("%v when writing wav data chunk size header", err) + } + } + + return e.addBuffer(buf) +} + +func (e *Encoder) writeMetadata() error { + chunkData := encodeInfoChunk(e) + if err := e.AddBE(CIDList); err != nil { + return fmt.Errorf("failed to write the LIST chunk ID", err) + } + if err := e.AddLE(uint32(len(chunkData))); err != nil { + return fmt.Errorf("failed to write the LIST chunk soize", err) + } + return e.AddBE(chunkData) +} + +// Close flushes the content to disk, make sure the headers are up to date +// Note that the underlying writer is NOT being closed. +func (e *Encoder) Close() error { + if e == nil || e.w == nil { + return nil + } + + // inject metadata at the end to not trip implementation not supporting + // metadata chunks + if e.Metadata != nil { + if err := e.writeMetadata(); err != nil { + return fmt.Errorf("failed to write metadata - %v", err) + } + } + + // go back and write total size in header + if _, err := e.w.Seek(4, 0); err != nil { + return err + } + if err := e.AddLE(uint32(e.WrittenBytes) - 8); err != nil { + return fmt.Errorf("%v when writing the total written bytes", err) + } + + // rewrite the audio chunk length header + if e.pcmChunkSizePos > 0 { + if _, err := e.w.Seek(int64(e.pcmChunkSizePos), 0); err != nil { + return err + } + chunksize := uint32((int(e.BitDepth) / 8) * int(e.NumChans) * e.frames) + if err := e.AddLE(uint32(chunksize)); err != nil { + return fmt.Errorf("%v when writing wav data chunk size header", err) + } + } + + // jump back to the end of the file. + if _, err := e.w.Seek(0, 2); err != nil { + return err + } + switch e.w.(type) { + case *os.File: + return e.w.(*os.File).Sync() + } + return nil +} diff --git a/vendor/github.com/go-audio/wav/list_chunk.go b/vendor/github.com/go-audio/wav/list_chunk.go new file mode 100644 index 0000000..5e08cb0 --- /dev/null +++ b/vendor/github.com/go-audio/wav/list_chunk.go @@ -0,0 +1,183 @@ +package wav + +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/mattetti/audio/riff" +) + +var ( + // See http://bwfmetaedit.sourceforge.net/listinfo.html + markerIART = [4]byte{'I', 'A', 'R', 'T'} + markerISFT = [4]byte{'I', 'S', 'F', 'T'} + markerICRD = [4]byte{'I', 'C', 'R', 'D'} + markerICOP = [4]byte{'I', 'C', 'O', 'P'} + markerIARL = [4]byte{'I', 'A', 'R', 'L'} + markerINAM = [4]byte{'I', 'N', 'A', 'M'} + markerIENG = [4]byte{'I', 'E', 'N', 'G'} + markerIGNR = [4]byte{'I', 'G', 'N', 'R'} + markerIPRD = [4]byte{'I', 'P', 'R', 'D'} + markerISRC = [4]byte{'I', 'S', 'R', 'C'} + markerISBJ = [4]byte{'I', 'S', 'B', 'J'} + markerICMT = [4]byte{'I', 'C', 'M', 'T'} + markerITRK = [4]byte{'I', 'T', 'R', 'K'} + markerITRKBug = [4]byte{'i', 't', 'r', 'k'} + markerITCH = [4]byte{'I', 'T', 'C', 'H'} + markerIKEY = [4]byte{'I', 'K', 'E', 'Y'} + markerIMED = [4]byte{'I', 'M', 'E', 'D'} +) + +// DecodeListChunk decodes a LIST chunk +func DecodeListChunk(d *Decoder, ch *riff.Chunk) error { + if ch == nil { + return fmt.Errorf("can't decode a nil chunk") + } + if d == nil { + return fmt.Errorf("nil decoder") + } + if ch.ID == CIDList { + // read the entire chunk in memory + buf := make([]byte, ch.Size) + var err error + if _, err = ch.Read(buf); err != nil { + return fmt.Errorf("failed to read the LIST chunk - %v", err) + } + r := bytes.NewReader(buf) + // INFO subchunk + scratch := make([]byte, 4) + if _, err = r.Read(scratch); err != nil { + return fmt.Errorf("failed to read the INFO subchunk - %v", err) + } + if !bytes.Equal(scratch, CIDInfo[:]) { + // "expected an INFO subchunk but got %s", string(scratch) + // TODO: support adtl subchunks + ch.Drain() + return nil + } + if d.Metadata == nil { + d.Metadata = &Metadata{} + } + + // the rest is a list of string entries + var ( + id [4]byte + size uint32 + ) + readSubHeader := func() error { + if err := binary.Read(r, binary.BigEndian, &id); err != nil { + return err + } + return binary.Read(r, binary.LittleEndian, &size) + } + + for err == nil { + err = readSubHeader() + if err != nil { + break + } + scratch = make([]byte, size) + r.Read(scratch) + switch id { + case markerIARL: + d.Metadata.Location = nullTermStr(scratch) + case markerIART: + d.Metadata.Artist = nullTermStr(scratch) + case markerISFT: + d.Metadata.Software = nullTermStr(scratch) + case markerICRD: + d.Metadata.CreationDate = nullTermStr(scratch) + case markerICOP: + d.Metadata.Copyright = nullTermStr(scratch) + case markerINAM: + d.Metadata.Title = nullTermStr(scratch) + case markerIENG: + d.Metadata.Engineer = nullTermStr(scratch) + case markerIGNR: + d.Metadata.Genre = nullTermStr(scratch) + case markerIPRD: + d.Metadata.Product = nullTermStr(scratch) + case markerISRC: + d.Metadata.Source = nullTermStr(scratch) + case markerISBJ: + d.Metadata.Subject = nullTermStr(scratch) + case markerICMT: + d.Metadata.Comments = nullTermStr(scratch) + case markerITRK, markerITRKBug: + d.Metadata.TrackNbr = nullTermStr(scratch) + case markerITCH: + d.Metadata.Technician = nullTermStr(scratch) + case markerIKEY: + d.Metadata.Keywords = nullTermStr(scratch) + case markerIMED: + d.Metadata.Medium = nullTermStr(scratch) + } + } + } + ch.Drain() + return nil +} + +func encodeInfoChunk(e *Encoder) []byte { + if e == nil || e.Metadata == nil { + return nil + } + buf := bytes.NewBuffer(nil) + + writeSection := func(id [4]byte, val string) { + buf.Write(id[:]) + binary.Write(buf, binary.LittleEndian, uint32(len(val)+1)) + buf.Write(append([]byte(val), 0x00)) + } + if e.Metadata.Artist != "" { + writeSection(markerIART, e.Metadata.Artist) + } + if e.Metadata.Comments != "" { + writeSection(markerICMT, e.Metadata.Comments) + } + if e.Metadata.Copyright != "" { + writeSection(markerICOP, e.Metadata.Copyright) + } + if e.Metadata.CreationDate != "" { + writeSection(markerICRD, e.Metadata.CreationDate) + } + if e.Metadata.Engineer != "" { + writeSection(markerIENG, e.Metadata.Engineer) + } + if e.Metadata.Technician != "" { + writeSection(markerITCH, e.Metadata.Technician) + } + if e.Metadata.Genre != "" { + writeSection(markerIGNR, e.Metadata.Genre) + } + if e.Metadata.Keywords != "" { + writeSection(markerIKEY, e.Metadata.Keywords) + } + if e.Metadata.Medium != "" { + writeSection(markerIMED, e.Metadata.Medium) + } + if e.Metadata.Title != "" { + writeSection(markerINAM, e.Metadata.Title) + } + if e.Metadata.Product != "" { + writeSection(markerIPRD, e.Metadata.Product) + } + if e.Metadata.Subject != "" { + writeSection(markerISBJ, e.Metadata.Subject) + } + if e.Metadata.Software != "" { + writeSection(markerISFT, e.Metadata.Software) + } + if e.Metadata.Source != "" { + writeSection(markerISRC, e.Metadata.Source) + } + if e.Metadata.Location != "" { + writeSection(markerIARL, e.Metadata.Location) + } + if e.Metadata.TrackNbr != "" { + writeSection(markerITRK, e.Metadata.TrackNbr) + } + + return append(CIDInfo, buf.Bytes()...) +} diff --git a/vendor/github.com/go-audio/wav/metadata.go b/vendor/github.com/go-audio/wav/metadata.go new file mode 100644 index 0000000..7bce523 --- /dev/null +++ b/vendor/github.com/go-audio/wav/metadata.go @@ -0,0 +1,144 @@ +package wav + +// Metadata represents optional metadata added to the wav file. +type Metadata struct { + SamplerInfo *SamplerInfo + // Artist of the original subject of the file. For example, Michaelangelo. + Artist string + // Comments provides general comments about the file or the subject of the + // file. If the comment is several sentences long, end each sentence with a + // period. Do not include newline characters. + Comments string + // Copyright records the copyright information for the file. + Copyright string + // CreationDate specifies the date the subject of the file was created. List + // dates in year-month-day format, padding one-digit months and days with a + // zero on the left. For example: 1553-05-03 for May 3, 1553. The year + // should always be given using four digits. + CreationDate string + // Engineer stores the name of the engineer who worked on the file. If there + // are multiple engineers, separate the names by a semicolon and a blank. + // For example: Smith, John; Adams, Joe. + Engineer string + // Technician identifies the technician who sampled the subject file. For + // example: Smith, John. + Technician string + // Genre describes the original work, such as jazz, classical, rock, etc. + Genre string + // Keywords provides a list of keywords that refer to the file or subject of + // the file. Separate multiple keywords with a semicolon and a blank. For + // example, Seattle; zoology; The Civil War. + Keywords string + // Medium describes the original subject of the file, such as record, CD and so forth. + Medium string + // Title stores the title of the subject of the file, such as bohemian rhapsody. + Title string + // Product AKA album specifies the name of the title the file was originally + // intended for: A Night at the Opera + Product string + // Subject describes the contents of the file, such as Metadata Management. + Subject string + // Software identifies the name of the software package used to create the + // file, such as go-audio. + Software string + // Source identifies the name of the person or organization who supplied the + // original subject of the file. For example: Splice. + Source string + // Location or Archival Location - Indicates where the subject of the file is archived. + Location string + // TrackNbr is the track number + TrackNbr string + // CuePoints is a list of cue points in the wav file. + CuePoints []*CuePoint +} + +// SamplerInfo is extra metadata pertinent to a sampler type usage. +type SamplerInfo struct { + // Manufacturer field specifies the MIDI Manufacturer's Association + // (MMA) Manufacturer code for the sampler intended to receive this file's + // waveform. Each manufacturer of a MIDI product is assigned a unique ID + // which identifies the company. If no particular manufacturer is to be + // specified, a value of 0 should be used. The value is stored with some + // extra information to enable translation to the value used in a MIDI + // System Exclusive transmission to the sampler. The high byte indicates the + // number of low order bytes (1 or 3) that are valid for the manufacturer + // code. For example, the value for Digidesign will be 0x01000013 (0x13) and + // the value for Microsoft will be 0x30000041 (0x00, 0x00, 0x41). + Manufacturer [4]byte + // Product field specifies the MIDI model ID defined by the manufacturer + // corresponding to the Manufacturer field. Contact the manufacturer of the + // sampler to get the model ID. If no particular manufacturer's product is + // to be specified, a value of 0 should be used. + Product [4]byte + // SamplePeriod The sample period specifies the duration of time that passes + // during the playback of one sample in nanoseconds (normally equal to 1 / + // Samplers Per Second, where Samples Per Second is the value found in the + // format chunk). + SamplePeriod uint32 + // MIDIUnityNote The MIDI unity note value has the same meaning as the instrument chunk's + // MIDI Unshifted Note field which specifies the musical note at which the + // sample will be played at it's original sample rate (the sample rate + // specified in the format chunk). + MIDIUnityNote uint32 + // MIDIPitchFraction The MIDI pitch fraction specifies the fraction of a + // semitone up from the specified MIDI unity note field. A value of + // 0x80000000 means 1/2 semitone (50 cents) and a value of 0x00000000 means + // no fine tuning between semitones. + MIDIPitchFraction uint32 + // SMPTEFormat The SMPTE format specifies the Society of Motion Pictures and + // Television E time format used in the following SMPTE Offset field. If a + // value of 0 is set, SMPTE Offset should also be set to 0. (0, 24, 25, 29, 30) + SMPTEFormat uint32 + // SMPTEOffset The SMPTE Offset value specifies the time offset to be used + // for the synchronization / calibration to the first sample in the + // waveform. This value uses a format of 0xhhmmssff where hh is a signed + // value that specifies the number of hours (-23 to 23), mm is an unsigned + // value that specifies the number of minutes (0 to 59), ss is an unsigned + // value that specifies the number of seconds (0 to 59) and ff is an + // unsigned value that specifies the number of frames (0 to -1). + SMPTEOffset uint32 + // NumSampleLoops The sample loops field specifies the number Sample Loop + // definitions in the following list. This value may be set to 0 meaning + // that no sample loops follow. + NumSampleLoops uint32 + // Loops A list of sample loops is simply a set of consecutive loop + // descriptions. The sample loops do not have to be in any particular order + // because each sample loop associated cue point position is used to + // determine the play order. + Loops []*SampleLoop +} + +// SampleLoop indicates a loop and its properties within the audio file +type SampleLoop struct { + // CuePointID - The Cue Point ID specifies the unique ID that corresponds to one of the + // defined cue points in the cue point list. Furthermore, this ID + // corresponds to any labels defined in the associated data list chunk which + // allows text labels to be assigned to the various sample loops. + CuePointID [4]byte + // Type - The type field defines how the waveform samples will be looped. + // 0 Loop forward (normal) + // 1 Alternating loop (forward/backward, also known as Ping Pong) + // 2 Loop backward (reverse) + // 3 Reserved for future standard types + // 32 - 0xFFFFFFFF Sampler specific types (defined by manufacturer) + Type uint32 + // Start - The start value specifies the byte offset into the waveform data + // of the first sample to be played in the loop. + Start uint32 + // End - The end value specifies the byte offset into the waveform data of + // the last sample to be played in the loop. + End uint32 + // Fraction - The fractional value specifies a fraction of a sample at which + // to loop. This allows a loop to be fine tuned at a resolution greater than + // one sample. The value can range from 0x00000000 to 0xFFFFFFFF. A value of + // 0 means no fraction, a value of 0x80000000 means 1/2 of a sample length. + // 0xFFFFFFFF is the smallest fraction of a sample that can be represented. + Fraction uint32 + // PlayCount - The play count value determines the number of times to play + // the loop. A value of 0 specifies an infinite sustain loop. An infinite + // sustain loop will continue looping until some external force interrupts + // playback, such as the musician releasing the key that triggered the + // wave's playback. All other values specify an absolute number of times to + // loop. + PlayCount uint32 +} diff --git a/vendor/github.com/go-audio/wav/smpl_chunk.go b/vendor/github.com/go-audio/wav/smpl_chunk.go new file mode 100644 index 0000000..c0db25e --- /dev/null +++ b/vendor/github.com/go-audio/wav/smpl_chunk.go @@ -0,0 +1,100 @@ +package wav + +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/mattetti/audio/riff" +) + +// smpl chunk is documented here: +// https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl + +// DecodeSamplerChunk decodes a smpl chunk and put the data in Decoder.Metadata.SamplerInfo +func DecodeSamplerChunk(d *Decoder, ch *riff.Chunk) error { + if ch == nil { + return fmt.Errorf("can't decode a nil chunk") + } + if d == nil { + return fmt.Errorf("nil decoder") + } + if ch.ID == CIDSmpl { + // read the entire chunk in memory + buf := make([]byte, ch.Size) + var err error + if _, err = ch.Read(buf); err != nil { + return fmt.Errorf("failed to read the smpl chunk - %v", err) + } + if d.Metadata == nil { + d.Metadata = &Metadata{} + } + + d.Metadata.SamplerInfo = &SamplerInfo{} + + r := bytes.NewReader(buf) + + scratch := make([]byte, 4) + if _, err = r.Read(scratch); err != nil { + return fmt.Errorf("failed to read the smpl Manufacturer") + } + copy(d.Metadata.SamplerInfo.Manufacturer[:], scratch[:4]) + if _, err = r.Read(scratch); err != nil { + return fmt.Errorf("failed to read the smpl Product") + } + copy(d.Metadata.SamplerInfo.Product[:], scratch[:4]) + + if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.SamplePeriod); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.MIDIUnityNote); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.MIDIPitchFraction); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.SMPTEFormat); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.SMPTEOffset); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &d.Metadata.SamplerInfo.NumSampleLoops); err != nil { + return err + } + var remaining uint32 + // sampler data + if err := binary.Read(r, binary.BigEndian, &remaining); err != nil { + return err + } + if d.Metadata.SamplerInfo.NumSampleLoops > 0 { + d.Metadata.SamplerInfo.Loops = []*SampleLoop{} + for i := uint32(0); i < d.Metadata.SamplerInfo.NumSampleLoops; i++ { + sl := &SampleLoop{} + if _, err = r.Read(scratch); err != nil { + return fmt.Errorf("failed to read the sample loop cue point id") + } + copy(sl.CuePointID[:], scratch[:4]) + if err := binary.Read(r, binary.LittleEndian, &sl.Type); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &sl.Start); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &sl.End); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &sl.Fraction); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &sl.PlayCount); err != nil { + return err + } + + d.Metadata.SamplerInfo.Loops = append(d.Metadata.SamplerInfo.Loops, sl) + } + } + } + ch.Drain() + return nil +} diff --git a/vendor/github.com/go-audio/wav/wav.go b/vendor/github.com/go-audio/wav/wav.go new file mode 100644 index 0000000..2d80bdb --- /dev/null +++ b/vendor/github.com/go-audio/wav/wav.go @@ -0,0 +1,27 @@ +/* + +Package wav is a package allowing developers to decode and encode audio PCM +data using the Waveform Audio File Format https://en.wikipedia.org/wiki/WAV + +*/ +package wav + +import "errors" + +var ( + // ErrPCMChunkNotFound indicates a bad audio file without data + ErrPCMChunkNotFound = errors.New("PCM Chunk not found in audio file") +) + +func nullTermStr(b []byte) string { + return string(b[:clen(b)]) +} + +func clen(n []byte) int { + for i := 0; i < len(n); i++ { + if n[i] == 0 { + return i + } + } + return len(n) +} diff --git a/vendor/github.com/mattetti/audio/LICENSE b/vendor/github.com/mattetti/audio/LICENSE new file mode 100644 index 0000000..a6de0d1 --- /dev/null +++ b/vendor/github.com/mattetti/audio/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Matt Aimonetti + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/mattetti/audio/riff/README.md b/vendor/github.com/mattetti/audio/riff/README.md new file mode 100644 index 0000000..37bf325 --- /dev/null +++ b/vendor/github.com/mattetti/audio/riff/README.md @@ -0,0 +1,6 @@ +# RIFF parser + +[![GoDoc](http://godoc.org/github.com/mattetti/audio/riff?status.svg)](http://godoc.org/github.com/mattetti/audio/riff) + +[![Build +Status](https://travis-ci.org/mattetti/audio.png)](https://travis-ci.org/mattetti/audio) diff --git a/vendor/github.com/mattetti/audio/riff/chunk.go b/vendor/github.com/mattetti/audio/riff/chunk.go new file mode 100644 index 0000000..9189281 --- /dev/null +++ b/vendor/github.com/mattetti/audio/riff/chunk.go @@ -0,0 +1,130 @@ +package riff + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "sync" +) + +// Chunk represents the header and containt of a sub block +// See https://tech.ebu.ch/docs/tech/tech3285.pdf to see how +// audio content is stored in a BWF/WAVE file. +type Chunk struct { + ID [4]byte + Size int + Pos int + R io.Reader + okChan chan bool + Wg *sync.WaitGroup +} + +func (ch *Chunk) DecodeWavHeader(p *Parser) error { + if ch == nil { + return fmt.Errorf("can't decode a nil chunk") + } + if ch.ID == FmtID { + p.wavHeaderSize = uint32(ch.Size) + if err := ch.ReadLE(&p.WavAudioFormat); err != nil { + return err + } + if err := ch.ReadLE(&p.NumChannels); err != nil { + return err + } + if err := ch.ReadLE(&p.SampleRate); err != nil { + return err + } + if err := ch.ReadLE(&p.AvgBytesPerSec); err != nil { + return err + } + if err := ch.ReadLE(&p.BlockAlign); err != nil { + return err + } + if err := ch.ReadLE(&p.BitsPerSample); err != nil { + return err + } + + // if we aren't dealing with a PCM file, we advance to reader to the + // end of the chunck. + if ch.Size > 16 { + extra := make([]byte, ch.Size-16) + ch.ReadLE(&extra) + } + } + return nil +} + +// Done signals the parent parser that we are done reading the chunk +// if the chunk isn't fully read, this code will do so before signaling. +func (ch *Chunk) Done() { + if !ch.IsFullyRead() { + ch.Drain() + } + if ch.Wg != nil { + ch.Wg.Done() + } +} + +// IsFullyRead checks if we're finished reading the chunk +func (ch *Chunk) IsFullyRead() bool { + if ch == nil || ch.R == nil { + return true + } + return ch.Size <= ch.Pos +} + +// Read implements the reader interface +func (ch *Chunk) Read(p []byte) (n int, err error) { + if ch == nil || ch.R == nil { + return 0, errors.New("nil chunk/reader pointer") + } + n, err = ch.R.Read(p) + ch.Pos += n + return n, err +} + +// ReadLE reads the Little Endian chunk data into the passed struct +func (ch *Chunk) ReadLE(dst interface{}) error { + if ch == nil || ch.R == nil { + return errors.New("nil chunk/reader pointer") + } + if ch.IsFullyRead() { + return io.EOF + } + ch.Pos += binary.Size(dst) + return binary.Read(ch.R, binary.LittleEndian, dst) +} + +// ReadBE reads the Big Endian chunk data into the passed struct +func (ch *Chunk) ReadBE(dst interface{}) error { + if ch.IsFullyRead() { + return io.EOF + } + ch.Pos += binary.Size(dst) + return binary.Read(ch.R, binary.LittleEndian, dst) +} + +// ReadByte reads and returns a single byte +func (ch *Chunk) ReadByte() (byte, error) { + if ch.IsFullyRead() { + return 0, io.EOF + } + var r byte + err := ch.ReadLE(&r) + return r, err +} + +// Drain discards the rest of the chunk +func (ch *Chunk) Drain() { + bytesAhead := ch.Size - ch.Pos + for bytesAhead > 0 { + readSize := int64(bytesAhead) + + if _, err := io.CopyN(ioutil.Discard, ch.R, readSize); err != nil { + return + } + bytesAhead -= int(readSize) + } +} diff --git a/vendor/github.com/mattetti/audio/riff/doc.go b/vendor/github.com/mattetti/audio/riff/doc.go new file mode 100644 index 0000000..051392a --- /dev/null +++ b/vendor/github.com/mattetti/audio/riff/doc.go @@ -0,0 +1,15 @@ +/* + +Package riff is package implementing a simple Resource Interchange File Format +(RIFF) parser with basic support for sub formats such as WAV. +The goal of this package is to give all the tools needed for a developer +to implement parse any kind of file formats using the RIFF container format. + +Support for PCM wav format was added so the headers are parsed, the duration and the raw sound data +of a wav file can be easily accessed (See the examples below) . + +For more information about RIFF: +https://en.wikipedia.org/wiki/Resource_Interchange_File_Format + +*/ +package riff diff --git a/vendor/github.com/mattetti/audio/riff/parser.go b/vendor/github.com/mattetti/audio/riff/parser.go new file mode 100644 index 0000000..ee11584 --- /dev/null +++ b/vendor/github.com/mattetti/audio/riff/parser.go @@ -0,0 +1,256 @@ +package riff + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "sync" + "time" +) + +// Parser is a struct containing the overall container information. +type Parser struct { + r io.Reader + // Chan is an Optional channel of chunks that is used to parse chunks + Chan chan *Chunk + // ChunkParserTimeout is the duration after which the main parser keeps going + // if the dev hasn't reported the chunk parsing to be done. + // By default: 2s + ChunkParserTimeout time.Duration + // The waitgroup is used to let the parser that it's ok to continue + // after a chunk was passed to the optional parser channel. + Wg *sync.WaitGroup + + // Must match RIFF + ID [4]byte + // This size is the size of the block + // controlled by the RIFF header. Normally this equals the file size. + Size uint32 + // Format name. + // The representation of data in , and the content of the + // of the ‘fmt’ chunk, depend on the format category. + // 0001h => Microsoft Pulse Code Modulation (PCM) format + // 0050h => MPEG-1 Audio (audio only) + Format [4]byte + + // WAV stuff + // size of the wav specific fmt header + wavHeaderSize uint32 + // A number indicating the WAVE format category of the file. The content of the + // portion of the ‘fmt’ chunk, and the interpretation of + // the waveform data, depend on this value. + // PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression. + WavAudioFormat uint16 + // The number of channels represented in the waveform data: 1 for mono or 2 for stereo. + // Audio: Mono = 1, Stereo = 2, etc. + // The EBU has defined the Multi-channel Broadcast Wave + // Format [4] where more than two channels of audio are required. + NumChannels uint16 + // The sampling rate (in sample per second) at which each channel should be played. + // 8000, 44100, etc. + SampleRate uint32 + // The average number of bytes per second at which the waveform data should be + // transferred. Playback software can estimate the buffer size using this value. + // SampleRate * NumChannels * BitsPerSample/8 + AvgBytesPerSec uint32 + // BlockAlign = SignificantBitsPerSample / 8 * NumChannels + // 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: + // NumChannels * BitsPerSample/8 The number of bytes for one sample including + // all channels. + // The block alignment (in bytes) of the waveform data. Playback software needs + // to process a multiple of bytes of data at a time, so the value of + // can be used for buffer alignment. + BlockAlign uint16 + // BitsPerSample 8, 16, 24... + // Only available for PCM + // This value specifies the number of bits used to define each sample. This value is usually 8, 16, 24 or 32. + // If the number of bits is not byte aligned (a multiple of 8) then the number of bytes used per sample is + // rounded up to the nearest byte size and the unused bytes are set to 0 and ignored. + // The field specifies the number of bits of data used to represent each sample of + // each channel. If there are multiple channels, the sample size is the same for each channel. + BitsPerSample uint16 +} + +// ParseHeaders reads the header of the passed container and populat the container with parsed info. +// Note that this code advances the container reader. +func (c *Parser) ParseHeaders() error { + id, size, err := c.IDnSize() + if err != nil { + return err + } + c.ID = id + if c.ID != RiffID { + return fmt.Errorf("%s - %s", c.ID, ErrFmtNotSupported) + } + c.Size = size + if err := binary.Read(c.r, binary.BigEndian, &c.Format); err != nil { + return err + } + + return nil +} + +// Duration returns the time duration for the current RIFF container +// based on the sub format (wav etc...) +func (c *Parser) Duration() (time.Duration, error) { + if c == nil { + return 0, errors.New("can't calculate the duration of a nil pointer") + } + if c.ID == [4]byte{} || c.AvgBytesPerSec == 0 { + err := c.Parse() + if err != nil { + return 0, nil + } + } + switch c.Format { + case WavFormatID: + return c.wavDuration() + default: + return 0, ErrFmtNotSupported + } +} + +// String implements the Stringer interface. +func (c *Parser) String() string { + out := fmt.Sprintf("Format: %s - ", c.Format) + if c.Format == WavFormatID { + out += fmt.Sprintf("%d channels @ %d / %d bits - ", c.NumChannels, c.SampleRate, c.BitsPerSample) + d, _ := c.Duration() + out += fmt.Sprintf("Duration: %f seconds", d.Seconds()) + } + return out +} + +// NextChunk returns a convenient structure to parse the next chunk. +// If the container is fully read, io.EOF is returned as an error. +func (c *Parser) NextChunk() (*Chunk, error) { + if c == nil { + return nil, errors.New("can't calculate the duration of a nil pointer") + } + id, size, err := c.IDnSize() + if err != nil { + return nil, err + } + + // all RIFF chunks (including WAVE "data" chunks) must be word aligned. + // 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. + // The "data" chunk header's size should not include this byte. + if size%2 == 1 { + size++ + } + + ch := &Chunk{ + ID: id, + Size: int(size), + R: c.r, + } + return ch, nil +} + +// IDnSize returns the next ID + block size +func (c *Parser) IDnSize() ([4]byte, uint32, error) { + var ID [4]byte + var blockSize uint32 + if err := binary.Read(c.r, binary.BigEndian, &ID); err != nil { + return ID, blockSize, err + } + if err := binary.Read(c.r, binary.LittleEndian, &blockSize); err != err { + return ID, blockSize, err + } + return ID, blockSize, nil +} + +// Parse parses the content of the file and populate the useful fields. +// If the parser has a chan set, chunks are sent to the channel. +func (p *Parser) Parse() error { + if p == nil { + return errors.New("can't calculate the wav duration of a nil pointer") + } + + if p.Size == 0 { + id, size, err := p.IDnSize() + if err != nil { + return err + } + p.ID = id + if p.ID != RiffID { + return fmt.Errorf("%s - %s", p.ID, ErrFmtNotSupported) + } + p.Size = size + if err := binary.Read(p.r, binary.BigEndian, &p.Format); err != nil { + return err + } + } + + var chunk *Chunk + var err error + for err == nil { + chunk, err = p.NextChunk() + if err != nil { + break + } + + if chunk.ID == FmtID { + chunk.DecodeWavHeader(p) + } else { + if p.Chan != nil { + if chunk.Wg == nil { + chunk.Wg = p.Wg + } + chunk.Wg.Add(1) + p.Chan <- chunk + // the channel has to release otherwise the goroutine is locked + chunk.Wg.Wait() + } + } + + // BFW: bext chunk described here + // https://tech.ebu.ch/docs/tech/tech3285.pdf + + if !chunk.IsFullyRead() { + chunk.Drain() + } + + } + if p.Wg != nil { + p.Wg.Wait() + } + + if p.Chan != nil { + close(p.Chan) + } + + if err == io.EOF { + return nil + } + return err +} + +// WavDuration returns the time duration of a wav container. +func (p *Parser) wavDuration() (time.Duration, error) { + if p.Size == 0 || p.AvgBytesPerSec == 0 { + return 0, fmt.Errorf("can't extract the duration due to the file not properly parsed") + } + duration := time.Duration((float64(p.Size) / float64(p.AvgBytesPerSec)) * float64(time.Second)) + return duration, nil +} + +// jumpTo advances the reader to the amount of bytes provided +func (p *Parser) jumpTo(bytesAhead int) error { + var err error + for bytesAhead > 0 { + readSize := bytesAhead + if readSize > 4000 { + readSize = 4000 + } + + buf := make([]byte, readSize) + err = binary.Read(p.r, binary.LittleEndian, &buf) + if err != nil { + return nil + } + bytesAhead -= readSize + } + return nil +} diff --git a/vendor/github.com/mattetti/audio/riff/riff.go b/vendor/github.com/mattetti/audio/riff/riff.go new file mode 100644 index 0000000..444c878 --- /dev/null +++ b/vendor/github.com/mattetti/audio/riff/riff.go @@ -0,0 +1,41 @@ +package riff + +import ( + "errors" + "io" + "sync" + "time" +) + +var ( + RiffID = [4]byte{'R', 'I', 'F', 'F'} + FmtID = [4]byte{'f', 'm', 't', ' '} + // To align RIFF chunks to certain boundaries (i.e. 2048bytes for CD-ROMs) the RIFF specification includes a JUNK chunk. + // Its contents are to be skipped when reading. When writing RIFFs, JUNK chunks should not have odd number as Size. + junkID = [4]byte{'J', 'U', 'N', 'K'} + WavFormatID = [4]byte{'W', 'A', 'V', 'E'} + // DataFormatID is the Wave Data Chunk ID, it contains the digital audio sample data which can be decoded using the format + // 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. + DataFormatID = [4]byte{'d', 'a', 't', 'a'} + rmiFormatID = [4]byte{'R', 'M', 'I', 'D'} + aviFormatID = [4]byte{'A', 'V', 'I', ' '} + // ErrFmtNotSupported is a generic error reporting an unknown format. + ErrFmtNotSupported = errors.New("format not supported") + // ErrUnexpectedData is a generic error reporting that the parser encountered unexpected data. + ErrUnexpectedData = errors.New("unexpected data content") +) + +// New creates a parser wrapper for a reader. +// Note that the reader doesn't get rewinded as the container is processed. +func New(r io.Reader) *Parser { + return &Parser{r: r, Wg: &sync.WaitGroup{}} +} + +// Duration returns the time duration of the passed reader if the sub format is supported. +func Duration(r io.Reader) (time.Duration, error) { + c := New(r) + if err := c.ParseHeaders(); err != nil { + return 0, err + } + return c.Duration() +} diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 0000000..835ba3e --- /dev/null +++ b/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md new file mode 100644 index 0000000..6483ba2 --- /dev/null +++ b/vendor/github.com/pkg/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## License + +BSD-2-Clause diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml new file mode 100644 index 0000000..a932ead --- /dev/null +++ b/vendor/github.com/pkg/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 0000000..842ee80 --- /dev/null +++ b/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,269 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required the errors.WithStack and errors.WithMessage +// functions destructure errors.Wrap into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface. +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// stackTracer interface is not exported by this package, but is considered a part +// of stable public API. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is call, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 0000000..2874a04 --- /dev/null +++ b/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,147 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} diff --git a/vendor/vendor.json b/vendor/vendor.json new file mode 100644 index 0000000..c2d834a --- /dev/null +++ b/vendor/vendor.json @@ -0,0 +1,31 @@ +{ + "comment": "", + "ignore": "test", + "package": [ + { + "checksumSHA1": "pnZHJigSA3Fn9PRe1xFBIun6ifQ=", + "path": "github.com/go-audio/audio", + "revision": "b697a35b5608fcf1c4f90a9d4f33d76bd70e8fe6", + "revisionTime": "2018-02-06T23:14:10Z" + }, + { + "checksumSHA1": "GkppKSJal6BMhO6435ssafzkMa4=", + "path": "github.com/go-audio/wav", + "revision": "474d6485029b52ab4078275503872e7b739fb89f", + "revisionTime": "2018-01-29T08:06:50Z" + }, + { + "checksumSHA1": "uRdoDQNpEqx6LaKqjoDo+ylknFI=", + "path": "github.com/mattetti/audio/riff", + "revision": "b7bc1ebaf222d206df57afe66d58ed2249a8061d", + "revisionTime": "2018-04-03T01:03:54Z" + }, + { + "checksumSHA1": "ljd3FhYRJ91cLZz3wsH9BQQ2JbA=", + "path": "github.com/pkg/errors", + "revision": "816c9085562cd7ee03e7f8188a1cfd942858cded", + "revisionTime": "2018-03-11T21:45:15Z" + } + ], + "rootPath": "github.com/soundcloud/go-funk" +}