From 37224ac84cf79023e8a87e34896b065f1fb66c00 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 13 May 2024 15:18:22 +0200 Subject: [PATCH] alsa-seq: configure pool sizes better The default kernel pool size on the input is 200 cells. A cell is about 28 bytes long so the maximum message that can be received in one go is about 5600 bytes. This causes problems when using amidi to upload larger sysex messages because they simply can't be received by the sequencer. It if however possible to increase this limit with the set_client_pool() function. Increase the pool size to at least the quantum_limit * 2. This ensures we can receive and send at least 2 quantums of raw data, which should be a fairly long sysex message. Make a min and max value for the pool size. There is an upper limit of 2000 in the kernel but make this configurable and clamp the final pool size to the min/max. Make the MAX_EVENT_SIZE 256, because this is how the sequencer seems to splits the input data as well and it results in less wasted space in the output buffer. See #4005 --- spa/plugins/alsa/alsa-seq-bridge.c | 15 ++++++++++++++- spa/plugins/alsa/alsa-seq.c | 27 +++++++++++++++++++++++++++ spa/plugins/alsa/alsa-seq.h | 4 +++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/spa/plugins/alsa/alsa-seq-bridge.c b/spa/plugins/alsa/alsa-seq-bridge.c index f02c985fd..748b4fd5c 100644 --- a/spa/plugins/alsa/alsa-seq-bridge.c +++ b/spa/plugins/alsa/alsa-seq-bridge.c @@ -928,6 +928,8 @@ impl_init(const struct spa_handle_factory *factory, reset_props(&this->props); this->quantum_limit = 8192; + this->min_pool_size = 500; + this->max_pool_size = 2000; for (i = 0; info && i < info->n_items; i++) { const char *k = info->items[i].key; @@ -942,6 +944,10 @@ impl_init(const struct spa_handle_factory *factory, spa_atou32(s, &this->quantum_limit, 0); } else if (spa_streq(k, SPA_KEY_API_ALSA_DISABLE_LONGNAME)) { this->props.disable_longname = spa_atob(s); + } else if (spa_streq(k, "api.alsa.seq.min-pool")) { + spa_atou32(s, &this->min_pool_size, 0); + } else if (spa_streq(k, "api.alsa.seq.max-pool")) { + spa_atou32(s, &this->max_pool_size, 0); } } @@ -978,7 +984,14 @@ impl_enum_interface_info(const struct spa_handle_factory *factory, static const struct spa_dict_item info_items[] = { { SPA_KEY_FACTORY_AUTHOR, "Wim Taymans " }, { SPA_KEY_FACTORY_DESCRIPTION, "Bridge midi ports with the alsa sequencer API" }, - { SPA_KEY_FACTORY_USAGE, "["SPA_KEY_API_ALSA_PATH"=]" }, + { SPA_KEY_FACTORY_USAGE, + "["SPA_KEY_API_ALSA_PATH"=] " + "[ clock.name=] " + "[ clock.quantum-limit=] " + "["SPA_KEY_API_ALSA_DISABLE_LONGNAME"=] " + "[ api.alsa.seq.min-pool=] " + "[ api.alsa.seq.max-pool=]" + }, }; static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); diff --git a/spa/plugins/alsa/alsa-seq.c b/spa/plugins/alsa/alsa-seq.c index 278458e7b..f19b5720c 100644 --- a/spa/plugins/alsa/alsa-seq.c +++ b/spa/plugins/alsa/alsa-seq.c @@ -250,7 +250,9 @@ int spa_alsa_seq_open(struct seq_state *state) snd_seq_port_subscribe_t *sub; snd_seq_addr_t addr; snd_seq_queue_timer_t *timer; + snd_seq_client_pool_t *pool; struct seq_conn reserve[16]; + size_t pool_size; if (state->opened) return 0; @@ -319,6 +321,31 @@ int spa_alsa_seq_open(struct seq_state *state) spa_log_warn(state->log, "failed to set queue timer: %s", snd_strerror(res)); } + /* Increase client pool sizes. This determines the max sysex message that + * can be received. */ + snd_seq_client_pool_alloca(&pool); + if ((res = snd_seq_get_client_pool(state->event.hndl, pool)) < 0) { + spa_log_warn(state->log, "failed to get pool: %s", snd_strerror(res)); + } else { + /* make sure we at least use the default size */ + pool_size = snd_seq_client_pool_get_output_pool(pool); + pool_size = SPA_MAX(pool_size, snd_seq_client_pool_get_input_pool(pool)); + + /* The pool size is in cells, which are about 24 bytes long. Try to + * make sure we can fit sysex of at least twice the quantum limit. */ + pool_size = SPA_MAX(pool_size, state->quantum_limit * 2 / 24); + /* The kernel ignores values larger than 2000 (by default) so clamp + * this here. It's configurable in case the kernel was modified. */ + pool_size = SPA_CLAMP(pool_size, state->min_pool_size, state->max_pool_size); + + snd_seq_client_pool_set_input_pool(pool, pool_size); + snd_seq_client_pool_set_output_pool(pool, pool_size); + + if ((res = snd_seq_set_client_pool(state->event.hndl, pool)) < 0) { + spa_log_warn(state->log, "failed to set pool: %s", snd_strerror(res)); + } + } + init_ports(state); if ((res = spa_system_timerfd_create(state->data_system, diff --git a/spa/plugins/alsa/alsa-seq.h b/spa/plugins/alsa/alsa-seq.h index d44ad6258..23f71d639 100644 --- a/spa/plugins/alsa/alsa-seq.h +++ b/spa/plugins/alsa/alsa-seq.h @@ -35,7 +35,7 @@ struct props { bool disable_longname; }; -#define MAX_EVENT_SIZE 1024 +#define MAX_EVENT_SIZE 256 #define MAX_PORTS 256 #define MAX_BUFFERS 32 @@ -132,6 +132,8 @@ struct seq_state { struct spa_io_position *position; uint32_t quantum_limit; + uint32_t min_pool_size; + uint32_t max_pool_size; int rate_denom; uint32_t duration;