forked from rickcrawford/go-ssml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
builder.go
219 lines (189 loc) · 4.3 KB
/
builder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package ssml
import (
"bytes"
"fmt"
"net/url"
"strings"
"time"
)
type Alphabet int
const (
ALPHABET_IPA Alphabet = iota
ALPHABET_X_SAMPA
)
func (s Alphabet) String() string {
switch s {
case ALPHABET_X_SAMPA:
return "x-sampa"
default:
return "ipa"
}
}
type Role int
const (
ROLE_VERB Role = iota
ROLE_PAST_PARTICIPLE
ROLE_NOUN
ROLE_SENSE
)
func (s Role) String() string {
switch s {
case ROLE_PAST_PARTICIPLE:
return "x-ivona:VBD"
case ROLE_NOUN:
return "x-ivona:NN"
case ROLE_SENSE:
return "x-ivona:SENSE_1"
default:
return "ivona:VB"
}
}
type InterpretAs int
const (
INTERPRET_AS_CHARACTERS InterpretAs = iota
INTERPRET_AS_CARDINAL
INTERPRET_AS_NUMBER
INTERPRET_AS_ORDINAL
INTERPRET_AS_DIGITS
INTERPRET_AS_FRACTION
INTERPRET_AS_UNIT
INTERPRET_AS_TIME
INTERPRET_AS_TELEPHONE
INTERPRET_AS_ADDRESS
INTERPRET_AS_INTERJECTION
)
func (s InterpretAs) String() string {
switch s {
case INTERPRET_AS_CARDINAL, INTERPRET_AS_NUMBER:
return "number"
case INTERPRET_AS_ORDINAL:
return "ordinal"
case INTERPRET_AS_DIGITS:
return "digits"
case INTERPRET_AS_FRACTION:
return "fraction"
case INTERPRET_AS_UNIT:
return "unit"
case INTERPRET_AS_TIME:
return "time"
case INTERPRET_AS_TELEPHONE:
return "telephone"
case INTERPRET_AS_ADDRESS:
return "address"
case INTERPRET_AS_INTERJECTION:
return "interjection"
default:
return "characters"
}
}
type DateFormat int
const (
DATE_FORMAT_MDY DateFormat = iota
DATE_FORMAT_DMY
DATE_FORMAT_YMD
DATE_FORMAT_MD
DATE_FORMAT_DM
DATE_FORMAT_YM
DATE_FORMAT_MY
DATE_FORMAT_D
DATE_FORMAT_M
DATE_FORMAT_Y
)
func (s DateFormat) String() string {
switch s {
case DATE_FORMAT_DMY:
return "dmy"
case DATE_FORMAT_YMD:
return "ymd"
case DATE_FORMAT_MD:
return "md"
case DATE_FORMAT_DM:
return "dm"
case DATE_FORMAT_YM:
return "ym"
case DATE_FORMAT_MY:
return "my"
case DATE_FORMAT_D:
return "d"
case DATE_FORMAT_M:
return "m"
case DATE_FORMAT_Y:
return "y"
default:
return "mdy"
}
}
type Builder interface {
Text(string) Builder
Space() Builder
Newline() Builder
Paragraph(string) Builder
Sentence(string) Builder
Break(time.Duration) Builder
StrongBreak() Builder
Audio(*url.URL) Builder
Word(string, Role) Builder
SayAs(string, InterpretAs) Builder
Date(time.Time, DateFormat) Builder
Phoneme(string, Alphabet, string) Builder
String() string
Raw() string
}
type builder struct {
buf bytes.Buffer
// rawBuf is the same as buf, but without any ssml tags--just the text.
// This is good for outputting user-friendly readable text while building
// the ssml output
rawBuf bytes.Buffer
}
func (r *builder) Text(text string) Builder {
r.buf.WriteString(text)
if !strings.HasPrefix(text, "<") {
r.rawBuf.WriteString(text)
}
return r
}
func (r *builder) Space() Builder {
r.buf.WriteByte(' ')
return r
}
func (r *builder) Newline() Builder {
r.buf.WriteByte('\n')
return r
}
func (r *builder) Paragraph(text string) Builder {
return r.Text("<p>").Text(text).Text("</p>")
}
func (r *builder) Sentence(text string) Builder {
return r.Text("<s>").Text(text).Text("</s>")
}
func (r *builder) Break(duration time.Duration) Builder {
return r.Text(fmt.Sprintf(`<break time="%dms" />`, int64(duration.Seconds()*1000)))
}
func (r *builder) StrongBreak() Builder {
return r.Text(`<break time="strong" />`)
}
func (r *builder) Audio(URL *url.URL) Builder {
return r.Text(fmt.Sprintf(`<audio src="%s" />`, URL.String()))
}
func (r *builder) Word(text string, role Role) Builder {
return r.Text(fmt.Sprintf(`<w role="%s" />`, role)).Text(text).Text("</w>")
}
func (r *builder) SayAs(text string, interpretAs InterpretAs) Builder {
return r.Text(fmt.Sprintf(`<say-as interpret-as="%s">`, interpretAs)).Text(text).Text("</say-as>")
}
func (r *builder) Date(date time.Time, format DateFormat) Builder {
return r.Text(fmt.Sprintf(`<say-as interpret-as="date" format="%s">`, format)).Text(date.Format("20060102")).Text("</say-as>")
}
func (r *builder) Phoneme(text string, alphabet Alphabet, ph string) Builder {
return r.Text(fmt.Sprintf(`<phoneme alphabet="%s" ph="%s">`, alphabet, ph)).Text(text).Text("</phoneme>")
}
func (r builder) String() string {
return fmt.Sprintf("<speak>%s</speak>", r.buf.String())
}
func (r builder) Raw() string {
return r.rawBuf.String()
}
func NewBuilder() Builder {
return &builder{}
}