diff options
| author | Peter Fors <peter.fors@mindkiller.com> | 2025-04-28 22:19:43 +0200 |
|---|---|---|
| committer | Peter Fors <peter.fors@mindkiller.com> | 2025-04-28 22:19:43 +0200 |
| commit | e08b851c79ae9a7fc0a2066e49110dc7fb426bce (patch) | |
| tree | c8458daee7201983903cf04413ff9a6072084028 /base/audio.c | |
| parent | c40f7421d8c1ccbe008dbd2191c6642625ae4b83 (diff) | |
reverted rewrite of ppu, optimized what functions should be forced inline, gained ~2.5% performance
Diffstat (limited to 'base/audio.c')
| -rw-r--r-- | base/audio.c | 458 |
1 files changed, 0 insertions, 458 deletions
diff --git a/base/audio.c b/base/audio.c deleted file mode 100644 index 1d385d5..0000000 --- a/base/audio.c +++ /dev/null @@ -1,458 +0,0 @@ -#include <stdio.h> -#include <stdint.h> -#include <math.h> -#include <stdlib.h> - -#define SAMPLE_RATE 48000 -#define NUM_CHANNELS 2 -#define FRAME_SIZE (NUM_CHANNELS * sizeof(short)) - -// static inline float smoothstep(float edge0, float edge1, float x) { -// x = (x - edge0) / (edge1 - edge0); // Scale x to [0, 1] -// x = x < 0.0f ? 0.0f : (x > 1.0f ? 1.0f : x); // Clamp to [0, 1] -// return x * x * (3.0f - 2.0f * x); // Smooth interpolation -// } - -// static inline float smootherstep(float edge0, float edge1, float x) { -// x = (x - edge0) / (edge1 - edge0); // Scale x to [0, 1] -// x = x < 0.0f ? 0.0f : (x > 1.0f ? 1.0f : x); // Clamp to [0, 1] -// return x * x * x * (x * (x * 6 - 15) + 10); // Modified curve -// } - -static inline float fast_cos(float x) { - float x2 = x * x; - return 1.0f - x2 * (0.5f - x2 * 0.04166667f); // Approximation of cos(x) -} - -static inline float cosine_smooth(float edge0, float edge1, float x) { - x = (x - edge0) / (edge1 - edge0); // Scale x to [0, 1] - x = x < 0.0f ? 0.0f : (x > 1.0f ? 1.0f : x); // Clamp to [0, 1] - return 0.5f * (1.0f - fast_cos(x * M_PI)); // Cosine smoothing -} - -static float filter_phase = 0.0f; -static float prev_output_sample_L = 0.0f; -static float prev_output_sample_R = 0.0f; - -static void audio_callback_thread(int16_t *audio_buffer, size_t frames) { - int filter_override = state.filter_override; // Manual override: -1 = auto, 0 = off, 1 = on - float filter_frequency = state.filter_frequency; // Frequency in Hz for squarewave toggle - - audio_callback(audio_buffer, frames); - - if(filter_override) { - float a = 1.0f * M_PI * 4000.0f / (SAMPLE_RATE + 1.0f * M_PI * 4000.0f); - float phase_increment = filter_frequency / SAMPLE_RATE; - - for(size_t i = 0; i < frames * 2; i += 2) { - float led_filter_active; - - if(filter_override == -1) { - filter_phase += phase_increment; - if(filter_phase >= 1.0f) filter_phase -= 1.0f; - - led_filter_active = cosine_smooth(0.45f, 0.50f, filter_phase) - cosine_smooth(0.95f, 1.00f, filter_phase); - - } else { - led_filter_active = 1.0f; // Manual override (1 = on) - } - - float input_sample_L = (float)audio_buffer[i] / 32767.0f; - float input_sample_R = (float)audio_buffer[i + 1] / 32767.0f; - - float filtered_sample_L = a * input_sample_L + (1.0f - a) * prev_output_sample_L; - float filtered_sample_R = a * input_sample_R + (1.0f - a) * prev_output_sample_R; - - prev_output_sample_L = filtered_sample_L; - prev_output_sample_R = filtered_sample_R; - - audio_buffer[i] = (int16_t)((1.0f - led_filter_active) * input_sample_L * 32767.0f + led_filter_active * filtered_sample_L * 32767.0f); - audio_buffer[i + 1] = (int16_t)((1.0f - led_filter_active) * input_sample_R * 32767.0f + led_filter_active * filtered_sample_R * 32767.0f); - } - } -} - -#ifdef __linux__ - - -#include <pipewire/pipewire.h> -#include <spa/param/audio/format-utils.h> -#include <spa/param/props.h> - -#define BUFFER_SIZE (512 * FRAME_SIZE) - -static struct pw_thread_loop *pa_thread_loop; -static struct pw_context *pa_context; -static struct pw_core *pa_core; -static struct pw_stream *pa_stream; -static struct spa_hook pa_stream_listener; -static uint64_t audio_clock_frequency; -static uint64_t playback_cursor; - -/* - * Called from PipeWire's real-time thread whenever new audio data is needed. - * We dequeue a buffer, call your audio_callback() to fill it, and then re-queue. - */ -static void on_process(void *userdata) { - struct pw_buffer *buffer; - struct spa_buffer *spa_buf; - int16_t *data; - uint32_t size; - uint32_t frames; - struct pw_time time_info; - - buffer = pw_stream_dequeue_buffer(pa_stream); - if(!buffer) { - /* No buffer available, skip. */ - return; - } - - spa_buf = buffer->buffer; - if(!spa_buf->datas || !spa_buf->datas[0].data) { - pw_stream_queue_buffer(pa_stream, buffer); - return; - } - - data = spa_buf->datas[0].data; - size = spa_buf->datas[0].maxsize; - frames = size / FRAME_SIZE; - - // if(pw_stream_get_time_n(pa_stream, &time_info, sizeof(time_info)) == 0) { - // playback_cursor = time_info.now; - // } - // printf("Cursor(ns): %luns\n", playback_cursor); - - audio_callback_thread(data, frames); - - if(spa_buf->datas[0].chunk) { - spa_buf->datas[0].chunk->size = frames * FRAME_SIZE; - spa_buf->datas[0].chunk->stride = FRAME_SIZE; - } - - pw_stream_queue_buffer(pa_stream, buffer); -} - -/* - * Initialize PipeWire, create the stream, and connect for audio playback. - * Returns immediately so your main thread can continue. - */ -int audio_initialize(void) { - pw_init(0, 0); - - pa_thread_loop = pw_thread_loop_new("my-audio-loop", 0); - if(pa_thread_loop) { - if(pw_thread_loop_start(pa_thread_loop) == 0) { - pw_thread_loop_lock(pa_thread_loop); - - pa_context = pw_context_new(pw_thread_loop_get_loop(pa_thread_loop), 0, 0); - if(pa_context) { - pa_core = pw_context_connect(pa_context, 0, 0); - if(pa_core){ - static const struct spa_dict_item items[] = { - SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_TYPE, "Audio"), - SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_CATEGORY, "Playback"), - SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_ROLE, "Game"), - SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, "512/48000") - }; - struct pw_properties *props = pw_properties_new_dict(&SPA_DICT_INIT(items, 4)); - // pw_properties_free(props); - - pa_stream = pw_stream_new(pa_core, "My Audio Stream", props); - if(pa_stream) { - static struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, .process = on_process, }; - pw_stream_add_listener(pa_stream, &pa_stream_listener, &stream_events, 0); - - /* - * Build two SPA params: - * 1) The audio format: S16_LE, SAMPLE_RATE, NUM_CHANNELS - * 2) The buffer param: request BUFFER_SIZE bytes per buffer - */ - uint8_t fmt_buffer[1024]; - struct spa_pod_builder fmt_builder = SPA_POD_BUILDER_INIT(fmt_buffer, sizeof(fmt_buffer)); - const struct spa_pod *fmt_param = spa_pod_builder_add_object( - &fmt_builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16_LE), - SPA_FORMAT_AUDIO_rate, SPA_POD_Int(SAMPLE_RATE), - SPA_FORMAT_AUDIO_channels, SPA_POD_Int(NUM_CHANNELS) - ); - - uint8_t buf_buffer[1024]; - struct spa_pod_builder buf_builder = SPA_POD_BUILDER_INIT(buf_buffer, sizeof(buf_buffer)); - struct spa_pod *buf_param = spa_pod_builder_add_object( - &buf_builder, - SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 2, 16), /* We'll request 8 buffers, each of size = BUFFER_SIZE bytes. */ - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(BUFFER_SIZE, BUFFER_SIZE, BUFFER_SIZE*8), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(FRAME_SIZE), - SPA_PARAM_BUFFERS_align, SPA_POD_Int(16) - ); - - const struct spa_pod *params[2]; - params[0] = fmt_param; - params[1] = buf_param; - - int res = pw_stream_connect(pa_stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_RT_PROCESS | PW_STREAM_FLAG_MAP_BUFFERS, params, 2); - pw_thread_loop_unlock(pa_thread_loop); - return 0; - - } else { - fprintf(stderr, "Failed to create PipeWire stream\n"); - } - pw_core_disconnect(pa_core); - } else { - fprintf(stderr, "Failed to connect context to core\n"); - } - pw_context_destroy(pa_context); - } else { - fprintf(stderr, "Failed to create PipeWire context\n"); - } - pw_thread_loop_unlock(pa_thread_loop); - pw_thread_loop_stop(pa_thread_loop); - } else { - fprintf(stderr, "Failed to start PipeWire thread loop\n"); - } - pw_thread_loop_destroy(pa_thread_loop); - } else { - fprintf(stderr, "Failed to create PipeWire thread loop\n"); - } - pw_deinit(); - return -1; -} - -/* - * Clean up PipeWire objects, stop the thread loop, and deinit. - * This should be called before your program exits. - */ -void audio_shutdown(void) { - if(!pa_thread_loop) { - return; - } - - pw_thread_loop_lock(pa_thread_loop); - - if(pa_stream){ - pw_stream_disconnect(pa_stream); - pw_stream_destroy(pa_stream); - } - - if(pa_core){ - pw_core_disconnect(pa_core); - } - - if(pa_context){ - pw_context_destroy(pa_context); - } - - pw_thread_loop_unlock(pa_thread_loop); - pw_thread_loop_stop(pa_thread_loop); - pw_thread_loop_destroy(pa_thread_loop); - pw_deinit(); -} - - - - - - - - - - - - - - - -#elif _WIN32 - -#define COBJMACROS -#include <windows.h> -#include <initguid.h> -#include <audioclient.h> -#include <mmdeviceapi.h> -#include <avrt.h> -#include <stdint.h> -#include <stdio.h> -#include <timeapi.h> - -/* - * Minimal WASAPI shared-mode audio playback with explicit 48kHz/16-bit/2ch. - */ - -#define NUM_CHANNELS 2 - -static IMMDeviceEnumerator *enumerator; -static IMMDevice *device_out; -static IAudioClient *audio_client_out; -static IAudioRenderClient *render_client; -static HANDLE audio_event; -static HANDLE audio_thread; -static int running; - -static DWORD WINAPI audio_thread_proc(void *arg) { - UINT32 buffer_size; - UINT32 padding; - UINT32 available; - uint8_t *data; - - IAudioClient_GetBufferSize(audio_client_out, &buffer_size); - - while(running) { - WaitForSingleObject(audio_event, INFINITE); - if(!running) { - break; - } - - IAudioClient_GetCurrentPadding(audio_client_out, &padding); - available = buffer_size - padding; - IAudioRenderClient_GetBuffer(render_client, available, &data); - audio_callback_thread((int16_t*)data, available); - IAudioRenderClient_ReleaseBuffer(render_client, available, 0); - } - return 0; -} - -void audio_initialize() { - WAVEFORMATEX wf; - REFERENCE_TIME dur_out; - - CoInitializeEx(0, COINIT_MULTITHREADED); - if(SUCCEEDED(CoCreateInstance(&CLSID_MMDeviceEnumerator, 0, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void**)&enumerator))) { - if(SUCCEEDED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, eRender, eConsole, &device_out))) { - if(SUCCEEDED(IMMDevice_Activate(device_out, &IID_IAudioClient, CLSCTX_ALL, 0, (void**)&audio_client_out))) { - wf.wFormatTag = WAVE_FORMAT_PCM; - wf.nChannels = NUM_CHANNELS; - wf.nSamplesPerSec = 48000; - wf.wBitsPerSample = 16; - wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - wf.cbSize = 0; - - IAudioClient_GetDevicePeriod(audio_client_out, &dur_out, 0); - IAudioClient_Initialize(audio_client_out, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, dur_out, 0, &wf, 0); - audio_event = CreateEvent(0, FALSE, FALSE, 0); - if(audio_event){ - IAudioClient_SetEventHandle(audio_client_out, audio_event); - IAudioClient_GetService(audio_client_out, &IID_IAudioRenderClient, (void**)&render_client); - IAudioClient_Start(audio_client_out); - - running = 1; - audio_thread = CreateThread(0, 0, audio_thread_proc, 0, 0, 0); - return; - } else { - printf("Failed to create audio event\n"); - } - audio_client_out->lpVtbl->Release(audio_client_out); - } else { - printf("Failed to activate audio client\n"); - } - device_out->lpVtbl->Release(device_out); - } else { - printf("Failed to get default audio endpoint\n"); - } - enumerator->lpVtbl->Release(enumerator); - } else { - printf("Failed to create MMDeviceEnumerator\n"); - } -} - -void audio_shutdown() { - running = 0; - if(audio_thread) { - SetEvent(audio_event); - WaitForSingleObject(audio_thread, INFINITE); - CloseHandle(audio_thread); - } - if(audio_event) { - CloseHandle(audio_event); - } - if(audio_client_out) { - IAudioClient_Stop(audio_client_out); - audio_client_out->lpVtbl->Release(audio_client_out); - } - if(render_client) { - render_client->lpVtbl->Release(render_client); - } - if(device_out) { - device_out->lpVtbl->Release(device_out); - } - if(enumerator) { - enumerator->lpVtbl->Release(enumerator); - } - CoUninitialize(); -} - -#endif - - - - - -// BELOW IS FOR FUTURE FRAME SYNCHRONIZATION!!! - -#if 0 -// Audio sync throttling logic (using audio playback clock) - -#define AUDIO_SAMPLE_RATE 48000 -#define FRAMETIME (1000000000 / 60) // NES: ~16.67ms per frame (replace as needed for PAL/other) - -static uint64_t emulator_start_time_ns = 0; -static uint64_t audio_start_time_ns = 0; - -// Stub: return current audio playback time in nanoseconds -uint64_t get_audio_playback_time_ns(void); - -// Call this once at emulation start -void audio_sync_init(uint64_t current_time_ns) { - emulator_start_time_ns = current_time_ns; - audio_start_time_ns = get_audio_playback_time_ns(); -} - -// Call this at the end of each frame -void audio_throttle_emulator(uint64_t frame_number, int64_t *frame_duration_ns) { - uint64_t expected_emulated_time = frame_number * FRAMETIME; - uint64_t actual_audio_time = get_audio_playback_time_ns() - audio_start_time_ns; - - int64_t drift = (int64_t)(actual_audio_time) - (int64_t)(expected_emulated_time); - - // Adjust frame duration to correct drift gradually - *frame_duration_ns -= drift / 8; - // Clamp adjustment to avoid jitter - if(*frame_duration_ns > FRAMETIME + 50000) { - *frame_duration_ns = FRAMETIME + 50000; - } else if(*frame_duration_ns < FRAMETIME - 50000) { - *frame_duration_ns = FRAMETIME - 50000; - } -} - -#ifdef _WIN32 -#include <windows.h> -#include <mmdeviceapi.h> -#include <audioclient.h> - -uint64_t get_audio_playback_time_ns(void) { - // WASAPI: query IAudioClock interface - // This is just a placeholder. You’ll need to cache IAudioClock *audio_clock externally. - extern IAudioClock *audio_clock; - UINT64 pos; - audio_clock->lpVtbl->GetPosition(audio_clock, &pos, 0); - return (pos * 1000000000ULL) / AUDIO_SAMPLE_RATE; -} - -#else -// PipeWire backend -#include <spa/clock/clock.h> -extern struct spa_clock *audio_clock; - -uint64_t get_audio_playback_time_ns(void) { - struct spa_clock_info info; - audio_clock->get_time(audio_clock, &info); - return info.nsec; -} -#endif - -#endif |
