diff mbox series

[alsa-lib] seq: Add API functions to set different tempo base values

Message ID 20240706074032.6256-1-tiwai@suse.de
State New
Headers show
Series [alsa-lib] seq: Add API functions to set different tempo base values | expand

Commit Message

Takashi Iwai July 6, 2024, 7:40 a.m. UTC
MIDI2 Set Tempo message uses 10ns-based values, and we need to update
the API to change the base time unit.

This patch adds a few new API functions:
- snd_seq_has_queue_tempo_base() returns 1 if the client supports a
  new tempo-base value; if 0, it's an old system and application has
  to use the tempo in the fixed 1us unit
- the tempo base can be changed with
  snd_seq_queue_tempo_set_tempo_base(), provided in nsec unit;
  the value has to be either 10 or 1000 (or 0 as default, equivalent
  with 1000)

The protocol version is checked and fallback to the fixed 1us base for
the old clients.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/seq.h                   |  3 ++
 include/sound/uapi/asequencer.h |  7 ++--
 src/Versions.in.in              |  3 ++
 src/seq/seq.c                   | 58 +++++++++++++++++++++++++++++++++
 src/seq/seq_hw.c                |  5 +++
 src/seq/seq_local.h             |  1 +
 6 files changed, 74 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/include/seq.h b/include/seq.h
index e55f5c16fada..3bad5dd9762c 100644
--- a/include/seq.h
+++ b/include/seq.h
@@ -506,13 +506,16 @@  unsigned int snd_seq_queue_tempo_get_tempo(const snd_seq_queue_tempo_t *info);
 int snd_seq_queue_tempo_get_ppq(const snd_seq_queue_tempo_t *info);
 unsigned int snd_seq_queue_tempo_get_skew(const snd_seq_queue_tempo_t *info);
 unsigned int snd_seq_queue_tempo_get_skew_base(const snd_seq_queue_tempo_t *info);
+unsigned int snd_seq_queue_tempo_get_tempo_base(const snd_seq_queue_tempo_t *info);
 void snd_seq_queue_tempo_set_tempo(snd_seq_queue_tempo_t *info, unsigned int tempo);
 void snd_seq_queue_tempo_set_ppq(snd_seq_queue_tempo_t *info, int ppq);
 void snd_seq_queue_tempo_set_skew(snd_seq_queue_tempo_t *info, unsigned int skew);
 void snd_seq_queue_tempo_set_skew_base(snd_seq_queue_tempo_t *info, unsigned int base);
+void snd_seq_queue_tempo_set_tempo_base(snd_seq_queue_tempo_t *info, unsigned int tempo_base);
 
 int snd_seq_get_queue_tempo(snd_seq_t *handle, int q, snd_seq_queue_tempo_t *tempo);
 int snd_seq_set_queue_tempo(snd_seq_t *handle, int q, snd_seq_queue_tempo_t *tempo);
+int snd_seq_has_queue_tempo_base(snd_seq_t *handle);
 
 /*
  */
diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h
index b913f31daa2d..923dfdddfc76 100644
--- a/include/sound/uapi/asequencer.h
+++ b/include/sound/uapi/asequencer.h
@@ -26,7 +26,7 @@ 
 #include <sound/asound.h>
 
 /** version of the sequencer */
-#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3)
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 4)
 
 /**
  * definition of sequencer event types
@@ -539,11 +539,12 @@  struct snd_seq_queue_status {
 /* queue tempo */
 struct snd_seq_queue_tempo {
 	int queue;			/* sequencer queue */
-	unsigned int tempo;		/* current tempo, us/tick */
+	unsigned int tempo;		/* current tempo, us/tick (or different time-base below) */
 	int ppq;			/* time resolution, ticks/quarter */
 	unsigned int skew_value;	/* queue skew */
 	unsigned int skew_base;		/* queue skew base */
-	char reserved[24];		/* for the future */
+	unsigned short tempo_base;	/* tempo base in nsec unit; either 10 or 1000 */
+	char reserved[22];		/* for the future */
 };
 
 
diff --git a/src/Versions.in.in b/src/Versions.in.in
index 1c40d461dce4..90849277c983 100644
--- a/src/Versions.in.in
+++ b/src/Versions.in.in
@@ -206,6 +206,9 @@  ALSA_1.2.13 {
 #ifdef HAVE_SEQ_SYMS
     @SYMBOL_PREFIX@snd_seq_create_ump_endpoint;
     @SYMBOL_PREFIX@snd_seq_create_ump_block;
+    @SYMBOL_PREFIX@snd_seq_queue_tempo_get_tempo_base;
+    @SYMBOL_PREFIX@snd_seq_queue_tempo_set_tempo_base;
+    @SYMBOL_PREFIX@snd_seq_has_tempo_base;
 #endif
 #ifdef HAVE_RAWMIDI_SYMS
     @SYMBOL_PREFIX@snd_ump_endpoint_info_clear;
diff --git a/src/seq/seq.c b/src/seq/seq.c
index a18f67e2632e..d0ecedf0838f 100644
--- a/src/seq/seq.c
+++ b/src/seq/seq.c
@@ -490,6 +490,21 @@  the buffer is flushed by #snd_seq_drain_output() call.
 You can schedule the event in a certain queue so that the tempo
 change happens at the scheduled time, too.
 
+The tempo is set as default in microsecond unit as defined for
+Standard MIDI Format 1.  But since the value in MIDI2 Set Tempo message
+is based on 10-nanosecand unit, sequencer queue also allows to set up
+in 10-nanosecond unit.  For that, change the tempo-base value in
+#snd_seq_queue_tempo_t to 10 via #snd_seq_queue_tempo_set_tempo_base()
+along with the 10-nanobased tempo value.  The default tempo base is 1000,
+i.e. 1 microsecond.
+Currently the API supports only either 0, 10 or 1000 as the tempo-base
+(where 0 is treated as 1000).
+
+The older kernel might not support the different tempo-base, and setting a
+different value from 1000 would fail.  The application may heck the
+availability of tempo-base change via #snd_seq_has_tempo_base() function
+beforehand, and re-calculate in microsecond unit as fallback.
+
 \subsection seq_ev_start Starting and stopping a queue
 
 To start, stop, or continue a queue, you need to send a queue-control
@@ -3878,6 +3893,19 @@  unsigned int snd_seq_queue_tempo_get_skew_base(const snd_seq_queue_tempo_t *info
 	return info->skew_base;
 }
 
+/**
+ * \brief Get the tempo base of a queue_status container
+ * \param info queue_status container
+ * \return tempo base time in nsec unit
+ *
+ * \sa snd_seq_get_queue_tempo()
+ */
+unsigned int snd_seq_queue_tempo_get_tempo_base(const snd_seq_queue_tempo_t *info)
+{
+	assert(info);
+	return info->tempo_base;
+}
+
 /**
  * \brief Set the tempo of a queue_status container
  * \param info queue_status container
@@ -3933,6 +3961,21 @@  void snd_seq_queue_tempo_set_skew_base(snd_seq_queue_tempo_t *info, unsigned int
 	info->skew_base = base;
 }
 
+/**
+ * \brief Set the tempo base of a queue_status container
+ * \param info queue_status container
+ * \param tempo_base tempo base time in nsec unit
+ *
+ * \sa snd_seq_get_queue_tempo()
+ */
+void snd_seq_queue_tempo_set_tempo_base(snd_seq_queue_tempo_t *info, unsigned int tempo_base)
+{
+	assert(info);
+	if (!tempo_base)
+		tempo_base = 1000;
+	info->tempo_base = tempo_base;
+}
+
 /**
  * \brief obtain the current tempo of the queue
  * \param seq sequencer handle
@@ -3962,10 +4005,25 @@  int snd_seq_get_queue_tempo(snd_seq_t *seq, int q, snd_seq_queue_tempo_t * tempo
 int snd_seq_set_queue_tempo(snd_seq_t *seq, int q, snd_seq_queue_tempo_t * tempo)
 {
 	assert(seq && tempo);
+	if (!seq->has_queue_tempo_base &&
+	    tempo->tempo_base && tempo->tempo_base != 1000)
+		return -EINVAL;
 	tempo->queue = q;
 	return seq->ops->set_queue_tempo(seq, tempo);
 }
 
+/**
+ * \brief inquiry the support of tempo base change
+ * \param seq sequencer handle
+ * \return 1 if the client supports the tempo base change, 0 if not
+ *
+ * \sa snd_seq_get_queue_tempo()
+ */
+int snd_seq_has_queue_tempo_base(snd_seq_t *seq)
+{
+	assert(seq);
+	return seq->has_queue_tempo_base;
+}
 
 /*----------------------------------------------------------------*/
 
diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c
index eeaf26e16d1c..e88a7b22b052 100644
--- a/src/seq/seq_hw.c
+++ b/src/seq/seq_hw.c
@@ -275,12 +275,15 @@  static int snd_seq_hw_get_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * te
 		/*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO failed");*/
 		return -errno;
 	}
+	if (!seq->has_queue_tempo_base)
+		tempo->tempo_base = 1000;
 	return 0;
 }
 
 static int snd_seq_hw_set_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo)
 {
 	snd_seq_hw_t *hw = seq->private_data;
+
 	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, tempo) < 0) {
 		/*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO failed");*/
 		return -errno;
@@ -587,6 +590,8 @@  int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode)
 	seq->ops = &snd_seq_hw_ops;
 	seq->private_data = hw;
 	seq->packet_size = sizeof(snd_seq_event_t);
+	seq->has_queue_tempo_base = ver >= SNDRV_PROTOCOL_VERSION(1, 0, 4);
+
 	client = snd_seq_hw_client_id(seq);
 	if (client < 0) {
 		snd_seq_close(seq);
diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h
index 263029702739..00a7615afeae 100644
--- a/src/seq/seq_local.h
+++ b/src/seq/seq_local.h
@@ -94,6 +94,7 @@  struct _snd_seq {
 	size_t tmpbufsize;		/* size of errbuf */
 	size_t packet_size;		/* input packet alignment size */
 	int midi_version;	/* current protocol version */
+	int has_queue_tempo_base;	/* support queue tempo-base? */
 
 	unsigned int num_ump_groups;		/* number of UMP groups */
 	snd_ump_endpoint_info_t *ump_ep;	/* optional UMP info */