Speech Library

I really like the speech capabilities and am adding this to a game that presents multiple choice questions for 3rd to 5th graders. The problem is the way some symbols are spoken as they are used in the questions and answers. For example, “/” is pronounced “slash” when I want “divided by.” I’ve put together a bit of code to substitute the pronunciations I want but don’t want to put too much work into it if someone has already done it or if there’s a better way. I may eventually put the substitution list into a table and I’m thinking of a way to check for any fraction rather than specific ones. Will appreciate advice. Thanks,

    function Speak ( Words, Speed )
        if Speed == nil then
            speech.rate = .4
        else
            speech.rate = Speed
        end 
        Words = ReplaceString ( Words, " / ", " divided by " )
        Words = ReplaceString ( Words, "*", " times " )
        Words = ReplaceString ( Words, "____", " fill in the blank " )
        Words = ReplaceString ( Words, "1/2", " one half " )
        Words = ReplaceString ( Words, "1/3", " one third " )
        Words = ReplaceString ( Words, "1/4", " one fourth " )
        speech.say ( Words )
    end
    
function ReplaceString ( Words, Pattern, Replacement )
        return ( Words:gsub ( Pattern, Replacement ) )
 end

@DaveW - it’s a great idea.

For fractions like 1/3, I don’t see any alternative to a table of substitutes if you want to speak them as you have above.

When it comes to fractions in general, Lua supports a simple form of regular expressions which may help. I’m no expert, but it certainly possible to identify text patterns such as “number / number” and substitute “divided by”.

eg
http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)

Another option is to embed codes in the questions, eg numbers or letters in square brackets, which tell Codea how to speak something, and which are edited out before the text is written on the screen.

For example, “What is [1/3,one third] of 9?” might tell Codea to write the first item in brackets but speak the second one. It is easy for Codea to then create two versions of the question, one for writing, one for speaking.

Here’s a capture that will find all number divided by number patterns:

string.gsub(string, "(%d) / (%d)", "%1 divided by %2")

I think that should work. I really like regex, so ask me if you ever need help :slight_smile:
Also take a look at this: http://www.lua.org/pil/20.1.html

@Ignatz, I like the bracket idea! That would be great for things that only happen once. I also was thinking of putting markup in the text for things that I want pronounced differently in different situations. For example, if a question is about possessives, the apostrophe may need to be pronounced. Thanks.

@TheSolderKing, That suggestion helps! I want to check to see if the numerator is 1 so as to know whether to pronounce the denominator as plural. I’ll have to work with this a bit. Thanks.

Here’s what I’ve wound up with. Not elegant but it seems to work for my needs. Someone may find it useful as a starting point.

   
    function Speak ( Words, Speed )
        if Speed == nil then
            speech.rate = .4
        else
            speech.rate = Speed
        end 
        Words = ReplaceString ( Words, "{/}", " divided by " ) -- Brackets are markup in text
        Words = ReplaceString ( Words, "{!}", "  apostrophe  " )
        Words = ReplaceString ( Words, "*", " times " )
        Words = ReplaceString ( Words, "____", " fill in the blank " )
        Words = FractionCheck ( Words )
        speech.say ( Words )
    end
    
    function ReplaceString ( Words, Pattern, Replacement )
        return ( Words:gsub ( Pattern, Replacement ) )
    end
    
function FractionCheck ( Words ) -- Locate fractions and format for speech.

    while true do -- Loop until end of Words
        Numerator = nil
        Denominator = nil
        Start = string.find ( Words, "/", 1, true )
        
        if Start ~= nil then -- Find numerator
            NumeratorStart = 1
            local IsNumber = true
            while IsNumber == true do
                Char = string.sub ( Words, Start-NumeratorStart, Start-NumeratorStart) 
                if Start-NumeratorStart > 0 and string.find ( "-0123456789", Char ) ~= nil then -- It's a number
                    NumeratorStart = NumeratorStart + 1
                else
                    Numerator = string.sub ( Words, Start-NumeratorStart+1, Start-1 ) 
                    IsNumber = false
                end 
            end   
        end
    
        if Start ~= nil then -- Find denominator
            DenominatorStart = 1
            local IsNumber = true
            while IsNumber == true do
                Char = string.sub ( Words, Start + DenominatorStart, Start + DenominatorStart ) 
                if Start + DenominatorStart < string.len ( Words ) and string.find ( "0123456789", Char ) ~= nil then -- It's a number
                    DenominatorStart = DenominatorStart + 1
                else
                    Denominator = string.sub ( Words, Start + 1, Start + DenominatorStart-1 )  
                    IsNumber = false
                end 
            end   
        end
    
        if Numerator ~= nil and Denominator ~= nil then -- It's a fraction
            Numerator = Numerator
            for Key, Value in pairs( Denominators ) do -- Lookup Denominator in table
                if Denominators[Key][1] == Denominator then
                    if Numerator == "1" or Numerator == "-1" then -- Singular
                        Denominator = Denominators[Key][2]
                    else 
                        Denominator = Denominators[Key][3]
                    end
                end                
            end
            Words = ( string.sub( Words, 1, Start - NumeratorStart ) .. Numerator .. "  " .. Denominator .. string.sub ( Words, Start + DenominatorStart ) )
        else 
            return ( Words )
        end
        
    end
end

function FractionsLoad()
    Denominators = {}
        Denominators[1] = {"1", " one ", " ones " }
        Denominators[2] = {"2", " half ", " halves " }
        Denominators[3] = {"3", " third ", " thirds " }
        Denominators[4] = {"4", " fourth ", " fourths " }
        Denominators[5] = {"5", " fifth ", " fifths " }
        Denominators[6] = {"6", " sixth ", " sixths " }
        Denominators[7] = {"7", " seventh ", " sevenths " }
        Denominators[8] = {"8", " eight ", " eights " }
        Denominators[9] = {"9", " ninth ", " ninths " }
        Denominators[10] = {"10", " tenth ", " tenths " }
        Denominators[11] = {"11", " eleventh ", " elevenths " }
        Denominators[12] = {"12", " twelth ", " twelths " }
        Denominators[13] = {"13", " thirteenth ", " thirteenths " }
        Denominators[14] = {"15", " fifteenth ", " fifteenths " }
        Denominators[15] = {"16", " sixteenth ", " sixteenth " }
        Denominators[16] = {"17", " seventeenth ", " seventeenths " }
        Denominators[17] = {"18", " eighteenth ", " eighteenths " }
        Denominators[18] = {"19", " nineteehth ", " nineteenths " }
        Denominators[20] = {"20", " twentieth ", " twentieths " }
        Denominators[21] = {"21", " twenty first ", " twenty firsts " }
        Denominators[22] = {"22", " twenty second ", " twenty seconds " }
        Denominators[23] = {"23", " twenty third ", " twenty thirds " }
        Denominators[24] = {"24", " twenty fourth ", " twenty fourths " }
        Denominators[25] = {"32", " thirty second ", " thirty seconds " }
        Denominators[26] = {"50", " fiftieth ", " fiftieths " }
        Denominators[28] = {"64", " sixthy fourth ", " sixty fourths " }
        Denominators[29] = {"100", " hundredth ", " hundredths " }
        Denominators[30] = {"1000", " one thousandth ", " one thousandths " }
    return
end