From b77b6d83267a33ee46a7fffbbd1cde81f099b2fb Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Sat, 19 Jul 2025 13:18:27 +0200 Subject: [PATCH] initial commit --- pulse-simple.odin | 135 ++++++++++++++++++ pulse.odin | 348 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 483 insertions(+) create mode 100644 pulse-simple.odin create mode 100644 pulse.odin diff --git a/pulse-simple.odin b/pulse-simple.odin new file mode 100644 index 0000000..9dc23d2 --- /dev/null +++ b/pulse-simple.odin @@ -0,0 +1,135 @@ +package pulse + +import "core:c" + +foreign import pulse "system:pulse-simple" + +PA_CHANNELS_MAX :: 32 + +pa_sample_format :: enum c.int { + INVALID = -1, + U8 = 0, + ALAW, + ULAW, + S16LE, + S16BE, + FLOAT32LE, + FLOAT32BE, + S32LE, + S32BE, + S24LE, + S24BE, + S24_32LE, + S24_32BE, +} + +pa_stream_direction :: enum c.int { + NODIRECTION, + PLAYBACK, + RECORD, + UPLOAD, +} + +pa_channel_position :: enum c.int { + INVALID = -1, + MONO = 0, + FRONT_LEFT, + FRONT_RIGHT, + FRONT_CENTER, + REAR_CENTER, + REAR_LEFT, + REAR_RIGHT, + LFE, + FRONT_LEFT_OF_CENTER, + FRONT_RIGHT_OF_CENTER, + SIDE_LEFT, + SIDE_RIGHT, + AUX0, + AUX1, + AUX2, + AUX3, + AUX4, + AUX5, + AUX6, + AUX7, + AUX8, + AUX9, + AUX10, + AUX11, + AUX12, + AUX13, + AUX14, + AUX15, + AUX16, + AUX17, + AUX18, + AUX19, + AUX20, + AUX21, + AUX22, + AUX23, + AUX24, + AUX25, + AUX26, + AUX27, + AUX28, + AUX29, + AUX30, + AUX31, + TOP_CENTER, + TOP_FRONT_LEFT, + TOP_FRONT_RIGHT, + TOP_FRONT_CENTER, + TOP_REAR_LEFT, + TOP_REAR_RIGHT, + TOP_REAR_CENTER, +} + +pa_simple :: struct {} +pa_sample_spec :: struct { + format: pa_sample_format, + rate: u32, + channels: u8, +} + +// A channel map which can be used to attach labels to specific channels of a stream +pa_channel_map :: struct { + channels: u8, + map_: [PA_CHANNELS_MAX]pa_channel_position, +} + +pa_buffer_attr :: struct { + maxlength: u32, + tlength: u32, + prebuf: u32, + minreq: u32, + fregzie: u32, +} + +pa_usec :: u64 + +@(link_prefix="pa_") +foreign pulse { + /* Create a new connection to the server. + Parameters + server Server name, or NULL for default + name A descriptive name for this client (application name, ...) + dir Open this stream for recording or playback? + dev Sink (resp. source) name, or NULL for default + stream_name A descriptive name for this stream (application name, song title, ...) + ss The sample type to use + map The channel map to use, or NULL for default + attr Buffering attributes, or NULL for default + error A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. + */ + simple_new :: proc(server: cstring, name: cstring, dir: pa_stream_direction, dev: cstring, stream_name: cstring, ss: ^pa_sample_spec, _map: ^pa_channel_map, attr: ^pa_buffer_attr, error: ^c.int) -> ^pa_simple --- + // Close and free the connection to the server. + // + // The connection object becomes invalid when this is called. + simple_free :: proc(s: ^pa_simple) --- + simple_write :: proc(s: ^pa_simple, data: rawptr, bytes: c.size_t, error: ^c.int) -> c.int --- + simple_drain :: proc(s: ^pa_simple, error: ^c.int) -> c.int --- + simple_read :: proc(s: ^pa_simple, data: rawptr, bytes: c.size_t, error: ^c.int) -> c.int --- + simple_get_latency :: proc(s: ^pa_simple, error: ^c.int) -> pa_usec --- + simple_flush :: proc(s: ^pa_simple, error: ^c.int) -> c.int --- +} diff --git a/pulse.odin b/pulse.odin new file mode 100644 index 0000000..3be2c84 --- /dev/null +++ b/pulse.odin @@ -0,0 +1,348 @@ +package pulse + +import "core:text/regex/virtual_machine" +import "core:sys/posix" +import "core:c" + +foreign import pulse "system:pulse" + +pa_context_flags :: enum c.int { + NOFLAGS, + NOAUTOSPAWN, + NOFAIL, +} + +pa_io_event_flags_bits :: enum c.int { + INPUT, + OUTPUT, + HANGUP, + ERROR, +} + +pa_context_state :: enum c.int { + UNCONNECTED, + CONNECTING, + AUTHORIZING, + SETTING_NAME, + READY, + FAILED, + TERMINATED, +} + +pa_update_mode :: enum c.int { + SET, + MERGE, + REPLACE, +} + +pa_sink_flags_bits :: enum c.int { + HW_VOLUME_CTRL, + LATENCY, + HARDWARE, + NETWORK, + HW_MUTE_CTRL, + DECIBEL_VOLUME, + FLAT_VOLUME, + DYNAMIC_LATENCY, + SET_FORMATS, +} + +pa_sink_state :: enum c.int { + INVALID = -1, + RUNNING, + IDLE, + SUSPENDED, +} + +pa_port_available :: enum c.int { + UNKNOWN, + NO, + YES, +} + +pa_device_port_type :: enum u32 { + UNKNOWN, + AUX, + SPEAKER, + HEADPHONES, + LINE, + MIC, + HEADSET, + HANDSET, + EARPIECE, + SPDIF, + HDMI, + TV, + RADIO, + VIDEO, + USB, + BLUETOOTH, + PORTABLE, + HANDSFREE, + CAR, + HIFI, + PHONE, + NETWORK, + ANALOG, +} + +pa_encoding :: enum c.int { + INVALID = -1, + ANY, + PCM, + AC3_IEC61937, + EAC_3_IEC61937, + MPEG_IEC61937, + DTS_IEC61937, + MPEG2_AAC_IEC61937, + TRUEHD_IEC61937, + DTSHD_IEC61937, +} + +pa_operation_state :: enum c.int { + RUNNING, + DONE, + CANCELLED, +} + +pa_source_flags_bits :: enum c.int { + HW_VOLUME_CTRL, + LATENCY, + HARDWARE, + NETWORK, + HW_MUTE_CTRL, + DECIBEL_VOLUME, + DYNAMIC_LATENCY, + FLAT_VOLUME, +} + +pa_source_state :: enum c.int { + INVALID = -1, + RUNNING, + IDLE, + SUSPENDED, +} + +pa_stream_flags_bits :: enum c.int { + START_CORKED, + INTERPOLATE_TIMING, + NOT_MONOTONIC, + AUTO_TIMING_UPDATE, + NO_REMAP_CHANNELS, + NO_REMIX_CHANNELS, + FIX_FORMAT, + FIX_RATE, + FIX_CHANNELS, + DONT_MOVE, + VARIABLE_RATE, + PEAK_DETECT, + START_MUTED, + ADJUST_LATENCY, + EARLY_REQUESTS, + DONT_INHIBIT_AUTO_SUSPEND, + START_UNMUTED, + FAIL_ON_SUSPEND, + RELATIVE_VOLUME, + PASSTHROUGH, +} + +pa_stream_flags :: bit_set[pa_stream_flags_bits; c.int] + +pa_sink_flags :: bit_set[pa_sink_flags_bits; c.int] +pa_io_event_flags :: bit_set[pa_io_event_flags_bits; c.int] +pa_source_flags :: bit_set[pa_source_flags_bits; c.int] + +pa_mainloop :: struct{} +pa_context :: struct{} +pa_io_event :: struct{} +pa_time_event :: struct{} +pa_defer_event :: struct{} +pa_operation :: struct{} +pa_proplist :: struct{} +pa_stream :: struct{} + +pa_volume :: u32 + +pa_cvolume :: struct { + channels: u8, + values: [PA_CHANNELS_MAX]pa_volume, +} + +pa_sink_port_info :: struct { + name: cstring, + description: cstring, + priority: u32, + available: pa_port_available, + availability_group: cstring, + type: pa_device_port_type, +} + +pa_format_info :: struct { + encoding: pa_encoding, + plist: ^pa_proplist, +} + +pa_sink_info :: struct { + name: cstring, + index: u32, + description: cstring, + sample_spec: pa_sample_spec, + channel_map: pa_channel_map, + owner_module: u32, + volume: pa_cvolume, + mute: c.int, + monitor_source: u32, + monitor_source_name: cstring, + latency: pa_usec, + driver: cstring, + flags: pa_sink_flags, + proplist: ^pa_proplist, + configured_latency: pa_usec, + base_volume: pa_volume, + state: pa_sink_state, + n_volume_steps: u32, + card: u32, + n_ports: u32, + ports: [^]^pa_sink_port_info, + active_port: ^pa_sink_port_info, + n_formats: u8, + formats: [^]^pa_format_info, +} + +pa_source_port_info :: struct { + name: cstring, + description: cstring, + priority: u32, + available: pa_port_available, + availability_group: cstring, + type: pa_device_port_type, +} + +pa_source_info :: struct { + name: cstring, + index: u32, + description: cstring, + sample_spec: pa_sample_spec, + channel_map: pa_channel_map, + owner_module: u32, + volume: pa_cvolume, + mute: c.int, + monitor_of_sink: u32, + monitor_of_sink_name: cstring, + latency: pa_usec, + driver: cstring, + flags: pa_source_flags, + proplist: ^pa_proplist, + configured_latency: pa_usec, + base_volume: pa_volume, + state: pa_source_state, + n_volume_steps: u32, + card: u32, + n_ports: u32, + ports: [^]^pa_source_port_info, + active_port: ^pa_source_port_info, + n_formats: u8, + formats: [^]^pa_format_info, +} + +pa_poll_func :: #type proc "c" (ufds: ^posix.pollfd, nfds: c.ulong, timeout: c.int, userdata: rawptr) -> c.int +pa_io_event_cb :: #type proc "c" (ea: ^pa_mainloop_api, e: ^pa_io_event, fd: c.int, events: pa_io_event_flags, userdata: rawptr) +pa_io_event_destroy_cb :: #type proc "c" (a: ^pa_mainloop_api, e: ^pa_io_event, userdata: rawptr) +pa_time_event_cb :: #type proc "c" (a: ^pa_mainloop_api, e: ^pa_time_event, tv: ^posix.timeval, userdata: rawptr) +pa_time_event_destroy_cb :: #type proc "c" (a: ^pa_mainloop_api, e: ^pa_time_event, userdata: rawptr) +pa_defer_event_cb :: #type proc "c" (a: ^pa_mainloop_api, e: ^pa_defer_event, userdata: rawptr) +pa_defer_event_destroy_cb :: #type proc "c" (a: ^pa_mainloop_api, e: ^pa_defer_event, userdata: rawptr) +pa_context_success_cb :: #type proc "c" (_c: ^pa_context, success: c.int, userdata: rawptr) +pa_context_notify_cb :: #type proc "c" (c: ^pa_context, userdata: rawptr) +pa_context_event_cb :: #type proc "c" (c: ^pa_context, name: cstring, p: ^pa_proplist, userdata: rawptr) +pa_sink_info_cb :: #type proc "c" (_c: ^pa_context, i: ^pa_sink_info, end_of_list: b32, userdata: rawptr) +pa_source_info_cb :: #type proc "c" (_c: ^pa_context, i: ^pa_source_info, end_of_list: b32, userdata: rawptr) +pa_operation_notify_cb :: #type proc "c" (o: ^pa_operation, userdata: rawptr) +pa_stream_request_cb :: #type proc "c" (p: ^pa_stream, nbytes: c.size_t, userdata: rawptr) + +pa_spawn_api :: struct { + prefork: #type proc "c" (), + postfork: #type proc "c" (), + atfork: #type proc "c" (), +} + +pa_mainloop_api :: struct { + userdata: rawptr, + io_new: #type proc "c" (a: ^pa_mainloop_api, fd: c.int, events: pa_io_event_flags, cb: pa_io_event_cb, userdata: rawptr) -> ^pa_io_event, + io_enable: #type proc "c" (e: ^pa_io_event, events: pa_io_event_flags), + io_free: #type proc "c" (e: ^pa_io_event), + io_set_destroy: #type proc "c" (e: ^pa_io_event, cb: pa_io_event_destroy_cb), + time_new: #type proc "c" (a: ^pa_mainloop_api, tv: ^posix.timeval, cb: pa_time_event_cb, userdata: rawptr) -> ^pa_time_event, + time_restart: #type proc "c" (e: ^pa_time_event, tv: ^posix.timeval), + time_free: #type proc "c" (e: ^pa_time_event), + time_set_destroy: #type proc "c" (e: ^pa_time_event, cb: pa_time_event_destroy_cb), + defer_new: #type proc "c" (a: ^pa_mainloop_api, cb: pa_defer_event_cb, userdata: rawptr) -> ^pa_defer_event, + defer_enable: #type proc "c" (e: ^pa_defer_event, b: c.int), + defer_free: #type proc "c" (e: ^pa_defer_event), + defer_set_destroy: #type proc "c" (e: ^pa_defer_event, cb: pa_defer_event_destroy_cb), + quit: #type proc "c" (a: ^pa_mainloop_api, retval: c.int), +} + +@(link_prefix="pa_") +foreign pulse { + strerror :: proc(error: c.int) -> cstring --- + + mainloop_new :: proc() -> ^pa_mainloop --- + mainloop_free :: proc(m: ^pa_mainloop) --- + mainloop_prepare :: proc(m: ^pa_mainloop, timeout: c.int) -> c.int --- + mainloop_poll :: proc(m: ^pa_mainloop) -> c.int --- + mainloop_dispatch :: proc(m: ^pa_mainloop) -> c.int --- + mainloop_get_retval :: proc(m: ^pa_mainloop) -> c.int --- + mainloop_iterate :: proc(m: ^pa_mainloop, block: b32, retval: ^c.int) -> c.int --- + mainloop_run :: proc(m: ^pa_mainloop, retval: ^c.int) -> c.int --- + mainloop_get_api :: proc(m: ^pa_mainloop) -> ^pa_mainloop_api --- + mainloop_quit :: proc(m: ^pa_mainloop, retval: c.int) --- + mainloop_wakeup :: proc(m: ^pa_mainloop) --- + mainloop_set_poll_func :: proc(m: ^pa_mainloop, poll_func: pa_poll_func, userdata: rawptr) --- + + context_new :: proc(mainloop: ^pa_mainloop_api, name: cstring) -> ^pa_context --- + context_new_with_proplist :: proc(mainloop: ^pa_mainloop_api, name: cstring, proplist: ^pa_proplist) -> ^pa_context --- + context_unref :: proc(c: ^pa_context) --- + context_ref :: proc(c: ^pa_context) -> ^pa_context --- + context_set_state_callback :: proc(c: ^pa_context, cb: pa_context_notify_cb, userdata: rawptr) --- + context_set_event_callback :: proc(c: ^pa_context, cb: pa_context_event_cb, userdata: rawptr) --- + context_errno :: proc(_c: ^pa_context) -> c.int --- + context_is_pending :: proc(_c: ^pa_context) -> c.int --- + context_get_state :: proc(c: ^pa_context) -> pa_context_state --- + context_connect :: proc(_c: ^pa_context, server: cstring, flags: pa_context_flags, api: ^pa_spawn_api) -> c.int --- + context_disconnect :: proc(_c: ^pa_context) --- + context_drain :: proc(c: ^pa_context, cb: pa_context_notify_cb, userdata: rawptr) -> ^pa_operation --- + context_exit_daemon :: proc(c: ^pa_context, cb: pa_context_success_cb, userdata: rawptr) -> ^pa_operation --- + context_set_default_sink :: proc(c: ^pa_context, name: cstring, cb: pa_context_success_cb, userdata: rawptr) -> ^pa_operation --- + context_set_default_source :: proc(c: ^pa_context, name: cstring, cb: pa_context_success_cb, userdata: rawptr) -> ^pa_operation --- + context_is_local :: proc(_c: ^pa_context) -> b32 --- + context_set_name :: proc(c: ^pa_context, name: cstring, cb: pa_context_success_cb, userdata: rawptr) -> ^pa_operation --- + context_get_server :: proc(c: ^pa_context) -> cstring --- + context_get_protocol_version :: proc(c: ^pa_context) -> u32 --- + context_get_server_protocol_version :: proc(c: ^pa_context) -> u32 --- + context_proplist_update :: proc(c: ^pa_context, mode: pa_update_mode, p: ^pa_proplist, cb: pa_context_success_cb, userdata: rawptr) -> ^pa_operation --- + context_proplist_remove :: proc(c: ^pa_context, keys: [^]cstring, cb: pa_context_success_cb, userdata: rawptr) -> ^pa_operation --- + context_get_index :: proc(s: ^pa_context) -> u32 --- + context_rttime_new :: proc(c: ^pa_context, usec: pa_usec, cb: pa_time_event_cb, userdata: rawptr) -> ^pa_time_event --- + context_rttime_restart :: proc(c: ^pa_context, e: ^pa_time_event, usec: pa_usec) --- + context_get_tile_size :: proc(_c: ^pa_context, ss: ^pa_sample_spec) -> c.size_t --- + context_load_cookie_from_file :: proc(_c: ^pa_context, cookie_file_path: cstring) -> c.int --- + context_get_sink_info_list :: proc(c: ^pa_context, cb: pa_sink_info_cb, userdata: rawptr) -> ^pa_operation --- + context_get_source_info_list :: proc(c: ^pa_context, cb: pa_source_info_cb, userdata: rawptr) -> ^pa_operation --- + context_set_source_mute_by_name :: proc(c: ^pa_context, name: cstring, mute: b32, cb: pa_context_success_cb, userdata: rawptr) -> ^pa_operation --- + + operation_ref :: proc(o: ^pa_operation) -> ^pa_operation --- + operation_unref :: proc(o: ^pa_operation) --- + operation_cancel :: proc(o: ^pa_operation) --- + operation_get_state :: proc(o: ^pa_operation) -> pa_operation_state --- + operation_set_state_callback :: proc(o: ^pa_operation, cb: pa_operation_notify_cb, userdata: rawptr) --- + + stream_new :: proc(c: ^pa_context, name: cstring, ss: ^pa_sample_spec, _map: ^pa_channel_map) -> ^pa_stream --- + stream_connect_record :: proc(s: ^pa_stream, dev: cstring, attr: ^pa_buffer_attr, flags: pa_stream_flags) -> c.int --- + stream_disconnect :: proc(s: ^pa_stream) -> c.int --- + stream_peek :: proc(p: ^pa_stream, data: ^rawptr, nbytes: ^c.size_t) -> c.int --- + stream_readable_size :: proc(p: ^pa_stream) -> c.size_t --- + stream_set_read_callback :: proc(p: ^pa_stream, cb: pa_stream_request_cb, userdata: rawptr) --- + stream_drop :: proc(p: ^pa_stream) -> c.int --- + stream_unref :: proc(p: ^pa_stream) --- +}