├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── example_test.go ├── ivona.go ├── ivona_test.go └── model.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### OSX ### 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear on external disk 16 | .Spotlight-V100 17 | .Trashes 18 | 19 | # Directories potentially created on remote AFP share 20 | .AppleDB 21 | .AppleDesktop 22 | Network Trash Folder 23 | Temporary Items 24 | .apdisk 25 | 26 | 27 | ### Go ### 28 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 29 | *.o 30 | *.a 31 | *.so 32 | 33 | # Folders 34 | _obj 35 | _test 36 | 37 | # Architecture specific extensions/prefixes 38 | *.[568vq] 39 | [568vq].out 40 | 41 | *.cgo1.go 42 | *.cgo2.c 43 | _cgo_defun.c 44 | _cgo_gotypes.go 45 | _cgo_export.* 46 | 47 | _testmain.go 48 | 49 | *.exe 50 | *.test 51 | *.prof 52 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | go: 6 | - 1.3 7 | - tip 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 José Padilla 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go IVONA [![Build Status](https://travis-ci.org/jpadilla/ivona-go.svg?branch=master)](https://travis-ci.org/jpadilla/ivona-go) 2 | 3 | Go client library for [IVONA Speech Cloud API](http://www.ivona.com/us/). 4 | 5 | ## Supported API Calls 6 | 7 | - CreateSpeech 8 | 9 | ## Versioning 10 | 11 | Each revision of the binding is tagged and the version is updated accordingly. 12 | 13 | Given Go's lack of built-in versioning, it is highly recommended you use a 14 | [package management tool](https://code.google.com/p/go-wiki/wiki/PackageManagementTools) in order 15 | to ensure a newer version of the binding does not affect backwards compatibility. 16 | 17 | To see the list of past versions, run `git tag`. To manually get an older 18 | version of the client, clone this repo, checkout the specific tag and build the 19 | library: 20 | 21 | ```sh 22 | git clone https://github.com/jpadilla/ivona-go.git 23 | cd ivona-go 24 | git checkout api_version_tag 25 | make build 26 | ``` 27 | 28 | ## Installation 29 | 30 | ``` 31 | go get github.com/jpadilla/ivona-go 32 | ``` 33 | 34 | ## Documentation 35 | 36 | For details on all the functionality in this library, see the [GoDoc](http://godoc.org/github.com/jpadilla/ivona-go) documentation. 37 | 38 | ## Example usage 39 | 40 | ```go 41 | package main 42 | 43 | import ( 44 | "log" 45 | 46 | ivona "github.com/jpadilla/ivona-go" 47 | ) 48 | 49 | func main() { 50 | client := ivona.New("IVONA_ACCESS_KEY", "IVONA_SECRET_KEY") 51 | options := ivona.NewSpeechOptions("Hello World") 52 | r, err := client.CreateSpeech(options) 53 | 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | 58 | log.Printf("%v\n", len(r.Audio)) 59 | log.Printf("%v\n", r.ContentType) 60 | log.Printf("%v\n", r.RequestID) 61 | } 62 | 63 | ``` 64 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package ivona_test 2 | 3 | import ( 4 | "log" 5 | 6 | ivona "github.com/jpadilla/ivona-go" 7 | ) 8 | 9 | func ExampleIvona_CreateSpeech() { 10 | client := ivona.New("IVONA_ACCESS_KEY", "IVONA_SECRET_KEY") 11 | options := ivona.NewSpeechOptions("Hello World") 12 | r, err := client.CreateSpeech(options) 13 | 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | log.Printf("%v\n", len(r.Audio)) 19 | log.Printf("%v\n", r.ContentType) 20 | log.Printf("%v\n", r.RequestID) 21 | } 22 | 23 | func ExampleIvona_ListVoices() { 24 | client := ivona.New("IVONA_ACCESS_KEY", "IVONA_SECRET_KEY") 25 | 26 | r, err := client.ListVoices(ivona.Voice{}) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | log.Printf("%v\n", len(r.Voices)) 32 | } 33 | -------------------------------------------------------------------------------- /ivona.go: -------------------------------------------------------------------------------- 1 | // Package ivona provides the binding for IVONA Speech Cloud API 2 | package ivona 3 | 4 | import ( 5 | "bytes" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/bmizerany/aws4" 9 | "io/ioutil" 10 | "net/http" 11 | ) 12 | 13 | // ivonaAPI is the public API IVONA Speech Cloud URL. 14 | const ivonaAPI = "https://tts.eu-west-1.ivonacloud.com" 15 | 16 | // createSpeechAPI is the public API IVONA Speech Cloud URL for the CreateSpeech action. 17 | const createSpeechAPI = ivonaAPI + "/CreateSpeech" 18 | const listVoicesAPI = ivonaAPI + "/ListVoices" 19 | 20 | // Ivona is used to invoke API calls 21 | type Ivona struct { 22 | AccessKey string 23 | SecretKey string 24 | } 25 | 26 | // New returns a new Ivona client. 27 | func New(accessKey string, secretKey string) *Ivona { 28 | return &Ivona{AccessKey: accessKey, SecretKey: secretKey} 29 | } 30 | 31 | // CreateSpeech performs a synthesis of the requested text and returns the audio stream containing the speech. 32 | func (client *Ivona) CreateSpeech(options SpeechOptions) (*SpeechResponse, error) { 33 | b, err := json.Marshal(options) 34 | 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | r, _ := http.NewRequest("POST", createSpeechAPI, bytes.NewReader(b)) 40 | r.Header.Set("Content-Type", "application/json") 41 | 42 | awsClient := aws4.Client{Keys: &aws4.Keys{ 43 | AccessKey: client.AccessKey, 44 | SecretKey: client.SecretKey, 45 | }} 46 | 47 | resp, err := awsClient.Do(r) 48 | 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | defer resp.Body.Close() 54 | 55 | data, err := ioutil.ReadAll(resp.Body) 56 | 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | if resp.StatusCode != 200 { 62 | return nil, fmt.Errorf("Got non 200 status code: %s %q", resp.Status, data) 63 | } 64 | 65 | return &SpeechResponse{ 66 | Audio: data, 67 | RequestID: resp.Header["X-Amzn-Ivonattsrequestid"][0], 68 | ContentType: resp.Header["Content-Type"][0], 69 | }, nil 70 | } 71 | 72 | // ListVoices retrieves list of voices from the api 73 | func (client *Ivona) ListVoices(options Voice) (*ListResponse, error) { 74 | b, err := json.Marshal(options) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | r, _ := http.NewRequest("POST", listVoicesAPI, bytes.NewReader(b)) 80 | r.Header.Set("Content-Type", "application/json") 81 | 82 | awsClient := aws4.Client{Keys: &aws4.Keys{ 83 | AccessKey: client.AccessKey, 84 | SecretKey: client.SecretKey, 85 | }} 86 | 87 | resp, err := awsClient.Do(r) 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | defer resp.Body.Close() 93 | 94 | data, err := ioutil.ReadAll(resp.Body) 95 | if err != nil { 96 | return nil, err 97 | } 98 | 99 | if resp.StatusCode != 200 { 100 | return nil, fmt.Errorf("Got non 200 status code: %s %q", resp.Status, data) 101 | } 102 | 103 | list := new(ListResponse) 104 | err = json.Unmarshal(data, list) 105 | if err != nil { 106 | return nil, err 107 | } 108 | 109 | list.RequestID = resp.Header["X-Amzn-Ivonattsrequestid"][0] 110 | list.ContentType = resp.Header["Content-Type"][0] 111 | 112 | return list, nil 113 | } 114 | -------------------------------------------------------------------------------- /ivona_test.go: -------------------------------------------------------------------------------- 1 | package ivona_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | ivona "github.com/jpadilla/ivona-go" 8 | ) 9 | 10 | var ( 11 | ivonaAccessKey = os.Getenv("IVONA_ACCESS_KEY") 12 | ivonaSecretKey = os.Getenv("IVONA_SECRET_KEY") 13 | testText = "Hello World" 14 | ) 15 | 16 | func init() { 17 | if len(ivonaAccessKey) == 0 || len(ivonaSecretKey) == 0 { 18 | panic("IVONA_ACCESS_KEY and IVONA_SECRET_KEY environment variables are needed to run tests!\n") 19 | } 20 | } 21 | 22 | func TestIvona_CreateSpeech(t *testing.T) { 23 | client := ivona.New(ivonaAccessKey, ivonaSecretKey) 24 | options := ivona.NewSpeechOptions(testText) 25 | r, err := client.CreateSpeech(options) 26 | 27 | if err != nil { 28 | t.Error(err) 29 | } 30 | 31 | audioLength := len(r.Audio) 32 | expectedAudioLength := 6314 33 | expectedContentType := "audio/mpeg" 34 | 35 | if r.ContentType != expectedContentType { 36 | t.Errorf("ContentType %v does not match", r.ContentType) 37 | } 38 | 39 | if audioLength != expectedAudioLength { 40 | t.Errorf("Audio length %v does not match", audioLength) 41 | } 42 | } 43 | 44 | func TestIvona_ListVoices(t *testing.T) { 45 | client := ivona.New(ivonaAccessKey, ivonaSecretKey) 46 | 47 | r, err := client.ListVoices(ivona.Voice{}) 48 | if err != nil { 49 | t.Error(err) 50 | } 51 | 52 | voicesLength := len(r.Voices) 53 | expectedVoicesLength := 51 54 | expectedContentType := "application/json" 55 | 56 | if voicesLength != expectedVoicesLength { 57 | t.Errorf("Voices length %v does not match", len(r.Voices)) 58 | } 59 | 60 | if r.ContentType != expectedContentType { 61 | t.Errorf("ContentType %v does not match", r.ContentType) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /model.go: -------------------------------------------------------------------------------- 1 | package ivona 2 | 3 | // SpeechResponse is the resource representing response from CreateSpeech action. 4 | type SpeechResponse struct { 5 | Audio []byte 6 | RequestID string 7 | ContentType string 8 | } 9 | 10 | // ListResponse is the resource representing response from ListVoices action. 11 | type ListResponse struct { 12 | Voices []Voice 13 | RequestID string 14 | ContentType string 15 | } 16 | 17 | // SpeechOptions is the set of parameters that can be used on the CreateSpeech action. 18 | // For more details see http://developer.ivona.com/en/speechcloud/api_ref_actions.html#CreateSpeech. 19 | type SpeechOptions struct { 20 | Input *Input 21 | OutputFormat *OutputFormat 22 | Parameters *Parameters 23 | Voice *Voice 24 | } 25 | 26 | // NewSpeechOptions is the set of default parameters that can be used the CreateSpeech action. 27 | // For more details see http://developer.ivona.com/en/speechcloud/api_ref_actions.html#CreateSpeech_DefaultValues. 28 | func NewSpeechOptions(data string) SpeechOptions { 29 | return SpeechOptions{ 30 | Input: &Input{ 31 | Data: data, 32 | Type: "text/plain", 33 | }, 34 | OutputFormat: &OutputFormat{ 35 | Codec: "MP3", 36 | SampleRate: 22050, 37 | }, 38 | Parameters: &Parameters{ 39 | Rate: "medium", 40 | Volume: "medium", 41 | SentenceBreak: 400, 42 | ParagraphBreak: 640, 43 | }, 44 | Voice: &Voice{ 45 | Name: "Salli", 46 | Language: "en-US", 47 | Gender: "Female", 48 | }, 49 | } 50 | } 51 | 52 | // Input contains attributes describing the user input. 53 | // For more details see http://developer.ivona.com/en/speechcloud/api_ref_data_types.html#DataTypes_Input. 54 | type Input struct { 55 | Data string 56 | Type string 57 | } 58 | 59 | // OutputFormat contains attributes describing the audio compression and format in which the returned stream should be encoded. 60 | // For more details see http://developer.ivona.com/en/speechcloud/api_ref_data_types.html#DataTypes_OutputFormat. 61 | type OutputFormat struct { 62 | Codec string 63 | SampleRate int 64 | } 65 | 66 | // Parameters contains additional attributes affecting the generated speech. 67 | // For more details see http://developer.ivona.com/en/speechcloud/api_ref_data_types.html#DataTypes_Parameters. 68 | type Parameters struct { 69 | Rate string 70 | Volume string 71 | SentenceBreak int 72 | ParagraphBreak int 73 | } 74 | 75 | // Voice contains a filter for the voice selection that should be used for the speech synthesis. 76 | // For more details see http://developer.ivona.com/en/speechcloud/api_ref_data_types.html#DataTypes_Voice. 77 | type Voice struct { 78 | Name string 79 | Language string 80 | Gender string 81 | } 82 | --------------------------------------------------------------------------------