Written by

Software Architect at Visum
Article Yuri Marx · Feb 21, 2022 6m read

REST Service for Convert text to audio using IRIS and Python gTTS

Hi Community,

Imagine enabling your application to read text to your customer? This is now possible with the new IRIS feature, Embedded Python. With this new functionality IRIS can natively run any open source or commercial Python libraries natively. gTTS (https://pypi.org/project/gTTS/) is a free library that transforms text into audio using the Google Translate service.

How to

Just pass the text by parameter and gTTS returns an mp3 file with the text transformed into audio. That is, your application can play the audio of any text! See how to do it:

1. Go to https://openexchange.intersystems.com/package/IRIS-Text2Audio and Click Download button.

2. Clone/git pull the repo into any local directory

$ git clone https://github.com/yurimarx/iris-tts.git

3. Open a Docker terminal in this directory and run:

$ docker-compose build

4. Run the IRIS container:

$ docker-compose up -d 

5. Go to your Postman (or other similar REST client) and config the request like this image:

Request TTS input

6. Click send and get a response with a player to play the mp3 file, like picture above.

Behind the scenes

1. The Dockerfile install IRIS with Python and gTTS library

 

Dockerfile

FROMintersystemsdc/iris-community

 

USERroot

 

ENVDEBIAN_FRONTENDnoninteractive

 

# install libraries required to gTTS to process TTS
RUNapt-get-yupdate\
    &&apt-get-yinstallapt-utils\
    &&apt-getinstall-ybuild-essentialunzippkg-configwget\
    &&apt-getinstall-ypython3-pip  

 

# use pip3 (the python zpm) to install gTTS dependencies
RUNpip3install--upgradepipsetuptoolswheel
RUNpip3install--target/usr/irissys/mgr/pythongTTS

 

USERroot  
WORKDIR/opt/irisbuild
RUNchown${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP}/opt/irisbuild
USER${ISC_PACKAGE_MGRUSER}

 

WORKDIR/opt/irisbuild
COPY  srcsrc
COPYInstaller.clsInstaller.cls
COPYmodule.xmlmodule.xml
COPYiris.scriptiris.script

 

USER${ISC_PACKAGE_MGRUSER}

 

RUNirisstartIRIS\
    &&irissessionIRIS<iris.script\
    &&irisstopIRISquietly

2. A ClassMethod was created with Python language configured that uses gTTS to transform text into audio and record it in an mp3 file:

 

Python method to generate audio from text

///TTS engine
Classdc.tts.TTSEngine
{

 

///Text to audio file
ClassMethodGenerateAudioFileFromText(sentence,language,domain)[Language=python]
{
        fromgttsimportgTTS
        importuuid

 

        tts =gTTS(sentence,lang=str(language),tld=str(domain))
        output =str(uuid.uuid1()) + '.mp3'
        tts.save('/opt/irisbuild/' +output)
        returnoutput
}

 

}

3. A REST API in ObjectScript was created to expose Python functionality as a TTS microservice (very fancy!)

 

TTS REST Service

Classdc.tts.TTSRESTAppExtends%CSP.REST
{

 

ParameterCHARSET="utf-8";

 

ParameterCONVERTINPUTSTREAM=1;

 

ParameterCONTENTTYPE="application/json";

 

ParameterVersion="1.0.0";

 

ParameterHandleCorsRequest=1;

 

XDataUrlMap[XMLNamespace="http://www.intersystems.com/urlmap"]
{
<Routes>
<!--ServerInfo-->
<RouteUrl="/"Method="GET"Call="GetInfo"Cors="true"/>
<!--Swaggerspecs-->
<RouteUrl="/_spec"Method="GET"Call="SwaggerSpec"/>

 

<!--generatetextfromaudiofile-->
<RouteUrl="/texttoaudio"Method="POST"Call="GenerateAudioFromText"/>

 

</Routes>
}

 

//Generate audio file from text

 

ClassMethodGenerateAudioFromText()As%Status
{
    SettSC=$$$OK
   
    try{
      // get the sentence to be processed
      Setsentence=$ZCONVERT(%request.Content.Read(),"I","UTF8")

 

      SetLanguage=%request.Get("lang")
      SetDomain=%request.Get("domain")

 

      SetLanguage=$GET(Language,0)
      IfLanguage=""{
        SetLanguage="en"
      }

 

      SetDomain=$GET(Domain,0)
      IfDomain=""{
        SetDomain="com"
      }

 

      //call embedded python classmethod to get mp3 audio file from text
      Setoutput=##class(dc.tts.TTSEngine).GenerateAudioFileFromText(sentence,Language,Domain)

 

      Set%response.ContentType="audio/mp3"
     
      Do%response.SetHeader("Content-Disposition","attachment;filename="""_output_"""")
      Set%response.NoCharSetConvert=1
      Set%response.Headers("Access-Control-Allow-Origin")="*"

 

      Setstream=##class(%Stream.FileBinary).%New()
      Setsc=stream.LinkToFile("/opt/irisbuild/"_output)
      Dostream.OutputToDevice()
       
      SettSC=$$$OK
 
    //returns error message to the user
    }catche{
        SettSC=e.AsStatus()
        SetpOutput=tSC
    }

 

    QuittSC
}

 

///General information
ClassMethodGetInfo()As%Status
{
  SETversion=..#Version
  SETfmt=##class(%SYS.NLS.Format).%New("ptbw")
 
  SETinfo={
    "Service":"TTS Service API",
    "version":(version),
    "Developer":"Yuri Gomes",
    "Status":"Ok",
    "Date":($ZDATETIME($HOROLOG))
  }
  Set%response.ContentType=..#CONTENTTYPEJSON
  Set%response.Headers("Access-Control-Allow-Origin")="*"

 

  Writeinfo.%ToJSON()
  Quit$$$OK
}

 

ClassMethod%ProcessResult(pStatusAs%Status={$$$OK},pResultAs%DynamicObject="")As%Status[Internal]
{
  #dim%responseAs%CSP.Response
  SETtSC=$$$OK
  IF$$$ISERR(pStatus){
    SET%response.Status=500
    SETtSC=..StatusToJSON(pStatus,.tJSON)
    IF$isobject(tJSON){
      SETpResult=tJSON
    }ELSE{
      SETpResult={"errors":[{"error":"Unknown error parsing status code"}]}
    }
  }
  ELSEIFpStatus=1{
    IF'$isobject(pResult){
      SETpResult={
      }
    }
  }
  ELSE{
    SET%response.Status=pStatus
    SETerror=$PIECE(pStatus," ",2,*)
    SETpResult={
      "error":(error)
    }
  }
 
  IFpResult.%Extends("%Library.DynamicAbstractObject"){
    WRITEpResult.%ToJSON()
  }
  ELSEIFpResult.%Extends("%JSON.Adaptor"){
    DOpResult.%JSONExport()
  }
  ELSEIFpResult.%Extends("%Stream.Object"){
    DOpResult.OutputToDevice()
  }
 
  QUITtSC
}

 

ClassMethodSwaggerSpec()As%Status
{
  SettSC=##class(%REST.API).GetWebRESTApplication($NAMESPACE,%request.Application,.swagger)
  Doswagger.info.%Remove("x-ISC_Namespace")
  Setswagger.basePath="/iris-tts"
  Setswagger.info.title="TTS Service API"
  Setswagger.info.version="1.0"
  Setswagger.host="localhost:52773"
  Return..%ProcessResult($$$OK,swagger)
}

 

}

Comments

Ben Spead · May 3, 2022

love it!!  I have previously said that I believe the most useful benefit of python for IRIS would be opening the gateway to python based libraries which do cool things.   this is an awesome example of that concept!!

0
Yuri Marx · May 3, 2022

Thanks! You right, I have other apps published on OEX to detect objects, encrypt, do geocoding, all using Python and IRIS.

0