├── .gitignore ├── LICENSE ├── README.md ├── doc └── speex_data.go ├── main.go └── speex ├── .gitignore ├── dec.go ├── dec_test.go └── example_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | speex-lib 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 winlin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-speex 2 | 3 | Golang binding for speex(https://github.com/winlinvip/speex) 4 | 5 | ## Usage 6 | 7 | First, get the source code: 8 | 9 | ``` 10 | go get -d github.com/winlinvip/go-speex 11 | ``` 12 | 13 | Then, compile the speex: 14 | 15 | ``` 16 | cd $GOPATH/src/github.com/winlinvip/go-speex && 17 | git clone https://github.com/winlinvip/speex.git speex-lib && 18 | cd speex-lib/ && bash autogen.sh && ./configure --prefix=`pwd`/objs --enable-static && make && make install && 19 | cd .. 20 | ``` 21 | 22 | Done, import and use the package: 23 | 24 | * [speex decoder](dec/example_test.go), decode the speex frame to PCM samples. 25 | 26 | To run all examples: 27 | 28 | ``` 29 | cd $GOPATH/src/github.com/winlinvip/go-speex && go test ./... 30 | ``` 31 | 32 | There are an example of SPEEX audio packets in FLV: 33 | 34 | * [avatar speex over FLV](doc/speex_data.go), user can use this file to decode to PCM. 35 | * [audio resample](https://github.com/winlinvip/go-aresample). 36 | 37 | For more information about SPEEX codec, read: 38 | 39 | * [github.com](https://github.com/winlinvip/speex), source code of speex codec. 40 | * [examples](http://www.speex.org/docs/manual/speex-manual/node13.html), encoder and decoder example. 41 | 42 | Winlin 2016 43 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Please use library. 2 | package main 3 | 4 | import ( 5 | _ "github.com/winlinvip/go-speex/speex" 6 | ) 7 | 8 | func main() { 9 | return 10 | } 11 | 12 | -------------------------------------------------------------------------------- /speex/.gitignore: -------------------------------------------------------------------------------- 1 | speex 2 | -------------------------------------------------------------------------------- /speex/dec.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 winlin 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | // The speex decoder, to decode the encoded speex frame to PCM samples. 23 | package speex 24 | 25 | /* 26 | #cgo CFLAGS: -I${SRCDIR}/../speex-lib/objs/include 27 | #cgo LDFLAGS: ${SRCDIR}/../speex-lib/objs/lib/libspeex.a -lm 28 | #include "speex/speex.h" 29 | 30 | typedef struct { 31 | SpeexBits bits; 32 | const SpeexMode* mode; 33 | void* state; 34 | 35 | int frame_size; 36 | int sample_rate; 37 | } speexdec_t; 38 | 39 | int speexdec_init(speexdec_t* h, int sample_rate, int channels) { 40 | h->mode = 0; 41 | h->state = 0; 42 | h->frame_size = h->sample_rate = 0; 43 | 44 | // TODO: support stereo speex. 45 | if (channels != 1) { 46 | return -1; 47 | } 48 | 49 | int frame_size; 50 | const SpeexMode* mode; 51 | if (1) { 52 | int spx_mode; 53 | switch (sample_rate) { 54 | case 8000: spx_mode = 0; break; 55 | case 16000: spx_mode = 1; break; 56 | case 32000: spx_mode = 2; break; 57 | default: return -1; 58 | } 59 | 60 | mode = speex_lib_get_mode(spx_mode); 61 | if (!mode) { 62 | return -1; 63 | } 64 | } 65 | h->mode = mode; 66 | 67 | void* state = speex_decoder_init(mode); 68 | if (!state) { 69 | return -1; 70 | } 71 | 72 | h->state = state; 73 | speex_bits_init(&h->bits); 74 | 75 | if (1) { 76 | spx_int32_t N; 77 | speex_decoder_ctl(state, SPEEX_GET_FRAME_SIZE, &N); 78 | h->frame_size = N; 79 | 80 | speex_decoder_ctl(state, SPEEX_GET_SAMPLING_RATE, &N); 81 | h->sample_rate = N; 82 | } 83 | 84 | return 0; 85 | } 86 | 87 | void speexdec_close(speexdec_t* h) { 88 | if (h->state) { 89 | speex_decoder_destroy(h->state); 90 | speex_bits_destroy(&h->bits); 91 | } 92 | h->state = 0; 93 | } 94 | 95 | int speexdec_decode(speexdec_t* h, char* frame, int nb_frame, char* pcm, int* pnb_pcm, int* is_done) { 96 | // the output pcm must equals to the frames(each is 16bits). 97 | if (*pnb_pcm != h->frame_size * sizeof(spx_int16_t)) { 98 | return -1; 99 | } 100 | 101 | if (speex_bits_remaining(&h->bits) < 5 || 102 | speex_bits_peek_unsigned(&h->bits, 5) == 0xF) { 103 | speex_bits_read_from(&h->bits, frame, nb_frame); 104 | } 105 | 106 | spx_int16_t* output = (spx_int16_t*)pcm; 107 | int ret = speex_decode_int(h->state, &h->bits, output); 108 | 109 | // 0 for no error, -1 for end of stream, -2 corrupt stream 110 | if (ret <= -2) { 111 | return ret; 112 | } 113 | 114 | if (ret == -1) { 115 | *pnb_pcm = 0; 116 | return 0; 117 | } 118 | 119 | if (speex_bits_remaining(&h->bits) < 5 || 120 | speex_bits_peek_unsigned(&h->bits, 5) == 0xF) { 121 | *is_done = 1; 122 | } 123 | 124 | return 0; 125 | } 126 | 127 | int speexdec_frame_size(speexdec_t* h) { 128 | return h->frame_size; 129 | } 130 | 131 | int speexdec_sample_rate(speexdec_t* h) { 132 | return h->sample_rate; 133 | } 134 | */ 135 | import "C" 136 | 137 | import ( 138 | "fmt" 139 | "unsafe" 140 | "bytes" 141 | ) 142 | 143 | type SpeexDecoder struct { 144 | m C.speexdec_t 145 | } 146 | 147 | func NewSpeexDecoder() *SpeexDecoder { 148 | return &SpeexDecoder{} 149 | } 150 | 151 | // @remark only support mono speex(channels must be 1). 152 | func (v *SpeexDecoder) Init(sampleRate, channels int) (err error) { 153 | if channels != 1 { 154 | return fmt.Errorf("only support mono(1), actual is %v", channels) 155 | } 156 | 157 | r := C.speexdec_init(&v.m, C.int(sampleRate), C.int(channels)) 158 | if int(r) != 0 { 159 | return fmt.Errorf("init decoder failed, err=%v", int(r)) 160 | } 161 | 162 | return 163 | } 164 | 165 | func (v *SpeexDecoder) Close() { 166 | C.speexdec_close(&v.m) 167 | } 168 | 169 | // @return pcm is nil when EOF. 170 | func (v *SpeexDecoder) Decode(frame []byte) (pcm []byte, err error) { 171 | p := (*C.char)(unsafe.Pointer(&frame[0])) 172 | pSize := C.int(len(frame)) 173 | 174 | pIsDone := C.int(0) 175 | 176 | var result bytes.Buffer 177 | 178 | for { 179 | if pIsDone == 1 { 180 | break 181 | } 182 | // each sample is 16bits(2bytes), 183 | // so we alloc the output to frame_size*2. 184 | nbPcmBytes := v.FrameSize()*2 185 | 186 | pcmTmp := make([]byte, nbPcmBytes) 187 | pPcm := (*C.char)(unsafe.Pointer(&pcmTmp[0])) 188 | pNbPcm := C.int(nbPcmBytes) 189 | 190 | r := C.speexdec_decode(&v.m, p, pSize, pPcm, &pNbPcm, &pIsDone) 191 | if int(r) != 0 { 192 | return nil,fmt.Errorf("decode failed, err=%v", int(r)) 193 | } 194 | // if pNbPcm is 0, which means the stream is end, need to jump out of cycle. 195 | if int(pNbPcm) == 0 { 196 | break 197 | } 198 | if int(pNbPcm) != nbPcmBytes { 199 | return nil,fmt.Errorf("invalid pcm size %v", int(pNbPcm)) 200 | } 201 | result.Write(pcmTmp) 202 | } 203 | pcm = result.Bytes() 204 | 205 | return 206 | } 207 | 208 | func (v *SpeexDecoder) FrameSize() int { 209 | return int(C.speexdec_frame_size(&v.m)) 210 | } 211 | 212 | func (v *SpeexDecoder) SampleRate() int { 213 | return int(C.speexdec_sample_rate(&v.m)) 214 | } 215 | 216 | func (v *SpeexDecoder) Channels() int { 217 | return 1 218 | } 219 | -------------------------------------------------------------------------------- /speex/dec_test.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 winlin 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | // @remark we must use another packet for utest, because the cgo will dup symbols. 23 | package speex_test 24 | 25 | import ( 26 | "testing" 27 | "github.com/winlinvip/go-speex/speex" 28 | ) 29 | 30 | func TestSpeexInit(t *testing.T) { 31 | var err error 32 | d := speex.NewSpeexDecoder() 33 | if err = d.Init(16000, 1); err != nil { 34 | t.Error("init decoder failed, err is", err) 35 | return 36 | } 37 | defer d.Close() 38 | 39 | if 320 != d.FrameSize() { 40 | t.Error("invalid frame size", d.FrameSize()) 41 | return 42 | } 43 | 44 | if 16000 != d.SampleRate() { 45 | t.Error("invalid sample rate", d.SampleRate()) 46 | return 47 | } 48 | 49 | var pcm []byte 50 | if pcm,err = d.Decode([]byte{ 51 | 0x3d, 0xdc, 0x20, 0x13, 0xf3, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 52 | 0x80, 0x61, 0xbf, 0xff, 0xf7, 0x6e, 0x3a, 0xa2, 0xff, 0xff, 0xf6, 0x01, 0x37, 0xd7, 0x49, 0x9d, 0xf7, 0xdf, 0xf2, 53 | 0x6f, 0x63, 0xda, 0xcd, 0xa4, 0x18, 0x47, 0xe6, 0x19, 0x47, 0x96, 0xf4, 0x32, 0xe6, 0x21, 0x26, 0x8d, 0x12, 0xee, 54 | 0x6d, 0x7c, 0x5b, 0x3f, 0x3c, 0x5f, 0xd7, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0x6a, 0xba, 55 | 0xb8, 0x4a, 0x74, 0x9a, 0xb4, 0x2d, 0xd8, 0xd8, 0xe1, 0xc3, 0x47, 0x25, 0xe8, 0x05, 0xa3, 0xbb, 0xd7, 0x66, 0x3a, 56 | 0x1b, 0xb7, 0xa4, 0x7d, 0xa2, 0xab, 0xfe, 0xd9, 0x08, 0x2c, 0x47, 57 | }); err != nil { 58 | t.Error("decode failed, err is", err) 59 | return 60 | } else if pcm == nil { 61 | t.Error("decode EOF") 62 | return 63 | } 64 | if len(pcm) != d.FrameSize()*2 { 65 | t.Error("invalid pcm size", len(pcm)) 66 | return 67 | } 68 | 69 | if pcm,err = d.Decode([]byte{ 70 | 0x3d, 0xca, 0x6c, 0x47, 0xd6, 0xbb, 0x47, 0x3e, 0x93, 0xe1, 0xf0, 0xc0, 0xe3, 0xdb, 0xc0, 0xbe, 0x57, 0xe6, 0x8b, 71 | 0x95, 0x5b, 0xe0, 0x92, 0xe0, 0x84, 0x4d, 0x69, 0x89, 0x27, 0xb3, 0x42, 0xb9, 0xa8, 0x15, 0xcd, 0xc5, 0xe5, 0x46, 72 | 0xd1, 0xcc, 0x73, 0x09, 0x3f, 0x29, 0x0a, 0xa6, 0x1f, 0xad, 0xe6, 0xfc, 0x2d, 0xd6, 0x4e, 0xf0, 0xc6, 0xd8, 0xf0, 73 | 0xff, 0x64, 0x41, 0x8d, 0x6c, 0x2e, 0x00, 0x7d, 0x0a, 0xcd, 0xbb, 0xf8, 0x5f, 0x5e, 0x75, 0xe9, 0x1f, 0x00, 0x95, 74 | 0xb0, 0x11, 0xd6, 0x5e, 0x33, 0xec, 0x33, 0x7b, 0xe2, 0xf1, 0x09, 0xca, 0xc5, 0x4a, 0xca, 0x02, 0x6b, 0x59, 0xc6, 75 | 0x36, 0xfa, 0x71, 0x3d, 0xa3, 0xbb, 0xa5, 0xbe, 0x36, 0x8b, 0xe7, 76 | }); err != nil { 77 | t.Error("decode failed, err is", err) 78 | return 79 | } else if pcm == nil { 80 | t.Error("decode EOF") 81 | return 82 | } 83 | if len(pcm) != d.FrameSize()*2 { 84 | t.Error("invalid pcm size", len(pcm)) 85 | return 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /speex/example_test.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 winlin 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | package speex_test 23 | 24 | import ( 25 | "fmt" 26 | "github.com/winlinvip/go-speex/speex" 27 | ) 28 | 29 | func ExampleSpeexDecoder() { 30 | var err error 31 | d := speex.NewSpeexDecoder() 32 | if err = d.Init(16000, 1); err != nil { 33 | fmt.Println("init decoder failed, err is", err) 34 | return 35 | } 36 | defer d.Close() 37 | 38 | fmt.Println("FrameSize:", d.FrameSize()) 39 | fmt.Println("SampleRate:", d.SampleRate()) 40 | 41 | var pcm []byte 42 | var frame []byte = []byte{ 43 | 0x3d, 0xdc, 0x20, 0x13, 0xf3, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 44 | 0x80, 0x61, 0xbf, 0xff, 0xf7, 0x6e, 0x3a, 0xa2, 0xff, 0xff, 0xf6, 0x01, 0x37, 0xd7, 0x49, 0x9d, 0xf7, 0xdf, 0xf2, 45 | 0x6f, 0x63, 0xda, 0xcd, 0xa4, 0x18, 0x47, 0xe6, 0x19, 0x47, 0x96, 0xf4, 0x32, 0xe6, 0x21, 0x26, 0x8d, 0x12, 0xee, 46 | 0x6d, 0x7c, 0x5b, 0x3f, 0x3c, 0x5f, 0xd7, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0x6a, 0xba, 47 | 0xb8, 0x4a, 0x74, 0x9a, 0xb4, 0x2d, 0xd8, 0xd8, 0xe1, 0xc3, 0x47, 0x25, 0xe8, 0x05, 0xa3, 0xbb, 0xd7, 0x66, 0x3a, 48 | 0x1b, 0xb7, 0xa4, 0x7d, 0xa2, 0xab, 0xfe, 0xd9, 0x08, 0x2c, 0x47, 49 | } 50 | if pcm,err = d.Decode(frame); err != nil { 51 | fmt.Println("decode failed, err is", err) 52 | return 53 | } 54 | fmt.Println("Frame:", len(frame)) 55 | fmt.Println("PCM:", len(pcm)) 56 | 57 | // Output: 58 | // FrameSize: 320 59 | // SampleRate: 16000 60 | // Frame: 106 61 | // PCM: 640 62 | } 63 | --------------------------------------------------------------------------------