create full flow with stt, anthropic, tts, and playback
This organizes some components like deepgram into own module, and also allows gets the full flow to work every time we run the program.
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
#include "deepgram_client.h"
|
||||
#include <string.h>
|
||||
|
||||
// Returns string that caller must free
|
||||
static char *dg_find_transcript(char *json_response) {
|
||||
printf("Parsing response: %s \n", json_response);
|
||||
char *start = strstr(json_response, "\"transcript\":\"");
|
||||
if (!start)
|
||||
return NULL;
|
||||
|
||||
start += 14; // Skip past "transcript":"
|
||||
char *end = strchr(start, '"'); // Find closing quote
|
||||
// Copy substring between start and end
|
||||
size_t len = end - start;
|
||||
char *result = malloc(len + 1);
|
||||
strncpy(result, start, len);
|
||||
result[len] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
char *dg_transcribe(FILE *audio_file) {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
struct curl_slist *headers = NULL;
|
||||
|
||||
// Initialize response buffer
|
||||
struct curl_response_data response = {0};
|
||||
response.data = malloc(1);
|
||||
response.size = 0;
|
||||
|
||||
// Get file size
|
||||
fseek(audio_file, 0, SEEK_END);
|
||||
long file_size = ftell(audio_file);
|
||||
fseek(audio_file, 0, SEEK_SET);
|
||||
|
||||
struct curl_file_data data;
|
||||
data.file = audio_file;
|
||||
data.size = file_size;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
// Set headers for Deepgram API
|
||||
char *deepgram_api_key = getenv("DEEPGRAM_API_KEY");
|
||||
if (!deepgram_api_key) {
|
||||
fprintf(stderr, "DEEPGRAM_API_KEY environment variable not set\n");
|
||||
free(response.data);
|
||||
curl_easy_cleanup(curl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char auth_header[256];
|
||||
snprintf(auth_header, sizeof(auth_header), "Authorization: Token %s", deepgram_api_key);
|
||||
headers = curl_slist_append(headers, auth_header);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL,
|
||||
"https://api.deepgram.com/v1/"
|
||||
"listen?encoding=linear16&sample_rate=48000&channels=1");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_read_callback_file);
|
||||
curl_easy_setopt(curl, CURLOPT_READDATA, &data);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, file_size);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_callback_response);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
fprintf(stderr, "curl failed: %s\n", curl_easy_strerror(res));
|
||||
free(response.data);
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("done performing curl\n");
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
// Parse transcript from complete response
|
||||
char *transcript = dg_find_transcript(response.data);
|
||||
free(response.data);
|
||||
|
||||
return transcript;
|
||||
}
|
||||
|
||||
struct curl_response_data *dg_text_to_speech(char *text) {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
struct curl_slist *headers = NULL;
|
||||
|
||||
// Create JSON payload - need to escape the text properly
|
||||
// Worst case: every char needs escaping, so allocate 2x + overhead
|
||||
size_t text_len = strlen(text);
|
||||
size_t payload_size = text_len * 2 + 100;
|
||||
char *json_payload = malloc(payload_size);
|
||||
|
||||
// Simple approach: replace problematic chars with spaces
|
||||
char *escaped_text = malloc(text_len + 1);
|
||||
strcpy(escaped_text, text);
|
||||
for (int i = 0; escaped_text[i]; i++) {
|
||||
if (escaped_text[i] == '"' || escaped_text[i] == '\n' ||
|
||||
escaped_text[i] == '\r' || escaped_text[i] == '\t') {
|
||||
escaped_text[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(json_payload, payload_size,
|
||||
"{"
|
||||
"\"text\":\"%s\""
|
||||
"}",
|
||||
escaped_text);
|
||||
|
||||
free(escaped_text);
|
||||
|
||||
// Initialize response buffer
|
||||
struct curl_response_data *response =
|
||||
malloc(sizeof(struct curl_response_data));
|
||||
response->data = malloc(1);
|
||||
response->size = 0;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
char *deepgram_api_key = getenv("DEEPGRAM_API_KEY");
|
||||
if (!deepgram_api_key) {
|
||||
fprintf(stderr, "DEEPGRAM_API_KEY environment variable not set\n");
|
||||
free(response->data);
|
||||
curl_easy_cleanup(curl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char auth_header[256];
|
||||
snprintf(auth_header, sizeof(auth_header), "Authorization: Token %s", deepgram_api_key);
|
||||
headers = curl_slist_append(headers, auth_header);
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL,
|
||||
"https://api.deepgram.com/v1/"
|
||||
"speak?encoding=linear16&sample_rate=48000");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_payload);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_callback_response);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
fprintf(stderr, "curl failed: %s\n", curl_easy_strerror(res));
|
||||
free(response->data);
|
||||
free(response);
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
printf("received audio of %d length for tts\n", (int)response->size);
|
||||
|
||||
// Debug: print first 100 chars to see if it's audio or error message
|
||||
printf("First 100 chars: %.100s\n", response->data);
|
||||
|
||||
free(json_payload);
|
||||
return response;
|
||||
}
|
||||
Reference in New Issue
Block a user