diff --git a/spa/plugins/audiomixer/audiomixer.c b/spa/plugins/audiomixer/audiomixer.c index 328706fc3..4c03e4d08 100644 --- a/spa/plugins/audiomixer/audiomixer.c +++ b/spa/plugins/audiomixer/audiomixer.c @@ -29,16 +29,31 @@ #include #include #include +#include #include -#include "conv.h" +#include "mix-ops.h" #define NAME "audiomixer" #define MAX_BUFFERS 64 #define MAX_PORTS 128 +#define PORT_DEFAULT_VOLUME 1.0 +#define PORT_DEFAULT_MUTE false + +struct port_props { + double volume; + int32_t mute; +}; + +static void port_props_reset(struct port_props *props) +{ + props->volume = PORT_DEFAULT_VOLUME; + props->mute = PORT_DEFAULT_MUTE; +} + struct buffer { struct spa_list link; bool outstanding; @@ -51,8 +66,12 @@ struct buffer { struct port { bool valid; + struct port_props props; + struct spa_io_buffers *io; - struct spa_io_control_range *range; + struct spa_io_control_range *io_range; + double *io_volume; + int32_t *io_mute; struct spa_port_info info; @@ -68,6 +87,10 @@ struct port { struct type { uint32_t node; uint32_t format; + uint32_t prop_volume; + uint32_t prop_mute; + uint32_t io_prop_volume; + uint32_t io_prop_mute; struct spa_type_io io; struct spa_type_param param; struct spa_type_media_type media_type; @@ -79,12 +102,17 @@ struct type { struct spa_type_data data; struct spa_type_param_buffers param_buffers; struct spa_type_param_meta param_meta; + struct spa_type_param_io param_io; }; static inline void init_type(struct type *type, struct spa_type_map *map) { type->node = spa_type_map_get_id(map, SPA_TYPE__Node); type->format = spa_type_map_get_id(map, SPA_TYPE__Format); + type->prop_volume = spa_type_map_get_id(map, SPA_TYPE_PROPS__volume); + type->prop_mute = spa_type_map_get_id(map, SPA_TYPE_PROPS__mute); + type->io_prop_volume = spa_type_map_get_id(map, SPA_TYPE_IO_INPUT_PROP_BASE "volume"); + type->io_prop_mute = spa_type_map_get_id(map, SPA_TYPE_IO_INPUT_PROP_BASE "mute"); spa_type_io_map(map, &type->io); spa_type_param_map(map, &type->param); spa_type_media_type_map(map, &type->media_type); @@ -96,6 +124,7 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_data_map(map, &type->data); spa_type_param_buffers_map(map, &type->param_buffers); spa_type_param_meta_map(map, &type->param_meta); + spa_type_param_io_map(map, &type->param_io); } struct impl { @@ -121,8 +150,11 @@ struct impl { struct spa_audio_info format; uint32_t bpf; + mix_clear_func_t clear; mix_func_t copy; mix_func_t add; + mix_scale_func_t copy_scale; + mix_scale_func_t add_scale; bool started; }; @@ -250,6 +282,11 @@ static int impl_node_add_port(struct spa_node *node, enum spa_direction directio port = GET_IN_PORT (this, port_id); port->valid = true; + + port_props_reset(&port->props); + port->io_volume = &port->props.volume; + port->io_mute = &port->props.mute; + spa_list_init(&port->queue); port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | SPA_PORT_INFO_FLAG_REMOVABLE | @@ -411,7 +448,8 @@ impl_node_port_enum_params(struct spa_node *node, uint32_t list[] = { t->param.idEnumFormat, t->param.idFormat, t->param.idBuffers, - t->param.idMeta }; + t->param.idMeta, + t->param.idIO }; if (*index < SPA_N_ELEMENTS(list)) param = spa_pod_builder_object(&b, id, t->param.List, @@ -458,6 +496,45 @@ impl_node_port_enum_params(struct spa_node *node, return 0; } } + else if (id == t->param.idIO) { + struct port_props *p = &port->props; + + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param_io.IO, + ":", t->param_io.id, "I", t->io.Buffers, + ":", t->param_io.size, "i", sizeof(struct spa_io_buffers)); + break; + case 1: + param = spa_pod_builder_object(&b, + id, t->param_io.IO, + ":", t->param_io.id, "I", t->io.ControlRange, + ":", t->param_io.size, "i", sizeof(struct spa_io_control_range)); + break; + case 2: + if (direction == SPA_DIRECTION_OUTPUT) + return 0; + + param = spa_pod_builder_object(&b, + id, t->param_io.IO, + ":", t->param_io.id, "I", t->io_prop_volume, + ":", t->param_io.size, "i", sizeof(struct spa_pod_double), + ":", t->param_io.propId, "I", t->prop_volume, + ":", t->param_io.propType, "dr", p->volume, 2, 0.0, 10.0); + break; + case 3: + param = spa_pod_builder_object(&b, + id, t->param_io.IO, + ":", t->param_io.id, "I", t->io_prop_mute, + ":", t->param_io.size, "i", sizeof(struct spa_pod_bool), + ":", t->param_io.propId, "I", t->prop_mute, + ":", t->param_io.propType, "b", p->mute); + break; + default: + return 0; + } + } else return -ENOENT; @@ -517,13 +594,19 @@ static int port_set_format(struct spa_node *node, return -EINVAL; } else { if (info.info.raw.format == t->audio_format.S16) { - this->copy = this->ops.copy[CONV_S16_S16]; - this->add = this->ops.add[CONV_S16_S16]; + this->clear = this->ops.clear[FMT_S16]; + this->copy = this->ops.copy[FMT_S16]; + this->add = this->ops.add[FMT_S16]; + this->copy_scale = this->ops.copy_scale[FMT_S16]; + this->add_scale = this->ops.add_scale[FMT_S16]; this->bpf = sizeof(int16_t) * info.info.raw.channels; } else if (info.info.raw.format == t->audio_format.F32) { - this->copy = this->ops.copy[CONV_F32_F32]; - this->add = this->ops.add[CONV_F32_F32]; + this->clear = this->ops.clear[FMT_F32]; + this->copy = this->ops.copy[FMT_F32]; + this->add = this->ops.add[FMT_F32]; + this->copy_scale = this->ops.copy_scale[FMT_F32]; + this->add_scale = this->ops.add_scale[FMT_F32]; this->bpf = sizeof(float) * info.info.raw.channels; } else @@ -648,7 +731,17 @@ impl_node_port_set_io(struct spa_node *node, if (id == t->io.Buffers) port->io = data; else if (id == t->io.ControlRange) - port->range = data; + port->io_range = data; + else if (id == t->io_prop_volume && direction == SPA_DIRECTION_INPUT) + if (SPA_POD_TYPE(data) == SPA_POD_TYPE_DOUBLE) + port->io_volume = &SPA_POD_VALUE(struct spa_pod_double, data); + else + return -EINVAL; + else if (id == t->io_prop_mute && direction == SPA_DIRECTION_INPUT) + if (SPA_POD_TYPE(data) == SPA_POD_TYPE_BOOL) + port->io_mute = &SPA_POD_VALUE(struct spa_pod_bool, data); + else + return -EINVAL; else return -ENOENT; @@ -695,14 +788,15 @@ impl_node_port_send_command(struct spa_node *node, } static inline void -add_port_data(struct impl *this, void *out, size_t outsize, size_t next, struct port *port, int layer) +add_port_data(struct impl *this, void *out, size_t outsize, struct port *port, int layer) { size_t insize; struct buffer *b; uint32_t index, offset, len1, len2, maxsize; - mix_func_t mix = layer == 0 ? this->copy : this->add; struct spa_data *d; void *data; + double volume = *port->io_volume; + bool mute = *port->io_mute; b = spa_list_first(&port->queue, struct buffer, link); @@ -718,9 +812,30 @@ add_port_data(struct impl *this, void *out, size_t outsize, size_t next, struct offset = index % maxsize; len1 = SPA_MIN(outsize, maxsize - offset); - mix(out, SPA_MEMBER(data, offset, void), len1); - if ((len2 = outsize - len1) > 0) - mix(out + len1, data, len2); + len2 = outsize - len1; + + if (volume < 0.001 || mute) { + /* silence, for the first layer clear, otherwise do nothing */ + if (layer == 0) { + this->clear(out, len1); + if (len2 > 0) + this->clear(out + len1, len2); + } + } + else if (volume < 0.999 || volume > 1.001) { + mix_func_t mix = layer == 0 ? this->copy : this->add; + + mix(out, SPA_MEMBER(data, offset, void), len1); + if (len2 > 0) + mix(out + len1, data, len2); + } + else { + mix_scale_func_t mix = layer == 0 ? this->copy_scale : this->add_scale; + + mix(out, SPA_MEMBER(data, offset, void), volume, len1); + if (len2 > 0) + mix(out + len1, data, volume, len2); + } port->queued_bytes -= outsize; @@ -777,14 +892,14 @@ static int mix_output(struct impl *this, size_t n_bytes) if (in_port->io == NULL || in_port->n_buffers == 0) continue; - if (spa_list_is_empty(&in_port->queue)) { + if (in_port->queued_bytes == 0) { spa_log_warn(this->log, NAME " %p: underrun stream %d", this, i); - in_port->queued_bytes = 0; continue; } - add_port_data(this, SPA_MEMBER(od[0].data, offset, void), len1, len2, in_port, layer); + + add_port_data(this, SPA_MEMBER(od[0].data, offset, void), len1, in_port, layer); if (len2 > 0) - add_port_data(this, od[0].data, len2, 0, in_port, layer); + add_port_data(this, od[0].data, len2, in_port, layer); layer++; } @@ -905,8 +1020,8 @@ static int impl_node_process_output(struct spa_node *node) continue; if (inport->queued_bytes == 0) { - if (inport->range && outport->range) - *inport->range = *outport->range; + if (inport->io_range && outport->io_range) + *inport->io_range = *outport->io_range; inio->status = SPA_STATUS_NEED_BUFFER; } else { inio->status = SPA_STATUS_OK; diff --git a/spa/plugins/audiomixer/meson.build b/spa/plugins/audiomixer/meson.build index 762941453..a9c8dd316 100644 --- a/spa/plugins/audiomixer/meson.build +++ b/spa/plugins/audiomixer/meson.build @@ -1,4 +1,4 @@ -audiomixer_sources = ['audiomixer.c', 'conv.c', 'plugin.c'] +audiomixer_sources = ['audiomixer.c', 'mix-ops.c', 'plugin.c'] audiomixerlib = shared_library('spa-audiomixer', audiomixer_sources, diff --git a/spa/plugins/audiomixer/conv.c b/spa/plugins/audiomixer/mix-ops.c similarity index 56% rename from spa/plugins/audiomixer/conv.c rename to spa/plugins/audiomixer/mix-ops.c index 50610e766..f3b9327a9 100644 --- a/spa/plugins/audiomixer/conv.c +++ b/spa/plugins/audiomixer/mix-ops.c @@ -17,22 +17,34 @@ * Boston, MA 02110-1301, USA. */ -#include "conv.h" +#include "mix-ops.h" static void -copy_s16_s16(void *dst, const void *src, int n_bytes) +clear_s16(void *dst, int n_bytes) +{ + memset(dst, 0, n_bytes); +} + +static void +clear_f32(void *dst, int n_bytes) +{ + memset(dst, 0, n_bytes); +} + +static void +copy_s16(void *dst, const void *src, int n_bytes) { memcpy(dst, src, n_bytes); } static void -copy_f32_f32(void *dst, const void *src, int n_bytes) +copy_f32(void *dst, const void *src, int n_bytes) { memcpy(dst, src, n_bytes); } static void -add_s16_s16(void *dst, const void *src, int n_bytes) +add_s16(void *dst, const void *src, int n_bytes) { const int16_t *s = src; int16_t *d = dst; @@ -48,7 +60,7 @@ add_s16_s16(void *dst, const void *src, int n_bytes) } static void -add_f32_f32(void *dst, const void *src, int n_bytes) +add_f32(void *dst, const void *src, int n_bytes) { const float *s = src; float *d = dst; @@ -62,15 +74,15 @@ add_f32_f32(void *dst, const void *src, int n_bytes) } static void -copy_scale_s16_s16(void *dst, const void *src, const void *scale, int n_bytes) +copy_scale_s16(void *dst, const void *src, const double scale, int n_bytes) { const int16_t *s = src; int16_t *d = dst;; - int32_t v = *(int16_t*)scale, t; + int32_t v = scale * (1 << 11), t; n_bytes /= sizeof(int16_t); while (n_bytes--) { - t = (*s * v) >> 16; + t = (*s * v) >> 11; *d = SPA_CLAMP(t, INT16_MIN, INT16_MAX); d++; s++; @@ -78,11 +90,11 @@ copy_scale_s16_s16(void *dst, const void *src, const void *scale, int n_bytes) } static void -copy_scale_f32_f32(void *dst, const void *src, const void *scale, int n_bytes) +copy_scale_f32(void *dst, const void *src, const double scale, int n_bytes) { const float *s = src; float *d = dst; - float v = *(float*)scale; + float v = scale; n_bytes /= sizeof(float); while (n_bytes--) { @@ -93,15 +105,15 @@ copy_scale_f32_f32(void *dst, const void *src, const void *scale, int n_bytes) } static void -add_scale_s16_s16(void *dst, const void *src, const void *scale, int n_bytes) +add_scale_s16(void *dst, const void *src, const double scale, int n_bytes) { const int16_t *s = src; int16_t *d = dst; - int32_t v = *(int16_t*)scale, t; + int32_t v = scale * (1 << 11), t; n_bytes /= sizeof(int16_t); while (n_bytes--) { - t = *d + ((*s * v) >> 16); + t = *d + ((*s * v) >> 11); *d = SPA_CLAMP(t, INT16_MIN, INT16_MAX); d++; s++; @@ -109,11 +121,11 @@ add_scale_s16_s16(void *dst, const void *src, const void *scale, int n_bytes) } static void -add_scale_f32_f32(void *dst, const void *src, const void *scale, int n_bytes) +add_scale_f32(void *dst, const void *src, const double scale, int n_bytes) { const float *s = src; float *d = dst; - float v = *(float*)scale; + float v = scale; n_bytes /= sizeof(float); while (n_bytes--) { @@ -124,7 +136,7 @@ add_scale_f32_f32(void *dst, const void *src, const void *scale, int n_bytes) } static void -copy_s16_s16_i(void *dst, int dst_stride, const void *src, int src_stride, int n_bytes) +copy_s16_i(void *dst, int dst_stride, const void *src, int src_stride, int n_bytes) { const int16_t *s = src; int16_t *d = dst; @@ -138,7 +150,7 @@ copy_s16_s16_i(void *dst, int dst_stride, const void *src, int src_stride, int n } static void -copy_f32_f32_i(void *dst, int dst_stride, const void *src, int src_stride, int n_bytes) +copy_f32_i(void *dst, int dst_stride, const void *src, int src_stride, int n_bytes) { const float *s = src; float *d = dst; @@ -152,7 +164,7 @@ copy_f32_f32_i(void *dst, int dst_stride, const void *src, int src_stride, int n } static void -add_s16_s16_i(void *dst, int dst_stride, const void *src, int src_stride, int n_bytes) +add_s16_i(void *dst, int dst_stride, const void *src, int src_stride, int n_bytes) { const int16_t *s = src; int16_t *d = dst; @@ -168,7 +180,7 @@ add_s16_s16_i(void *dst, int dst_stride, const void *src, int src_stride, int n_ } static void -add_f32_f32_i(void *dst, int dst_stride, const void *src, int src_stride, int n_bytes) +add_f32_i(void *dst, int dst_stride, const void *src, int src_stride, int n_bytes) { const float *s = src; float *d = dst; @@ -182,15 +194,15 @@ add_f32_f32_i(void *dst, int dst_stride, const void *src, int src_stride, int n_ } static void -copy_scale_s16_s16_i(void *dst, int dst_stride, const void *src, int src_stride, const void *scale, int n_bytes) +copy_scale_s16_i(void *dst, int dst_stride, const void *src, int src_stride, const double scale, int n_bytes) { const int16_t *s = src; int16_t *d = dst; - int32_t v = *(int16_t*)scale, t; + int32_t v = scale * (1 << 11), t; n_bytes /= sizeof(int16_t); while (n_bytes--) { - t = (*s * v) >> 16; + t = (*s * v) >> 11; *d = SPA_CLAMP(t, INT16_MIN, INT16_MAX); d += dst_stride; s += src_stride; @@ -198,11 +210,11 @@ copy_scale_s16_s16_i(void *dst, int dst_stride, const void *src, int src_stride, } static void -copy_scale_f32_f32_i(void *dst, int dst_stride, const void *src, int src_stride, const void *scale, int n_bytes) +copy_scale_f32_i(void *dst, int dst_stride, const void *src, int src_stride, const double scale, int n_bytes) { const float *s = src; float *d = dst; - float v = *(float*)scale; + float v = scale; n_bytes /= sizeof(float); while (n_bytes--) { @@ -213,15 +225,15 @@ copy_scale_f32_f32_i(void *dst, int dst_stride, const void *src, int src_stride, } static void -add_scale_s16_s16_i(void *dst, int dst_stride, const void *src, int src_stride, const void *scale, int n_bytes) +add_scale_s16_i(void *dst, int dst_stride, const void *src, int src_stride, const double scale, int n_bytes) { const int16_t *s = src; int16_t *d = dst; - int32_t v = *(int16_t*)scale, t; + int32_t v = scale * (1 << 11), t; n_bytes /= sizeof(int16_t); while (n_bytes--) { - t = *d + ((*s * v) >> 16); + t = *d + ((*s * v) >> 11); *d = SPA_CLAMP(t, INT16_MIN, INT16_MAX); d += dst_stride; s += src_stride; @@ -229,11 +241,11 @@ add_scale_s16_s16_i(void *dst, int dst_stride, const void *src, int src_stride, } static void -add_scale_f32_f32_i(void *dst, int dst_stride, const void *src, int src_stride, const void *scale, int n_bytes) +add_scale_f32_i(void *dst, int dst_stride, const void *src, int src_stride, const double scale, int n_bytes) { const float *s = src; float *d = dst; - float v = *(float*)scale; + float v = scale; n_bytes /= sizeof(float); while (n_bytes--) { @@ -245,20 +257,22 @@ add_scale_f32_f32_i(void *dst, int dst_stride, const void *src, int src_stride, void spa_audiomixer_get_ops(struct spa_audiomixer_ops *ops) { - ops->copy[CONV_S16_S16] = copy_s16_s16; - ops->copy[CONV_F32_F32] = copy_f32_f32; - ops->add[CONV_S16_S16] = add_s16_s16; - ops->add[CONV_F32_F32] = add_f32_f32; - ops->copy_scale[CONV_S16_S16] = copy_scale_s16_s16; - ops->copy_scale[CONV_F32_F32] = copy_scale_f32_f32; - ops->add_scale[CONV_S16_S16] = add_scale_s16_s16; - ops->add_scale[CONV_F32_F32] = add_scale_f32_f32; - ops->copy_i[CONV_S16_S16] = copy_s16_s16_i; - ops->copy_i[CONV_F32_F32] = copy_f32_f32_i; - ops->add_i[CONV_S16_S16] = add_s16_s16_i; - ops->add_i[CONV_F32_F32] = add_f32_f32_i; - ops->copy_scale_i[CONV_S16_S16] = copy_scale_s16_s16_i; - ops->copy_scale_i[CONV_F32_F32] = copy_scale_f32_f32_i; - ops->add_scale_i[CONV_S16_S16] = add_scale_s16_s16_i; - ops->add_scale_i[CONV_F32_F32] = add_scale_f32_f32_i; + ops->clear[FMT_S16] = clear_s16; + ops->clear[FMT_F32] = clear_f32; + ops->copy[FMT_S16] = copy_s16; + ops->copy[FMT_F32] = copy_f32; + ops->add[FMT_S16] = add_s16; + ops->add[FMT_F32] = add_f32; + ops->copy_scale[FMT_S16] = copy_scale_s16; + ops->copy_scale[FMT_F32] = copy_scale_f32; + ops->add_scale[FMT_S16] = add_scale_s16; + ops->add_scale[FMT_F32] = add_scale_f32; + ops->copy_i[FMT_S16] = copy_s16_i; + ops->copy_i[FMT_F32] = copy_f32_i; + ops->add_i[FMT_S16] = add_s16_i; + ops->add_i[FMT_F32] = add_f32_i; + ops->copy_scale_i[FMT_S16] = copy_scale_s16_i; + ops->copy_scale_i[FMT_F32] = copy_scale_f32_i; + ops->add_scale_i[FMT_S16] = add_scale_s16_i; + ops->add_scale_i[FMT_F32] = add_scale_f32_i; } diff --git a/spa/plugins/audiomixer/conv.h b/spa/plugins/audiomixer/mix-ops.h similarity index 71% rename from spa/plugins/audiomixer/conv.h rename to spa/plugins/audiomixer/mix-ops.h index 37490b47c..fd4d6a4b7 100644 --- a/spa/plugins/audiomixer/conv.h +++ b/spa/plugins/audiomixer/mix-ops.h @@ -22,28 +22,30 @@ #include +typedef void (*mix_clear_func_t) (void *dst, int n_bytes); typedef void (*mix_func_t) (void *dst, const void *src, int n_bytes); -typedef void (*mix_scale_func_t) (void *dst, const void *src, const void *scale, int n_bytes); +typedef void (*mix_scale_func_t) (void *dst, const void *src, const double scale, int n_bytes); typedef void (*mix_i_func_t) (void *dst, int dst_stride, const void *src, int src_stride, int n_bytes); typedef void (*mix_scale_i_func_t) (void *dst, int dst_stride, - const void *src, int src_stride, const void *scale, int n_bytes); + const void *src, int src_stride, const double scale, int n_bytes); enum { - CONV_S16_S16, - CONV_F32_F32, - CONV_MAX, + FMT_S16, + FMT_F32, + FMT_MAX, }; struct spa_audiomixer_ops { - mix_func_t copy[CONV_MAX]; - mix_func_t add[CONV_MAX]; - mix_scale_func_t copy_scale[CONV_MAX]; - mix_scale_func_t add_scale[CONV_MAX]; - mix_i_func_t copy_i[CONV_MAX]; - mix_i_func_t add_i[CONV_MAX]; - mix_scale_i_func_t copy_scale_i[CONV_MAX]; - mix_scale_i_func_t add_scale_i[CONV_MAX]; + mix_clear_func_t clear[FMT_MAX]; + mix_func_t copy[FMT_MAX]; + mix_func_t add[FMT_MAX]; + mix_scale_func_t copy_scale[FMT_MAX]; + mix_scale_func_t add_scale[FMT_MAX]; + mix_i_func_t copy_i[FMT_MAX]; + mix_i_func_t add_i[FMT_MAX]; + mix_scale_i_func_t copy_scale_i[FMT_MAX]; + mix_scale_i_func_t add_scale_i[FMT_MAX]; }; void spa_audiomixer_get_ops(struct spa_audiomixer_ops *ops); diff --git a/spa/plugins/audiotestsrc/audiotestsrc.c b/spa/plugins/audiotestsrc/audiotestsrc.c index 0e938df5b..2e940e3c1 100644 --- a/spa/plugins/audiotestsrc/audiotestsrc.c +++ b/spa/plugins/audiotestsrc/audiotestsrc.c @@ -680,7 +680,7 @@ impl_node_port_enum_params(struct spa_node *node, param = spa_pod_builder_object(&b, id, t->param_io.IO, ":", t->param_io.id, "I", t->io_prop_wave, - ":", t->param_io.size, "i", sizeof(uint32_t), + ":", t->param_io.size, "i", sizeof(struct spa_pod_id), ":", t->param_io.propId, "I", t->prop_wave, ":", t->param_io.propType, "Ie", p->wave, 2, t->wave_sine, @@ -690,7 +690,7 @@ impl_node_port_enum_params(struct spa_node *node, param = spa_pod_builder_object(&b, id, t->param_io.IO, ":", t->param_io.id, "I", t->io_prop_freq, - ":", t->param_io.size, "i", sizeof(double), + ":", t->param_io.size, "i", sizeof(struct spa_pod_double), ":", t->param_io.propId, "I", t->prop_freq, ":", t->param_io.propType, "dr", p->freq, 2, 0.0, 50000000.0); break; @@ -698,7 +698,7 @@ impl_node_port_enum_params(struct spa_node *node, param = spa_pod_builder_object(&b, id, t->param_io.IO, ":", t->param_io.id, "I", t->io_prop_volume, - ":", t->param_io.size, "i", sizeof(double), + ":", t->param_io.size, "i", sizeof(struct spa_pod_double), ":", t->param_io.propId, "I", t->prop_volume, ":", t->param_io.propType, "dr", p->volume, 2, 0.0, 10.0); break;