Skip to content

Instantly share code, notes, and snippets.

@moutend
Created June 14, 2017 00:39
Show Gist options
  • Save moutend/119d52aa917278291ad998845d45c18d to your computer and use it in GitHub Desktop.
Save moutend/119d52aa917278291ad998845d45c18d to your computer and use it in GitHub Desktop.
// +build windows
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
"strings"
"syscall"
"unsafe"
"github.com/go-ole/go-ole"
"github.com/moutend/go-wss"
)
type FilenameFlag struct {
Value string
}
func (f *FilenameFlag) Set(value string) (err error) {
if !strings.HasSuffix(value, ".wav") {
err = fmt.Errorf("specify WAVE audio file (*.wav)")
return
}
f.Value = value
return
}
func (f *FilenameFlag) String() string {
return f.Value
}
func GetSpeechStreamSync(text string) (stream []byte, err error) {
var modSpeechSynthesizer, _ = syscall.LoadDLL("speechsynthesizer.dll")
var procGetSpeechStreamSync, _ = modSpeechSynthesizer.FindProc("CreateSpeechStream")
var buf *wss.IBuffer
ole.RoInitialize(1)
//defer ole.RoUninitialize()
var textHString ole.HString
if text == "" {
text = "Hello, World!"
}
if textHString, err = ole.NewHString(text); err != nil {
return
}
defer ole.DeleteHString(textHString)
hr, _, _ := procGetSpeechStreamSync.Call(
uintptr(unsafe.Pointer(textHString)),
uintptr(unsafe.Pointer(&buf)))
if hr != 0 {
err = fmt.Errorf("unknown error %v", hr)
return
}
defer buf.Release()
var length uint32
if err = buf.GetLength(&length); err != nil {
return
}
// Casting Ibuffer to IBufferByteAccess
var unk *ole.IUnknown
if buf.PutQueryInterface(ole.IID_IUnknown, &unk); err != nil {
return
}
var bba *wss.IBufferByteAccess
if err = unk.PutQueryInterface(wss.IID_IBufferByteAccess, &bba); err != nil {
return
}
// Extract the backing array.
var bufPtr *byte
if err = bba.Buffer(&bufPtr); err != nil {
return
}
rawBufPtr := uintptr(unsafe.Pointer(bufPtr))
sliceHeader := reflect.SliceHeader{Data: rawBufPtr, Len: int(length), Cap: int(length)}
stream = *(*[]byte)(unsafe.Pointer(&sliceHeader))
return
}
func main() {
var err error
if err = run(os.Args); err != nil {
log.Fatal(err)
}
return
}
func run(args []string) (err error) {
var filenameFlag FilenameFlag
var stream []byte
var text string
f := flag.NewFlagSet(args[0], flag.ExitOnError)
f.Var(&filenameFlag, "output", "file name")
f.Var(&filenameFlag, "o", "Alias of --output")
f.Parse(args[1:])
text = strings.Join(f.Args(), " ")
if filenameFlag.Value == "" {
filenameFlag.Value = "voice.wav"
}
if stream, err = GetSpeechStreamSync(text); err != nil {
return
}
return ioutil.WriteFile(filenameFlag.Value, stream, 0644)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment