From 6446db7860bc5a991dcde7b59df9c6f1cd9bb508 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 14 Jun 2022 19:23:22 +0900 Subject: [PATCH] seq: event: add boxed structure for event Current implementation has ALSASeq.EventCntr for user applications to handle batch of events. The properties of event is directly retrieved from it by index number, however it's necessarily useful. This commit adds boxed structure specific to each event. Signed-off-by: Takashi Sakamoto --- src/seq/alsaseq-enum-types.h | 18 + src/seq/alsaseq.h | 1 + src/seq/alsaseq.map | 49 + src/seq/event.c | 1621 ++++++++++++++++++++++++++++++++++ src/seq/event.h | 112 +++ src/seq/meson.build | 2 + tests/alsaseq-enums | 8 + 7 files changed, 1811 insertions(+) create mode 100644 src/seq/event.c create mode 100644 src/seq/event.h diff --git a/src/seq/alsaseq-enum-types.h b/src/seq/alsaseq-enum-types.h index 9d00c17..6da4963 100644 --- a/src/seq/alsaseq-enum-types.h +++ b/src/seq/alsaseq-enum-types.h @@ -361,6 +361,24 @@ typedef enum { ALSASEQ_USER_CLIENT_ERROR_QUEUE_PERMISSION, } ALSASeqUserClientError; +/** + * ALSASeqEventError: + * @ALSASEQ_EVENT_ERROR_FAILED: General error due to unspecified reason. + * @ALSASEQ_EVENT_ERROR_INVALID_DATA_TYPE: The type of requested data is invalid in the event. + * @ALSASEQ_EVENT_ERROR_INVALID_LENGTH_MODE: The mode of length for requested data is invalid in + * the event. + * @ALSASEQ_EVENT_ERROR_INVALID_TSTAMP_MODE: The type of time stamp for requested data is is + * invalid in the event. + * + * A set of error code for [struct@GLib.Error] with `struct@EventError` domain. + */ +typedef enum { + ALSASEQ_EVENT_ERROR_FAILED = 0, + ALSASEQ_EVENT_ERROR_INVALID_DATA_TYPE, + ALSASEQ_EVENT_ERROR_INVALID_LENGTH_MODE, + ALSASEQ_EVENT_ERROR_INVALID_TSTAMP_MODE, +} ALSASeqEventError; + G_END_DECLS #endif diff --git a/src/seq/alsaseq.h b/src/seq/alsaseq.h index ce06789..d4917f6 100644 --- a/src/seq/alsaseq.h +++ b/src/seq/alsaseq.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/src/seq/alsaseq.map b/src/seq/alsaseq.map index 66292aa..1becdaf 100644 --- a/src/seq/alsaseq.map +++ b/src/seq/alsaseq.map @@ -220,4 +220,53 @@ ALSA_GOBJECT_0_3_0 { "alsaseq_remove_filter_new_with_tag"; "alsaseq_remove_filter_new_with_tick_time"; "alsaseq_remove_filter_new_with_real_time"; + + "alsaseq_event_error_get_type"; + "alsaseq_event_error_quark"; + + "alsaseq_event_get_type"; + "alsaseq_event_new"; + "alsaseq_event_get_event_type"; + "alsaseq_event_get_length_mode"; + "alsaseq_event_get_tstamp_mode"; + "alsaseq_event_get_time_mode"; + "alsaseq_event_set_time_mode"; + "alsaseq_event_get_priority_mode"; + "alsaseq_event_set_priority_mode"; + "alsaseq_event_get_tag"; + "alsaseq_event_set_tag"; + "alsaseq_event_get_queue_id"; + "alsaseq_event_set_queue_id"; + "alsaseq_event_get_source"; + "alsaseq_event_set_source"; + "alsaseq_event_get_destination"; + "alsaseq_event_set_destination"; + "alsaseq_event_get_tick_time"; + "alsaseq_event_set_tick_time"; + "alsaseq_event_get_real_time"; + "alsaseq_event_set_real_time"; + "alsaseq_event_get_note_data"; + "alsaseq_event_set_note_data"; + "alsaseq_event_get_ctl_data"; + "alsaseq_event_set_ctl_data"; + "alsaseq_event_get_byte_data"; + "alsaseq_event_set_byte_data"; + "alsaseq_event_get_quadlet_data"; + "alsaseq_event_set_quadlet_data"; + "alsaseq_event_get_blob_data"; + "alsaseq_event_set_blob_data"; + "alsaseq_event_get_pointer_data"; + "alsaseq_event_set_pointer_data"; + "alsaseq_event_get_queue_data"; + "alsaseq_event_set_queue_data"; + "alsaseq_event_get_tick_time_data"; + "alsaseq_event_set_tick_time_data"; + "alsaseq_event_get_real_time_data"; + "alsaseq_event_set_real_time_data"; + "alsaseq_event_get_addr_data"; + "alsaseq_event_set_addr_data"; + "alsaseq_event_get_connect_data"; + "alsaseq_event_set_connect_data"; + "alsaseq_event_get_result_data"; + "alsaseq_event_set_result_data"; } ALSA_GOBJECT_0_2_0; diff --git a/src/seq/event.c b/src/seq/event.c new file mode 100644 index 0000000..1c6daf2 --- /dev/null +++ b/src/seq/event.c @@ -0,0 +1,1621 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "privates.h" + +/** + * ALSASeqEvent: + * A boxed structure for sequencer event. + * + * A [struct@Event] expresses any type of event in ALSA Sequencer. The event consists of some + * product-type propertiess and two variant-type properties. + * + * The instance should be released by call of [func@GObject.boxed_free] since it can point to the + * other allocated object for blob data, therefore it's preferable to use [func@GObject.boxed_copy] + * to duplicate the instance so that the blob data is going to be duplicated as well. + * + * This is the list of product-type properties: + * + * - the type of event + * - the mode of time stamp + * - the mode of time + * - the mode of length + * - the mode of priority + * - the numeric value of tag associated to the event + * - the numeric identifier of queue to schedule the event + * - destination address + * - source address + * + * One of variant-type property is for time stamp. + * + * - tick count as time stamp of event + * - real time as time stamp of event + * + * Another variant-type property is for data of event. + * + * - note + * - control + * - 12 bytes + * - 3 quadlets + * - blob as variable length of bytes + * - pointer in VMA of user process + * - queue control + * - tick count as arbitrary time stamp + * - real time as arbitrary time stamp + * - arbitrary address + * - connection between source and destination addresses + * - result + * + * The type of time stamp is associated to the mode of time stamp, while the type of data is + * associated to the type of event loosely. Each of the variant type property has single storage + * internally, thus an event can includes the sole variant. + */ + +static ALSASeqEvent *seq_event_copy(const ALSASeqEvent *self) +{ + ALSASeqEvent *ev; + + ev = g_malloc0(sizeof(*ev)); + memcpy(ev, self, sizeof(*ev)); + + // NOTE: The instance of boxed structure should have the same size, thus variable length of + // event with flattened layout is unavailable. The event is converted to the one pointing to + // heap buffer for data. + switch (ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) { + case SNDRV_SEQ_EVENT_LENGTH_VARIABLE: + { + const guint8 *src; + guint8 *dst; + gsize length; + + src = (const guint8 *)self; + src += sizeof(*ev); + + length = ev->data.ext.len; + dst = g_malloc(length); + memcpy(dst, src, length); + + ev->data.ext.ptr = dst; + break; + } + case SNDRV_SEQ_EVENT_LENGTH_VARUSR: + case SNDRV_SEQ_EVENT_LENGTH_FIXED: + default: + break; + } + + return ev; +} + +static void free_blob_data(ALSASeqEvent *ev) +{ + // NOTE: free the allocated object when it follows to instance object. + switch (ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) { + case SNDRV_SEQ_EVENT_LENGTH_VARIABLE: + { + guint8 *ptr = (guint8 *)ev; + ptr += sizeof(*ev); + if (ptr != ev->data.ext.ptr) + g_free(ev->data.ext.ptr); + break; + } + case SNDRV_SEQ_EVENT_LENGTH_VARUSR: + case SNDRV_SEQ_EVENT_LENGTH_FIXED: + default: + break; + } +} + +static void seq_event_free(ALSASeqEvent *ev) +{ + free_blob_data(ev); + g_free(ev); +} + +G_DEFINE_BOXED_TYPE(ALSASeqEvent, alsaseq_event, seq_event_copy, seq_event_free); + +/** + * alsaseq_event_error_quark: + * + * Return the [alias@GLib.Quark] for [struct@GLib.Error] which has code of [enum@EventError] + * enumerations. + * + * Returns: A [alias@GLib.Quark]. + */ +G_DEFINE_QUARK(alsaseq-event-error-quark, alsaseq_event_error) + +/** + * alsaseq_event_new: + * @event_type: A [enum@EventType]. + * + * Allocate and return an instance of [struct@Event]. + * + * Returns: An instance of [struct@Event]. + */ +ALSASeqEvent *alsaseq_event_new(ALSASeqEventType event_type) +{ + struct snd_seq_event src = { 0 }; + + src.type = event_type; + + return g_boxed_copy(ALSASEQ_TYPE_EVENT, &src); +} + +/** + * alsaseq_event_get_event_type: + * @self: A [struct@Event]. + * @event_type: (out): A [enum@EventType]. + * + * Get the type of event. + */ +void alsaseq_event_get_event_type(const ALSASeqEvent *self, ALSASeqEventType *event_type) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(event_type != NULL); + + *event_type = self->type; +} + +/** + * alsaseq_event_get_length_mode: + * @self: A [struct@Event]. + * @length_mode: (out): A [enum@EventLengthMode] for the mode of data length. + * + * Get the mode of data length for the event. + */ +void alsaseq_event_get_length_mode(const ALSASeqEvent *self, ALSASeqEventLengthMode *length_mode) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(length_mode != NULL); + + *length_mode = self->flags & SNDRV_SEQ_EVENT_LENGTH_MASK; +} + +/** + * alsaseq_event_get_tstamp_mode: + * @self: A [struct@Event]. + * @tstamp_mode: (out): A [enum@EventTstampMode] for the mode of time stamp. + * + * Get the mode of time stamp for the event. + */ +void alsaseq_event_get_tstamp_mode(const ALSASeqEvent *self, ALSASeqEventTstampMode *tstamp_mode) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(tstamp_mode != NULL); + + *tstamp_mode = self->flags & SNDRV_SEQ_TIME_STAMP_MASK; +} + +/** + * alsaseq_event_get_time_mode: + * @self: A [struct@Event]. + * @time_mode: (out): A [enum@EventTimeMode] for the mode of time. + * + * Get the mode of time for the event. + */ +void alsaseq_event_get_time_mode(const ALSASeqEvent *self, ALSASeqEventTimeMode *time_mode) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(time_mode != NULL); + + *time_mode = self->flags & SNDRV_SEQ_TIME_MODE_MASK; +} + +/** + * alsaseq_event_set_time_mode: + * @self: A [struct@Event]. + * @time_mode: A [enum@EventTimeMode] for the mode of time. + * + * Set the mode of time for the event. + */ +void alsaseq_event_set_time_mode(ALSASeqEvent *self, ALSASeqEventTimeMode time_mode) +{ + g_return_if_fail(self != NULL); + + self->flags &= ~SNDRV_SEQ_TIME_MODE_MASK; + self->flags |= time_mode; +} + +/** + * alsaseq_event_get_priority_mode: + * @self: A [struct@Event]. + * @priority_mode: (out): A [enum@EventPriorityMode] The mode of priority. + * + * Get the mode of priority for the event. + */ +void alsaseq_event_get_priority_mode(const ALSASeqEvent *self, + ALSASeqEventPriorityMode *priority_mode) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(priority_mode != NULL); + + *priority_mode = self->flags & SNDRV_SEQ_PRIORITY_MASK; +} + +/** + * alsaseq_event_set_priority_mode: + * @self: A [struct@Event]. + * @priority_mode: A [enum@EventPriorityMode] for the mode of priority. + * + * Set the mode of priority for the event. + */ +void alsaseq_event_set_priority_mode(ALSASeqEvent *self, ALSASeqEventPriorityMode priority_mode) +{ + g_return_if_fail(self != NULL); + + self->flags &= ~SNDRV_SEQ_PRIORITY_MASK; + self->flags |= priority_mode; +} + +/** + * alsaseq_event_get_tag: + * @self: A [struct@Event]. + * @tag: (out): The numeric value of tag. + * + * Get the numeric value of tag associated to the event. + */ +void alsaseq_event_get_tag(const ALSASeqEvent *self, gint8 *tag) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(tag != NULL); + + *tag = self->tag; +} + +/** + * alsaseq_event_set_tag: + * @self: A [struct@Event]. + * @tag: The numeric value of tag. + * + * Set the numeric value of tag associated to the event. + */ +void alsaseq_event_set_tag(ALSASeqEvent *self, gint8 tag) +{ + g_return_if_fail(self != NULL); + + self->tag = tag; +} + +/** + * alsaseq_event_get_queue_id: + * @self: A [struct@Event]. + * @queue_id: (out): The numeric identifier of queue. + * + * Get the numeric identifier of queue to schedule the event. + */ +void alsaseq_event_get_queue_id(const ALSASeqEvent *self, guint8 *queue_id) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(queue_id != NULL); + + *queue_id = self->queue; +} + +/** + * alsaseq_event_set_queue_id: + * @self: A [struct@Event]. + * @queue_id: The numeric identifier of queue. + * + * Set the numeric identifier of queue to schedule the event. + */ +void alsaseq_event_set_queue_id(ALSASeqEvent *self, guint8 queue_id) +{ + g_return_if_fail(self != NULL); + + self->queue = queue_id; +} + +/** + * alsaseq_event_get_source: + * @self: A [struct@Event]. + * @addr: (out) (transfer none): A [struct@Addr] for source address. + * + * Refer to the source address of event. + */ +void alsaseq_event_get_source(const ALSASeqEvent *self, const ALSASeqAddr **addr) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(addr != NULL); + + *addr = &self->source; +} + +/** + * alsaseq_event_set_source: + * @self: A [struct@Event]. + * @addr: A [struct@Addr] for source address. + * + * Copy the address as source of event. + */ +void alsaseq_event_set_source(ALSASeqEvent *self, const ALSASeqAddr *addr) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(addr != NULL); + + self->source = *addr; +} + +/** + * alsaseq_event_get_destination: + * @self: A [struct@Event]. + * @addr: (out) (transfer none): A [struct@Addr] for event destination. + * + * Refer to the destination address of event. + */ +void alsaseq_event_get_destination(const ALSASeqEvent *self, const ALSASeqAddr **addr) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(addr != NULL); + + *addr = &self->dest; +} + +/** + * alsaseq_event_set_destination: + * @self: A [struct@Event]. + * @addr: A [struct@Addr] for event destination. + * + * Copy the address as destination of event. + */ +void alsaseq_event_set_destination(ALSASeqEvent *self, const ALSASeqAddr *addr) +{ + g_return_if_fail(self != NULL); + g_return_if_fail(addr != NULL); + + self->dest = *addr; +} + +static gboolean validate_event_tstamp_mode(const struct snd_seq_event *ev, + ALSASeqEventTstampMode mode, GError **error) +{ + int val = ev->flags & SNDRV_SEQ_TIME_STAMP_MASK; + + if (val != mode) { + return FALSE; + } + + return TRUE; +} + +/** + * alsaseq_event_get_tick_time: + * @self: A [struct@Event]. + * @tick_time: (out) (transfer none): The tick time of event. + * @error: A [struct@GLib.Error]. + * + * Get the tick time of event, available only when [method@Event.get_tstamp_mode is + * [enum@EventTstampMode].TICK. + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_tick_time(const ALSASeqEvent *self, guint *tick_time, GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(tick_time != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_tstamp_mode(self, ALSASEQ_EVENT_TSTAMP_MODE_TICK, error)) + return FALSE; + + *tick_time = self->time.tick; + + return TRUE; +} + +/** + * alsaseq_event_set_tick_time: + * @self: A [struct@Event]. + * @tick_time: The tick time of event. + * @error: A [struct@GLib.Error]. + * + * Copy the tick time to the event, available only when [method@Event.get_tstamp_mode results in + * [enum@EventTstampMode].TICK. + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_tick_time(ALSASeqEvent *self, guint tick_time, GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_tstamp_mode(self, ALSASEQ_EVENT_TSTAMP_MODE_TICK, error)) + return FALSE; + + self->time.tick = tick_time; + + return TRUE; +} + +/** + * alsaseq_event_get_real_time: + * @self: A [struct@Event]. + * @real_time: (array fixed-size=2) (out) (transfer none): The real time of event. + * @error: A [struct@GLib.Error]. + * + * Copy the real time to the event, available only when [method@Event.get_tstamp_mode] results in + * [enum@EventTstampMode].REAL. + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_real_time(const ALSASeqEvent *self, const guint32 *real_time[2], + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(real_time != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_tstamp_mode(self, ALSASEQ_EVENT_TSTAMP_MODE_REAL, error)) + return FALSE; + + *real_time = (const guint32 *)&self->time.time; + + return TRUE; +} + +/** + * alsaseq_event_set_real_time: + * @self: A [struct@Event]. + * @real_time: (array fixed-size=2): The real time of event. + * @error: A [struct@GLib.Error]. + * + * Copy the real time to the event, available only when [method@Event.get_tstamp_mode is + * [enum@EventTstampMode].REAL. + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_real_time(ALSASeqEvent *self, const guint32 real_time[2], GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(real_time != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_tstamp_mode(self, ALSASEQ_EVENT_TSTAMP_MODE_REAL, error)) + return FALSE; + + self->time.time.tv_sec = real_time[0]; + self->time.time.tv_nsec = real_time[1]; + + return TRUE; +} + +// NOTE: Some events for System V IPC was added in alsa-driver v0.5.0 and removed in alsa-driver +// v0.9.0. The event was designed to deliver key and length in `struct snd_seq_ev_ipc`. +// +// - 140: IPCSHM +// - 145: USR_VARIPC0 +// - 146: USR_VARIPC1 +// - 147: USR_VARIPC2 +// - 148: USR_VARIPC3 +// - 149: USR_VARIPC4 +// +// Some events for instrument memory bank was added in alsa-driver v0.3.0 and removed in +// Linux kernel v2.6.25. They were specific to some hardware electronic musical synthesizers. +// The data was delivered by two structures; `struct snd_seq_ev_inst_begin` and +// `struct snd_seq_ev_ext` with LENGTH_VARUSR flag for direct dispatch. +// +// - 100: INSTR_BEGIN +// - 101: INSTR_END +// - 102: INSTR_INFO +// - 103: INSTR_INFO_RESULT +// - 104: INSTR_FINFO +// - 105: INSTR_FINFO_RESULT +// - 106: INSTR_RESET +// - 107: INSTR_STATUS +// - 108: INSTR_STATUS_RESULT +// - 109: INSTR_PUT +// - 110: INSTR_GET +// - 111: INSTR_GET_RESULT +// - 112: INSTR_FREE +// - 113: INSTR_LIST +// - 114: INSTR_LIST_RESULT +// - 115: INSTR_CLUSTER +// - 116: INSTR_CLUSTER_GET +// - 117: INSTR_CLUSTER_RESULT +// - 118: INSTR_CHANGE +// +// Some events for hardware sampler function was added in alsa-driver v0.5.0 and removed in Linux +// kernel v2.6.25. The data was delivered by `struct snd_seq_ev_sample_control`. +// +// - 70: SAMPLE +// - 71: SAMPLE_CLUSTER +// - 72: SAMPLE_START +// - 73: SAMPLE_STOP +// - 74: SAMPLE_FREQ +// - 75: SAMPLE_VOLUME +// - 76: SAMPLE_LOOP +// - 77: SAMPLE_POSITION +// - 78: SAMPLE_PRIVATE1 +// + +enum seq_event_data_flag { + // The userspace application can decide the association between type and data voluntarily. + SEQ_EVENT_DATA_FLAG_USER = 0x00100000, + + // The event doesn't have explicit data. + SEQ_EVENT_DATA_FLAG_BLANK = 0x01000000, + + // This is not delivered to user space. It's jsut ignored to write by user space application. + SEQ_EVENT_DATA_FLAG_NONE = 0x02000000, + + // Fixed length of data. + SEQ_EVENT_DATA_FLAG_RESULT = 0x00000001, + SEQ_EVENT_DATA_FLAG_NOTE = 0x00000002, + SEQ_EVENT_DATA_FLAG_CONTROL = 0x00000004, + SEQ_EVENT_DATA_FLAG_QUEUE = 0x00000008, + SEQ_EVENT_DATA_FLAG_ADDR = 0x00000010, + SEQ_EVENT_DATA_FLAG_CONNECT = 0x00000020, + SEQ_EVENT_DATA_FLAG_BYTE = 0x00000040, + SEQ_EVENT_DATA_FLAG_QUADLET = 0x00000080, + SEQ_EVENT_DATA_FLAG_TSTAMP = 0x00000100, + SEQ_EVENT_DATA_FLAG_FIXED_ANY = 0x00000fff, + + // Variable length of data. + SEQ_EVENT_DATA_FLAG_BLOB = 0x00001000, + SEQ_EVENT_DATA_FLAG_VARIABLE_ANY = 0x0000f000, + + // Pointer to arbitrary data in user space. + SEQ_EVENT_DATA_FLAG_POINTER = 0x00010000, + SEQ_EVENT_DATA_FLAG_VARUSR_ANY = 0x000f0000, + + SEQ_EVENT_DATA_FLAG_ANY = 0x000fffff, +}; + +static const enum seq_event_data_flag seq_event_data_flags[] = { + [0 ... 4] = SEQ_EVENT_DATA_FLAG_RESULT, + [5 ... 9] = SEQ_EVENT_DATA_FLAG_NOTE, + [10 ... 24] = SEQ_EVENT_DATA_FLAG_CONTROL, + [25 ... 29] = SEQ_EVENT_DATA_FLAG_FIXED_ANY, + [30 ... 39] = SEQ_EVENT_DATA_FLAG_QUEUE, + [40 ... 49] = SEQ_EVENT_DATA_FLAG_BLANK, + [50 ... 59] = SEQ_EVENT_DATA_FLAG_ANY, // Used by kernel clients. + [60 ... 65] = SEQ_EVENT_DATA_FLAG_ADDR, + [66 ... 69] = SEQ_EVENT_DATA_FLAG_CONNECT, + [70 ... 79] = SEQ_EVENT_DATA_FLAG_FIXED_ANY, // For ancient sample type. + [80 ... 89] = SEQ_EVENT_DATA_FLAG_FIXED_ANY, // For ancient sample type. + [90 ... 99] = SEQ_EVENT_DATA_FLAG_FIXED_ANY | SEQ_EVENT_DATA_FLAG_USER, + [100 ... 109] = SEQ_EVENT_DATA_FLAG_FIXED_ANY, // For ancient instrument type. + [110 ... 119] = SEQ_EVENT_DATA_FLAG_FIXED_ANY, // For ancient instrument type. + [120 ... 129] = SEQ_EVENT_DATA_FLAG_FIXED_ANY, + [130 ... 134] = SEQ_EVENT_DATA_FLAG_BLOB, + [135 ... 139] = SEQ_EVENT_DATA_FLAG_VARIABLE_ANY | SEQ_EVENT_DATA_FLAG_USER, + [140 ... 149] = SEQ_EVENT_DATA_FLAG_VARIABLE_ANY, // For ancient IPC type. + [150 ... 254] = SEQ_EVENT_DATA_FLAG_ANY, // Used by kernel clients. + [255] = SEQ_EVENT_DATA_FLAG_NONE, +}; + +static gboolean validate_event_type(const struct snd_seq_event *ev, enum seq_event_data_flag flag, + GError **error) +{ + if ((seq_event_data_flags[ev->type] & flag) == 0) { + g_set_error_literal(error, ALSASEQ_EVENT_ERROR, ALSASEQ_EVENT_ERROR_INVALID_DATA_TYPE, + "The type of requested data is invalid in the event"); + return FALSE; + } + + return TRUE; +} + +static gboolean validate_event_type_and_length(const struct snd_seq_event *ev, + enum seq_event_data_flag flag, + GError **error) +{ + int expected, val = ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK; + + if (!validate_event_type(ev, flag, error)) + return FALSE; + + switch (flag) { + case SEQ_EVENT_DATA_FLAG_RESULT: + case SEQ_EVENT_DATA_FLAG_NOTE: + case SEQ_EVENT_DATA_FLAG_CONTROL: + case SEQ_EVENT_DATA_FLAG_QUEUE: + case SEQ_EVENT_DATA_FLAG_ADDR: + case SEQ_EVENT_DATA_FLAG_CONNECT: + case SEQ_EVENT_DATA_FLAG_BYTE: + case SEQ_EVENT_DATA_FLAG_QUADLET: + case SEQ_EVENT_DATA_FLAG_TSTAMP: + expected = SNDRV_SEQ_EVENT_LENGTH_FIXED; + break; + case SEQ_EVENT_DATA_FLAG_BLOB: + expected = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + break; + case SEQ_EVENT_DATA_FLAG_POINTER: + expected = SNDRV_SEQ_EVENT_LENGTH_VARUSR; + break; + default: + expected = 0xffffffff; + break; + } + + if (val != expected) { + g_set_error_literal(error, ALSASEQ_EVENT_ERROR, ALSASEQ_EVENT_ERROR_INVALID_LENGTH_MODE, + "The mode of length for requested data is invalid in the event"); + return FALSE; + } + + return TRUE; +} + +static void clear_data_fields(struct snd_seq_event *ev) +{ + free_blob_data(ev); + memset(&ev->data, 0, sizeof(ev->data)); +} + +static void ensure_fixed_length_event(struct snd_seq_event *ev) +{ + clear_data_fields(ev); + + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; +} + +static void ensure_variable_length_event(struct snd_seq_event *ev, const guint8 *buf, gsize length) +{ + guint8 *ptr; + + clear_data_fields(ev); + + ptr = g_malloc(length); + memcpy(ptr, buf, length); + + ev->data.ext.ptr = ptr; + ev->data.ext.len = length; + + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE; +} + +static void ensure_pointer_length_event(struct snd_seq_event *ev) +{ + clear_data_fields(ev); + + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARUSR; +} + +/** + * alsaseq_event_get_note_data: + * @self: A [struct@Event]. + * @data: (out) (transfer none): The note data of event. + * @error: A [struct@GLib.Error]. + * + * Refer to the note data, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].NOTE + * - [enum@EventType].NOTEON + * - [enum@EventType].NOTEOFF + * - [enum@EventType].KEYPRESS + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_note_data(const ALSASeqEvent *self, const ALSASeqEventDataNote **data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_NOTE, error)) + return FALSE; + + *data = (const ALSASeqEventDataNote *)&self->data.note; + + return TRUE; +} + +/** + * alsaseq_event_set_note_data: + * @self: A [struct@Event]. + * @data: The note data of event. + * @error: A [struct@GLib.Error]. + * + * Copy the note data, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].NOTE + * - [enum@EventType].NOTEON + * - [enum@EventType].NOTEOFF + * - [enum@EventType].KEYPRESS + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_note_data(ALSASeqEvent *self, const ALSASeqEventDataNote *data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_NOTE, error)) + return FALSE; + + ensure_fixed_length_event(self); + self->data.note = *(struct snd_seq_ev_note *)data; + + return TRUE; +} + +/** + * alsaseq_event_get_ctl_data: + * @self: A [struct@Event]. + * @data: (out) (transfer none): The control data of event. + * @error: A [struct@GLib.Error]. + * + * Refer to the control data, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].CONTROLLER + * - [enum@EventType].PGMCHANGE + * - [enum@EventType].CHANPRESS + * - [enum@EventType].PITCHBEND + * - [enum@EventType].CONTROL14 + * - [enum@EventType].NONREGPARAM + * - [enum@EventType].REGPARAM + * - [enum@EventType].SONGPOS + * - [enum@EventType].SONGSEL + * - [enum@EventType].QFRAME + * - [enum@EventType].TIMESIGN + * - [enum@EventType].KEYSIGN + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_ctl_data(const ALSASeqEvent *self, const ALSASeqEventDataCtl **data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_CONTROL, error)) + return FALSE; + + *data = (const ALSASeqEventDataCtl *)&self->data.control; + + return TRUE; +} + +/** + * alsaseq_event_set_ctl_data: + * @self: A [struct@Event]. + * @data: The control data of event. + * @error: A [struct@GLib.Error]. + * + * Copy the control data, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].CONTROLLER + * - [enum@EventType].PGMCHANGE + * - [enum@EventType].CHANPRESS + * - [enum@EventType].PITCHBEND + * - [enum@EventType].CONTROL14 + * - [enum@EventType].NONREGPARAM + * - [enum@EventType].REGPARAM + * - [enum@EventType].SONGPOS + * - [enum@EventType].SONGSEL + * - [enum@EventType].QFRAME + * - [enum@EventType].TIMESIGN + * - [enum@EventType].KEYSIGN + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_ctl_data(ALSASeqEvent *self, const ALSASeqEventDataCtl *data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_CONTROL, error)) + return FALSE; + + ensure_fixed_length_event(self); + self->data.control = *(struct snd_seq_ev_ctrl *)data; + + return TRUE; +} + +/** + * alsaseq_event_get_byte_data: + * @self: A [struct@Event]. + * @data: (array fixed-size=12) (out) (transfer none): The byte data of event. + * @error: A [struct@GLib.Error]. + * + * Get the byte data, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_byte_data(const ALSASeqEvent *self, const guint8 *data[12], + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_BYTE, error)) + return FALSE; + + // NOTE: PyGObject v3.42 aborts with NULL pointer to fixed array... + *data = self->data.raw8.d; + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_BYTE, error)) + return FALSE; + + return TRUE; +} + +/** + * alsaseq_event_set_byte_data: + * @self: A [struct@Event]. + * @data: (array fixed-size=12): The byte data of event. + * @error: A [struct@GLib.Error]. + * + * Copy the byte data, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_byte_data(ALSASeqEvent *self, const guint8 data[12], GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_BYTE, error)) + return FALSE; + + ensure_fixed_length_event(self); + memcpy(self->data.raw8.d, data, sizeof(self->data.raw8.d)); + + return TRUE; +} + +/** + * alsaseq_event_get_quadlet_data: + * @self: A [struct@Event]. + * @data: (array fixed-size=3) (out) (transfer none): The quadlet data of event. + * @error: A [struct@GLib.Error]. + * + * Get the quadlet data of event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_quadlet_data(const ALSASeqEvent *self, const guint32 *data[3], + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_QUADLET, error)) + return FALSE; + + // NOTE: PyGObject v3.42 aborts with NULL pointer to fixed array... + *data = self->data.raw32.d; + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_QUADLET, error)) + return FALSE; + + return TRUE; +} + +/** + * alsaseq_event_set_quadlet_data: + * @self: A [struct@Event]. + * @data: (array fixed-size=3): The quadlet data of event. + * @error: A [struct@GLib.Error]. + * + * Copy the quadlet data to the event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_quadlet_data(ALSASeqEvent *self, const guint32 data[3], GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_QUADLET, error)) + return FALSE; + + ensure_fixed_length_event(self); + memcpy(self->data.raw32.d, data, sizeof(self->data.raw32.d)); + + return TRUE; +} + +/** + * alsaseq_event_get_blob_data: + * @self: A [struct@Event]. + * @data: (array length=length) (out) (transfer none): The pointer to blob data. + * @length: The length of data in byte unit. + * @error: A [struct@GLib.Error]. + * + * Refer to the blob data, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].SYSEX + * - [enum@EventType].BOUNCE + * - [enum@EventType].USR_VAR0 + * - [enum@EventType].USR_VAR1 + * - [enum@EventType].USR_VAR2 + * - [enum@EventType].USR_VAR3 + * - [enum@EventType].USR_VAR4 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_blob_data(const ALSASeqEvent *self, const guint8 **data, gsize *length, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(length != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_BLOB, error)) + return FALSE; + + *data = self->data.ext.ptr; + *length = self->data.ext.len; + + return TRUE; +} + +/** + * alsaseq_event_set_blob_data: + * @self: A [struct@Event]. + * @data: (array length=length): The pointer to blob data for the event. + * @length: The length of data. + * @error: A [struct@GLib.Error]. + * + * Copy the quadlet data to the event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].SYSEX + * - [enum@EventType].BOUNCE + * - [enum@EventType].USR_VAR0 + * - [enum@EventType].USR_VAR1 + * - [enum@EventType].USR_VAR2 + * - [enum@EventType].USR_VAR3 + * - [enum@EventType].USR_VAR4 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_blob_data(ALSASeqEvent *self, const guint8 *data, gsize length, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_BLOB, error)) + return FALSE; + + ensure_variable_length_event(self, data, length); + + return TRUE; +} + +/** + * alsaseq_event_get_pointer_data: + * @self: A [struct@Event]. + * @data: (out) (transfer none): A pointer to user data. + * @length: (out): The length of user data. + * @error: A [struct@GLib.Error]. + * + * Get the pointer and length in data of event. It has no gurantee that the dereference of pointer + * is safe in VMA of user process, thus users have to know the purpose of the pointer in advance; + * e.g. inter-process communication between forked parent and child processes. + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_pointer_data(const ALSASeqEvent *self, gconstpointer *data, + gsize *length, GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(length != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_POINTER, error)) + return FALSE; + + *data = self->data.ext.ptr; + *length = self->data.ext.len; + + return TRUE; +} + +/** + * alsaseq_event_set_pointer_data: + * @self: A [struct@Event]. + * @data: A pointer to user data. + * @length: The length of user data. + * @error: A [struct@GLib.Error]. + * + * Copy the pointer and length to data of event. When using the type, the event should be deliverd + * directly without scheduling to queue. The aim of data pointed by it is arbitrary, thus + * programmer needs to decide protocol between transmitter and receiver in advance. + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_pointer_data(ALSASeqEvent *self, const gpointer data, gsize length, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(length > 0, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_POINTER, error)) + return FALSE; + + ensure_pointer_length_event(self); + self->data.ext.ptr = data; + self->data.ext.len = length; + + return TRUE; +} + +/** + * alsaseq_event_get_queue_data: + * @self: A [struct@Event]. + * @data: (out) (transfer none): The queue data of event. + * @error: A [struct@GLib.Error]. + * + * Get the queue data of event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].START + * - [enum@EventType].CONTINUE + * - [enum@EventType].STOP + * - [enum@EventType].SETPOS_TICK + * - [enum@EventType].SETPOS_TIME + * - [enum@EventType].TEMPO + * - [enum@EventType].CLOCK + * - [enum@EventType].TICK + * - [enum@EventType].QUEUE_SKEW + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_queue_data(const ALSASeqEvent *self, const ALSASeqEventDataQueue **data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_QUEUE, error)) + return FALSE; + + *data = (const ALSASeqEventDataQueue *)&self->data.queue; + + return TRUE; +} + +/** + * alsaseq_event_set_queue_data: + * @self: A [struct@Event]. + * @data: The queue data of event. + * @error: A [struct@GLib.Error]. + * + * Copy the queue data to the event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].START + * - [enum@EventType].CONTINUE + * - [enum@EventType].STOP + * - [enum@EventType].SETPOS_TICK + * - [enum@EventType].SETPOS_TIME + * - [enum@EventType].TEMPO + * - [enum@EventType].CLOCK + * - [enum@EventType].TICK + * - [enum@EventType].QUEUE_SKEW + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_queue_data(ALSASeqEvent *self, const ALSASeqEventDataQueue *data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_QUEUE, error)) + return FALSE; + + ensure_fixed_length_event(self); + self->data.queue = *(struct snd_seq_ev_queue_control *)data; + + return TRUE; +} + +/** + * alsaseq_event_get_tick_time_data: + * @self: A [struct@Event]. + * @tick_time: (out) (transfer none): The tick time data of event. + * + * Get the tick time data of event, available when [method@Event.get_tstamp_mode is + * [enum@EventTstampMode].TICK and [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_tick_time_data(const ALSASeqEvent *self, guint *tick_time, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(tick_time != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_TSTAMP, error)) + return FALSE; + + if (!validate_event_tstamp_mode(self, ALSASEQ_EVENT_TSTAMP_MODE_TICK, error)) + return FALSE; + + *tick_time = self->data.time.tick; + + return TRUE; +} + +/** + * alsaseq_event_set_tick_time_data: + * @self: A [struct@Event]. + * @tick_time: The tick time data of event. + * + * Copy the tick time data to the event, available when [method@Event.get_tstamp_mode is + * [enum@EventTstampMode].TICK and [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_tick_time_data(ALSASeqEvent *self, guint tick_time, GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_TSTAMP, error)) + return FALSE; + + if (!validate_event_tstamp_mode(self, ALSASEQ_EVENT_TSTAMP_MODE_TICK, error)) + return FALSE; + + ensure_fixed_length_event(self); + self->data.time.tick = tick_time; + + return TRUE; +} + +/** + * alsaseq_event_get_real_time_data: + * @self: A [struct@Event]. + * @real_time: (array fixed-size=2) (out) (transfer none): The real time data of event. + * + * Get the real time data of event, available when [method@Event.get_tstamp_mode is + * [enum@EventTstampMode].REAL and [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_real_time_data(const ALSASeqEvent *self, const guint32 *real_time[2], + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(real_time != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_TSTAMP, error)) + return FALSE; + + if (!validate_event_tstamp_mode(self, ALSASEQ_EVENT_TSTAMP_MODE_REAL, error)) + return FALSE; + + *real_time = (const guint32 *)&self->data.time.time; + + return TRUE; +} + +/** + * alsaseq_event_set_real_time_data: + * @self: A [struct@Event]. + * @real_time: (array fixed-size=2) : The real time data of event. + * + * Copy the real time data to the event, available [method@Event.get_tstamp_mode is + * [enum@EventTstampMode].REAL and [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_real_time_data(ALSASeqEvent *self, const guint32 real_time[2], + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(real_time != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_TSTAMP, error)) + return FALSE; + + if (!validate_event_tstamp_mode(self, ALSASEQ_EVENT_TSTAMP_MODE_REAL, error)) + return FALSE; + + ensure_fixed_length_event(self); + self->data.time.time.tv_sec = real_time[0]; + self->data.time.time.tv_nsec = real_time[1]; + + return TRUE; +} + +/** + * alsaseq_event_get_addr_data: + * @self: A [struct@Event]. + * @data: (out) (transfer none): The address data of event. + * @error: A [struct@GLib.Error]. + * + * Get the address data of event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].CLIENT_START + * - [enum@EventType].CLIENT_EXIT + * - [enum@EventType].CLIENT_CHANGE + * - [enum@EventType].PORT_START + * - [enum@EventType].PORT_EXIT + * - [enum@EventType].PORT_CHANGE + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_addr_data(const ALSASeqEvent *self, const ALSASeqAddr **data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_ADDR, error)) + return FALSE; + + *data = &self->data.addr; + + return TRUE; +} + +/** + * alsaseq_event_set_addr_data: + * @self: A [struct@Event]. + * @data: The address data of event. + * @error: A [struct@GLib.Error]. + * + * Get the address data of event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].CLIENT_START + * - [enum@EventType].CLIENT_EXIT + * - [enum@EventType].CLIENT_CHANGE + * - [enum@EventType].PORT_START + * - [enum@EventType].PORT_EXIT + * - [enum@EventType].PORT_CHANGE + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_addr_data(ALSASeqEvent *self, const ALSASeqAddr *data, GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_ADDR, error)) + return FALSE; + + ensure_fixed_length_event(self); + self->data.addr = *data; + + return TRUE; +} + +/** + * alsaseq_event_get_connect_data: + * @self: A [struct@Event]. + * @data: (out) (transfer none): The connect data of event. + * @error: A [struct@GLib.Error]. + * + * Get the connect data of event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].PORT_SUBSCRIBED + * - [enum@EventType].PORT_UNSUBSCRIBED + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_connect_data(const ALSASeqEvent *self, + const ALSASeqEventDataConnect **data, GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_CONNECT, error)) + return FALSE; + + *data = (const ALSASeqEventDataConnect *)&self->data.connect; + + return TRUE; +} + +/** + * alsaseq_event_set_connect_data: + * @self: A [struct@Event]. + * @data: The connect data of event. + * @error: A [struct@GLib.Error]. + * + * Copy the connect data to the event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].PORT_SUBSCRIBED + * - [enum@EventType].PORT_UNSUBSCRIBED + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_connect_data(ALSASeqEvent *self, const ALSASeqEventDataConnect *data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_CONNECT, error)) + return FALSE; + + ensure_fixed_length_event(self); + self->data.connect = *(struct snd_seq_connect *)data; + + return TRUE; +} + +/** + * alsaseq_event_get_result_data: + * @self: A [struct@Event]. + * @data: (out) (transfer none): The result data of event. + * @error: A [struct@GLib.Error]. + * + * Get the result data of event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].SYSTEM + * - [enum@EventType].RESULT + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_get_result_data(const ALSASeqEvent *self, + const ALSASeqEventDataResult **data, GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type_and_length(self, SEQ_EVENT_DATA_FLAG_RESULT, error)) + return FALSE; + + *data = (const ALSASeqEventDataResult *)&self->data.result; + + return TRUE; +} + +/** + * alsaseq_event_set_result_data: + * @self: A [struct@Event]. + * @data: The result data of event. + * @error: A [struct@GLib.Error]. + * + * Copy the result data to the event, available when [method@Event.get_event_type] results in one of: + * + * - [enum@EventType].SYSTEM + * - [enum@EventType].RESULT + * - [enum@EventType].USR0 + * - [enum@EventType].USR1 + * - [enum@EventType].USR2 + * - [enum@EventType].USR3 + * - [enum@EventType].USR4 + * - [enum@EventType].USR5 + * - [enum@EventType].USR6 + * - [enum@EventType].USR7 + * - [enum@EventType].USR8 + * - [enum@EventType].USR9 + * + * Returns: %TRUE when the overall operation finishes successfully, else %FALSE. + */ +gboolean alsaseq_event_set_result_data(ALSASeqEvent *self, const ALSASeqEventDataResult *data, + GError **error) +{ + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!validate_event_type(self, SEQ_EVENT_DATA_FLAG_RESULT, error)) + return FALSE; + + ensure_fixed_length_event(self); + self->data.result = *(struct snd_seq_result *)data; + + return TRUE; +} diff --git a/src/seq/event.h b/src/seq/event.h new file mode 100644 index 0000000..0d040f4 --- /dev/null +++ b/src/seq/event.h @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef __ALSA_GOBJECT_ALSASEQ_EVENT_H__ +#define __ALSA_GOBJECT_ALSASEQ_EVENT_H__ + +#include + +G_BEGIN_DECLS + +#define ALSASEQ_TYPE_EVENT (alsaseq_event_get_type()) + +typedef struct snd_seq_event ALSASeqEvent; + +GType alsaseq_event_get_type() G_GNUC_CONST; + +#define ALSASEQ_EVENT_ERROR (alsaseq_event_error_quark()) + +GQuark alsaseq_event_error_quark(); + +ALSASeqEvent *alsaseq_event_new(ALSASeqEventType event_type); + +void alsaseq_event_get_event_type(const ALSASeqEvent *self, ALSASeqEventType *event_type); + +void alsaseq_event_get_length_mode(const ALSASeqEvent *self, ALSASeqEventLengthMode *length_mode); +void alsaseq_event_get_tstamp_mode(const ALSASeqEvent *self, ALSASeqEventTstampMode *tstamp_mode); + +void alsaseq_event_get_time_mode(const ALSASeqEvent *self, ALSASeqEventTimeMode *time_mode); +void alsaseq_event_set_time_mode(ALSASeqEvent *self, ALSASeqEventTimeMode time_mode); + +void alsaseq_event_get_priority_mode(const ALSASeqEvent *self, + ALSASeqEventPriorityMode *priority_mode); +void alsaseq_event_set_priority_mode(ALSASeqEvent *self, ALSASeqEventPriorityMode priority_mode); + +void alsaseq_event_get_tag(const ALSASeqEvent *self, gint8 *tag); +void alsaseq_event_set_tag(ALSASeqEvent *self, gint8 tag); + +void alsaseq_event_get_queue_id(const ALSASeqEvent *self, guint8 *queue_id); +void alsaseq_event_set_queue_id(ALSASeqEvent *self, guint8 queue_id); + +void alsaseq_event_get_source(const ALSASeqEvent *self, const ALSASeqAddr **addr); +void alsaseq_event_set_source(ALSASeqEvent *self, const ALSASeqAddr *addr); + +void alsaseq_event_get_destination(const ALSASeqEvent *self, const ALSASeqAddr **addr); +void alsaseq_event_set_destination(ALSASeqEvent *self, const ALSASeqAddr *addr); + +gboolean alsaseq_event_get_tick_time(const ALSASeqEvent *self, guint *tick_time, GError **error); +gboolean alsaseq_event_set_tick_time(ALSASeqEvent *self, guint tick_time, GError **error); + +gboolean alsaseq_event_get_real_time(const ALSASeqEvent *self, const guint32 *real_time[2], + GError **error); +gboolean alsaseq_event_set_real_time(ALSASeqEvent *self, const guint32 real_time[2], + GError **error); + +gboolean alsaseq_event_get_note_data(const ALSASeqEvent *self, const ALSASeqEventDataNote **data, + GError **error); +gboolean alsaseq_event_set_note_data(ALSASeqEvent *self, const ALSASeqEventDataNote *data, + GError **error); + +gboolean alsaseq_event_get_ctl_data(const ALSASeqEvent *self, const ALSASeqEventDataCtl **data, + GError **error); +gboolean alsaseq_event_set_ctl_data(ALSASeqEvent *self, const ALSASeqEventDataCtl *data, + GError **error); + +gboolean alsaseq_event_get_byte_data(const ALSASeqEvent *self, const guint8 *data[12], + GError **error); +gboolean alsaseq_event_set_byte_data(ALSASeqEvent *self, const guint8 data[12], GError **error); + +gboolean alsaseq_event_get_quadlet_data(const ALSASeqEvent *self, const guint32 *data[3], + GError **error); +gboolean alsaseq_event_set_quadlet_data(ALSASeqEvent *self, const guint32 data[3], + GError **error); + +gboolean alsaseq_event_get_blob_data(const ALSASeqEvent *self, const guint8 **data, gsize *length, + GError **error); +gboolean alsaseq_event_set_blob_data(ALSASeqEvent *self, const guint8 *data, gsize length, + GError **error); + +gboolean alsaseq_event_get_pointer_data(const ALSASeqEvent *self, gconstpointer *data, + gsize *length, GError **error); +gboolean alsaseq_event_set_pointer_data(ALSASeqEvent *self, const gpointer data, gsize length, + GError **error); + +gboolean alsaseq_event_get_queue_data(const ALSASeqEvent *self, + const ALSASeqEventDataQueue **data, GError **error); +gboolean alsaseq_event_set_queue_data(ALSASeqEvent *self, const ALSASeqEventDataQueue *data, + GError **error); + +gboolean alsaseq_event_get_tick_time_data(const ALSASeqEvent *self, guint *tick_time, + GError **error); +gboolean alsaseq_event_set_tick_time_data(ALSASeqEvent *self, guint tick_time, GError **error); + +gboolean alsaseq_event_get_real_time_data(const ALSASeqEvent *self, const guint32 *real_time[2], + GError **error); +gboolean alsaseq_event_set_real_time_data(ALSASeqEvent *self, const guint32 real_time[2], + GError **error); + +gboolean alsaseq_event_get_addr_data(const ALSASeqEvent *self, const ALSASeqAddr **data, + GError **error); +gboolean alsaseq_event_set_addr_data(ALSASeqEvent *self, const ALSASeqAddr *data, GError **error); + +gboolean alsaseq_event_get_connect_data(const ALSASeqEvent *self, + const ALSASeqEventDataConnect **data, GError **error); +gboolean alsaseq_event_set_connect_data(ALSASeqEvent *self, const ALSASeqEventDataConnect *data, + GError **error); + +gboolean alsaseq_event_get_result_data(const ALSASeqEvent *self, + const ALSASeqEventDataResult **data, GError **error); +gboolean alsaseq_event_set_result_data(ALSASeqEvent *self, const ALSASeqEventDataResult *data, + GError **error); + +G_END_DECLS + +#endif diff --git a/src/seq/meson.build b/src/seq/meson.build index 24b8b11..2009f22 100644 --- a/src/seq/meson.build +++ b/src/seq/meson.build @@ -29,6 +29,7 @@ sources = files( 'event-cntr.c', 'queue-timer-common.c', 'queue-timer-alsa.c', + 'event.c', ) headers = files( @@ -52,6 +53,7 @@ headers = files( 'event-cntr.h', 'queue-timer-common.h', 'queue-timer-alsa.h', + 'event.h', ) privates = files( diff --git a/tests/alsaseq-enums b/tests/alsaseq-enums index 5be53fa..3a0ea6c 100644 --- a/tests/alsaseq-enums +++ b/tests/alsaseq-enums @@ -164,6 +164,13 @@ user_client_error_types = ( 'QUEUE_PERMISSION', ) +event_error_types = ( + 'FAILED', + 'INVALID_DATA_TYPE', + 'INVALID_LENGTH_MODE', + 'INVALID_TSTAMP_MODE', +) + types = { ALSASeq.SpecificAddress: specific_address_types, ALSASeq.SpecificClientId: specific_client_id_types, @@ -182,6 +189,7 @@ types = { ALSASeq.QueueTimerType: queue_timer_types, ALSASeq.RemoveFilterFlag: remove_filter_flags, ALSASeq.UserClientError: user_client_error_types, + ALSASeq.EventError: event_error_types, } for obj, types in types.items(): -- 2.47.3