@@ -427,6 +427,151 @@ def is_available(cls):
427427 return diagnose .check_network_connection ()
428428
429429
430+ class WatsonSTT (AbstractSTTEngine ):
431+ """
432+ Speech-To-Text implementation which relies on the IBM Watson Speech-To-Text
433+ API. This requires an IBM Bluemix account, but the first 1000 minutes of
434+ transcribing per month are free.
435+
436+ To obtain a login:
437+ 1. Register for IBM Bluemix here:
438+ https://console.ng.bluemix.net/registration/
439+ 2. Once you've logged in, click the "Use Services & APIs" link on the
440+ dashboard
441+ 3. Click the "Speech To Text" icon
442+ 4. In the form on the right, leave all options as defaults and click Create
443+ 5. You'll now have a new service listed on your dashboard. If you click
444+ that service there will be a navigation option for "Service Credentials"
445+ in the left hand nav. Find your username and password there.
446+
447+ Excerpt from sample profile.yml:
448+
449+ ...
450+ timezone: US/Pacific
451+ stt_engine: watson
452+ watson:
453+ username: $YOUR_USERNAME_HERE
454+ password: $YOUR_PASSWORD_HERE
455+
456+ """
457+
458+ SLUG = 'watson'
459+
460+ def __init__ (self , username = None , password = None , language = 'en-us' ):
461+ # FIXME: get init args from config
462+ """
463+ Arguments:
464+ username - the watson api username credential
465+ password - the watson api password credential
466+ """
467+ self ._logger = logging .getLogger (__name__ )
468+ self ._username = None
469+ self ._password = None
470+ self ._http = requests .Session ()
471+ self .username = username
472+ self .password = password
473+
474+ @property
475+ def request_url (self ):
476+ return self ._request_url
477+
478+ @property
479+ def username (self ):
480+ return self ._username
481+
482+ @username .setter
483+ def username (self , value ):
484+ self ._username = value
485+
486+ @property
487+ def password (self ):
488+ return self ._password
489+
490+ @password .setter
491+ def password (self , value ):
492+ self ._password = value
493+
494+ @classmethod
495+ def get_config (cls ):
496+ # FIXME: Replace this as soon as we have a config module
497+ config = {}
498+ # HMM dir
499+ # Try to get hmm_dir from config
500+ profile_path = jasperpath .config ('profile.yml' )
501+ if os .path .exists (profile_path ):
502+ with open (profile_path , 'r' ) as f :
503+ profile = yaml .safe_load (f )
504+ if 'watson' in profile :
505+ if 'username' in profile ['watson' ]:
506+ config ['username' ] = profile ['watson' ]['username' ]
507+ if 'password' in profile ['watson' ]:
508+ config ['password' ] = profile ['watson' ]['password' ]
509+ return config
510+
511+ def transcribe (self , fp ):
512+ """
513+ Performs STT via the Watson Speech-to-Text API, transcribing an audio
514+ file and returning an English string.
515+
516+ Arguments:
517+ fp -- the path to the .wav file to be transcribed
518+ """
519+
520+ if not self .username :
521+ self ._logger .critical ('Username missing, transcription request ' +
522+ 'aborted.' )
523+ return []
524+ elif not self .password :
525+ self ._logger .critical ('Password missing, transcription ' +
526+ 'request aborted.' )
527+ return []
528+
529+ wav = wave .open (fp , 'rb' )
530+ frame_rate = wav .getframerate ()
531+ wav .close ()
532+ data = fp .read ()
533+
534+ headers = {'content-type' :
535+ 'audio/l16; rate=%s; channels=1' % frame_rate }
536+ r = self ._http .post (
537+ 'https://stream.watsonplatform.net/' +
538+ 'speech-to-text/api/v1/recognize?continuous=true' ,
539+ data = data , headers = headers , auth = (self .username , self .password )
540+ )
541+ try :
542+ r .raise_for_status ()
543+ except requests .exceptions .HTTPError as e :
544+ self ._logger .critical ('Request failed with http status %d' ,
545+ r .status_code )
546+ if r .status_code == requests .codes ['forbidden' ]:
547+ self ._logger .warning ('Status 403 is probably caused by ' +
548+ 'invalid credentials.' )
549+ return []
550+ r .encoding = 'utf-8'
551+ try :
552+ response = r .json ()
553+ if len (response ['results' ]) == 0 :
554+ # Response result is empty
555+ raise ValueError ('Nothing has been transcribed.' )
556+ results = [alt ['transcript' ] for alt
557+ in response ['results' ][0 ]['alternatives' ]]
558+ except ValueError as e :
559+ self ._logger .warning ('Empty response: %s' , e .args [0 ])
560+ results = []
561+ except (KeyError , IndexError ):
562+ self ._logger .warning ('Cannot parse response.' , exc_info = True )
563+ results = []
564+ else :
565+ # Convert all results to uppercase
566+ results = tuple (result .strip ().upper () for result in results )
567+ self ._logger .info ('Transcribed: %r' , results )
568+ return results
569+
570+ @classmethod
571+ def is_available (cls ):
572+ return diagnose .check_network_connection ()
573+
574+
430575class AttSTT (AbstractSTTEngine ):
431576 """
432577 Speech-To-Text implementation which relies on the AT&T Speech API.
0 commit comments