summaryrefslogtreecommitdiff
path: root/sound/core/seq/seq_timer.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2024-07-05 18:03:42 +0200
committerTakashi Iwai <tiwai@suse.de>2024-07-06 09:38:27 +0200
commitfefbbdfb59d3a20fd98734363e6dd9fa7cc65c70 (patch)
tree061d4b19a5afb6974d1011e412edf6e0d0d39436 /sound/core/seq/seq_timer.c
parenta892b700e63bcb3679ebed1a0c3160e7b9225138 (diff)
ALSA: seq: Add tempo base unit for MIDI2 Set Tempo messages
MIDI2 Set Tempo message defines the tempo in 10ns unit for finer accuracy, while MIDI1 was defined in 1us unit. For adapting this different unit, introduce "tempo_base" field to snd_seq_queue_tempo struct so that user-space can pass the proper tempo base unit. The accepted value is limited, it must be either 0, 10 or 1000. The protocol version is bumped to 1.0.4 along with this. The access with the older protocol version ignores the tempo-base value in ioctls and always treats as 1000. Reviewed-by: Jaroslav Kysela <perex@perex.cz> Link: https://patch.msgid.link/20240705160344.6481-1-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/core/seq/seq_timer.c')
-rw-r--r--sound/core/seq/seq_timer.c21
1 files changed, 15 insertions, 6 deletions
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index ad2b97e2762d..c9f0392ac7f1 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -20,14 +20,17 @@
static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
{
- if (tmr->tempo < 1000000)
- tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;
+ unsigned int threshold =
+ tmr->tempo_base == 1000 ? 1000000 : 10000;
+
+ if (tmr->tempo < threshold)
+ tmr->tick.resolution = (tmr->tempo * tmr->tempo_base) / tmr->ppq;
else {
/* might overflow.. */
unsigned int s;
s = tmr->tempo % tmr->ppq;
- s = (s * 1000) / tmr->ppq;
- tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;
+ s = (s * tmr->tempo_base) / tmr->ppq;
+ tmr->tick.resolution = (tmr->tempo / tmr->ppq) * tmr->tempo_base;
tmr->tick.resolution += s;
}
if (tmr->tick.resolution <= 0)
@@ -79,6 +82,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
/* setup defaults */
tmr->ppq = 96; /* 96 PPQ */
tmr->tempo = 500000; /* 120 BPM */
+ tmr->tempo_base = 1000; /* 1us */
snd_seq_timer_set_tick_resolution(tmr);
tmr->running = 0;
@@ -164,8 +168,9 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
return 0;
}
-/* set current tempo and ppq in a shot */
-int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
+/* set current tempo, ppq and base in a shot */
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
+ unsigned int tempo_base)
{
int changed;
@@ -173,6 +178,9 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
return -EINVAL;
if (tempo <= 0 || ppq <= 0)
return -EINVAL;
+ /* allow only 10ns or 1us tempo base for now */
+ if (tempo_base && tempo_base != 10 && tempo_base != 1000)
+ return -EINVAL;
guard(spinlock_irqsave)(&tmr->lock);
if (tmr->running && (ppq != tmr->ppq)) {
/* refuse to change ppq on running timers */
@@ -183,6 +191,7 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
changed = (tempo != tmr->tempo) || (ppq != tmr->ppq);
tmr->tempo = tempo;
tmr->ppq = ppq;
+ tmr->tempo_base = tempo_base ? tempo_base : 1000;
if (changed)
snd_seq_timer_set_tick_resolution(tmr);
return 0;