|
|
@@ -0,0 +1,214 @@ |
|
|
|
/* |
|
|
|
Copyright (C) 2001 Paul Davis |
|
|
|
Copyright (C) 2002 Dave LaRose |
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
|
|
|
it under the terms of the GNU General Public License as published by |
|
|
|
the Free Software Foundation; either version 2 of the License, or |
|
|
|
(at your option) any later version. |
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, |
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
GNU General Public License for more details. |
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License |
|
|
|
along with this program; if not, write to the Free Software |
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
|
|
|
|
|
|
$Id$ |
|
|
|
*/ |
|
|
|
|
|
|
|
#include <jack/hardware.h> |
|
|
|
#include <jack/alsa_driver.h> |
|
|
|
#include <jack/hdsp.h> |
|
|
|
#include <jack/error.h> |
|
|
|
#include <jack/internal.h> |
|
|
|
|
|
|
|
/* Constants to make working with the hdsp matrix mixer easier */ |
|
|
|
static const int HDSP_MINUS_INFINITY_GAIN = 0; |
|
|
|
static const int HDSP_UNITY_GAIN = 32768; |
|
|
|
static const int HDSP_MAX_GAIN = 65535; |
|
|
|
|
|
|
|
/* |
|
|
|
* Use these two arrays to choose the value of the input_channel |
|
|
|
* argument to hsdp_set_mixer_gain(). hdsp_physical_input_index[n] |
|
|
|
* selects the nth optical/analog input. audio_stream_index[n] |
|
|
|
* selects the nth channel being received from the host via pci/pccard. |
|
|
|
*/ |
|
|
|
static const int hdsp_num_input_channels = 52; |
|
|
|
static const int hdsp_physical_input_index[] = { |
|
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, |
|
|
|
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}; |
|
|
|
static const int hdsp_audio_stream_index[] = { |
|
|
|
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, |
|
|
|
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; |
|
|
|
|
|
|
|
/* |
|
|
|
* Use this array to choose the value of the output_channel |
|
|
|
* argument to hsdp_set_mixer_gain(). hdsp_physical_output_index[26] |
|
|
|
* and hdsp_physical_output_index[27] refer to the two "line out" |
|
|
|
* channels (1/4" phone jack on the front of digiface/multiface). |
|
|
|
*/ |
|
|
|
static const int hdsp_num_output_channels = 28; |
|
|
|
static const int hdsp_physical_output_index[] = { |
|
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, |
|
|
|
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}; |
|
|
|
|
|
|
|
|
|
|
|
/* Function for checking argument values */ |
|
|
|
static int clamp_int(int value, int lower_bound, int upper_bound) |
|
|
|
{ |
|
|
|
if(value < lower_bound) { |
|
|
|
return lower_bound; |
|
|
|
} |
|
|
|
if(value > upper_bound) { |
|
|
|
return upper_bound; |
|
|
|
} |
|
|
|
return value; |
|
|
|
} |
|
|
|
|
|
|
|
/* Note(XXX): Maybe should share this code with hammerfall.c? */ |
|
|
|
static void |
|
|
|
set_control_id (snd_ctl_elem_id_t *ctl, const char *name) |
|
|
|
{ |
|
|
|
snd_ctl_elem_id_set_name (ctl, name); |
|
|
|
snd_ctl_elem_id_set_numid (ctl, 0); |
|
|
|
snd_ctl_elem_id_set_interface (ctl, SND_CTL_ELEM_IFACE_PCM); |
|
|
|
snd_ctl_elem_id_set_device (ctl, 0); |
|
|
|
snd_ctl_elem_id_set_subdevice (ctl, 0); |
|
|
|
snd_ctl_elem_id_set_index (ctl, 0); |
|
|
|
} |
|
|
|
|
|
|
|
/* The hdsp matrix mixer lets you connect pretty much any input to */ |
|
|
|
/* any output with gain from -inf to about +2dB. Pretty slick. */ |
|
|
|
/* This routine makes a convenient way to set the gain from */ |
|
|
|
/* input_channel to output_channel (see hdsp_physical_input_index */ |
|
|
|
/* etc. above. */ |
|
|
|
/* gain is an int from 0 to 65535, with 0 being -inf gain, and */ |
|
|
|
/* 65535 being about 2dB. */ |
|
|
|
static int hdsp_set_mixer_gain(jack_hardware_t *hw, int input_channel, |
|
|
|
int output_channel, int gain) |
|
|
|
{ |
|
|
|
hdsp_t *h = (hdsp_t *) hw->private; |
|
|
|
snd_ctl_elem_value_t *ctl; |
|
|
|
snd_ctl_elem_id_t *ctl_id; |
|
|
|
int err; |
|
|
|
|
|
|
|
/* Check args */ |
|
|
|
input_channel = clamp_int(input_channel, 0, hdsp_num_input_channels); |
|
|
|
output_channel = clamp_int(output_channel, 0, hdsp_num_output_channels); |
|
|
|
gain = clamp_int(gain, HDSP_MINUS_INFINITY_GAIN, HDSP_MAX_GAIN); |
|
|
|
|
|
|
|
/* Allocate control element and select "Mixer" control */ |
|
|
|
snd_ctl_elem_value_alloca (&ctl); |
|
|
|
snd_ctl_elem_id_alloca (&ctl_id); |
|
|
|
set_control_id (ctl_id, "Mixer"); |
|
|
|
snd_ctl_elem_value_set_id (ctl, ctl_id); |
|
|
|
|
|
|
|
/* Apparently non-standard and unstable interface for the */ |
|
|
|
/* mixer control. */ |
|
|
|
snd_ctl_elem_value_set_integer (ctl, 0, input_channel); |
|
|
|
snd_ctl_elem_value_set_integer (ctl, 1, output_channel); |
|
|
|
snd_ctl_elem_value_set_integer (ctl, 2, gain); |
|
|
|
|
|
|
|
/* Commit the mixer value and check for errors */ |
|
|
|
if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) != 0) { |
|
|
|
jack_error ("ALSA/HDSP: cannot set mixer gain (%s)", snd_strerror (err)); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
/* Note (XXX): Perhaps we should maintain a cache of the current */ |
|
|
|
/* mixer values, since it's not clear how to query them from the */ |
|
|
|
/* hdsp hardware. We'll leave this out until a little later. */ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int hdsp_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
|
|
|
|
/* For each input channel */ |
|
|
|
for (i = 0; i < 26; i++) { |
|
|
|
/* Monitoring requested for this channel? */ |
|
|
|
if(mask & (1<<i)) { |
|
|
|
/* Yes. Connect physical input to output */ |
|
|
|
if(hdsp_set_mixer_gain (hw, hdsp_physical_input_index[i], |
|
|
|
hdsp_physical_output_index[i], |
|
|
|
HDSP_UNITY_GAIN) != 0) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
/* ...and disconnect the corresponding software */ |
|
|
|
/* channel */ |
|
|
|
if(hdsp_set_mixer_gain (hw, hdsp_audio_stream_index[i], |
|
|
|
hdsp_physical_output_index[i], |
|
|
|
HDSP_MINUS_INFINITY_GAIN) != 0) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} else { |
|
|
|
/* No. Disconnect physical input from output */ |
|
|
|
if(hdsp_set_mixer_gain (hw, hdsp_physical_input_index[i], |
|
|
|
hdsp_physical_output_index[i], |
|
|
|
HDSP_MINUS_INFINITY_GAIN) != 0) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
/* ...and connect the corresponding software */ |
|
|
|
/* channel */ |
|
|
|
if(hdsp_set_mixer_gain (hw, hdsp_audio_stream_index[i], |
|
|
|
hdsp_physical_output_index[i], |
|
|
|
HDSP_UNITY_GAIN) != 0) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
/* Cache the monitor mask */ |
|
|
|
hw->input_monitor_mask = mask; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int hdsp_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode) |
|
|
|
{ |
|
|
|
// Empty for now, until Dave understands more about clock sync so |
|
|
|
// he can test. |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
jack_alsa_hdsp_release (jack_hardware_t *hw) |
|
|
|
|
|
|
|
{ |
|
|
|
hdsp_t *h = (hdsp_t *) hw->private; |
|
|
|
|
|
|
|
if (h != 0) { |
|
|
|
free (h); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Mostly copied directly from hammerfall.c */ |
|
|
|
jack_hardware_t * |
|
|
|
jack_alsa_hdsp_hw_new (alsa_driver_t *driver) |
|
|
|
|
|
|
|
{ |
|
|
|
jack_hardware_t *hw; |
|
|
|
hdsp_t *h; |
|
|
|
|
|
|
|
hw = (jack_hardware_t *) malloc (sizeof (jack_hardware_t)); |
|
|
|
|
|
|
|
/* Not using clock lock-sync-whatever in home hardware setup */ |
|
|
|
/* yet. Will write this code when can test it. */ |
|
|
|
/* hw->capabilities = Cap_HardwareMonitoring|Cap_AutoSync|Cap_WordClock|Cap_ClockMaster|Cap_ClockLockReporting; */ |
|
|
|
hw->capabilities = Cap_HardwareMonitoring; |
|
|
|
hw->input_monitor_mask = 0; |
|
|
|
hw->private = 0; |
|
|
|
|
|
|
|
hw->set_input_monitor_mask = hdsp_set_input_monitor_mask; |
|
|
|
hw->change_sample_clock = hdsp_change_sample_clock; |
|
|
|
hw->release = jack_alsa_hdsp_release; |
|
|
|
|
|
|
|
h = (hdsp_t *) malloc (sizeof (hdsp_t)); |
|
|
|
h->driver = driver; |
|
|
|
hw->private = h; |
|
|
|
|
|
|
|
return hw; |
|
|
|
} |