33import os
44import json
55import pyaudio
6- import oracledb
6+ import requests
7+ import time
8+ import wave
79from datetime import datetime
10+ import oracledb
811import oci
912from oci .config import from_file
1013from oci .auth .signers .security_token_signer import SecurityTokenSigner
1518)
1619from aiohttp import web
1720
18- # Global variables to store the latest data
21+ from oci .ai_speech import AIServiceSpeechClient
22+ from oci .ai_speech .models import SynthesizeSpeechDetails
23+
1924latest_thetime = None
2025latest_question = None
2126latest_answer = None
2227compartment_id = os .getenv ('COMPARTMENT_ID' )
2328print (f"compartment_id: { compartment_id } " )
24- pw = getpass .getpass ("Enter database user password:" )
2529
26- # Use this when making a connection with a wallet
2730connection = oracledb .connect (
2831 user = "moviestream" ,
29- password = pw ,
32+ password = "Welcome12345" ,
3033 dsn = "selectaidb_high" ,
31- config_dir = r"C:\Users\paulp\Downloads\Wallet_SelectAIDB" ,
32- wallet_location = r"C:\Users\paulp\Downloads\Wallet_SelectAIDB"
34+ config_dir = "/Users/pparkins/Downloads/Wallet_SelectAIDB" ,
35+ wallet_location = "/Users/pparkins/Downloads/Wallet_SelectAIDB" ,
36+ wallet_password = "Welcome12345"
3337)
3438print (f"Successfully connected to Oracle Database Connection: { connection } " )
3539
36- # Create a FIFO queue
3740queue = asyncio .Queue ()
3841
39- # Set audio parameters
4042SAMPLE_RATE = 16000
4143FORMAT = pyaudio .paInt16
4244CHANNELS = 1
4345BUFFER_DURATION_MS = 96
44-
45- # Calculate the number of frames per buffer
4646FRAMES_PER_BUFFER = int (SAMPLE_RATE * BUFFER_DURATION_MS / 1000 )
4747
48- # Variables to keep track of results and state
4948cummulativeResult = ""
5049isSelect = False
5150last_result_time = None
5251
53- def authenticator ():
54- config = from_file ("~/.oci/config" , "MYSPEECHAIPROFILE" )
55- with open (config ["security_token_file" ], "r" ) as f :
56- token = f .readline ()
57- private_key = oci .signer .load_private_key_from_file (config ["key_file" ])
58- auth = SecurityTokenSigner (token = token , private_key = private_key )
59- return auth
60-
6152def audio_callback (in_data , frame_count , time_info , status ):
62- # This function will be called by PyAudio when there's new audio data
6353 queue .put_nowait (in_data )
6454 return (None , pyaudio .paContinue )
6555
6656p = pyaudio .PyAudio ()
6757
68- # Open the stream
6958stream = p .open (
7059 format = FORMAT ,
7160 channels = CHANNELS ,
@@ -77,14 +66,36 @@ def audio_callback(in_data, frame_count, time_info, status):
7766
7867stream .start_stream ()
7968config = from_file ()
80- isInsertResults = True
69+ isInsertResults = False
8170
8271async def send_audio (client ):
8372 while True :
8473 data = await queue .get ()
85- # Send it over the websocket
8674 await client .send_data (data )
8775
76+ def play_audio (file_path ):
77+ try :
78+ wf = wave .open (file_path , 'rb' )
79+ p = pyaudio .PyAudio ()
80+ stream = p .open (
81+ format = p .get_format_from_width (wf .getsampwidth ()),
82+ channels = wf .getnchannels (),
83+ rate = wf .getframerate (),
84+ output = True
85+ )
86+
87+ data = wf .readframes (1024 )
88+ while data :
89+ stream .write (data )
90+ data = wf .readframes (1024 )
91+
92+ stream .stop_stream ()
93+ stream .close ()
94+ p .terminate ()
95+ print ("Audio playback finished." )
96+ except Exception as e :
97+ print (f"Error playing audio: { e } " )
98+
8899class SpeechListener (RealtimeClientListener ):
89100 def on_result (self , result ):
90101 global cummulativeResult , isSelect , last_result_time
@@ -93,33 +104,37 @@ def on_result(self, result):
93104 cummulativeResult += transcription
94105 print (f"Received final results: { transcription } " )
95106 print (f"Current cummulative result: { cummulativeResult } " )
96- if cummulativeResult .lower ().startswith ("select ai " ):
97- cummulativeResult = cummulativeResult [len ("select ai " ):].strip ()
107+ if cummulativeResult .lower ().startswith ("hey db " ):
108+ cummulativeResult = cummulativeResult [len ("hey db " ):].strip ()
98109 isSelect = True
99- elif cummulativeResult .lower ().startswith ("select the eye " ):
100- cummulativeResult = cummulativeResult [len ("select the eye " ):].strip ()
110+ elif cummulativeResult .lower ().startswith ("adb " ):
111+ cummulativeResult = cummulativeResult [len ("adb " ):].strip ()
101112 isSelect = True
102113 else :
103114 cummulativeResult = ""
104115 last_result_time = asyncio .get_event_loop ().time ()
105116 else :
106117 print (f"Received partial results: { result ['transcriptions' ][0 ]['transcription' ]} " )
107118
108-
109119 def on_ack_message (self , ackmessage ):
110- return super ().on_ack_message (ackmessage )
120+ """Handle acknowledgment messages (required by the abstract class)."""
121+ print (f"ACK received: { ackmessage } " )
111122
112123 def on_connect (self ):
113- return super ().on_connect ()
124+ """Handle connection event (required by the abstract class)."""
125+ print ("Connected to Realtime Speech Service." )
114126
115127 def on_connect_message (self , connectmessage ):
116- return super ().on_connect_message (connectmessage )
128+ """Handle connection messages (required by the abstract class)."""
129+ print (f"Connect message: { connectmessage } " )
117130
118131 def on_network_event (self , ackmessage ):
119- return super ().on_network_event (ackmessage )
132+ """Handle network events (required by the abstract class)."""
133+ print (f"Network event: { ackmessage } " )
120134
121- def on_error (self ):
122- return super ().on_error ()
135+ def on_error (self , exception ):
136+ """Handle errors (required by the abstract class)."""
137+ print (f"An error occurred: { exception } " )
123138
124139async def check_idle ():
125140 global last_result_time , isSelect
@@ -129,93 +144,111 @@ async def check_idle():
129144 isSelect = False
130145 await asyncio .sleep (1 )
131146
132- # Function to execute AI query and optionally insert results into the table
133- # For example Select AI I am looking for the top five selling movies for the latest month please
147+
148+ def authenticator ():
149+ config = from_file ("~/.oci/config" , "MYSPEECHAIPROFILE" )
150+ with open (config ["security_token_file" ], "r" ) as f :
151+ token = f .readline ()
152+ private_key = oci .signer .load_private_key_from_file (config ["key_file" ])
153+ auth = SecurityTokenSigner (token = token , private_key = private_key )
154+ return auth
134155def executeSelectAI ():
135156 global cummulativeResult , isInsertResults , latest_thetime , latest_question , latest_answer
136157 print (f"executeSelectAI called cummulative result: { cummulativeResult } " )
137158
138- # AI query - todo use openai_gpt4o
139159 query = """SELECT DBMS_CLOUD_AI.GENERATE(
140160 prompt => :prompt,
141161 profile_name => 'openai_gpt35',
142- action => 'narrate ')
162+ action => 'chat ')
143163 FROM dual"""
144164
145165 try :
146166 with connection .cursor () as cursor :
147- cursor .execute (query , prompt = cummulativeResult )
167+ cursor .execute (query , { ' prompt' : cummulativeResult } )
148168 result = cursor .fetchone ()
149169 if result and isinstance (result [0 ], oracledb .LOB ):
150170 text_result = result [0 ].read ()
151- print (text_result )
171+ print (f"Query result: { text_result } " )
152172
153173 latest_thetime = datetime .now ()
154174 latest_question = cummulativeResult
155- latest_answer = text_result [:3000 ] # Truncate if necessary
156- cummulativeResult = ""
175+ latest_answer = text_result [:3000 ]
176+
177+ cummulativeResult = ""
178+
179+ # API key-based authentication...
180+ config = oci .config .from_file ("~/.oci/config" , "DEFAULT" )
181+ speech_client = AIServiceSpeechClient (config )
182+
183+ text_to_speech = SynthesizeSpeechDetails (
184+ text = f" { latest_answer } " ,
185+ is_stream_enabled = False ,
186+ configuration = oci .ai_speech .models .TtsOracleConfiguration (
187+ model_family = "ORACLE" ,
188+ # Brian Annabelle Bob Stacy Phil Cindy Brad
189+ model_details = oci .ai_speech .models .TtsOracleTts2NaturalModelDetails (voice_id = "Brian" ),
190+ speech_settings = oci .ai_speech .models .TtsOracleSpeechSettings (
191+ speech_mark_types = ["WORD" ]
192+ ),
193+ )
194+ )
195+
196+ response = speech_client .synthesize_speech (synthesize_speech_details = text_to_speech )
197+
198+ with open ("TTSoutput.wav" , "wb" ) as audio_file :
199+ audio_file .write (response .data .content )
200+
201+ print ("Speech synthesis completed and saved as TTSoutput.wav" )
202+
203+ # Play the generated speech
204+ play_audio ("TTSoutput.wav" )
157205
158- if isInsertResults :
159- insert_query = """
160- INSERT INTO selectai_data (thetime, question, answer)
161- VALUES (:thetime, :question, :answer)
162- """
163- cursor .execute (insert_query , {
164- 'thetime' : latest_thetime ,
165- 'question' : latest_question ,
166- 'answer' : latest_answer
167- })
168- connection .commit ()
169- print ("Insert successful." )
170- else :
171- print (result )
172206 except Exception as e :
173207 print (f"An error occurred: { e } " )
174208
175- cummulativeResult = ""
176-
177209async def handle_request (request ):
178210 global latest_thetime , latest_question , latest_answer
179211 data = {
180- "thetime" : latest_thetime .isoformat () if latest_thetime else None , # Convert datetime to ISO format
212+ "thetime" : latest_thetime .isoformat () if latest_thetime else None ,
181213 "question" : latest_question ,
182214 "answer" : latest_answer
183215 }
184216 return web .json_response (data )
185217
186-
187218if __name__ == "__main__" :
188- # Run the event loop
189- def message_callback (message ):
190- print (f"Received message: { message } " )
219+ loop = asyncio .new_event_loop () # Fix event loop issue
220+ asyncio .set_event_loop (loop )
191221
192222 realtime_speech_parameters = RealtimeParameters ()
193223 realtime_speech_parameters .language_code = "en-US"
194224 realtime_speech_parameters .model_domain = (
195225 realtime_speech_parameters .MODEL_DOMAIN_GENERIC
196226 )
197- realtime_speech_parameters .partial_silence_threshold_in_ms = 0
198227 realtime_speech_parameters .final_silence_threshold_in_ms = 2000
199- realtime_speech_parameters .should_ignore_invalid_customizations = False
200- realtime_speech_parameters .stabilize_partial_results = (
201- realtime_speech_parameters .STABILIZE_PARTIAL_RESULTS_NONE
202- )
203228
204229 realtime_speech_url = "wss://realtime.aiservice.us-phoenix-1.oci.oraclecloud.com"
205230 client = RealtimeClient (
206231 config = config ,
207232 realtime_speech_parameters = realtime_speech_parameters ,
208233 listener = SpeechListener (),
209234 service_endpoint = realtime_speech_url ,
210- signer = authenticator () ,
235+ signer = None ,
211236 compartment_id = compartment_id ,
212237 )
213238
214- loop = asyncio .get_event_loop ()
239+ # Instance, resource principal, or session token-based authentication (as shown below) can also be used
240+ # client = AIServiceSpeechClient(
241+ # config=config,
242+ # realtime_speech_parameters=realtime_speech_parameters,
243+ # listener=SpeechListener(),
244+ # service_endpoint=realtime_speech_url,
245+ # signer=authenticator(),
246+ # compartment_id=compartment_id,
247+ # )
248+
215249 loop .create_task (send_audio (client ))
216250 loop .create_task (check_idle ())
217251
218- # Set up the HTTP server
219252 app = web .Application ()
220253 app .router .add_get ('/selectai_data' , handle_request )
221254 runner = web .AppRunner (app )
0 commit comments