All of lore.kernel.org
 help / color / mirror / Atom feed
From: Clemens Ladisch <clemens@ladisch.de>
To: Takashi Iwai <tiwai@suse.de>
Cc: alsa-devel@alsa-project.org, Takashi Sakamoto <o-takashi@sakamocchi.jp>
Subject: [PATCH 2/2] ALSA: firewire-lib: limit the MIDI data rate
Date: Tue, 25 Nov 2014 22:54:10 +0100	[thread overview]
Message-ID: <5474FA82.4030408@ladisch.de> (raw)
In-Reply-To: <5474FA18.7000501@ladisch.de>

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: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/amdtp.c |   61 +++++++++++++++++++++++++++++++++++++++++++-----
 sound/firewire/amdtp.h |    2 ++
 2 files changed, 57 insertions(+), 6 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 17548d8..c0c0c5e 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -22,6 +22,12 @@
 #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.
  * In any case, this is more than enough for the MIDI data rate.
  */
@@ -226,6 +232,14 @@ sfc_found:
 	for (i = 0; i < pcm_channels; i++)
 		s->pcm_positions[i] = i;
 	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);

@@ -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,
 			    __be32 *buffer, unsigned int frames)
 {
@@ -474,16 +518,21 @@ static void amdtp_fill_midi(struct amdtp_stream *s,
 	u8 *b;

 	for (f = 0; f < frames; f++) {
-		buffer[s->midi_position] = 0;
 		b = (u8 *)&buffer[s->midi_position];

 		port = (s->data_block_counter + f) % 8;
-		if ((f >= MAX_MIDI_RX_BLOCKS) ||
-		    (s->midi[port] == NULL) ||
-		    (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0))
-			b[0] = 0x80;
-		else
+		if (f < MAX_MIDI_RX_BLOCKS &&
+		    midi_ratelimit_per_packet(s, port) &&
+		    s->midi[port] != NULL &&
+		    snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) {
+			midi_rate_use_one_byte(s, port);
 			b[0] = 0x81;
+		} else {
+			b[0] = 0x80;
+			b[1] = 0;
+		}
+		b[2] = 0;
+		b[3] = 0;

 		buffer += s->data_block_quadlets;
 	}
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index cd4c4df..8a03a91 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -148,6 +148,8 @@ struct amdtp_stream {
 	bool double_pcm_frames;

 	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. */
 	unsigned int tx_dbc_interval;

  reply	other threads:[~2014-11-25 21:54 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-25 21:52 [PATCH 1/2] ALSA: firewire-lib: remove rx_blocks_for_midi quirk Clemens Ladisch
2014-11-25 21:54 ` Clemens Ladisch [this message]
2014-11-26  8:43 ` Takashi Iwai
2014-11-26  9:17   ` Clemens Ladisch
2014-11-26 14:22 ` Takashi Sakamoto
2014-11-26 14:38   ` Clemens Ladisch
2014-11-27 12:45     ` Takashi Sakamoto
2014-12-07 13:31       ` Takashi Sakamoto
2015-01-16 21:18         ` Clemens Ladisch
2015-01-16 21:54           ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5474FA82.4030408@ladisch.de \
    --to=clemens@ladisch.de \
    --cc=alsa-devel@alsa-project.org \
    --cc=o-takashi@sakamocchi.jp \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.