commit 7cc2f28241b5f9f21ee7c7d376acd340be75e705 Author: Sheldon Lee Date: Thu Oct 10 20:11:57 2024 +0800 Initial commit diff --git a/.astylerc b/.astylerc new file mode 100644 index 0000000..528f93c --- /dev/null +++ b/.astylerc @@ -0,0 +1,3 @@ +style=linux +indent=tab +align-pointer=type diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f7fcb2a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.c] +indent_style = tab +indent_size = 8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05d8217 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +obj/ +main +*.out + +logfile.txt diff --git a/FiraCode-Regular.ttf b/FiraCode-Regular.ttf new file mode 100644 index 0000000..bd73685 Binary files /dev/null and b/FiraCode-Regular.ttf differ diff --git a/example-test-code/main.c.bak b/example-test-code/main.c.bak new file mode 100644 index 0000000..f4c2ba0 --- /dev/null +++ b/example-test-code/main.c.bak @@ -0,0 +1,517 @@ +#include +#include +#include +#include +#include + +#include "log.h" + +const int SCREEN_WIDTH = 640; +const int SCREEN_HEIGHT = 480; + +const int MAX_RECORDING_DEVICES = 10; +const int MAX_RECORDING_SECONDS = 1; +const int RECORDING_BUFFER_SECONDS = MAX_RECORDING_SECONDS + 1; +/* + * Application + */ +int init(); +void destroy(); +int select_device(SDL_KeyboardEvent* key_event, int max_index); + +typedef enum { + SELECTING_DEVICE, + RECORDING, + RECORDED, + PLAYBACK, +} application_state; + +typedef struct { + application_state application_state; + int device_index; + int recording_device_id; + int playback_device_id; +} state; + +void application_state_to_string(application_state state, char* string); +/* + * Visual + */ +SDL_Window* window = NULL; +SDL_Renderer* renderer = NULL; +TTF_Font* global_font = NULL; + +typedef struct { + SDL_Texture* texture; + int width; + int height; +} text_texture; + +int text_texture_init(text_texture* text_texture); +int text_texture_load(text_texture* text_texture, char* string); +int text_texture_render(text_texture* text_texture, int x, int y); +int text_texture_free(text_texture* text_texture); +/* + * Audio + */ +int num_devices = 0; + +SDL_AudioSpec* recording_spec; +SDL_AudioSpec* playback_spec; +SDL_AudioSpec* received_recording_spec; +SDL_AudioSpec* received_playback_spec; +Uint8* recording_buffer = NULL; +unsigned int recording_buffer_size = 0; +unsigned int recording_buffer_position = 0; +unsigned int recording_buffer_position_max = 0; + +SDL_AudioDeviceID audio_recording_init(int index, void* userdata); +SDL_AudioDeviceID audio_playback_init(void* userdata); +void audio_buffer_destroy(); +void audio_recording_callback( void* userdata, Uint8* stream, int len ); +void audio_playback_callback( void* userdata, Uint8* stream, int len ); + +void handle_input(SDL_Event* event, state* state); +void handle_task(state* state); +int main(int argc, char* argv[]) +{ + if (!init()) return 1; + + num_devices = SDL_GetNumAudioDevices(1); + printf("Num audio devices: %i\n", num_devices); + char char_buffer[64]; + char default_fmt[] = "Device selected: %i"; + + state state = { + SELECTING_DEVICE, + -1, + 0, + 0, + }; + + text_texture display_text_texture; + text_texture_init(&display_text_texture); + 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]); + 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); + application_state_to_string(state.application_state, char_buffer); + text_texture_load(&state_text_texture, char_buffer); + + SDL_Event event; + int running = 1; + while (running) { + while (SDL_PollEvent(&event)) { + SDL_Keycode keysym = event.key.keysym.sym; + switch (event.type) { + + case SDL_QUIT: + running = 0; + break; + + case SDL_KEYDOWN: + handle_input(&event, &state); + + 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); + break; + + } + } + + handle_task(&state); + + // 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); + } + + text_texture_free(&display_text_texture); + destroy(); + + return 0; +} + +void handle_input(SDL_Event* event, state* state) +{ + SDL_Keycode keysym = event->key.keysym.sym; + switch (state->application_state) { + + case SELECTING_DEVICE: + switch (keysym) { + case SDLK_y: + case SDLK_RETURN: + if (state->device_index == -1 || !state->recording_device_id) break; + recording_buffer_position = 0; + SDL_PauseAudioDevice(state->recording_device_id, 0); + /*SDL_UnlockAudioDevice(state->recording_device_id);*/ + state->application_state = RECORDING; + break; + case SDLK_ESCAPE: + state->device_index = -1; + SDL_CloseAudioDevice(state->recording_device_id); + break; + } + int device_index_new = select_device(&event->key, num_devices); + if (device_index_new == -1) break; + if (state->device_index != -1 && device_index_new == -1) break; + state->device_index = device_index_new; + SDL_CloseAudioDevice(state->recording_device_id); + state->recording_device_id = audio_recording_init(state->device_index, NULL); + break; + + case RECORDING: + switch (keysym) { + case SDLK_y: + SDL_PauseAudioDevice(state->recording_device_id, 0); + break; + case SDLK_q: + case SDLK_ESCAPE: + SDL_PauseAudioDevice(state->recording_device_id, 1); + audio_buffer_destroy(); + state->application_state = SELECTING_DEVICE; + break; + } + break; + + case RECORDED: + switch (keysym) { + case SDLK_y: + case SDLK_RETURN: + if (!state->playback_device_id) { + state->playback_device_id = audio_playback_init(NULL); + break; + } + recording_buffer_position = 0; + SDL_PauseAudioDevice(state->playback_device_id, 0); + /*SDL_UnlockAudioDevice(state->playback_device_id);*/ + state->application_state = PLAYBACK; + break; + case SDLK_q: + case SDLK_ESCAPE: + audio_buffer_destroy(); + state->application_state = SELECTING_DEVICE; + break; + } + break; + + case PLAYBACK: + switch (keysym) { + case SDLK_q: + case SDLK_ESCAPE: + state->application_state = RECORDED; + break; + } + break; + + } +} + +void handle_task(state* state) +{ + switch (state->application_state) { + + case SELECTING_DEVICE: + break; + + case RECORDING: + SDL_LockAudioDevice(state->recording_device_id); + /*printf("buf pos: %u\n", recording_buffer_position);*/ + if (recording_buffer_position >= recording_buffer_position_max) { + SDL_UnlockAudioDevice(state->recording_device_id); + SDL_PauseAudioDevice(state->recording_device_id, 1); + SDL_CloseAudioDevice(state->recording_device_id); + state->application_state = RECORDED; + break; + } + SDL_UnlockAudioDevice(state->recording_device_id); + break; + + case RECORDED: + break; + + case PLAYBACK: + SDL_LockAudioDevice(state->playback_device_id); + if (recording_buffer_position >= recording_buffer_position_max) { + SDL_UnlockAudioDevice(state->playback_device_id); + SDL_PauseAudioDevice(state->playback_device_id, 1); + SDL_CloseAudioDevice(state->playback_device_id); + state->application_state = RECORDED; + break; + } + SDL_UnlockAudioDevice(state->playback_device_id); + break; + + } +} + +int init() +{ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError()); + return 0; + } + + if (TTF_Init() == -1) { + printf("TTF could not initialize! TTF Error: %s\n", TTF_GetError()); + return 0; + } + + window = SDL_CreateWindow("SDL record", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, + SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + + if (!window) { + printf("Window could not be created! SDL Error: %s\n", SDL_GetError()); + return 0; + } + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) { + printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); + return 0; + } + + global_font = TTF_OpenFont("FiraCode-Regular.ttf", 24); + if (!global_font) { + printf("Failed to load font! TTF_Error: %s\n", TTF_GetError()); + return 0; + } + + recording_spec = malloc(sizeof(SDL_AudioSpec)); + received_recording_spec = malloc(sizeof(SDL_AudioSpec)); + playback_spec = malloc(sizeof(SDL_AudioSpec)); + received_playback_spec = malloc(sizeof(SDL_AudioSpec)); + return 1; +} + +void destroy() +{ + free(recording_spec); + free(received_recording_spec); + free(playback_spec); + free(received_playback_spec); + free(recording_buffer); + + TTF_CloseFont(global_font); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + TTF_Quit(); + 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) { + printf("Not number: %c <%u>\n", keycode, keycode); + return -1; + } else { + printf("Is number: %c <%u>\n", 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 SELECTING_DEVICE: + sprintf(string, "Selecting Device [0-9] -> [y/RET], "); + break; + case RECORDING: + sprintf(string, "Recording"); + break; + case RECORDED: + sprintf(string, "Recorded"); + break; + case PLAYBACK: + sprintf(string, "Playing"); + break; + } +} + +int text_texture_init(text_texture* text_texture) +{ + text_texture->texture = NULL; + text_texture->width = 0; + text_texture->height = 0; + return 1; +} + +int text_texture_load(text_texture* text_texture, char* string) +{ + if (text_texture->texture) { + SDL_DestroyTexture(text_texture->texture); + text_texture->texture = NULL; + } + + SDL_Color text_color = { 255, 255, 255 }; + SDL_Surface* text_surface = TTF_RenderText_Solid(global_font, string, text_color); + if (!text_surface) { + printf("Unable to render text surface! TTF_Error: %s\n", TTF_GetError()); + return 0; + } + + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, text_surface); + if (!texture) { + printf("Unable to create texture from rendered text! SDL_Error: %s\n", SDL_GetError()); + return 0; + } + + text_texture->texture = SDL_CreateTextureFromSurface(renderer, text_surface); + text_texture->width = text_surface->w; + text_texture->height = text_surface->h; + + SDL_FreeSurface(text_surface); + return 1; +} + +int text_texture_render(text_texture* text_texture, int x, int y) +{ + if (!text_texture) { + printf("text_texture uninitialized"); + return 0; + } + if (!text_texture->texture) { + printf("text_texture->texture uninitialized"); + return 0; + } + + int quad_width = text_texture->width; + int quad_height = text_texture->height; + SDL_Rect render_quad = { x, y, quad_width, quad_height }; + SDL_RenderCopy(renderer, text_texture->texture, NULL, &render_quad); + + return 1; +} + +int text_texture_free(text_texture* text_texture) +{ + if (!text_texture->texture) { + return 0; + } + SDL_DestroyTexture(text_texture->texture); + return 1; +} + +SDL_AudioDeviceID audio_recording_init(int index, void* userdata) +{ + memset(recording_spec, 0, sizeof(SDL_AudioSpec)); + memset(received_recording_spec, 0, sizeof(SDL_AudioSpec)); + recording_spec->freq = 44100; + recording_spec->format = AUDIO_F32; + recording_spec->channels = 2; + recording_spec->samples = 4096; + recording_spec->callback = audio_recording_callback; + recording_spec->userdata = userdata; + + SDL_AudioDeviceID device_id = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(index, 1), 1, recording_spec, received_recording_spec, SDL_AUDIO_ALLOW_FORMAT_CHANGE); + printf("Recording Device: %s\n", SDL_GetAudioDeviceName(index, 1)); + if (!device_id) { + printf("Failed to open recording device! SDL Error: %s", SDL_GetError()); + return 0; + } + + unsigned int bytes_per_sample = received_recording_spec->channels * (SDL_AUDIO_BITSIZE(received_recording_spec->format)/8); + unsigned int bytes_per_second = received_recording_spec->freq * bytes_per_sample; + recording_buffer_size = RECORDING_BUFFER_SECONDS * bytes_per_second; + recording_buffer_position_max = MAX_RECORDING_SECONDS * bytes_per_second; + + recording_buffer = malloc(recording_buffer_size); + memset(recording_buffer, 0, recording_buffer_size); + + return device_id; +} + +SDL_AudioDeviceID audio_playback_init(void* userdata) +{ + if (!recording_buffer) { + printf("Audio buffer not initialized"); + return 0; + } + + memset(playback_spec, 0, sizeof(SDL_AudioSpec)); + memset(received_playback_spec, 0, sizeof(SDL_AudioSpec)); + playback_spec->freq = 44100; + playback_spec->format = AUDIO_F32; + playback_spec->channels = 2; + playback_spec->samples = 4096; + playback_spec->callback = audio_playback_callback; + playback_spec->callback = userdata; + + SDL_AudioDeviceID device_id = SDL_OpenAudioDevice(NULL, 0, playback_spec, received_playback_spec, SDL_AUDIO_ALLOW_FORMAT_CHANGE); + if (!device_id) { + printf("Failed to open playback device! SDL Error: %s", SDL_GetError()); + return 0; + } + + return device_id; +} + +int audio_device_stop(int device_id) +{ + SDL_PauseAudioDevice(device_id, 1); + SDL_CloseAudioDevice(device_id); + return 1; +} + +void audio_buffer_destroy() +{ + free(recording_buffer); + recording_buffer = NULL; + recording_buffer_size = 0; + recording_buffer_position = 0; +} + +void audio_recording_callback(void* userdata, Uint8* stream, int len) +{ + memcpy(&recording_buffer[recording_buffer_position], stream, len); + recording_buffer_position += len; + char string[64]; + sprintf(string, "Record pos: %u %u", recording_buffer_position, len); + log_message(LOG_INFO, string); +} + +void audio_playback_callback(void* userdata, Uint8* stream, int len) +{ + memcpy(stream, &recording_buffer[recording_buffer_position], len); + recording_buffer_position += len; + char string[64]; + sprintf(string, "Playback pos: %u %u", recording_buffer_position, len); + log_message(LOG_INFO, string); +} diff --git a/example-test-code/main.c.bak1 b/example-test-code/main.c.bak1 new file mode 100644 index 0000000..14b10a3 --- /dev/null +++ b/example-test-code/main.c.bak1 @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include "log.h" + +SDL_AudioSpec* desiredSpec; +void audio_callback(void* userdata, Uint8* stream, int len) +{ + // Fill the audio buffer with data + char string[64]; + sprintf(string, "stream: %u len: %u\n", stream, len); + printf(string, "stream: %u len: %u\n", stream, len); + log_message(LOG_INFO, string); +} + +int main(int argc, char* argv[]) +{ + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + // Handle error + } + + desiredSpec = malloc(sizeof(SDL_AudioSpec)); + memset(desiredSpec, 0, sizeof(SDL_AudioSpec)); + desiredSpec->freq = 44100; // Sample rate + desiredSpec->format = AUDIO_F32; // Audio format + desiredSpec->channels = 2; + desiredSpec->samples = 4096; // Buffer size + desiredSpec->callback = audio_callback; // Set the callback + + SDL_AudioDeviceID audioDevice = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(2, 1), 1, desiredSpec, NULL, SDL_AUDIO_ALLOW_FORMAT_CHANGE); + if (SDL_GetError()[0] != '\0') { + printf("SDL Error : %s\n", SDL_GetError()); + } + printf("Name: %s\n", SDL_GetAudioDeviceName(2, 1)); + if (audioDevice == 0) { + // Handle error + } + + // Start audio playback + SDL_PauseAudioDevice(audioDevice, 0); + if (SDL_GetError()[0] != '\0') { + printf("SDL Error : %s\n", SDL_GetError()); + } + + SDL_Event event; + int running = 1; + while (running) { + while (SDL_PollEvent(&event)) { + SDL_Keycode keysym = event.key.keysym.sym; + switch (event.type) { + case SDL_QUIT: + running = 0; + break; + } + } + } + + SDL_CloseAudioDevice(audioDevice); + SDL_Quit(); + return 0; +} + diff --git a/example-test-code/main.c.bak2 b/example-test-code/main.c.bak2 new file mode 100644 index 0000000..90a8bbb --- /dev/null +++ b/example-test-code/main.c.bak2 @@ -0,0 +1,98 @@ +#include +#include +#include + +typedef struct { + Uint8* data; + int current_pos; + int max_length; +} BufferType; + +SDL_AudioDeviceID dev; + +void audio_callback(void* userdata, Uint8* stream, int len) +{ + BufferType* buffer = (BufferType*)userdata; + + printf("callback: %u %u\n", *stream, len); + int space_left = buffer->max_length - buffer->current_pos; + if (space_left <= 0) { + // Buffer is full; stop the device + SDL_PauseAudioDevice(dev, 1); + printf("Buffer full, stopping recording.\n"); + return; + } + + if (len > space_left) { + len = space_left; + } + + memcpy(buffer->data + buffer->current_pos, stream, len); + buffer->current_pos += len; +} + +int main(int argc, char* argv[]) +{ + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + BufferType audio_buffer; + audio_buffer.max_length = 44100 * 5 * sizeof(float) * 2; // 5 seconds of stereo float audio + audio_buffer.data = (Uint8*)malloc(audio_buffer.max_length); + if (audio_buffer.data == NULL) { + fprintf(stderr, "Failed to allocate audio buffer\n"); + SDL_Quit(); + return 1; + } + audio_buffer.current_pos = 0; + + SDL_AudioSpec desired_spec, obtained_spec; + SDL_zero(desired_spec); + desired_spec.freq = 44100; + desired_spec.format = AUDIO_F32SYS; + desired_spec.channels = 2; + desired_spec.samples = 512; + desired_spec.callback = audio_callback; + desired_spec.userdata = &audio_buffer; + + dev = SDL_OpenAudioDevice(NULL, 1, &desired_spec, &obtained_spec, 0); + if (dev == 0) { + fprintf(stderr, "Failed to open audio device: %s\n", SDL_GetError()); + free(audio_buffer.data); + SDL_Quit(); + return 1; + } + + printf("Audio device opened successfully.\n"); + printf("Obtained Spec:\n"); + printf(" Frequency: %d\n", obtained_spec.freq); + printf(" Format: 0x%X\n", obtained_spec.format); + printf(" Channels: %d\n", obtained_spec.channels); + printf(" Samples: %d\n", obtained_spec.samples); + + SDL_PauseAudioDevice(dev, 0); // Start recording + printf("Recording started.\n"); + + SDL_Event event; + while (audio_buffer.current_pos < audio_buffer.max_length) { + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + printf("Quit event received.\n"); + goto cleanup; + } + } + SDL_Delay(100); + } + + printf("Recording complete. Total bytes recorded: %d\n", audio_buffer.current_pos); + +cleanup: + SDL_PauseAudioDevice(dev, 1); // Stop recording + SDL_CloseAudioDevice(dev); + free(audio_buffer.data); + SDL_Quit(); + + return 0; +} diff --git a/example-test-code/temp.cpp b/example-test-code/temp.cpp new file mode 100644 index 0000000..170c812 --- /dev/null +++ b/example-test-code/temp.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include + +using namespace std; + +SDL_AudioSpec want, have; +SDL_AudioDeviceID player; + +void SDLCALL callback_player( void* userdata, Uint8* stream, int len) +{ + + for (int i = 0; i < len; i ++) { + auto value = Uint8(sin(i) * 1000); + stream[i] = value ; + } +} + +auto setupAudioPlayer() +{ + + printf("### Device Player ###\n"); + int num = SDL_GetNumAudioDevices(false); + printf("num: %d\n", num); + for (int i = 0; i < num; i++) { + auto name = SDL_GetAudioDeviceName(i, false); + printf("%d: %s\n", i, name); + } + + int deviceIndex; + printf("select device to player audio: "); + scanf("%d", &deviceIndex); + printf("\n"); + + SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */ + want.freq = 44100; + want.format = AUDIO_F32; + want.channels = 2; + want.samples = 1024; + want.callback = callback_player; // you wrote this function elsewhere. + //want.size = 512; + + SDL_AudioDeviceID player = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(deviceIndex, false), SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE); + if (player == 0) { + printf("Failed to open player device! SDL Error: %s", SDL_GetError()); + + } else { + + printf("freq %d \nformat %d \nchannels %d \nsamples %d\n", have.freq, have.format, have.channels, have.samples); + } + + return player; +} + +void destroyAudio() +{ + + SDL_CloseAudioDevice(player); + SDL_Quit(); +} + +int main(int n, char** args) +{ + + int err = SDL_Init(SDL_INIT_AUDIO); + printf("error: %d", err); + if (err < 0) { + printf("erorr: %d\n", err); + printf("Failed to init audio! SDL Error: %s", SDL_GetError()); + } + //recorder = setupAudioRecorder(); + auto player = setupAudioPlayer(); + printf("player: %d\n", player); + //loadWave(); + + printf("playing\n"); + //SDL_PauseAudioDevice(recorder, 0); + SDL_PauseAudioDevice(player, 0); + //SDL_SemWait(sem); + SDL_Delay(10000); + destroyAudio(); + //system("pause"); + return 0; +} diff --git a/log.c b/log.c new file mode 100644 index 0000000..6312a28 --- /dev/null +++ b/log.c @@ -0,0 +1,29 @@ +// Chat GPT code +#include "log.h" + +const char* get_current_time() +{ + static char buffer[20]; + time_t now = time(NULL); + struct tm* tm_info = localtime(&now); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info); + return buffer; +} + +void log_message(LogLevel level, const char* message) +{ + const char* level_strings[] = { "INFO", "WARNING", "ERROR" }; + + // Open the log file in append mode + FILE* log_file = fopen("logfile.txt", "a"); + if (log_file == NULL) { + fprintf(stderr, "Could not open log file for writing.\n"); + return; + } + + // Write the log message to the file + fprintf(log_file, "[%s] [%s] %s\n", get_current_time(), level_strings[level], message); + + // Close the log file + fclose(log_file); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..f8dad15 --- /dev/null +++ b/log.h @@ -0,0 +1,14 @@ +// Chat GPT code +#include +#include +#include + +typedef enum { + LOG_INFO, + LOG_WARNING, + LOG_ERROR +} LogLevel; + +const char* get_current_time(); + +void log_message(LogLevel level, const char* message); diff --git a/main.c b/main.c new file mode 100644 index 0000000..e025d94 --- /dev/null +++ b/main.c @@ -0,0 +1,510 @@ +#include +#include +#include +#include +#include + +#include "log.h" + +const int SCREEN_WIDTH = 640; +const int SCREEN_HEIGHT = 480; + +const int MAX_RECORDING_DEVICES = 10; +const int MAX_RECORDING_SECONDS = 1; +const int RECORDING_BUFFER_SECONDS = MAX_RECORDING_SECONDS + 1; +/* + * Application + */ +int init(); +void destroy(); +int select_device(SDL_KeyboardEvent* key_event, int max_index); + +typedef enum { + SELECTING_DEVICE, + RECORDING, + RECORDED, + PLAYBACK, +} application_state; + +typedef struct { + application_state application_state; + int device_index; + int recording_device_id; + int playback_device_id; +} state; + +void application_state_to_string(application_state state, char* string); +/* + * Visual + */ +SDL_Window* window = NULL; +SDL_Renderer* renderer = NULL; +TTF_Font* global_font = NULL; + +typedef struct { + SDL_Texture* texture; + int width; + int height; +} text_texture; + +int text_texture_init(text_texture* text_texture); +int text_texture_load(text_texture* text_texture, char* string); +int text_texture_render(text_texture* text_texture, int x, int y); +int text_texture_free(text_texture* text_texture); +/* + * Audio + */ +int num_devices = 0; + +SDL_AudioSpec recording_spec; +SDL_AudioSpec playback_spec; +SDL_AudioSpec received_recording_spec; +SDL_AudioSpec received_playback_spec; +Uint8* recording_buffer = NULL; +unsigned int recording_buffer_size = 0; +unsigned int recording_buffer_position = 0; +unsigned int recording_buffer_position_max = 0; + +SDL_AudioDeviceID audio_recording_init(int index, void* userdata); +SDL_AudioDeviceID audio_playback_init(void* userdata); +void audio_buffer_destroy(); +void audio_recording_callback( void* userdata, Uint8* stream, int len ); +void audio_playback_callback( void* userdata, Uint8* stream, int len ); + +void handle_input(SDL_Event* event, state* state); +void handle_task(state* state); +int main(int argc, char* argv[]) +{ + if (!init()) return 1; + + num_devices = SDL_GetNumAudioDevices(1); + printf("Num audio devices: %i\n", num_devices); + char char_buffer[64]; + char default_fmt[] = "Device selected: %i"; + + state state = { + SELECTING_DEVICE, + -1, + 0, + 0, + }; + + text_texture display_text_texture; + text_texture_init(&display_text_texture); + 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]); + 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); + application_state_to_string(state.application_state, char_buffer); + text_texture_load(&state_text_texture, char_buffer); + + SDL_Event event; + int running = 1; + while (running) { + while (SDL_PollEvent(&event)) { + SDL_Keycode keysym = event.key.keysym.sym; + switch (event.type) { + + case SDL_QUIT: + running = 0; + break; + + case SDL_KEYDOWN: + handle_input(&event, &state); + break; + + } + } + + handle_task(&state); + + 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); + + // 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); + } + + text_texture_free(&display_text_texture); + for (int i = 0; i < num_devices; i++) { + text_texture_free(&device_textures[i]); + } + destroy(); + + return 0; +} + +void handle_input(SDL_Event* event, state* state) +{ + SDL_Keycode keysym = event->key.keysym.sym; + switch (state->application_state) { + + case SELECTING_DEVICE: + switch (keysym) { + case SDLK_y: + case SDLK_RETURN: + SDL_CloseAudioDevice(state->recording_device_id); + state->recording_device_id = audio_recording_init(state->device_index, NULL); + if (state->device_index == -1 || !state->recording_device_id) break; + recording_buffer_position = 0; + SDL_PauseAudioDevice(state->recording_device_id, 0); + /*SDL_UnlockAudioDevice(state->recording_device_id);*/ + state->application_state = RECORDING; + break; + case SDLK_ESCAPE: + state->device_index = -1; + SDL_CloseAudioDevice(state->recording_device_id); + break; + } + int device_index_new = select_device(&event->key, num_devices); + if (device_index_new == -1) break; + if (state->device_index != -1 && device_index_new == -1) break; + state->device_index = device_index_new; + break; + + case RECORDING: + switch (keysym) { + case SDLK_y: + SDL_PauseAudioDevice(state->recording_device_id, 0); + break; + case SDLK_q: + case SDLK_ESCAPE: + SDL_PauseAudioDevice(state->recording_device_id, 1); + audio_buffer_destroy(); + state->application_state = SELECTING_DEVICE; + break; + } + break; + + case RECORDED: + switch (keysym) { + case SDLK_y: + case SDLK_RETURN: + SDL_CloseAudioDevice(state->playback_device_id); + state->playback_device_id = audio_playback_init(NULL); + recording_buffer_position = 0; + SDL_PauseAudioDevice(state->playback_device_id, 0); + /*SDL_UnlockAudioDevice(state->playback_device_id);*/ + state->application_state = PLAYBACK; + break; + case SDLK_q: + case SDLK_ESCAPE: + audio_buffer_destroy(); + state->application_state = SELECTING_DEVICE; + break; + } + break; + + case PLAYBACK: + switch (keysym) { + case SDLK_q: + case SDLK_ESCAPE: + state->application_state = RECORDED; + break; + } + break; + + } +} + +void handle_task(state* state) +{ + switch (state->application_state) { + + case SELECTING_DEVICE: + break; + + case RECORDING: + SDL_LockAudioDevice(state->recording_device_id); + /*printf("buf pos: %u\n", recording_buffer_position);*/ + if (recording_buffer_position >= recording_buffer_position_max) { + SDL_UnlockAudioDevice(state->recording_device_id); + SDL_PauseAudioDevice(state->recording_device_id, 1); + SDL_CloseAudioDevice(state->recording_device_id); + state->application_state = RECORDED; + break; + } + SDL_UnlockAudioDevice(state->recording_device_id); + break; + + case RECORDED: + break; + + case PLAYBACK: + SDL_LockAudioDevice(state->playback_device_id); + if (recording_buffer_position >= recording_buffer_position_max) { + SDL_UnlockAudioDevice(state->playback_device_id); + SDL_PauseAudioDevice(state->playback_device_id, 1); + SDL_CloseAudioDevice(state->playback_device_id); + state->application_state = RECORDED; + break; + } + SDL_UnlockAudioDevice(state->playback_device_id); + break; + + } +} + +int init() +{ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError()); + return 0; + } + + if (TTF_Init() == -1) { + printf("TTF could not initialize! TTF Error: %s\n", TTF_GetError()); + return 0; + } + + window = SDL_CreateWindow("SDL record", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, + SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + + if (!window) { + printf("Window could not be created! SDL Error: %s\n", SDL_GetError()); + return 0; + } + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) { + printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); + return 0; + } + + global_font = TTF_OpenFont("FiraCode-Regular.ttf", 24); + if (!global_font) { + printf("Failed to load font! TTF_Error: %s\n", TTF_GetError()); + return 0; + } + + return 1; +} + +void destroy() +{ + free(recording_buffer); + + TTF_CloseFont(global_font); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + TTF_Quit(); + 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) { + printf("Not number: %c <%u>\n", keycode, keycode); + return -1; + } else { + printf("Is number: %c <%u>\n", 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 SELECTING_DEVICE: + sprintf(string, "Selecting Device [0-9] -> [y/RET], "); + break; + case RECORDING: + sprintf(string, "Recording"); + break; + case RECORDED: + sprintf(string, "Recorded"); + break; + case PLAYBACK: + sprintf(string, "Playing"); + break; + } +} + +int text_texture_init(text_texture* text_texture) +{ + text_texture->texture = NULL; + text_texture->width = 0; + text_texture->height = 0; + return 1; +} + +int text_texture_load(text_texture* text_texture, char* string) +{ + if (text_texture->texture) { + SDL_DestroyTexture(text_texture->texture); + text_texture->texture = NULL; + } + + SDL_Color text_color = { 255, 255, 255 }; + SDL_Surface* text_surface = TTF_RenderText_Solid(global_font, string, text_color); + if (!text_surface) { + printf("Unable to render text surface! TTF_Error: %s\n", TTF_GetError()); + return 0; + } + + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, text_surface); + if (!texture) { + printf("Unable to create texture from rendered text! SDL_Error: %s\n", SDL_GetError()); + return 0; + } + + text_texture->texture = SDL_CreateTextureFromSurface(renderer, text_surface); + text_texture->width = text_surface->w; + text_texture->height = text_surface->h; + + SDL_FreeSurface(text_surface); + return 1; +} + +int text_texture_render(text_texture* text_texture, int x, int y) +{ + if (!text_texture) { + printf("text_texture uninitialized"); + return 0; + } + if (!text_texture->texture) { + printf("text_texture->texture uninitialized"); + return 0; + } + + int quad_width = text_texture->width; + int quad_height = text_texture->height; + SDL_Rect render_quad = { x, y, quad_width, quad_height }; + SDL_RenderCopy(renderer, text_texture->texture, NULL, &render_quad); + + return 1; +} + +int text_texture_free(text_texture* text_texture) +{ + if (!text_texture->texture) { + return 0; + } + SDL_DestroyTexture(text_texture->texture); + return 1; +} + +SDL_AudioDeviceID audio_recording_init(int index, void* userdata) +{ + SDL_zero(recording_spec); + recording_spec.freq = 44100; + recording_spec.format = AUDIO_F32; + recording_spec.channels = 2; + recording_spec.samples = 4096; + recording_spec.callback = audio_recording_callback; + recording_spec.userdata = userdata; + + SDL_AudioDeviceID device_id = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(index, 1), 1, &recording_spec, &received_recording_spec, SDL_AUDIO_ALLOW_FORMAT_CHANGE); + printf("Recording Device: %s\n", SDL_GetAudioDeviceName(index, 1)); + if (!device_id) { + printf("Failed to open recording device! SDL Error: %s", SDL_GetError()); + return 0; + } + + unsigned int bytes_per_sample = received_recording_spec.channels * (SDL_AUDIO_BITSIZE(received_recording_spec.format)/8); + unsigned int bytes_per_second = received_recording_spec.freq * bytes_per_sample; + recording_buffer_size = RECORDING_BUFFER_SECONDS * bytes_per_second; + recording_buffer_position_max = MAX_RECORDING_SECONDS * bytes_per_second; + + recording_buffer = malloc(recording_buffer_size); + memset(recording_buffer, 0, recording_buffer_size); + + return device_id; +} + +SDL_AudioDeviceID audio_playback_init(void* userdata) +{ + if (!recording_buffer) { + printf("Audio buffer not initialized"); + return 0; + } + + SDL_zero(playback_spec); + playback_spec.freq = 44100; + playback_spec.format = AUDIO_F32; + playback_spec.channels = 2; + playback_spec.samples = 4096; + playback_spec.callback = audio_playback_callback; + playback_spec.callback = userdata; + + SDL_AudioDeviceID device_id = SDL_OpenAudioDevice(NULL, 0, &playback_spec, &received_playback_spec, SDL_AUDIO_ALLOW_FORMAT_CHANGE); + if (!device_id) { + printf("Failed to open playback device! SDL Error: %s", SDL_GetError()); + return 0; + } + + return device_id; +} + +int audio_device_stop(int device_id) +{ + SDL_PauseAudioDevice(device_id, 1); + SDL_CloseAudioDevice(device_id); + return 1; +} + +void audio_buffer_destroy() +{ + free(recording_buffer); + recording_buffer = NULL; + recording_buffer_size = 0; + recording_buffer_position = 0; +} + +void audio_recording_callback(void* userdata, Uint8* stream, int len) +{ + memcpy(&recording_buffer[recording_buffer_position], stream, len); + recording_buffer_position += len; + char string[64]; + sprintf(string, "Record pos: %u %u", recording_buffer_position, len); + printf(string, "Record pos: %u %u\n", recording_buffer_position, len); + log_message(LOG_INFO, string); +} + +void audio_playback_callback(void* userdata, Uint8* stream, int len) +{ + memcpy(stream, &recording_buffer[recording_buffer_position], len); + recording_buffer_position += len; + char string[64]; + sprintf(string, "Playback pos: %u %u", recording_buffer_position, len); + printf(string, "Playback pos: %u %u", recording_buffer_position, len); + log_message(LOG_INFO, string); +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..2b66093 --- /dev/null +++ b/makefile @@ -0,0 +1,29 @@ +CC = gcc + +OBJD = obj +SRCS := $(wildcard *.c) +OBJS := $(SRCS:%.c=$(OBJD)/%.o) + +# Compiler flags +CCFLAGS = -g +LDFLAGS = -lSDL2 -lSDL2_image -lSDL2_ttf + +# Target executable name +TARGET = main + +# Build target +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(CCFLAGS) $^ -o $(TARGET) $(LDFLAGS) + +$(OBJS): $(OBJD)/%.o: %.c + mkdir -p $(@D) + $(CC) $(CCFLAGS) -c $? -o $@ + +clean: + rm -r $(TARGET) $(OBJD) + +run: $(TARGET) + ./$(TARGET) +