Since moving to Bangkok, my excuse for never properly (or even partially) learning a foreign language has been removed. So of course there had to be some way to manage my vocabulary lists etc. OK, so chuck them into Excel, but not very exciting or interactive. But then I discovered the iOS voice synthesiser. With a simple command at the Bash terminal you can make it speak in a varieties of tongues. For example
~$ say -v Kanya Hello World
Will repeat "Hello World" in a Thai accent. But then you also enter
~$ say -v Kayna สวัสดีโลก
to read out Thai script.
This opens up all sorts of possibilities for language learning by combining the above commands with lists of vocabulary managed in Excel. Download it, using the links below, to see it in action (note, your virus checker may complain as this is an Excel file with VBA macros in it).
One of the problems with creating the Excel spreadsheet was the rather poor support for Unicode in the Mac version. Something which might not trouble the average English speaking monoglot. In order to successfully script the data from the Excel cells to the voice synthesiser, I had to knock up a UTF-8 encoding routine. It seems that although Excel strings are 2 bytes long (so, at best only partial support for Unicode, as some codepoints are >16 bits), various other parts of the system, including piping output via the MacScript command is still in ASCII land.
Here is the code of the UTF-8 function:
Private Function UTF8Encode(b() As Byte) As Byte()
' b - byte array containing the word or phrase
' Byte(), UTF8 byte array ready for writing to file ' Function to convert a Unicode Byte array into a byte array that can be written to create a UTF8 Encoded file. ' Note the function supports the one, two and three byte UTF8 forms. ' Note: the MS VBA documentation is confusing. It says the String types only supports single byte charset ' however, thankfully, it does in fact contain 2 byte Unicode values. ' Wrote this routine as last resort, tried many ways to get unicode chars to a file or to a shell script call ' but this was the only way could get to work. ' RT Perkin ' 30/10/2015 Dim b1, b2, b3 As Byte ' UTF8 encoded bytes Dim u1, u2 As Byte ' Unicode input bytes Dim out As New Collection ' Collection to build output array Dim i, j As Integer Dim unicode As Long If UBound(b) <= 0 Then Exit Function End If For i = 0 To UBound(b) Step 2 u1 = b(i) u2 = b(i + 1) unicode = u2 * 256 + u1 If unicode < &H80 Then ' Boils down to ASCII, one byte UTF-8 out.Add (u1) ElseIf unicode < &H800 Then ' Two byte UTF-8 b1 = &H80 Or (&H3F And u1) b2 = &HC0 Or (Int(u1 / 64)) Or ((&H7 And u2) * 4) out.Add (b2) ' Add most significant byte first out.Add (b1) ElseIf unicode < &H10000 Then ' Three byte UTF-8 ' Thai chars are in this range b1 = &H80 Or (&H3F And u1) b2 = &H80 Or (Int(u1 / 64)) Or ((&HF And u2) * 4) b3 = &HE0 Or (Int(u2 / 16)) out.Add (b3) ' Add most significant byte first out.Add (b2) out.Add (b1) Else ' This case wont arise as VBA strings are 2 byte. Which makes some Unicode codepoints uncodeable. End If Next Dim outBytes() As Byte ReDim outBytes(1 To out.Count) For j = 1 To out.Count outBytes(j) = CByte(out.Item(j)) Next UTF8Encode = outBytes End Function
And here is the code for the speech routine (Excel 2011 version):
Private Function Speak(word As String, voice As String, speed As Long, out As String) As Boolean
' word - word to say
' voice - the voice name for the 'say' command
' speed - the speed of playback, 0 for default
' out - if present, the name of a file to store the audio file (rather the playing the audio)
' Boolean, success indicator
' Function to use Mac's 'say' command to speak a word or phrase.
' This works by generating a script file containing the 'say' command, and then executing the script.
' It was not possible to directly execute the 'say' command becuase the MacScript (or something in the pipe)
' does not handle Unicode, so for Thai, this does not work.
' RT Perkin
Dim b() As Byte
Dim UTF() As Byte
Dim retCode As String
Dim speedCmd As String
Dim outCmd As String
Speak = True
If voice = "" Then
MsgBox "Enter a voice name on the #TOOLS tab before using the speech function", vbCritical
Speak = False
' Convert it to UTF8
b = EscapeWord(word)
UTF = UTF8Encode(b)
' Delete any previous file, as file is not replaced on Binary write
On Error Resume Next ' Disable, as will raise error if file absent
retCode = MacScript("do shell script ""rm /Users/Shared/UTF8TalkFile.sh""")
On Error GoTo 0
' Check parms
If speed <> 0 Then
speedCmd = " -r " & speed
If out <> "" Then
outCmd = " -o " & out
' Write the text to a command script
' Note: the Open command seems to require the old Mac colon separator for path names
Open "Macintosh HD:Users:Shared:UTF8TalkFile.sh" For Binary As #1
Put #1, , "say -v " & voice & speedCmd & outCmd & " "
Put #1, , UTF
' Use MacScript to execute the BASH script containing created above
' First set execute permissions.
retCode = MacScript("do shell script ""chmod +x /Users/Shared/UTF8TalkFile.sh""")
' Note that some strings will confuse the 'say' command. Catch errors.
On Error GoTo BadInput
retCode = MacScript("do shell script ""/Users/Shared/UTF8TalkFile.sh""")
MsgBox "Illegal characters in input, eg brackets etc", vbExclamation
Speak = False