Files
c-ai-conversation/audio.c
T
2025-07-27 15:06:10 -07:00

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(&params);
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;
}