Skip to content

Commit

Permalink
Updated Windows to support exporting (#48)
Browse files Browse the repository at this point in the history
Adds dataFormat parameter
Also fixed macOS exporting (#71)
  • Loading branch information
vitaminj committed Dec 19, 2017
1 parent 9de2097 commit 14cbc7e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 15 deletions.
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ say.speak(text, voice || null, speed || null, callback || null)

#### Export Audio:

* MacOS Only
* MacOS & Windows Only (Windows ignores endian and data type parts of the dataFormat, can only output sample size to 8 or 16 bit)
* Speed: 1 = 100%, 0.5 = 50%, 2 = 200%, etc
* dataFormat: As per [macOS say](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/say.1.html) e.g. "BEF16@22100" is big endian, floating point, 16bit, 22100kHz, defaults to "LEF32@32000" if unspecified

```javascript
say.export(text, voice || null, speed || null, filename, callback || null)
say.export(text, voice || null, speed || null, filename, callback || null, dataFormat || null)
```

#### Stop Speaking:
Expand All @@ -80,7 +81,7 @@ Platform | Speak | Export | Stop | Speed | Voice
---------|-------|--------|------|-------|------
macOS | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
Linux | :white_check_mark: | :no_entry_sign: | :white_check_mark: | :white_check_mark: | :white_check_mark:
Windows | :white_check_mark: | :no_entry_sign: | :white_check_mark: | :white_check_mark: | :white_check_mark:
Windows | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:


## macOS Notes
Expand All @@ -94,11 +95,6 @@ say -v "?"
As an example, the default voice is `Alex` and the voice used by Siri is `Samantha`.


## Windows Notes

The `.export()` method is not available.


## Linux Notes

Linux support requires [Festival](http://www.cstr.ed.ac.uk/projects/festival/), which uses less friendly names for its voices. Voices for Festival sometimes need to be installed separately. You can check which voices are available by running `festival`, typing `(voice_`, and pressing Tab. Then take the name of the voice you'd like to try, minus the parentheses, and pass it in to say.js.
Expand Down
19 changes: 17 additions & 2 deletions platform/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ class SayPlatformBase {
* @param {number|null} speed Speed of text (e.g. 1.0 for normal, 0.5 half, 2.0 double)
* @param {string} filename Path to file to write audio to, e.g. "greeting.wav"
* @param {Function|null} callback A callback of type function(err) to return.
* @param {string} dataFormat A dataFormat string as per macOS say e.g. "BEF16@22100"
*/
export (text, voice, speed, filename, callback) {
export (text, voice, speed, filename, callback, dataFormat) {
if (typeof callback !== 'function') {
callback = () => {}
}
Expand All @@ -83,8 +84,22 @@ class SayPlatformBase {
})
}

if (!dataFormat) {
dataFormat = 'LEF32@32000'
}
const dataFormatParts = /(BE|LE)(F|I|UI)(\d+)@(\d+)/.exec(dataFormat)
if (!dataFormatParts) {
throw new Error('Invalid dataFormat')
}
const dataFormatInfo = {
endian: dataFormatParts[1],
dataType: dataFormatParts[2],
sampleSize: parseInt(dataFormatParts[3], 10),
sampleRate: parseInt(dataFormatParts[4], 10)
}

try {
var {command, args, pipedData, options} = this.buildExportCommand({text, voice, speed})
var {command, args, pipedData, options} = this.buildExportCommand({text, voice, speed, filename, dataFormatInfo})
} catch (error) {
return setImmediate(() => {
callback(error)
Expand Down
4 changes: 2 additions & 2 deletions platform/darwin.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class SayPlatformDarwin extends SayPlatformBase {
return {command: COMMAND, args, pipedData, options}
}

buildExportCommand ({text, voice, speed, filename}) {
buildExportCommand ({text, voice, speed, filename, dataFormatInfo}) {
let args = []
let pipedData = ''
let options = {}
Expand All @@ -43,7 +43,7 @@ class SayPlatformDarwin extends SayPlatformBase {
}

if (filename) {
args.push('-o', filename, '--data-format=LEF32@32000')
args.push('-o', filename, `--data-format=${dataFormatInfo.endian}${dataFormatInfo.dataType}${dataFormatInfo.sampleSize}@${dataFormatInfo.sampleRate}`)
}

return {command: COMMAND, args, pipedData, options}
Expand Down
19 changes: 16 additions & 3 deletions platform/win32.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class SayPlatformWin32 extends SayPlatformBase {
this.baseSpeed = BASE_SPEED
}

buildSpeakCommand ({text, voice, speed}) {
buildCommand ({text, voice, speed, filename, dataFormatInfo}) {
let args = []
let pipedData = ''
let options = {}
Expand All @@ -27,6 +27,15 @@ class SayPlatformWin32 extends SayPlatformBase {
psCommand += `$speak.Rate = ${adjustedSpeed};`
}

if (filename) {
const audioBitsPerSample = (dataFormatInfo.sampleSize <= 8) ? 'Eight' : 'Sixteen'
const escapedFilename = filename.replace(/\\/g, '\\\\').replace(/"/g, '\\"\\"').replace(/`/g, '``')
psCommand += `$formatSampleSize = [System.Speech.AudioFormat.AudioBitsPerSample]::${audioBitsPerSample};`
psCommand += `$formatChannels = [System.Speech.AudioFormat.AudioChannel]::Mono;`
psCommand += `$format = New-Object System.Speech.AudioFormat.SpeechAudioFormatInfo ${dataFormatInfo.sampleRate}, $formatSampleSize, $formatChannels;`
psCommand += `$speak.SetOutputToWaveFile(\\"${escapedFilename}\\", $format);`
}

psCommand += `$speak.Speak([Console]::In.ReadToEnd())`

pipedData += text
Expand All @@ -36,8 +45,12 @@ class SayPlatformWin32 extends SayPlatformBase {
return {command: COMMAND, args, pipedData, options}
}

buildExportCommand ({text, voice, speed, filename}) {
throw new Error(`say.export(): does not support platform ${this.platform}`)
buildSpeakCommand ({text, voice, speed}) {
return this.buildCommand({text, voice, speed})
}

buildExportCommand ({text, voice, speed, filename, dataFormatInfo}) {
return this.buildCommand({text, voice, speed, filename, dataFormatInfo})
}

runStopCommand () {
Expand Down

0 comments on commit 14cbc7e

Please sign in to comment.