sdl-audio/main.c

316 lines
7.5 KiB
C
Raw Normal View History

2024-10-10 20:11:57 +08:00
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include "texture.h"
#include "audio.h"
#include "log.h"
2024-10-13 23:03:27 +08:00
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
2024-10-10 20:11:57 +08:00
typedef enum {
SELECTING_DEVICE,
RECORDING,
RECORDED,
PLAYBACK,
} application_state;
2024-10-17 03:16:41 +08:00
typedef struct {
2024-10-10 20:11:57 +08:00
application_state application_state;
audio_state audio;
2024-10-10 20:11:57 +08:00
int device_index;
2024-10-17 03:16:41 +08:00
int update_ui;
2024-10-10 20:11:57 +08:00
} state;
int init();
void destroy();
int select_device(SDL_KeyboardEvent* key_event, int max_index);
2024-10-10 20:11:57 +08:00
void application_state_to_string(application_state state, char* string);
2024-10-10 20:11:57 +08:00
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
int num_devices = 0;
2024-10-10 20:11:57 +08:00
int handle_input(SDL_Event* event, state* state);
2024-10-10 20:11:57 +08:00
void handle_task(state* state);
int main(int argc, char* argv[])
{
if (!init()) return 1;
num_devices = SDL_GetNumAudioDevices(1);
log_message(LOG_INFO, "Num audio devices: %i", num_devices);
2024-10-10 20:11:57 +08:00
char char_buffer[64];
char default_fmt[] = "Device selected: %i";
state state;
state.application_state = SELECTING_DEVICE;
if (!audio_init(&state.audio)) return 1;
state.device_index = -1;
2024-10-17 03:16:41 +08:00
state.update_ui = 0;
2024-10-10 20:11:57 +08:00
text_texture display_text_texture;
text_texture_init(&display_text_texture, renderer);
2024-10-10 20:11:57 +08:00
sprintf(char_buffer, default_fmt, state.device_index);
text_texture_load(&display_text_texture, char_buffer);
text_texture device_textures[num_devices];
for (int i = 0; i < num_devices; i++) {
text_texture_init(&device_textures[i], renderer);
2024-10-10 20:11:57 +08:00
sprintf(char_buffer, "%i: %s", i, SDL_GetAudioDeviceName(i, 1));
text_texture_load(&device_textures[i], char_buffer);
}
text_texture state_text_texture;
text_texture_init(&state_text_texture, renderer);
2024-10-10 20:11:57 +08:00
application_state_to_string(state.application_state, char_buffer);
text_texture_load(&state_text_texture, char_buffer);
SDL_Event event;
2024-10-10 20:53:49 +08:00
while (1) {
2024-10-10 20:11:57 +08:00
while (SDL_PollEvent(&event)) {
SDL_Keycode keysym = event.key.keysym.sym;
switch (event.type) {
case SDL_QUIT:
2024-10-10 20:53:49 +08:00
goto cleanup;
2024-10-10 20:11:57 +08:00
break;
case SDL_KEYDOWN:
if (!handle_input(&event, &state)) goto cleanup;
2024-10-10 20:11:57 +08:00
break;
}
}
handle_task(&state);
2024-10-17 03:16:41 +08:00
if (state.update_ui) {
sprintf(char_buffer, default_fmt, state.device_index);
text_texture_load(&display_text_texture, char_buffer);
application_state_to_string(state.application_state, char_buffer);
text_texture_load(&state_text_texture, char_buffer);
log_message(LOG_INFO, "Update UI State: %s", char_buffer);
state.update_ui = 0;
}
2024-10-10 20:11:57 +08:00
// Clear the screen
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
// Render
text_texture_render(&display_text_texture, 0, 0);
if (state.application_state == SELECTING_DEVICE) {
int text_y_offset_step = display_text_texture.height * 2;
int text_y_offset = text_y_offset_step;
for (int i = 0; i < num_devices; i++) {
text_texture_render(&device_textures[i], 0, text_y_offset);
text_y_offset += text_y_offset_step;
}
}
text_texture_render(&state_text_texture, 0, SCREEN_HEIGHT - state_text_texture.height);
// Update the screen
SDL_RenderPresent(renderer);
}
2024-10-10 20:53:49 +08:00
cleanup:
log_message(LOG_INFO, "Cleanup start");
audio_destroy(&state.audio);
log_message(LOG_INFO, "Audio done");
2024-10-10 20:11:57 +08:00
text_texture_free(&display_text_texture);
for (int i = 0; i < num_devices; i++) {
text_texture_free(&device_textures[i]);
}
log_message(LOG_INFO, "Texture done");
2024-10-10 20:11:57 +08:00
destroy();
log_message(LOG_INFO, "Done");
2024-10-10 20:11:57 +08:00
return 0;
}
int handle_input(SDL_Event* event, state* state)
2024-10-10 20:11:57 +08:00
{
SDL_Keycode keysym = event->key.keysym.sym;
switch (state->application_state) {
2024-10-10 20:11:57 +08:00
case SELECTING_DEVICE:
switch (keysym) {
case SDLK_y:
case SDLK_RETURN:
if (state->device_index == -1 || !state->audio.recording_device_id) break;
audio_buffer_reset(&state->audio);
state->audio.recording_buffer_position = 0;
audio_playback_pause(&state->audio, 1, 0);
2024-10-10 20:11:57 +08:00
state->application_state = RECORDING;
2024-10-17 03:16:41 +08:00
state->update_ui = 1;
2024-10-10 20:11:57 +08:00
break;
case SDLK_q:
2024-10-10 20:11:57 +08:00
case SDLK_ESCAPE:
return 0;
2024-10-10 20:11:57 +08:00
break;
}
int device_index_new = select_device(&event->key, num_devices);
if (device_index_new == -1) break;
if (device_index_new == state->device_index) break;
2024-10-10 20:11:57 +08:00
state->device_index = device_index_new;
SDL_CloseAudioDevice(state->audio.recording_device_id);
audio_recording_init(&state->audio, state->device_index);
2024-10-17 03:16:41 +08:00
state->update_ui = 1;
2024-10-10 20:53:49 +08:00
2024-10-10 20:11:57 +08:00
break;
case RECORDING:
switch (keysym) {
case SDLK_q:
case SDLK_ESCAPE:
audio_playback_pause(&state->audio, 1, 1);
2024-10-10 20:11:57 +08:00
state->application_state = SELECTING_DEVICE;
2024-10-17 03:16:41 +08:00
state->update_ui = 1;
2024-10-10 20:11:57 +08:00
break;
}
break;
2024-10-10 20:11:57 +08:00
case RECORDED:
switch (keysym) {
case SDLK_y:
case SDLK_RETURN:
if (!state->audio.playback_device_id)
audio_playback_init(&state->audio);
state->audio.recording_buffer_position = 0;
audio_playback_pause(&state->audio, 0, 0);
2024-10-10 20:11:57 +08:00
state->application_state = PLAYBACK;
2024-10-17 03:16:41 +08:00
state->update_ui = 1;
2024-10-10 20:11:57 +08:00
break;
case SDLK_q:
case SDLK_ESCAPE:
state->application_state = SELECTING_DEVICE;
2024-10-17 03:16:41 +08:00
state->update_ui = 1;
2024-10-10 20:11:57 +08:00
break;
}
break;
case PLAYBACK:
switch (keysym) {
case SDLK_q:
case SDLK_ESCAPE:
audio_playback_pause(&state->audio, 0, 1);
2024-10-10 20:11:57 +08:00
state->application_state = RECORDED;
2024-10-17 03:16:41 +08:00
state->update_ui = 1;
2024-10-10 20:11:57 +08:00
break;
}
break;
}
return 1;
2024-10-10 20:11:57 +08:00
}
void handle_task(state* state)
{
switch (state->application_state) {
case SELECTING_DEVICE:
break;
case RECORDING:
2024-10-17 03:16:41 +08:00
if (state->audio.recording_buffer_position >= state->audio.recording_buffer_size) {
2024-10-10 20:11:57 +08:00
state->application_state = RECORDED;
2024-10-17 03:16:41 +08:00
state->update_ui = 1;
}
2024-10-10 20:11:57 +08:00
break;
case RECORDED:
break;
case PLAYBACK:
2024-10-17 03:16:41 +08:00
if (state->audio.recording_buffer_position >= state->audio.recording_buffer_size) {
2024-10-10 20:11:57 +08:00
state->application_state = RECORDED;
2024-10-17 03:16:41 +08:00
state->update_ui = 1;
}
2024-10-10 20:11:57 +08:00
break;
}
}
int init()
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
log_message(LOG_ERROR, "SDL could not initialize! SDL Error: %s", SDL_GetError());
2024-10-10 20:11:57 +08:00
return 0;
}
if (TTF_Init() == -1) {
log_message(LOG_ERROR, "TTF could not initialize! TTF Error: %s", TTF_GetError());
2024-10-10 20:11:57 +08:00
return 0;
}
window = SDL_CreateWindow("SDL record", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!window) {
log_message(LOG_ERROR, "Window could not be created! SDL Error: %s", SDL_GetError());
2024-10-10 20:11:57 +08:00
return 0;
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!renderer) {
log_message(LOG_ERROR, "Renderer could not be created! SDL_Error: %s", SDL_GetError());
2024-10-10 20:11:57 +08:00
return 0;
}
return 1;
}
void destroy()
{
SDL_DestroyRenderer(renderer);
log_message(LOG_INFO, "Renderer done");
2024-10-10 20:11:57 +08:00
SDL_DestroyWindow(window);
log_message(LOG_INFO, "Window done");
2024-10-10 20:11:57 +08:00
TTF_Quit();
log_message(LOG_INFO, "TTF done");
2024-10-10 20:11:57 +08:00
SDL_Quit();
}
int select_device(SDL_KeyboardEvent* key_event, int max_index)
{
SDL_Keycode keycode = key_event->keysym.sym;
/*SDL_Keycode mod = key_event->keysym.mod;*/
2024-10-10 20:11:57 +08:00
int keycode_is_num = (keycode >= SDLK_0 && keycode <= SDLK_9);
/*int no_mod = (mod == 0);*/
/*if (!keycode_is_num || !no_mod) {*/
if (!keycode_is_num) {
log_message(LOG_INFO, "Not number: %c <%u>", keycode, keycode);
2024-10-10 20:11:57 +08:00
return -1;
} else {
log_message(LOG_INFO, "Is number: %c <%u>", keycode, keycode);
2024-10-10 20:11:57 +08:00
}
int index = keycode - SDLK_0;
if (index >= max_index) return -1;
return index;
}
void application_state_to_string(application_state state, char* string)
{
switch (state) {
case SELECTING_DEVICE:
sprintf(string, "Selecting Device [0-9] -> [y/RET], ");
break;
case RECORDING:
2024-10-13 23:03:27 +08:00
sprintf(string, "Recording [ESC]: CANCEL");
2024-10-10 20:11:57 +08:00
break;
case RECORDED:
2024-10-13 23:03:27 +08:00
sprintf(string, "Recorded [y/RET]: PLAY | [ESC]: SELECT");
2024-10-10 20:11:57 +08:00
break;
case PLAYBACK:
2024-10-13 23:03:27 +08:00
sprintf(string, "Playing [ESC]: CANCEL");
2024-10-10 20:11:57 +08:00
break;
}
}