-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathspeech.go
140 lines (127 loc) · 4.97 KB
/
speech.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
package edgetts
import (
"errors"
"github.com/lib-x/edgetts/internal/communicate"
"github.com/lib-x/edgetts/internal/communicateOption"
"github.com/lib-x/edgetts/internal/ttsTask"
"io"
"sync"
)
var (
NoPackTaskEntries = errors.New("no pack task entries")
)
type Speech struct {
vm *VoiceManager
options []Option
tasks []ttsTask.Tasker
}
func (s *Speech) convertToInternalOpt() *communicateOption.CommunicateOption {
opt := &option{}
for _, apply := range s.options {
apply(opt)
}
return opt.toInternalOption()
}
// NewSpeech creates a new Speech instance.
// It takes a variadic parameter:
// - options: a slice of communicateOption.Option that will be used to configure the Speech instance.
// The function returns a pointer to the newly created Speech instance and an error if any occurs during the creation process.
func NewSpeech(options ...Option) (*Speech, error) {
s := &Speech{
options: options,
tasks: make([]ttsTask.Tasker, 0),
vm: NewVoiceManager(),
}
return s, nil
}
// GetVoiceList retrieves the list of voices available for the speech.
// It returns a slice of Voice objects and an error if any occurs during the retrieval process.
func (s *Speech) GetVoiceList() ([]Voice, error) {
return s.vm.ListVoices()
}
// AddSingleTask adds a single task to the speech.
// It takes two parameters:
// - text: the text to be synthesized.
// - output: the output of the single task, which will finally be written into a file.
// The function returns an error if there is an issue with the communication.
func (s *Speech) AddSingleTask(text string, output io.Writer) error {
opt := s.convertToInternalOpt()
c, err := communicate.NewCommunicate(text, opt)
if err != nil {
return err
}
task := &ttsTask.SingleTask{
Text: text,
Communicate: c,
Output: output,
}
s.tasks = append(s.tasks, task)
return nil
}
// AddPackTask adds a pack task to the speech.
// It takes four parameters:
// - dataEntries: a map where the key is the entry name and the value is the entry text to be synthesized.
// - entryCreator: a function that creates a writer for each entry. This can be a packer context related writer, such as a zip writer.
// - output: the output of the pack task, which will finally be written into a file.
// - metaData: optional parameter. It is the data which will be serialized into a json file. The name uses the key and value as the key-value pair.
// The function returns an error if there are no pack task entries.
func (s *Speech) AddPackTask(dataEntries map[string]string, entryCreator func(name string) (io.Writer, error), output io.Writer, metaData ...map[string]any) error {
return s.AddPackTaskWithCustomOptions(dataEntries, nil, entryCreator, output, metaData...)
}
// AddPackTaskWithCustomOptions adds a pack task with options to the speech.
// It takes four parameters:
// - dataEntries: a map where the key is the entry name and the value is the entry text to be synthesized.
// - entriesOption: a map where the key is the entry name and the value is the entry option to be used for the entry.
// - entryCreator: a function that creates a writer for each entry. This can be a packer context related writer, such as a zip writer.
// - output: the output of the pack task, which will finally be written into a file.
// - metaData: optional parameter. It is the data which will be serialized into a json file. The name uses the key and value as the key-value pair.
// The function returns an error if there are no pack task entries.
func (s *Speech) AddPackTaskWithCustomOptions(dataEntries map[string]string, entriesOption map[string][]Option, entryCreator func(name string) (io.Writer, error), output io.Writer, metaData ...map[string]any) error {
taskCount := len(dataEntries)
if taskCount == 0 {
return NoPackTaskEntries
}
packEntries := make([]*ttsTask.PackEntry, 0, taskCount)
for name, text := range dataEntries {
// empty text will cause goroutine leak,ignore it
if text == "" {
continue
}
packEntry := &ttsTask.PackEntry{
Text: text,
EntryName: name,
}
if entriesOption != nil {
if entryOpt, ok := entriesOption[name]; ok {
opt := &option{}
for _, apply := range entryOpt {
apply(opt)
}
packEntry.EntryCommunicateOpt = opt.toInternalOption()
}
}
packEntries = append(packEntries, packEntry)
}
packTask := &ttsTask.PackTask{
CommunicateOpt: s.convertToInternalOpt(),
PackEntryCreator: entryCreator,
PackEntries: packEntries,
Output: output,
MetaData: metaData,
}
s.tasks = append(s.tasks, packTask)
return nil
}
// StartTasks starts all the tasks in the Speech instance.
// It creates a WaitGroup and adds the total number of tasks to it.
// Then it starts each task in a separate goroutine and waits for all of them to finish.
// The function returns an error if any occurs during the execution of the tasks.
func (s *Speech) StartTasks() error {
wg := &sync.WaitGroup{}
wg.Add(len(s.tasks))
for _, task := range s.tasks {
go task.Start(wg)
}
wg.Wait()
return nil
}