├── LICENSE ├── README.md ├── cmd └── say │ ├── main.go │ └── make.bash ├── flite.go └── flite_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2017 Milan Nikolic (gen2brain) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | * Neither the name of the copyright holder(s) nor the names of its 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## flite-go [![GoDoc](https://godoc.org/github.com/gen2brain/flite-go?status.svg)](https://godoc.org/github.com/gen2brain/flite-go) 2 | 3 | Golang bindings for [Flite](http://www.speech.cs.cmu.edu/flite/index.html) (festival-lite) 4 | 5 | ### Requirements 6 | 7 | * [Flite](http://www.speech.cs.cmu.edu/flite/index.html) 8 | 9 | ##### Ubuntu 10 | 11 | apt-get install flite-dev 12 | 13 | ##### Fedora 14 | 15 | dnf install flite-devel 16 | 17 | ### Installation 18 | 19 | go get -v github.com/gen2brain/flite-go 20 | 21 | ### Example 22 | 23 | ```go 24 | package main 25 | 26 | import "github.com/gen2brain/flite-go" 27 | 28 | func main() { 29 | // The valid names are "awb", "kal16", "kal", "rms" and "slt" 30 | voice, err := flite.VoiceSelect("kal") 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | // Use "play" for output and it will be sent to the audio device 36 | flite.TextToSpeech("Hello World", voice, "output.wav") 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /cmd/say/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/gen2brain/flite-go" 10 | ) 11 | 12 | func main() { 13 | v := flag.String("v", "slt", "voice to use, valid values are awb, kal16, kal, rms, slt") 14 | flag.Parse() 15 | 16 | if flag.NArg() < 1 { 17 | fmt.Printf("usage:\n\t%s \"text to speak\"\n", os.Args[0]) 18 | os.Exit(1) 19 | } 20 | 21 | voice, err := flite.VoiceSelect(*v) 22 | if err != nil { 23 | fmt.Println(err) 24 | os.Exit(1) 25 | } 26 | 27 | flite.TextToSpeech(strings.Join(flag.Args(), " "), voice, "play") 28 | } 29 | -------------------------------------------------------------------------------- /cmd/say/make.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CHROOT="/usr/x86_64-pc-linux-gnu-static" 4 | 5 | export CC=gcc 6 | export PKG_CONFIG_PATH="$CHROOT/usr/lib/pkgconfig" 7 | export PKG_CONFIG_LIBDIR="$CHROOT/usr/lib/pkgconfig" 8 | export LIBRARY_PATH="$CHROOT/usr/lib:$CHROOT/lib" 9 | 10 | CGO_LDFLAGS="-L$CHROOT/usr/lib -L$CHROOT/lib -lasound" \ 11 | CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \ 12 | go build -v -x -ldflags "-linkmode external -s -w" github.com/gen2brain/flite-go/cmd/say 13 | -------------------------------------------------------------------------------- /flite.go: -------------------------------------------------------------------------------- 1 | // Package flite implements bindings for Flite (festival-lite) 2 | package flite 3 | 4 | /* 5 | #cgo CFLAGS: -I/usr/include/flite 6 | #cgo LDFLAGS: -lflite_cmu_us_awb -lflite_cmu_us_kal16 -lflite_cmu_us_kal -lflite_cmu_us_rms -lflite_cmu_us_slt -lflite_usenglish -lflite_cmulex -lflite -lm 7 | 8 | #include "flite.h" 9 | #include 10 | 11 | cst_voice* register_cmu_us_awb(); 12 | cst_voice* register_cmu_us_kal16(); 13 | cst_voice* register_cmu_us_kal(); 14 | cst_voice* register_cmu_us_rms(); 15 | cst_voice* register_cmu_us_slt(); 16 | */ 17 | import "C" 18 | 19 | import ( 20 | "fmt" 21 | "unsafe" 22 | ) 23 | 24 | // Voice type 25 | type Voice struct { 26 | name *int8 27 | features *features 28 | ffunctions *features 29 | init *utterance 30 | } 31 | 32 | func (v *Voice) cptr() *C.cst_voice { 33 | return (*C.cst_voice)(unsafe.Pointer(v)) 34 | } 35 | 36 | // newVoiceFromPointer returns new Voice from pointer 37 | func newVoiceFromPointer(ptr unsafe.Pointer) *Voice { 38 | return (*Voice)(ptr) 39 | } 40 | 41 | // Wave type 42 | type Wave struct { 43 | Type *int8 44 | SampleRate int32 45 | NumSamples int32 46 | NumChannels int32 47 | Padding [4]byte 48 | Samples *int16 49 | } 50 | 51 | func (w *Wave) cptr() *C.cst_wave { 52 | return (*C.cst_wave)(unsafe.Pointer(w)) 53 | } 54 | 55 | // newWaveFromPointer returns new Wave from pointer 56 | func newWaveFromPointer(ptr unsafe.Pointer) *Wave { 57 | return (*Wave)(ptr) 58 | } 59 | 60 | // features type 61 | type features struct { 62 | head *featValPair 63 | ctx unsafe.Pointer 64 | } 65 | 66 | // utterance type 67 | type utterance struct { 68 | features *features 69 | ffunctions *features 70 | relations *features 71 | ctx unsafe.Pointer 72 | } 73 | 74 | // featValPair type 75 | type featValPair struct { 76 | name string 77 | val *val 78 | next *featValPair 79 | } 80 | 81 | // val type 82 | type val struct { 83 | C [16]byte 84 | } 85 | 86 | // VoiceSelect returns a Voice for the voice name. 87 | // The valid names are "awb", "kal16", "kal", "rms", "slt". 88 | // It will return error if there is no match. 89 | func VoiceSelect(name string) (*Voice, error) { 90 | var ret *C.struct_cst_voice_struct 91 | switch name { 92 | case "awb": 93 | ret = C.register_cmu_us_awb() 94 | case "kal16": 95 | ret = C.register_cmu_us_kal16() 96 | case "kal": 97 | ret = C.register_cmu_us_kal() 98 | case "rms": 99 | ret = C.register_cmu_us_rms() 100 | case "slt": 101 | ret = C.register_cmu_us_slt() 102 | default: 103 | return nil, fmt.Errorf("flite: no match for voice: %s", name) 104 | } 105 | 106 | if ret == nil { 107 | return nil, fmt.Errorf("flite: no voice") 108 | } 109 | 110 | v := newVoiceFromPointer(unsafe.Pointer(ret)) 111 | return v, nil 112 | } 113 | 114 | // TextToSpeech synthesizes the text with the given voice. 115 | // outtype may be a filename where the generated wav is written to, or "play" and it will be sent to the audio device, or "none" and it will be discarded. 116 | // The return value is the number of seconds of speech generated. 117 | func TextToSpeech(text string, voice *Voice, outtype string) float64 { 118 | ctext := C.CString(text) 119 | defer C.free(unsafe.Pointer(ctext)) 120 | 121 | cvoice := voice.cptr() 122 | 123 | couttype := C.CString(outtype) 124 | defer C.free(unsafe.Pointer(couttype)) 125 | 126 | ret := C.flite_text_to_speech(ctext, cvoice, couttype) 127 | v := (float64)(ret) 128 | return v 129 | } 130 | 131 | // TextToWave returns a waveform synthesized from the text with the given voice. 132 | func TextToWave(text string, voice *Voice) *Wave { 133 | ctext := C.CString(text) 134 | defer C.free(unsafe.Pointer(ctext)) 135 | 136 | cvoice := voice.cptr() 137 | 138 | ret := C.flite_text_to_wave(ctext, cvoice) 139 | v := newWaveFromPointer(unsafe.Pointer(ret)) 140 | return v 141 | } 142 | -------------------------------------------------------------------------------- /flite_test.go: -------------------------------------------------------------------------------- 1 | package flite 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestTextToSpeech(t *testing.T) { 9 | voice, err := VoiceSelect("awb") 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | 14 | s := TextToSpeech("Hello World", voice, "play") 15 | if s == 0 { 16 | t.Fatalf("0 seconds of speech generated") 17 | } 18 | } 19 | 20 | func TestTextToWave(t *testing.T) { 21 | voice, err := VoiceSelect("awb") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | wav := TextToWave("Hello World", voice) 27 | if wav.NumChannels != 1 { 28 | t.Fatalf("Wave number of channels != 1") 29 | } 30 | 31 | if wav.SampleRate != 16000 { 32 | t.Fatalf("Wave sample rate != 16000") 33 | } 34 | } 35 | 36 | func TestVoiceSelect(t *testing.T) { 37 | var voices = []string{"awb", "kal16", "kal", "rms", "slt"} 38 | 39 | for _, v := range voices { 40 | voice, err := VoiceSelect(v) 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | 45 | TextToSpeech("Testing "+v, voice, "play") 46 | time.Sleep(1 * time.Second) 47 | 48 | TextToSpeech("Hello World", voice, "play") 49 | time.Sleep(1 * time.Second) 50 | } 51 | } 52 | --------------------------------------------------------------------------------