Guest User

Troubleshooting Cartesia API Connection in Python TTS Application

a guest
Nov 14th, 2024
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.70 KB | Source Code | 0 0
  1. import os
  2. import requests
  3. import json
  4. from pydub import AudioSegment
  5. import io
  6. import tkinter as tk
  7. from tkinter import ttk, messagebox
  8. import tempfile
  9. import threading
  10. import re
  11. from dotenv import load_dotenv
  12.  
  13. # Cartesia imports
  14. from cartesia.tts import CartesiaTTS
  15. from cartesia._types import (
  16.     AudioOutputFormat,
  17.     AudioDataReturnType
  18. )
  19.  
  20. # Load environment variables
  21. load_dotenv()
  22.  
  23. class ScriptToConversation:
  24.     def __init__(self):
  25.         print("Initializing application...")
  26.         self.api_key = os.getenv('X-API-Key')
  27.         if not self.api_key:
  28.             tk.messagebox.showerror("API Key Error", "No API key found in .env file")
  29.             raise ValueError("API Key not found in .env file")
  30.        
  31.         print(f"Using API key: {self.api_key[:8]}...")
  32.        
  33.         # Initialize Cartesia client
  34.         try:
  35.             # Set the API key as an environment variable
  36.             import httpx  # Import here to help diagnose network issues
  37.            
  38.             # Validate API key first
  39.             response = httpx.get(
  40.                 'https://api.cartesia.ai/voices/',
  41.                 headers={
  42.                     'X-API-Key': self.api_key,
  43.                     'Cartesia-Version': '2024-06-10'  # Latest version from the error message
  44.                 }
  45.             )
  46.            
  47.             # If the request is successful, proceed with CartesiaTTS
  48.             if response.status_code == 200:
  49.                 os.environ['CARTESIA_API_KEY'] = self.api_key
  50.                 os.environ['CARTESIA_BASE_URL'] = 'https://api.cartesia.ai'
  51.                 os.environ['CARTESIA_API_VERSION'] = '2024-06-10'  # Set the version for the SDK
  52.                 self.client = CartesiaTTS()
  53.                 print("Cartesia client initialized successfully")
  54.             else:
  55.                 raise ValueError(f"API request failed with status {response.status_code}: {response.text}")
  56.        
  57.         except Exception as e:
  58.             error_msg = f"Failed to initialize Cartesia client: {e}"
  59.             print(error_msg)
  60.             tk.messagebox.showerror("Initialization Error", error_msg)
  61.             raise
  62.        
  63.         # Create main window
  64.         self.root = tk.Tk()
  65.         self.root.title("Script to Conversation Converter")
  66.         self.root.geometry("600x800")
  67.        
  68.         # Script input
  69.         script_frame = ttk.Frame(self.root, padding="10")
  70.         script_frame.pack(fill=tk.BOTH, expand=True)
  71.         ttk.Label(script_frame, text="Paste your script here:").pack()
  72.         self.script_text = tk.Text(script_frame, height=20)
  73.         self.script_text.pack(fill=tk.BOTH, expand=True)
  74.        
  75.         # Voice selection
  76.         voice_frame = ttk.Frame(self.root, padding="10")
  77.         voice_frame.pack(fill=tk.X)
  78.        
  79.         # Populate voice selection dropdowns with a variety of voices
  80.         self.available_voices = []
  81.        
  82.         # Speaker A voice selection
  83.         ttk.Label(voice_frame, text="Speaker A Voice:").grid(row=0, column=0, padx=5)
  84.         self.speaker_a_voice = ttk.Combobox(voice_frame, width=30, values=self.available_voices)
  85.         self.speaker_a_voice.grid(row=0, column=1, padx=5)
  86.        
  87.         # Speaker B voice selection
  88.         ttk.Label(voice_frame, text="Speaker B Voice:").grid(row=1, column=0, padx=5)
  89.         self.speaker_b_voice = ttk.Combobox(voice_frame, width=30, values=self.available_voices)
  90.         self.speaker_b_voice.grid(row=1, column=1, padx=5)
  91.        
  92.         # Convert button
  93.         ttk.Button(self.root, text="Convert to Audio", command=self.process_script).pack(pady=10)
  94.        
  95.         # Status label
  96.         self.status_label = ttk.Label(self.root, text="")
  97.         self.status_label.pack(pady=5)
  98.        
  99.         print("GUI initialized, loading voices...")
  100.         # Start voice loading in a separate thread
  101.         threading.Thread(target=self.load_voices, daemon=True).start()
  102.  
  103.     def update_status(self, message):
  104.         """Update status label safely from any thread"""
  105.         if not self.root.winfo_exists():
  106.             return
  107.         self.root.after(0, lambda: self.status_label.config(text=message))
  108.  
  109.     def load_voices(self):
  110.         try:
  111.             self.update_status("Loading voices...")
  112.             print("Attempting to retrieve voices...")
  113.             # Retrieve available voices
  114.             available_voices = self.client.get_voices()
  115.            
  116.             # Convert voices to a list of names for the dropdown
  117.             voice_names = list(available_voices.keys())
  118.            
  119.             # Update the dropdown menu
  120.             self.available_voices = voice_names
  121.             self.speaker_a_voice['values'] = self.available_voices
  122.             self.speaker_b_voice['values'] = self.available_voices
  123.            
  124.             # Set a default voice if available
  125.             if voice_names:
  126.                 self.speaker_a_voice.set(voice_names[0])
  127.                 if len(voice_names) > 1:
  128.                     self.speaker_b_voice.set(voice_names[1])
  129.            
  130.             print(f"Loaded {len(voice_names)} voices successfully")
  131.             self.update_status("Voices loaded successfully")
  132.        
  133.         except Exception as e:
  134.             error_msg = f"Error loading voices: {e}"
  135.             print(error_msg)
  136.             print(f"Base URL: {os.environ.get('CARTESIA_BASE_URL')}")
  137.             print(f"API Key: {self.api_key[:8]}...")
  138.             tk.messagebox.showerror("Voice Loading Error", error_msg)
  139.  
  140.     def generate_audio(self, text, voice_name):
  141.         """Generate audio using Cartesia API with enhanced error handling"""
  142.         try:
  143.             print(f"Generating audio for text: {text}")
  144.            
  145.             # Extract voice ID from the voice name (assuming format like "Voice Name (voice_id)")
  146.             match = re.search(r'\(([^)]+)\)', voice_name)
  147.             if not match:
  148.                 raise ValueError(f"Could not extract voice ID from: {voice_name}")
  149.             voice_id = match.group(1)
  150.            
  151.             # Use Cartesia's bytes method for audio generation
  152.             try:
  153.                 audio_response = self.client.tts.bytes(
  154.                     model_id=voice_id,
  155.                     transcript=text,
  156.                     voice={"id": voice_id},  # Use a dictionary instead of a custom type
  157.                     language="en",
  158.                     output_format=AudioOutputFormat.PCM_44100,
  159.                     duration=None,  # Let the API determine duration
  160.                     return_type=AudioDataReturnType.BYTES
  161.                 )
  162.                
  163.                 return {
  164.                     'audio': audio_response,
  165.                     'sampling_rate': 44100
  166.                 }
  167.            
  168.             except Exception as api_err:
  169.                 error_msg = f"Cartesia API Error: {api_err}"
  170.                 print(error_msg)
  171.                 tk.messagebox.showerror("Audio Generation Failed", error_msg)
  172.                 return None
  173.        
  174.         except Exception as e:
  175.             error_msg = f"Error in audio generation: {e}"
  176.             print(error_msg)
  177.             tk.messagebox.showerror("Generation Error", error_msg)
  178.             return None
  179.  
  180.     def process_script(self):
  181.         """Process the script and generate the conversation audio"""
  182.         def process():
  183.             try:
  184.                 self.update_status("Processing script...")
  185.                
  186.                 script = self.script_text.get("1.0", tk.END).strip()
  187.                
  188.                 # Split the script into sections
  189.                 lines = script.split('\n')
  190.                 current_speaker = None
  191.                 current_text = []
  192.                 conversation_segments = []
  193.                
  194.                 for line in lines:
  195.                     line = line.strip()
  196.                     if not line:
  197.                         continue
  198.                        
  199.                     if line.startswith("Speaker A:"):
  200.                         if current_speaker and current_text:
  201.                             conversation_segments.append((current_speaker, ' '.join(current_text)))
  202.                         current_speaker = "A"
  203.                         current_text = [line.replace("Speaker A:", "").strip()]
  204.                     elif line.startswith("Speaker B:"):
  205.                         if current_speaker and current_text:
  206.                             conversation_segments.append((current_speaker, ' '.join(current_text)))
  207.                         current_speaker = "B"
  208.                         current_text = [line.replace("Speaker B:", "").strip()]
  209.                     else:
  210.                         current_text.append(line)
  211.                
  212.                 # Add the last segment
  213.                 if current_speaker and current_text:
  214.                     conversation_segments.append((current_speaker, ' '.join(current_text)))
  215.                
  216.                 # Generate audio for each segment
  217.                 combined_audio = AudioSegment.empty()
  218.                
  219.                 for speaker, text in conversation_segments:
  220.                     voice_name = self.speaker_a_voice.get() if speaker == "A" else self.speaker_b_voice.get()
  221.                     self.update_status(f"Generating audio for Speaker {speaker}...")
  222.                     audio_response = self.generate_audio(text, voice_name)
  223.                    
  224.                     if audio_response:
  225.                         # Convert the audio bytes to AudioSegment
  226.                         audio_segment = AudioSegment(
  227.                             data=audio_response['audio'],
  228.                             sample_width=2,  # 16-bit
  229.                             frame_rate=audio_response['sampling_rate'],
  230.                             channels=1
  231.                         )
  232.                        
  233.                         # Add a small pause between segments
  234.                         if len(combined_audio) > 0:
  235.                             combined_audio += AudioSegment.silent(duration=500)  # 500ms pause
  236.                        
  237.                         combined_audio += audio_segment
  238.                
  239.                 # Save the final audio file
  240.                 output_file = "conversation.wav"
  241.                 combined_audio.export(output_file, format="wav")
  242.                 self.update_status(f"Conversation saved as {output_file}")
  243.                
  244.             except Exception as e:
  245.                 print(f"Error: {str(e)}")
  246.                 self.update_status(f"Error: {str(e)}")
  247.        
  248.         # Run processing in background thread
  249.         threading.Thread(target=process, daemon=True).start()
  250.  
  251.     def run(self):
  252.         """Start the application"""
  253.         print("Starting application...")
  254.         self.root.mainloop()
  255.  
  256. if __name__ == "__main__":
  257.     app = ScriptToConversation()
  258.     app.run()
  259.  
Tags: api problems
Advertisement
Add Comment
Please, Sign In to add comment