Skip to content

Commit

Permalink
seq: Add API functions to set different tempo base values
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
tiwai committed Jul 5, 2024
1 parent 568b2ac commit d00799d
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 3 deletions.
3 changes: 3 additions & 0 deletions include/seq.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/*
*/
Expand Down
7 changes: 4 additions & 3 deletions include/sound/uapi/asequencer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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; /* nsec unit; either 10 or 1000 */
char reserved[22]; /* for the future */
};


Expand Down
3 changes: 3 additions & 0 deletions src/Versions.in.in
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
58 changes: 58 additions & 0 deletions src/seq/seq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

/*----------------------------------------------------------------*/

Expand Down
5 changes: 5 additions & 0 deletions src/seq/seq_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/seq/seq_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit d00799d

Please sign in to comment.