8870741c3e
* capture audio with alsa * add brainstorm thoughts * use basecamp project for managing this project * add libcurl basic example * write pcm data to file * transcribe audio file with deepgram * transcribe raw audio * hit anthropic API for ai question * 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. * organize audio and intelligence modules * organize header files into include directory * docs: add readme * docs: explain future work * docs: add disclaimer about hardcoded audio params
216 lines
6.4 KiB
C
216 lines
6.4 KiB
C
#include "audio.h"
|
|
|
|
static void print_pcm_state(snd_pcm_t *pcm_handle) {
|
|
snd_pcm_state_t state = snd_pcm_state(pcm_handle);
|
|
const char *state_name = snd_pcm_state_name(state);
|
|
printf("State of pcm handle: %s\n", state_name);
|
|
}
|
|
|
|
int audio_play(char *buf, size_t byte_count) {
|
|
printf("going to play audio of %d bytes of data\n", (int)byte_count);
|
|
|
|
snd_pcm_t *pcm_handle;
|
|
// The total frames in the buffer is how many bytes divided by 2 because
|
|
// of linear16 encoding, one channel
|
|
size_t total_frames = byte_count / 2;
|
|
|
|
int err;
|
|
err = snd_pcm_open(&pcm_handle, ALSA_SPEAKER_HARDWARE_DEVICE,
|
|
SND_PCM_STREAM_PLAYBACK, 0);
|
|
if (err < 0) {
|
|
fprintf(stderr, "unable to open playback device");
|
|
return err;
|
|
}
|
|
print_pcm_state(pcm_handle);
|
|
|
|
const char *name = snd_pcm_name(pcm_handle);
|
|
printf("found this device: %s\n", name);
|
|
|
|
snd_pcm_info_t *pcm_info;
|
|
snd_pcm_info_malloc(&pcm_info);
|
|
err = snd_pcm_info(pcm_handle, pcm_info);
|
|
if (err < 0) {
|
|
snd_pcm_close(pcm_handle);
|
|
printf("unable to get info of pcm device");
|
|
return -1;
|
|
}
|
|
name = snd_pcm_info_get_name(pcm_info);
|
|
if (err < 0) {
|
|
printf("unable to get card info");
|
|
snd_pcm_close(pcm_handle);
|
|
snd_pcm_info_free(pcm_info);
|
|
return -1;
|
|
}
|
|
printf("got pcm name: %s \n", name);
|
|
|
|
print_pcm_state(pcm_handle);
|
|
snd_pcm_info_free(pcm_info);
|
|
if ((err = snd_pcm_set_params(pcm_handle, SND_PCM_FORMAT_S16_LE,
|
|
SND_PCM_ACCESS_RW_INTERLEAVED, 1, 48000, 1,
|
|
500000)) < 0) { /* 0.5sec */
|
|
printf("Playback open error: %s\n", snd_strerror(err));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// we want to read in a chunk of data and writei to the pcm
|
|
size_t frames_written = 0;
|
|
size_t frames_in_each_chunk = 1024; // how many frames to read per chunk (2048
|
|
// bytes since each frame is 2 bytes)
|
|
while (frames_written < total_frames) {
|
|
size_t frames_to_write =
|
|
frames_in_each_chunk < (total_frames - frames_written)
|
|
? frames_in_each_chunk
|
|
: (total_frames - frames_written);
|
|
// We move the pointer x2 because each frame is 2 bytes so the next audio
|
|
// sample is 16 bits forward, not 8
|
|
char *start_ptr = buf + (frames_written * 2);
|
|
snd_pcm_sframes_t result =
|
|
snd_pcm_writei(pcm_handle, start_ptr, frames_to_write);
|
|
if (result < 0) {
|
|
result = snd_pcm_recover(pcm_handle, result, 0);
|
|
if (result < 0) {
|
|
printf("snd_pcm_writei failed: %s\n", snd_strerror(result));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result > 0 && result < frames_to_write) {
|
|
printf("Short write \n");
|
|
}
|
|
|
|
frames_written += result;
|
|
}
|
|
|
|
/* pass the remaining samples, otherwise they're dropped in close */
|
|
err = snd_pcm_drain(pcm_handle);
|
|
if (err < 0) {
|
|
printf("snd_pcm_drain failed: %s\n", snd_strerror(err));
|
|
}
|
|
snd_pcm_close(pcm_handle);
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *audio_capture(uint seconds) {
|
|
char *output_file = "output.pcm";
|
|
|
|
snd_pcm_t *pcm_handle = NULL;
|
|
|
|
int err;
|
|
err = snd_pcm_open(&pcm_handle, ALSA_MIC_HARDWARE_DEVICE,
|
|
SND_PCM_STREAM_CAPTURE, 0);
|
|
if (err < 0) {
|
|
printf("unable to open pcm device");
|
|
return NULL;
|
|
}
|
|
print_pcm_state(pcm_handle);
|
|
|
|
const char *name = snd_pcm_name(pcm_handle);
|
|
printf("found this device: %s\n", name);
|
|
|
|
snd_pcm_info_t *pcm_info;
|
|
snd_pcm_info_malloc(&pcm_info);
|
|
err = snd_pcm_info(pcm_handle, pcm_info);
|
|
if (err < 0) {
|
|
snd_pcm_close(pcm_handle);
|
|
printf("unable to get info of pcm device");
|
|
return NULL;
|
|
}
|
|
name = snd_pcm_info_get_name(pcm_info);
|
|
if (err < 0) {
|
|
printf("unable to get card info");
|
|
snd_pcm_close(pcm_handle);
|
|
snd_pcm_info_free(pcm_info);
|
|
return NULL;
|
|
}
|
|
printf("got pcm name: %s \n", name);
|
|
|
|
print_pcm_state(pcm_handle);
|
|
snd_pcm_info_free(pcm_info);
|
|
|
|
snd_pcm_hw_params_t *params;
|
|
err = snd_pcm_hw_params_malloc(¶ms);
|
|
if (err < 0) {
|
|
fprintf(stderr, "unable to mallow hw params");
|
|
return NULL;
|
|
}
|
|
err = snd_pcm_hw_params_any(pcm_handle, params);
|
|
if (err < 0) {
|
|
fprintf(stderr, "unable to find ranges for hw device");
|
|
snd_pcm_close(pcm_handle);
|
|
snd_pcm_hw_params_free(params);
|
|
return NULL;
|
|
}
|
|
|
|
uint period_size = 1024;
|
|
uint buffer_size = 4096;
|
|
uint channel_count = 1;
|
|
uint bytes_per_sample = 2; // FORMAT_S16_LE
|
|
uint samples_per_second = 48000;
|
|
snd_pcm_hw_params_set_access(pcm_handle, params,
|
|
SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
snd_pcm_hw_params_set_channels(pcm_handle, params, channel_count);
|
|
snd_pcm_hw_params_set_buffer_size(pcm_handle, params, buffer_size);
|
|
snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE);
|
|
snd_pcm_hw_params_set_rate(pcm_handle, params, samples_per_second, 0);
|
|
snd_pcm_hw_params_set_periods(pcm_handle, params, period_size, 0);
|
|
|
|
err = snd_pcm_hw_params(pcm_handle, params);
|
|
if (err < 0) {
|
|
fprintf(stderr, "unable to prepare pcm\n");
|
|
snd_pcm_close(pcm_handle);
|
|
snd_pcm_hw_params_free(params);
|
|
return NULL;
|
|
}
|
|
|
|
print_pcm_state(pcm_handle);
|
|
|
|
err = snd_pcm_start(pcm_handle);
|
|
if (err < 0) {
|
|
fprintf(stderr, "unable to start pcm");
|
|
snd_pcm_close(pcm_handle);
|
|
snd_pcm_hw_params_free(params);
|
|
return NULL;
|
|
}
|
|
|
|
print_pcm_state(pcm_handle);
|
|
|
|
const uint seconds_to_capture = seconds;
|
|
const uint periods_to_read =
|
|
(samples_per_second * seconds_to_capture) / period_size;
|
|
char *buf = malloc(period_size * channel_count * bytes_per_sample);
|
|
uint periods_read = 0;
|
|
/* Read one period at a time from the hardware buffer
|
|
* We set the hardware buffer to read 4096 frames.
|
|
* The period size is 1024 frames.
|
|
* Each period is 2048 BYTES because each frame is 2 bytes.
|
|
* Each frame is 2 bytes because of 1 channel, and 16 bit format
|
|
*/
|
|
FILE *file = fopen(output_file, "wb");
|
|
if (file == NULL) {
|
|
perror("Error opening file");
|
|
return NULL;
|
|
}
|
|
printf("======READING FRAMES for 5 seconds========\n");
|
|
while (snd_pcm_readi(pcm_handle, (void *)buf, period_size) > 0 &&
|
|
periods_read < periods_to_read) {
|
|
// Because each frame is 2 bytes (format/bit rate), cast to int16
|
|
// int16_t *samples = (int16_t *)buf;
|
|
// for (int i = 0; i < period_size; i++) {
|
|
// printf("%d ", samples[i]);
|
|
// }
|
|
|
|
fwrite(buf, sizeof(int16_t), period_size, file);
|
|
periods_read++;
|
|
}
|
|
printf("done capturing audio\n");
|
|
|
|
fclose(file);
|
|
|
|
free(buf);
|
|
snd_pcm_close(pcm_handle);
|
|
snd_pcm_hw_params_free(params);
|
|
|
|
return output_file;
|
|
}
|