sdl-audio/main.c

397 lines
9.5 KiB
C

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include "texture.h"
#include "audio.h"
#include "net.h"
#include "log.h"
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define TEXT_LINES 20
#define BODY_TEXT_LINES TEXT_LINES-2
#define FONT_SIZE SCREEN_HEIGHT/TEXT_LINES
#define PORT 55555
typedef enum {
SOCKET_LISTEN,
SELECTING_DEVICE,
RECORDING,
RECORDED,
PLAYBACK,
} application_state;
typedef struct {
application_state application_state;
audio_state audio;
int device_index;
int update_ui;
sem_t sem;
} state;
int init();
void destroy();
int select_device(SDL_KeyboardEvent* key_event, int max_index);
void application_state_to_string(application_state state, char* string);
void* broadcast_t(void* args);
void* peer_discovery_t(void* args);
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
int num_devices = 0;
int handle_input(SDL_Event* event, state* state);
void handle_task(state* state);
void render_body_text(text_texture* textures, int len);
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);
char char_buffer[64];
char device_selected_fmt[] = "Device selected: %i";
state state;
state.application_state = SOCKET_LISTEN;
//state.application_state = SELECTING_DEVICE;
if (!audio_init(&state.audio)) return 1;
state.device_index = -1;
state.update_ui = 0;
sem_init(&state.sem, 0, 1);
text_texture display_text_texture;
text_texture_init(&display_text_texture, renderer, FONT_SIZE);
sprintf(char_buffer, device_selected_fmt, state.device_index);
text_texture_load(&display_text_texture, char_buffer);
text_texture state_text_texture;
text_texture_init(&state_text_texture, renderer, FONT_SIZE);
application_state_to_string(state.application_state, char_buffer);
text_texture_load(&state_text_texture, char_buffer);
text_texture_frame socket_frame;
text_texture_frame_init(&socket_frame, BODY_TEXT_LINES, renderer, FONT_SIZE);
text_texture_frame select_device_frame;
text_texture_frame_init(&select_device_frame, num_devices, renderer, FONT_SIZE);
for (int i = 0; i < select_device_frame.len; i++) {
sprintf(char_buffer, "%i: %s", i, SDL_GetAudioDeviceName(i, 1));
text_texture_load(&select_device_frame.textures[i], char_buffer);
}
pthread_t peer_discovery_thread;
pthread_create(&peer_discovery_thread, NULL, peer_discovery_t, &state);
pthread_t broadcast_thread;
pthread_create(&broadcast_thread, NULL, broadcast_t, &state);
SDL_Event event;
while (1) {
sem_wait(&state.sem);
while (SDL_PollEvent(&event)) {
SDL_Keycode keysym = event.key.keysym.sym;
switch (event.type) {
case SDL_QUIT:
goto cleanup;
break;
case SDL_KEYDOWN:
if (!handle_input(&event, &state)) goto cleanup;
break;
}
}
handle_task(&state);
if (state.update_ui) {
sprintf(char_buffer, device_selected_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);
int i = 0;
struct sockaddr_in* peer_addr;
while (net_get_peers(&peer_addr)) {
if (i >= socket_frame.len) break;;
char* ip = inet_ntoa(peer_addr->sin_addr);
int port = ntohs(peer_addr->sin_port);
sprintf(char_buffer, "%s:%i", ip, port);
text_texture_load(&socket_frame.textures[i], char_buffer);
i++;
}
for (; i < socket_frame.len; i++) text_texture_load(&socket_frame.textures[i], "");
state.update_ui = 0;
}
// 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 == SOCKET_LISTEN) {
text_texture_frame_render(&socket_frame, 0, FONT_SIZE);
}
if (state.application_state == SELECTING_DEVICE) {
text_texture_frame_render(&select_device_frame, 0, FONT_SIZE);
}
text_texture_render(&state_text_texture, 0, SCREEN_HEIGHT - state_text_texture.height);
// Update the screen
SDL_RenderPresent(renderer);
sem_post(&state.sem);
}
cleanup:
log_message(LOG_INFO, "Cleanup start");
audio_destroy(&state.audio);
log_message(LOG_INFO, "Audio done");
text_texture_destroy(&display_text_texture);
text_texture_destroy(&state_text_texture);
text_texture_frame_destroy(&socket_frame);
text_texture_frame_destroy(&select_device_frame);
log_message(LOG_INFO, "Texture done");
destroy();
log_message(LOG_INFO, "Done");
return 0;
}
int handle_input(SDL_Event* event, state* state)
{
SDL_Keycode keysym = event->key.keysym.sym;
switch (state->application_state) {
case SOCKET_LISTEN:
switch (keysym) {
case SDLK_1:
log_message(LOG_INFO, "Socket button 1 pressed");
net_broadcast();
break;
case SDLK_2:
log_message(LOG_INFO, "Socket button 2 pressed");
net_print_peers();
break;
}
break;
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_device_pause(&state->audio, 1, 0);
state->application_state = RECORDING;
state->update_ui = 1;
break;
case SDLK_q:
case SDLK_ESCAPE:
return 0;
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;
state->device_index = device_index_new;
audio_device_close(&state->audio, 1);
audio_recording_init(&state->audio, state->device_index);
state->update_ui = 1;
break;
case RECORDING:
switch (keysym) {
case SDLK_q:
case SDLK_ESCAPE:
audio_device_pause(&state->audio, 1, 1);
state->application_state = SELECTING_DEVICE;
state->update_ui = 1;
break;
}
break;
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_device_pause(&state->audio, 0, 0);
state->application_state = PLAYBACK;
state->update_ui = 1;
break;
case SDLK_q:
case SDLK_ESCAPE:
state->application_state = SELECTING_DEVICE;
state->update_ui = 1;
break;
}
break;
case PLAYBACK:
switch (keysym) {
case SDLK_q:
case SDLK_ESCAPE:
audio_device_pause(&state->audio, 0, 1);
state->application_state = RECORDED;
state->update_ui = 1;
break;
}
break;
}
return 1;
}
void handle_task(state* state)
{
switch (state->application_state) {
case SOCKET_LISTEN:
break;
case SELECTING_DEVICE:
break;
case RECORDING:
if (state->audio.recording_buffer_position >= state->audio.recording_buffer_size) {
state->application_state = RECORDED;
state->update_ui = 1;
}
break;
case RECORDED:
break;
case PLAYBACK:
if (state->audio.recording_buffer_position >= state->audio.recording_buffer_size) {
state->application_state = RECORDED;
state->update_ui = 1;
}
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());
return 0;
}
if (TTF_Init() == -1) {
log_message(LOG_ERROR, "TTF could not initialize! TTF Error: %s", TTF_GetError());
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());
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());
return 0;
}
net_init(PORT);
return 1;
}
void destroy()
{
SDL_DestroyRenderer(renderer);
log_message(LOG_INFO, "Renderer done");
SDL_DestroyWindow(window);
log_message(LOG_INFO, "Window done");
TTF_Quit();
log_message(LOG_INFO, "TTF done");
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;*/
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);
return -1;
} else {
log_message(LOG_INFO, "Is number: %c <%u>", keycode, keycode);
}
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 SOCKET_LISTEN:
sprintf(string, "Socket");
break;
case SELECTING_DEVICE:
sprintf(string, "Selecting Device [0-9] -> [y/RET]");
break;
case RECORDING:
sprintf(string, "Recording [ESC]: CANCEL");
break;
case RECORDED:
sprintf(string, "Recorded [y/RET]: PLAY | [ESC]: SELECT");
break;
case PLAYBACK:
sprintf(string, "Playing [ESC]: CANCEL");
break;
}
}
void* broadcast_t(void* args)
{
while (1) {
net_broadcast();
sleep(1);
}
}
void* peer_discovery_t(void* args)
{
state* state = args;
while (1) {
int added_client = net_listen_peers();
int pruned_client = net_prune_peers();
sem_wait(&state->sem);
state->update_ui = added_client || pruned_client;
sem_post(&state->sem);
}
}