From 01ac0f65dc38e6b0756eeebc92753f117c5283df Mon Sep 17 00:00:00 2001 From: Mike D Pilsbury Date: Thu, 7 Mar 2024 12:16:09 +0000 Subject: [PATCH] Partially document exported types and functions --- decoder.go | 2 ++ doc.go | 20 ++++++++++++++++++++ error.go | 43 +++++++++++++++++++++++++++++++------------ opus.go | 10 ++++++++++ 4 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 doc.go diff --git a/decoder.go b/decoder.go index 2b597b0..37fd0c9 100644 --- a/decoder.go +++ b/decoder.go @@ -9,6 +9,7 @@ import ( "unsafe" ) +// Decoder decodes an opus bitstream into PCM. type Decoder struct { file *C.OggOpusFile data []byte @@ -17,6 +18,7 @@ type Decoder struct { duration time.Duration } +// NewDecoder creates a new opus Decoder. func NewDecoder(data []byte) (*Decoder, error) { var opusFileErr C.int file := C.op_open_memory((*C.uchar)(&data[0]), C.size_t(len(data)), &opusFileErr) diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..3c12ba4 --- /dev/null +++ b/doc.go @@ -0,0 +1,20 @@ +/* +Package opus provides decoding of opus files, +using the xiph.org C libraries +[opus], [opusfile], and [ogg]. + +Rather than use cgo to utilise system installed instances of the libraries +(that may or may not be installed on a given system), +the source for the libraries is included with this package. +The libraries are compiled, with the help of cgo, when the package is compiled. +This means that a C compiler (such as gcc or clang) and a linker need to be +available on the path for cgo to use when building the package. + +It may take several seconds to build this package the first time, +until the Go tools cache the result of the build. + +[opus]: https://github.com/xiph/opus +[opusfile]: https://github.com/xiph/opusfile +[ogg]: https://github.com/xiph/ogg +*/ +package opus diff --git a/error.go b/error.go index e4b330f..48dc1ec 100644 --- a/error.go +++ b/error.go @@ -8,82 +8,113 @@ import ( "math" ) +// OpusFileError represents an error from the opusfile C library. type OpusFileError struct { code int text string } +func (err *OpusFileError) Error() string { + return fmt.Sprintf("opusfile error %d, '%s'", err.code, err.text) +} + +// Code returns the numeric error code reported by the opusfile C library. +func (err *OpusFileError) Code() int { + return err.code +} + +// Text provides text that describes the nature of the error. +func (err *OpusFileError) Text() string { + return err.text +} + +// Errors from the opusfile C library. var ( + // A request did not succeed. OP_FALSE = OpusFileError{ code: C.OP_FALSE, text: "A request did not succeed.", } + // Currently not used externally. OP_EOF = OpusFileError{ code: C.OP_EOF, text: "Currently not used externally.", } + // There was a hole in the page sequence numbers (e.g., a page was corrupt or missing). OP_HOLE = OpusFileError{ code: C.OP_HOLE, text: "There was a hole in the page sequence numbers (e.g., a page was corrupt or missing).", } + // An underlying read, seek, or tell operation failed when it should have succeeded. OP_EREAD = OpusFileError{ code: C.OP_EREAD, text: "An underlying read, seek, or tell operation failed when it should have succeeded.", } + // A NULL pointer was passed where one was unexpected, or an internal memory allocation failed, or an internal library error was encountered. OP_EFAULT = OpusFileError{ code: C.OP_EFAULT, text: "A NULL pointer was passed where one was unexpected, or an internal memory allocation failed, or an internal library error was encountered.", } + // The stream used a feature that is not implemented, such as an unsupported channel family. OP_EIMPL = OpusFileError{ code: C.OP_EIMPL, text: "The stream used a feature that is not implemented, such as an unsupported channel family.", } + // One or more parameters to a function were invalid. OP_EINVAL = OpusFileError{ code: C.OP_EINVAL, text: "One or more parameters to a function were invalid.", } + // A purported Ogg Opus stream did not begin with an Ogg page, a purported header packet did not start with one of the required strings, "OpusHead" or "OpusTags", or a link in a chained file was encountered that did not contain any logical Opus streams. OP_ENOTFORMAT = OpusFileError{ code: C.OP_ENOTFORMAT, text: "A purported Ogg Opus stream did not begin with an Ogg page, a purported header packet did not start with one of the required strings, \"OpusHead\" or \"OpusTags\", or a link in a chained file was encountered that did not contain any logical Opus streams.", } + // A required header packet was not properly formatted, contained illegal values, or was missing altogether. OP_EBADHEADER = OpusFileError{ code: C.OP_EBADHEADER, text: "A required header packet was not properly formatted, contained illegal values, or was missing altogether.", } + // The ID header contained an unrecognized version number. OP_EVERSION = OpusFileError{ code: C.OP_EVERSION, text: "The ID header contained an unrecognized version number.", } + // Currently not used at all. OP_ENOTAUDIO = OpusFileError{ code: C.OP_ENOTAUDIO, text: "Currently not used at all.", } + // An audio packet failed to decode properly. More... OP_EBADPACKET = OpusFileError{ code: C.OP_EBADPACKET, text: "An audio packet failed to decode properly. More...", } + // We failed to find data we had seen before, or the bitstream structure was sufficiently malformed that seeking to the target destination was impossible. OP_EBADLINK = OpusFileError{ code: C.OP_EBADLINK, text: "We failed to find data we had seen before, or the bitstream structure was sufficiently malformed that seeking to the target destination was impossible.", } + // An operation that requires seeking was requested on an unseekable stream. OP_ENOSEEK = OpusFileError{ code: C.OP_ENOSEEK, text: "An operation that requires seeking was requested on an unseekable stream.", } + // The first or last granule position of a link failed basic validity checks. OP_EBADTIMESTAMP = OpusFileError{ code: C.OP_EBADTIMESTAMP, text: "The first or last granule position of a link failed basic validity checks.", @@ -108,18 +139,6 @@ var allOpusFileErrors = []OpusFileError{ OP_EBADTIMESTAMP, } -func (err *OpusFileError) Error() string { - return fmt.Sprintf("opusfile error %d, '%s'", err.code, err.text) -} - -func (err *OpusFileError) Code() int { - return err.code -} - -func (err *OpusFileError) Text() string { - return err.text -} - func errorFromOpusFileError(code C.int) error { for _, err := range allOpusFileErrors { if err.code == int(code) { diff --git a/opus.go b/opus.go index 4294b62..2677484 100644 --- a/opus.go +++ b/opus.go @@ -7,6 +7,16 @@ import ( _ "github.com/pekim/opus/c-sources" ) +/* +Test checks to see if some data appears to be the start of an Opus stream. + +For good results, you will need at least 57 bytes (for a pure Opus-only stream). +Something like 512 bytes will give more reliable results for multiplexed streams. +This function is meant to be a quick-rejection filter. +Its purpose is not to guarantee that a stream is a valid Opus stream, +but to ensure that it looks enough like Opus that it isn't going to be recognized as some other format +(except possibly an Opus stream that is also multiplexed with other codecs, such as video). +*/ func Test(data []byte) error { result := C.op_test(nil, (*C.uchar)(&data[0]), C.ulong(min(512, len(data)))) if result < 0 {