Commit 25ca917c authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Takashi Iwai

ALSA: firewire-lib: limit the MIDI data rate

Do no send MIDI bytes at the full rate at which FireWire packets happen
to be sent, but restrict them to the actual rate of a real MIDI port.
This is required by the specification, and prevents data loss when the
device's buffer overruns.
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Reviewed-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Tested-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 5c697e5b
...@@ -21,6 +21,12 @@ ...@@ -21,6 +21,12 @@
#define CYCLES_PER_SECOND 8000 #define CYCLES_PER_SECOND 8000
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
/*
* Nominally 3125 bytes/second, but the MIDI port's clock might be
* 1% too slow, and the bus clock 100 ppm too fast.
*/
#define MIDI_BYTES_PER_SECOND 3093
/* /*
* Several devices look only at the first eight data blocks. * Several devices look only at the first eight data blocks.
* In any case, this is more than enough for the MIDI data rate. * In any case, this is more than enough for the MIDI data rate.
...@@ -226,6 +232,14 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, ...@@ -226,6 +232,14 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
for (i = 0; i < pcm_channels; i++) for (i = 0; i < pcm_channels; i++)
s->pcm_positions[i] = i; s->pcm_positions[i] = i;
s->midi_position = s->pcm_channels; s->midi_position = s->pcm_channels;
/*
* We do not know the actual MIDI FIFO size of most devices. Just
* assume two bytes, i.e., one byte can be received over the bus while
* the previous one is transmitted over MIDI.
* (The value here is adjusted for midi_ratelimit_per_packet().)
*/
s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
} }
EXPORT_SYMBOL(amdtp_stream_set_parameters); EXPORT_SYMBOL(amdtp_stream_set_parameters);
...@@ -467,6 +481,36 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s, ...@@ -467,6 +481,36 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
} }
} }
/*
* To avoid sending MIDI bytes at too high a rate, assume that the receiving
* device has a FIFO, and track how much it is filled. This values increases
* by one whenever we send one byte in a packet, but the FIFO empties at
* a constant rate independent of our packet rate. One packet has syt_interval
* samples, so the number of bytes that empty out of the FIFO, per packet(!),
* is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
* fractional values, the values in midi_fifo_used[] are measured in bytes
* multiplied by the sample rate.
*/
static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
{
int used;
used = s->midi_fifo_used[port];
if (used == 0) /* common shortcut */
return true;
used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
used = max(used, 0);
s->midi_fifo_used[port] = used;
return used < s->midi_fifo_limit;
}
static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
{
s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
}
static void amdtp_fill_midi(struct amdtp_stream *s, static void amdtp_fill_midi(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames) __be32 *buffer, unsigned int frames)
{ {
...@@ -474,16 +518,21 @@ static void amdtp_fill_midi(struct amdtp_stream *s, ...@@ -474,16 +518,21 @@ static void amdtp_fill_midi(struct amdtp_stream *s,
u8 *b; u8 *b;
for (f = 0; f < frames; f++) { for (f = 0; f < frames; f++) {
buffer[s->midi_position] = 0;
b = (u8 *)&buffer[s->midi_position]; b = (u8 *)&buffer[s->midi_position];
port = (s->data_block_counter + f) % 8; port = (s->data_block_counter + f) % 8;
if ((f >= MAX_MIDI_RX_BLOCKS) || if (f < MAX_MIDI_RX_BLOCKS &&
(s->midi[port] == NULL) || midi_ratelimit_per_packet(s, port) &&
(snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0)) s->midi[port] != NULL &&
b[0] = 0x80; snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) {
else midi_rate_use_one_byte(s, port);
b[0] = 0x81; b[0] = 0x81;
} else {
b[0] = 0x80;
b[1] = 0;
}
b[2] = 0;
b[3] = 0;
buffer += s->data_block_quadlets; buffer += s->data_block_quadlets;
} }
......
...@@ -148,6 +148,8 @@ struct amdtp_stream { ...@@ -148,6 +148,8 @@ struct amdtp_stream {
bool double_pcm_frames; bool double_pcm_frames;
struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
int midi_fifo_limit;
int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
/* quirk: fixed interval of dbc between previos/current packets. */ /* quirk: fixed interval of dbc between previos/current packets. */
unsigned int tx_dbc_interval; unsigned int tx_dbc_interval;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment