OSDN Git Service

ALSA: hdsp: hardware output loopback
authorJasmin Fazlic <superfassl@gmail.com>
Mon, 1 Feb 2021 14:28:52 +0000 (15:28 +0100)
committerTakashi Iwai <tiwai@suse.de>
Tue, 2 Feb 2021 09:34:27 +0000 (10:34 +0100)
Output loopback is a feature where you can record what you hear.
The HDSP series of the RME interfaces provides this functionality
at the hardware level and this patch exposes controls to enable or
disable it per output (playback) channel.

This probably works on other cards but due to a lack of hardware
it is only tested and enabled for the HDSP9632 card with this patch.

Should this patch be accepted a separate patch will be posted to
https://github.com/alsa-project/alsa-tools/tree/master/hdspmixer
which adds "LPBK" buttons to each output in the playback strip for
the user to be able to control this feature from the user land.
Users from Windows tool TotalMixFX should be familiar with this.

Signed-off-by: Jasmin Fazlic <superfassl@gmail.com>
Link: https://lore.kernel.org/r/95cb3117-e85a-51a6-c2ce-bf736e70fc4c@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/rme9652/hdsp.c

index cea53a8..6d90293 100644 (file)
@@ -469,6 +469,7 @@ struct hdsp {
        unsigned char         qs_out_channels;
        unsigned char         ds_out_channels;
        unsigned char         ss_out_channels;
+       u32                   io_loopback;          /* output loopback channel states*/
 
        struct snd_dma_buffer capture_dma_buf;
        struct snd_dma_buffer playback_dma_buf;
@@ -3253,6 +3254,60 @@ static const struct snd_kcontrol_new snd_hdsp_96xx_aeb =
                        HDSP_AnalogExtensionBoard);
 static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK;
 
+
+static bool hdsp_loopback_get(struct hdsp *const hdsp, const u8 channel)
+{
+       return hdsp->io_loopback & (1 << channel);
+}
+
+static int hdsp_loopback_set(struct hdsp *const hdsp, const u8 channel, const bool enable)
+{
+       if (hdsp_loopback_get(hdsp, channel) == enable)
+               return 0;
+
+       hdsp->io_loopback ^= (1 << channel);
+
+       hdsp_write(hdsp, HDSP_inputEnable + (4 * (hdsp->max_channels + channel)), enable);
+
+       return 1;
+}
+
+static int snd_hdsp_loopback_get(struct snd_kcontrol *const kcontrol,
+                                struct snd_ctl_elem_value *const ucontrol)
+{
+       struct hdsp *const hdsp = snd_kcontrol_chip(kcontrol);
+       const u8 channel = snd_ctl_get_ioff(kcontrol, &ucontrol->id);
+
+       if (channel >= hdsp->max_channels)
+               return -ENOENT;
+
+       ucontrol->value.integer.value[0] = hdsp_loopback_get(hdsp, channel);
+
+       return 0;
+}
+
+static int snd_hdsp_loopback_put(struct snd_kcontrol *const kcontrol,
+                                struct snd_ctl_elem_value *const ucontrol)
+{
+       struct hdsp *const hdsp = snd_kcontrol_chip(kcontrol);
+       const u8 channel = snd_ctl_get_ioff(kcontrol, &ucontrol->id);
+       const bool enable = ucontrol->value.integer.value[0] & 1;
+
+       if (channel >= hdsp->max_channels)
+               return -ENOENT;
+
+       return hdsp_loopback_set(hdsp, channel, enable);
+}
+
+static struct snd_kcontrol_new snd_hdsp_loopback_control = {
+       .iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
+       .name = "Output Loopback",
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info = snd_ctl_boolean_mono_info,
+       .get = snd_hdsp_loopback_get,
+       .put = snd_hdsp_loopback_put
+};
+
 static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
 {
        unsigned int idx;
@@ -3297,6 +3352,17 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
                }
        }
 
+       /* Output loopback controls for H9632 cards */
+       if (hdsp->io_type == H9632) {
+               snd_hdsp_loopback_control.count = hdsp->max_channels;
+               kctl = snd_ctl_new1(&snd_hdsp_loopback_control, hdsp);
+               if (kctl == NULL)
+                       return -ENOMEM;
+               err = snd_ctl_add(card, kctl);
+               if (err < 0)
+                       return err;
+       }
+
        /* AEB control for H96xx card */
        if (hdsp->io_type == H9632 || hdsp->io_type == H9652) {
                if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_96xx_aeb, hdsp))) < 0)
@@ -4956,7 +5022,7 @@ static int snd_hdsp_enable_io (struct hdsp *hdsp)
 
 static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
 {
-       int status, aebi_channels, aebo_channels;
+       int status, aebi_channels, aebo_channels, i;
 
        switch (hdsp->io_type) {
        case Digiface:
@@ -4983,6 +5049,12 @@ static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
                hdsp->ss_out_channels = H9632_SS_CHANNELS+aebo_channels;
                hdsp->ds_out_channels = H9632_DS_CHANNELS+aebo_channels;
                hdsp->qs_out_channels = H9632_QS_CHANNELS+aebo_channels;
+               /* Disable loopback of output channels, as the set function
+                * only sets on a change we fake all bits (channels) as enabled.
+                */
+               hdsp->io_loopback = 0xffffffff;
+               for (i = 0; i < hdsp->max_channels; ++i)
+                       hdsp_loopback_set(hdsp, i, false);
                break;
 
        case Multiface: