mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-23 18:07:03 +00:00
ALSA: firewire-lib: Add support for MIDI capture/playback
For capturing/playbacking MIDI messages, this commit adds one MIDI conformant data channel. This data channel has multiplexed 8 MIDI data streams. So this data channel can transfer messages from/to 8 MIDI ports. And this commit allows to set PCM format even if AMDTP streams already start. I suppose the case that PCM substreams are going to be joined into AMDTP streams when AMDTP streams are already started for MIDI substreams. Each driver must count how many PCM/MIDI substreams use AMDTP streams to stop AMDTP streams. There are differences between specifications about MIDI conformant data. About the multiplexing, IEC 61883-6:2002, itself, has no information. It describes labels and bytes for MIDI messages and refers to MMA/AMEI RP-027 for 'successfull implementation'. MMA/AMEI RP-027 describes 8 MPX-MIDI data streams for one MIDI conformant data channel. IEC 61883-6:2005 adds 'sequence multiplexing' and apply this way and describe incompatibility between 2002 and 2005. So this commit applies IEC 61883-6:2005. When we find some devices compliant to IEC 61883-6:2002, then this difference should be handles as device quirk in additional work. About the number of bytes in an MIDI conformant data, IEC 61883-6:2002 describe 0,1,2,3 bytes. MMA/AMEI RP-027 describes 'MIDI1.0-1x-SPEED', 'MIDI1.0-2x-SPEED', 'MIDI1.0-3x-SPEED' modes and the maximum bytes for each mode corresponds to 1, 2, 3 bytes. The 'MIDI1.0-2x/3x-SPEED' modes are accompanied with 'negotiation procedure' and 'encapsulation details' but there is no specifications for them. So this commit implements 'MIDI1.0-1x-SPEED' mode for playback, but allows to pick up 1-3 bytes for capturing. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
2b3fc456fe
commit
83d8d72dff
@ -12,6 +12,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/rawmidi.h>
|
||||
#include "amdtp.h"
|
||||
|
||||
#define TICKS_PER_CYCLE 3072
|
||||
@ -123,9 +124,12 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
|
||||
[CIP_SFC_176400] = 176400,
|
||||
[CIP_SFC_192000] = 192000,
|
||||
};
|
||||
unsigned int sfc;
|
||||
unsigned int sfc, midi_channels;
|
||||
|
||||
if (WARN_ON(amdtp_stream_running(s)))
|
||||
midi_channels = DIV_ROUND_UP(midi_ports, 8);
|
||||
|
||||
if (WARN_ON(amdtp_stream_running(s)) ||
|
||||
WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
|
||||
return;
|
||||
|
||||
for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
|
||||
@ -142,7 +146,7 @@ sfc_found:
|
||||
pcm_channels *= 2;
|
||||
}
|
||||
s->sfc = sfc;
|
||||
s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
|
||||
s->data_block_quadlets = pcm_channels + midi_channels;
|
||||
s->pcm_channels = pcm_channels;
|
||||
s->midi_ports = midi_ports;
|
||||
|
||||
@ -200,7 +204,7 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
|
||||
void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
|
||||
snd_pcm_format_t format)
|
||||
{
|
||||
if (WARN_ON(amdtp_stream_running(s)))
|
||||
if (WARN_ON(amdtp_stream_pcm_running(s)))
|
||||
return;
|
||||
|
||||
switch (format) {
|
||||
@ -507,11 +511,41 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
|
||||
static void amdtp_fill_midi(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int f, port;
|
||||
u8 *b;
|
||||
|
||||
for (i = 0; i < frames; ++i)
|
||||
buffer[s->pcm_channels + i * s->data_block_quadlets] =
|
||||
cpu_to_be32(0x80000000);
|
||||
for (f = 0; f < frames; f++) {
|
||||
buffer[s->pcm_channels + 1] = 0;
|
||||
b = (u8 *)&buffer[s->pcm_channels + 1];
|
||||
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
if ((s->midi[port] == NULL) ||
|
||||
(snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0))
|
||||
b[0] = 0x80;
|
||||
else
|
||||
b[0] = 0x81;
|
||||
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
static void amdtp_pull_midi(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
unsigned int f, port;
|
||||
int len;
|
||||
u8 *b;
|
||||
|
||||
for (f = 0; f < frames; f++) {
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
b = (u8 *)&buffer[s->pcm_channels + 1];
|
||||
|
||||
len = b[0] - 0x80;
|
||||
if ((1 <= len) && (len <= 3) && (s->midi[port]))
|
||||
snd_rawmidi_receive(s->midi[port], b + 1, len);
|
||||
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_pcm_pointers(struct amdtp_stream *s,
|
||||
@ -688,6 +722,9 @@ static void handle_in_packet(struct amdtp_stream *s,
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (pcm)
|
||||
s->transfer_samples(s, pcm, buffer, data_blocks);
|
||||
|
||||
if (s->midi_ports)
|
||||
amdtp_pull_midi(s, buffer, data_blocks);
|
||||
}
|
||||
|
||||
s->data_block_counter = (data_block_counter + data_blocks) & 0xff;
|
||||
|
@ -46,9 +46,21 @@ enum cip_sfc {
|
||||
#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
|
||||
SNDRV_PCM_FMTBIT_S32)
|
||||
|
||||
|
||||
/*
|
||||
* AMDTP packet can include channels for MIDI conformant data.
|
||||
* Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
|
||||
* Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
|
||||
*
|
||||
* This module supports maximum 1 MIDI conformant data channels.
|
||||
* Then this AMDTP packets can transfer maximum 8 MIDI data streams.
|
||||
*/
|
||||
#define AMDTP_MAX_CHANNELS_FOR_MIDI 1
|
||||
|
||||
struct fw_unit;
|
||||
struct fw_iso_context;
|
||||
struct snd_pcm_substream;
|
||||
struct snd_rawmidi_substream;
|
||||
|
||||
enum amdtp_stream_direction {
|
||||
AMDTP_OUT_STREAM = 0,
|
||||
@ -90,6 +102,8 @@ struct amdtp_stream {
|
||||
unsigned int pcm_buffer_pointer;
|
||||
unsigned int pcm_period_pointer;
|
||||
bool pointer_flush;
|
||||
|
||||
struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
|
||||
};
|
||||
|
||||
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
@ -138,6 +152,17 @@ static inline bool amdtp_streaming_error(struct amdtp_stream *s)
|
||||
return s->packet_index < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_stream_pcm_running - check PCM substream is running or not
|
||||
* @s: the AMDTP stream
|
||||
*
|
||||
* If this function returns true, PCM substream in the AMDTP stream is running.
|
||||
*/
|
||||
static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
|
||||
{
|
||||
return !!s->pcm;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_stream_pcm_trigger - start/stop playback from a PCM device
|
||||
* @s: the AMDTP stream
|
||||
@ -153,6 +178,24 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
|
||||
ACCESS_ONCE(s->pcm) = pcm;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
|
||||
* @s: the AMDTP stream
|
||||
* @port: index of MIDI port
|
||||
* @midi: the MIDI device to be started, or %NULL to stop the current device
|
||||
*
|
||||
* Call this function on a running isochronous stream to enable the actual
|
||||
* transmission of MIDI data. This function should be called from the MIDI
|
||||
* device's .trigger callback.
|
||||
*/
|
||||
static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
|
||||
unsigned int port,
|
||||
struct snd_rawmidi_substream *midi)
|
||||
{
|
||||
if (port < s->midi_ports)
|
||||
ACCESS_ONCE(s->midi[port]) = midi;
|
||||
}
|
||||
|
||||
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
|
||||
{
|
||||
return sfc & 1;
|
||||
|
Loading…
Reference in New Issue
Block a user