History

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.

Takashi Iwai committed 2 years ago

Commit fefbbdf1 parent a892b705 files · +30 −12Browse files

Unified · Split

5 files
  1. include/uapi/sound/asequencer.h +4 −3
  2. sound/core/seq/seq_clientmgr.c +4 −0
  3. sound/core/seq/seq_queue.c +4 −2
  4. sound/core/seq/seq_timer.c +15 −6
  5. sound/core/seq/seq_timer.h +3 −1
include/uapi/sound/asequencer.h+4−3
@@ -10,7 +10,7 @@ · 9 unmodified lines
10#include <sound/asound.h>10#include <sound/asound.h>
11 11
12/** version of the sequencer */12/** version of the sequencer */
13#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3)13#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 4)
14 14
15/**15/**
16 * definition of sequencer event types16 * definition of sequencer event types
@@ -523,11 +523,12 @@ struct snd_seq_queue_status { · 506 unmodified lines
523/* queue tempo */523/* queue tempo */
524struct snd_seq_queue_tempo {524struct snd_seq_queue_tempo {
525 int queue; /* sequencer queue */525 int queue; /* sequencer queue */
526 unsigned int tempo; /* current tempo, us/tick */526 unsigned int tempo; /* current tempo, us/tick (or different time-base below) */
527 int ppq; /* time resolution, ticks/quarter */527 int ppq; /* time resolution, ticks/quarter */
528 unsigned int skew_value; /* queue skew */528 unsigned int skew_value; /* queue skew */
529 unsigned int skew_base; /* queue skew base */529 unsigned int skew_base; /* queue skew base */
530 char reserved[24]; /* for the future */530 unsigned short tempo_base; /* tempo base in nsec unit; either 10 or 1000 */
531 char reserved[22]; /* for the future */
531};532};
532 533
533 534
sound/core/seq/seq_clientmgr.c+4−0
@@ -1718,6 +1718,8 @@ static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client, · 1717 unmodified lines
1718 tempo->ppq = tmr->ppq;1718 tempo->ppq = tmr->ppq;
1719 tempo->skew_value = tmr->skew;1719 tempo->skew_value = tmr->skew;
1720 tempo->skew_base = tmr->skew_base;1720 tempo->skew_base = tmr->skew_base;
1721 if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 4))
1722 tempo->tempo_base = tmr->tempo_base;
1721 queuefree(queue);1723 queuefree(queue);
1722 1724
1723 return 0;1725 return 0;
@@ -1739,6 +1741,8 @@ static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client, · 15 unmodified lines
1739 struct snd_seq_queue_tempo *tempo = arg;1741 struct snd_seq_queue_tempo *tempo = arg;
1740 int result;1742 int result;
1741 1743
1744 if (client->user_pversion < SNDRV_PROTOCOL_VERSION(1, 0, 4))
1745 tempo->tempo_base = 0;
1742 result = snd_seq_set_queue_tempo(client->number, tempo);1746 result = snd_seq_set_queue_tempo(client->number, tempo);
1743 return result < 0 ? result : 0;1747 return result < 0 ? result : 0;
1744}1748}
sound/core/seq/seq_queue.c+4−2
@@ -460,7 +460,8 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client, · 459 unmodified lines
460 return -EPERM;460 return -EPERM;
461 }461 }
462 462
463 result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq);463 result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq,
464 info->tempo_base);
464 if (result >= 0 && info->skew_base > 0)465 if (result >= 0 && info->skew_base > 0)
465 result = snd_seq_timer_set_skew(q->timer, info->skew_value,466 result = snd_seq_timer_set_skew(q->timer, info->skew_value,
466 info->skew_base);467 info->skew_base);
@@ -724,7 +725,7 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, · 257 unmodified lines
724 725
725 tmr = q->timer;726 tmr = q->timer;
726 if (tmr->tempo)727 if (tmr->tempo)
727 bpm = 60000000 / tmr->tempo;728 bpm = (60000 * tmr->tempo_base) / tmr->tempo;
728 else729 else
729 bpm = 0;730 bpm = 0;
730 731
@@ -741,6 +742,7 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, · 10 unmodified lines
741 snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");742 snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");
742 snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq);743 snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq);
743 snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo);744 snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo);
745 snd_iprintf(buffer, "tempo base : %d ns\n", tmr->tempo_base);
744 snd_iprintf(buffer, "current BPM : %d\n", bpm);746 snd_iprintf(buffer, "current BPM : %d\n", bpm);
745 snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);747 snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
746 snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick);748 snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick);
sound/core/seq/seq_timer.c+15−6
@@ -20,14 +20,17 @@ · 19 unmodified lines
20 20
21static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)21static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
22{22{
23 if (tmr->tempo < 1000000)23 unsigned int threshold =
24 tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;24 tmr->tempo_base == 1000 ? 1000000 : 10000;
25
26 if (tmr->tempo < threshold)
27 tmr->tick.resolution = (tmr->tempo * tmr->tempo_base) / tmr->ppq;
25 else {28 else {
26 /* might overflow.. */29 /* might overflow.. */
27 unsigned int s;30 unsigned int s;
28 s = tmr->tempo % tmr->ppq;31 s = tmr->tempo % tmr->ppq;
29 s = (s * 1000) / tmr->ppq;32 s = (s * tmr->tempo_base) / tmr->ppq;
30 tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;33 tmr->tick.resolution = (tmr->tempo / tmr->ppq) * tmr->tempo_base;
31 tmr->tick.resolution += s;34 tmr->tick.resolution += s;
32 }35 }
33 if (tmr->tick.resolution <= 0)36 if (tmr->tick.resolution <= 0)
@@ -79,6 +82,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr) · 45 unmodified lines
79 /* setup defaults */82 /* setup defaults */
80 tmr->ppq = 96; /* 96 PPQ */83 tmr->ppq = 96; /* 96 PPQ */
81 tmr->tempo = 500000; /* 120 BPM */84 tmr->tempo = 500000; /* 120 BPM */
85 tmr->tempo_base = 1000; /* 1us */
82 snd_seq_timer_set_tick_resolution(tmr);86 snd_seq_timer_set_tick_resolution(tmr);
83 tmr->running = 0;87 tmr->running = 0;
84 88
@@ -164,8 +168,9 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) · 79 unmodified lines
164 return 0;168 return 0;
165}169}
166 170
167/* set current tempo and ppq in a shot */171/* set current tempo, ppq and base in a shot */
168int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)172int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
173 unsigned int tempo_base)
169{174{
170 int changed;175 int changed;
171 176
@@ -173,6 +178,9 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq) · 1 unmodified line
173 return -EINVAL;178 return -EINVAL;
174 if (tempo <= 0 || ppq <= 0)179 if (tempo <= 0 || ppq <= 0)
175 return -EINVAL;180 return -EINVAL;
181 /* allow only 10ns or 1us tempo base for now */
182 if (tempo_base && tempo_base != 10 && tempo_base != 1000)
183 return -EINVAL;
176 guard(spinlock_irqsave)(&tmr->lock);184 guard(spinlock_irqsave)(&tmr->lock);
177 if (tmr->running && (ppq != tmr->ppq)) {185 if (tmr->running && (ppq != tmr->ppq)) {
178 /* refuse to change ppq on running timers */186 /* 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) · 4 unmodified lines
183 changed = (tempo != tmr->tempo) || (ppq != tmr->ppq);191 changed = (tempo != tmr->tempo) || (ppq != tmr->ppq);
184 tmr->tempo = tempo;192 tmr->tempo = tempo;
185 tmr->ppq = ppq;193 tmr->ppq = ppq;
194 tmr->tempo_base = tempo_base ? tempo_base : 1000;
186 if (changed)195 if (changed)
187 snd_seq_timer_set_tick_resolution(tmr);196 snd_seq_timer_set_tick_resolution(tmr);
188 return 0;197 return 0;
sound/core/seq/seq_timer.h+3−1
@@ -36,6 +36,7 @@ struct snd_seq_timer { · 35 unmodified lines
36 36
37 unsigned int skew;37 unsigned int skew;
38 unsigned int skew_base;38 unsigned int skew_base;
39 unsigned int tempo_base;
39 40
40 struct timespec64 last_update; /* time of last clock update, used for interpolation */41 struct timespec64 last_update; /* time of last clock update, used for interpolation */
41 42
@@ -116,7 +117,8 @@ int snd_seq_timer_stop(struct snd_seq_timer *tmr); · 74 unmodified lines
116int snd_seq_timer_start(struct snd_seq_timer *tmr);117int snd_seq_timer_start(struct snd_seq_timer *tmr);
117int snd_seq_timer_continue(struct snd_seq_timer *tmr);118int snd_seq_timer_continue(struct snd_seq_timer *tmr);
118int snd_seq_timer_set_tempo(struct snd_seq_timer *tmr, int tempo);119int snd_seq_timer_set_tempo(struct snd_seq_timer *tmr, int tempo);
119int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq);120int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
121 unsigned int tempo_base);
120int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position);122int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position);
121int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position);123int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position);
122int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base);124int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base);