From: Dirk Jagdmann Date: Wed, 17 Nov 2004 11:41:56 +0000 (+0000) Subject: envy24control: midi support for mixer X-Git-Tag: v1.0.8~15 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=1be230856ccefa0731a952a248fd70d37a1df80f;p=alsa-tools.git envy24control: midi support for mixer Hello devlopers, I have made the mixer sliders in envy24control react to MIDI controllers. This way the hardware mixer can be controlled from a sequencer program or some external MIDI controller hardware. envy24control became a standard ALSA sequencer client which you can connect to arbitrary other MIDI sources/sinks. Signed-off-by: Dirk Jagdmann --- diff --git a/envy24control/Makefile.am b/envy24control/Makefile.am index 71200ed..97b4b71 100644 --- a/envy24control/Makefile.am +++ b/envy24control/Makefile.am @@ -1,7 +1,7 @@ AM_CFLAGS = @ENVY24CONTROL_CFLAGS@ bin_PROGRAMS = envy24control man_MANS = envy24control.1 -envy24control_SOURCES = envy24control.c envy24control.h levelmeters.c \ +envy24control_SOURCES = envy24control.c envy24control.h levelmeters.c midi.c \ mixer.c patchbay.c hardware.c driverevents.c volume.c \ profiles.c profiles.h envy24control_LDFLAGS = @ENVY24CONTROL_LIBS@ diff --git a/envy24control/envy24control.1 b/envy24control/envy24control.1 index 5e9f6e4..93f039c 100644 --- a/envy24control/envy24control.1 +++ b/envy24control/envy24control.1 @@ -1,72 +1,75 @@ -.TH "envy24control" 1 "16 May 2004" -.SH NAME +.TH "envy24control" "1" "16 May 2004" "" "" +.SH "NAME" envy24control \- GUI control tool for Envy24 (ice1712) based soundcards, under ALSA. -.SH SYNOPSIS -\fBenvy24control\fP [\fI\-c\fP card-number] [\fI\-D\fP control-name] [\fI\-o\fP 0-num DACs max 8] [\fI\-i\fP 0-num ADCs max 8] [\fI\-p\fP 0-8] [\fI\-s\fP 0-2] [\fI\-f\fP ] [\fI\-v\fP] [|] +.SH "SYNOPSIS" +\fBenvy24control\fP [\fI\-c\fP card\-number] [\fI\-D\fP control\-name] [\fI\-o\fP 0\-num DACs max 8] [\fI\-i\fP 0\-num ADCs max 8] [\fI\-p\fP 0\-8] [\fI\-s\fP 0\-2] [\fI\-f\fP ] [\fI\-v\fP] [|] [\fI\-m\fP midi\-channel] -.SH DESCRIPTION +.SH "DESCRIPTION" \fBenvy24control\fP allows control of the digital mixer, channel gains and other hardware settings for sound cards based on the ice1712 chipset (Midiman Delta series, Terratec EWS and EWX series). It also displays a level meter for each input and output channel. -.SH INVOKING - -\fBenvy24control\fP [\fI\-c\fP card-number] [\fI\-D\fP control-name] [\fI\-o\fP 0-num DACs max 8] [\fI\-i\fP 0-num ADCs max 8] [\fI\-p\fP 0-8] [\fI\-s\fP 0-2] [\fI\-f\fP ] [\fI\-v\fP] [|] -.TP -If no control-name is given, then the first sound card is used. +.SH "INVOKING" +\fBenvy24control\fP [\fI\-c\fP card\-number] [\fI\-D\fP control\-name] [\fI\-o\fP 0\-num DACs max 8] [\fI\-i\fP 0\-num ADCs max 8] [\fI\-p\fP 0\-8] [\fI\-s\fP 0\-2] [\fI\-f\fP ] [\fI\-v\fP] [|] [\fI\-m\fP midi\-channel] +.TP +If no control\-name is given, then the first sound card is used. .SS Options -.TP -\fI\-c\fP card-number -Use the card specified by card-number rather than the first card. +.TP +\fI\-c\fP card\-number +Use the card specified by card\-number rather than the first card. This is equivalent with \fI\-Dhw:n\fP option where \fIn\fP is the card number. -.TP -\fI\-D\fP control-name -Use the card specified by control-name rather than the first card, +.TP +\fI\-D\fP control\-name +Use the card specified by control\-name rather than the first card, normally this will be of the form hw:\fIn\fP where \fIn\fP is the sound -card number (zero-based). This is only needed if you have more than one -Envy24-based card or if your Envy24 card is not configured as the first +card number (zero\-based). This is only needed if you have more than one +Envy24\-based card or if your Envy24 card is not configured as the first card in your ALSA driver setup. -.TP +.TP \fI\-o\fP outputs Limit number of analog line outputs to display. Default is the number of DACs in the driver specification. Maximum is 8. The number of analog output lines can only be reduced from the available number of output lines. -.TP +.TP \fI\-i\fP inputs Limit number of analog line inputs to display. Default is the number of ADCs in the driver specification. Maximum is 8. The number of analog input lines can only be reduced from the available number of input lines. -.TP +.TP \fI\-p\fP pcm outputs Limit number of PCM outputs to display. Default is 8. -.TP +.TP \fI\-s\fP spdif inputs/outputs Limit number of SPDIF inputs/outputs to display. Default is 2. -.TP +.TP \fI\-f\fP name and path of the profiles file name. From/to this file will envy24control read/write the alsactl settings. If is not given DEFAULT_PROFILERC or for restore if DEFAULT_PROFILERC doesn't exists SYS_PROFILERC will be used. -.TP +.TP \fI\-v\fP view spdif playback channels in the mixer. It is not possible to manage something (muting, volume levels). It is only to view the levelmeters. Default is no view of spdif playback channels in the mixer. - -.SH SEE ALSO +.TP +\fI\-m\fP midi\-channel +Use MIDI controller values to control the Faders in the mixer view. +The application will react to controllers on channel midi\-channel and +send controllers on this channel when the user moves the GUI sliders. +.SH "SEE ALSO" \fB alsamixer(1), amixer(1), gamix(1) \fP -.SH AUTHOR +.SH "AUTHOR" \fBenvy24control\fP is by Jaroslav Kysela This document is by James Tappin . Last updated by Dirk Kalis . diff --git a/envy24control/envy24control.c b/envy24control/envy24control.c index dc80dfd..c385658 100644 --- a/envy24control/envy24control.c +++ b/envy24control/envy24control.c @@ -21,6 +21,7 @@ ******************************************************************************/ #include "envy24control.h" +#include "midi.h" #define _GNU_SOURCE #include @@ -1926,7 +1927,7 @@ static void create_profiles(GtkWidget *main, GtkWidget *notebook, int page) static void usage(void) { - fprintf(stderr, "usage: envy24control [-c card#] [-D control-name] [-o num-outputs] [-i num-inputs] [-p num-pcm-outputs] [-s num-spdif-in/outs] [-v] [-f profiles-file] [profile name|profile id]\n"); + fprintf(stderr, "usage: envy24control [-c card#] [-D control-name] [-o num-outputs] [-i num-inputs] [-p num-pcm-outputs] [-s num-spdif-in/outs] [-v] [-f profiles-file] [profile name|profile id] [-m channel-num]\n"); fprintf(stderr, "\t-c, --card\tAlsa card number to control\n"); fprintf(stderr, "\t-D, --device\tcontrol-name\n"); fprintf(stderr, "\t-o, --outputs\tLimit number of analog line outputs to display\n"); @@ -1935,6 +1936,7 @@ static void usage(void) fprintf(stderr, "\t-s, --spdif\tLimit number of spdif inputs/outputs to display\n"); fprintf(stderr, "\t-v, --view_spdif_playback\tshows the spdif playback channels in the mixer\n"); fprintf(stderr, "\t-f, --profiles_file\tuse file as profiles file\n"); + fprintf(stderr, "\t-m, --midichannel\tmidi channel number for controller control\n"); } int main(int argc, char **argv) @@ -1946,17 +1948,19 @@ int main(int argc, char **argv) snd_ctl_elem_value_t *val; int npfds; struct pollfd *pfds; + int midi_fd, midi_channel = -1; int page; int input_channels_set = 0; int output_channels_set = 0; static struct option long_options[] = { {"device", 1, 0, 'D'}, {"card", 1, 0, 'c'}, + {"profiles_file", 1, 0, 'f'}, {"inputs", 1, 0, 'i'}, + {"midichannel", 1, 0, 'm'}, {"outputs", 1, 0, 'o'}, {"pcm_outputs", 1, 0, 'p'}, {"spdif", 1, 0, 's'}, - {"profiles_file", 1, 0, 'f'}, {"view_spdif_playback", 0, 0, 'v'}, { NULL } }; @@ -1977,8 +1981,16 @@ int main(int argc, char **argv) view_spdif_playback = 0; profiles_file_name = DEFAULT_PROFILERC; default_profile = NULL; - while ((c = getopt_long(argc, argv, "D:c:i:o:p:s:f:v", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "D:c:f:i:m:o:p:s:v", long_options, NULL)) != -1) { switch (c) { + case 'D': + name = optarg; + card_number = atoi(strchr(name, ':') + sizeof(char)); + if (card_number < 0 || card_number >= MAX_CARD_NUMBERS) { + fprintf(stderr, "envy24control: invalid card number %d\n", card_number); + exit(1); + } + break; case 'c': i = atoi(optarg); if (i < 0 || i >= MAX_CARD_NUMBERS) { @@ -1989,13 +2001,8 @@ int main(int argc, char **argv) sprintf(tmpname, "hw:%d", i); name = tmpname; break; - case 'D': - name = optarg; - card_number = atoi(strchr(name, ':') + sizeof(char)); - if (card_number < 0 || card_number >= MAX_CARD_NUMBERS) { - fprintf(stderr, "envy24control: invalid card number %d\n", card_number); - exit(1); - } + case 'f': + profiles_file_name = optarg; break; case 'i': input_channels = atoi(optarg); @@ -2005,6 +2012,14 @@ int main(int argc, char **argv) } input_channels_set = 1; break; + case 'm': + midi_channel = atoi(optarg); + if (midi_channel < 1 || midi_channel > 16) { + fprintf(stderr, "envy24control: invalid midi channel number %i\n", midi_channel); + exit(1); + } + --midi_channel; + break; case 'o': output_channels = atoi(optarg); if (output_channels < 0 || output_channels > MAX_OUTPUT_CHANNELS) { @@ -2027,9 +2042,6 @@ int main(int argc, char **argv) exit(1); } break; - case 'f': - profiles_file_name = optarg; - break; case 'v': view_spdif_playback = 1; break; @@ -2083,6 +2095,8 @@ int main(int argc, char **argv) patchbay_init(); hardware_init(); analog_volume_init(); + if (midi_channel >= 0) + midi_fd = midi_init(argv[0], midi_channel); fprintf(stderr, "using\t --- input_channels: %i\n\t --- output_channels: %i\n\t --- pcm_output_channels: %i\n\t --- spdif in/out channels: %i\n", \ input_channels, output_channels, pcm_output_channels, spdif_channels); @@ -2123,6 +2137,9 @@ int main(int argc, char **argv) ctl); snd_ctl_subscribe_events(ctl, 1); } + if (midi_fd >= 0) { + gdk_input_add(midi_fd, GDK_INPUT_READ, midi_process, NULL); + } gtk_timeout_add(40, level_meters_timeout_callback, NULL); gtk_timeout_add(100, master_clock_status_timeout_callback, NULL); gtk_timeout_add(100, internal_clock_status_timeout_callback, NULL); @@ -2141,6 +2158,7 @@ int main(int argc, char **argv) gtk_main(); snd_ctl_close(ctl); + midi_close(); return EXIT_SUCCESS; } diff --git a/envy24control/midi.c b/envy24control/midi.c new file mode 100644 index 0000000..c89ba1f --- /dev/null +++ b/envy24control/midi.c @@ -0,0 +1,188 @@ +/***************************************************************************** + envy24control.c - Env24 chipset (ICE1712) control utility + midi controller code + (c) 2004 by Dirk Jagdmann + + 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +******************************************************************************/ + +#include +#include +#include "midi.h" +#include +#include + +static snd_seq_t *seq=0; +static int client, clientId, port, ch; +static char *portname=0, *appname=0; +static int nofeedback=-1; +static int maxstreams=0; +static uint8_t currentvalues[128]; + +void midi_maxstreams(int m) +{ + maxstreams=(m-1)*2; +} + +int midi_close() +{ + int i=0; + if(seq) + i=snd_seq_close(seq); + + seq=0; + client=port=0; + if(portname) + free(portname), portname=0; + if(appname) + free(appname), appname=0; + + return i; +} + +static void do_controller(int c, int v) +{ + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, port); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_direct(&ev); + snd_seq_ev_set_controller(&ev,ch,c,v); + snd_seq_event_output(seq, &ev); + snd_seq_drain_output(seq); +} + +int midi_controller(int c, int v) +{ + if(c==nofeedback) return 0; + if(c<0 || c>127) return 0; + + v*=127; v/=96; + if(v<0) v=0; + if(v>127) v=127; + currentvalues[c]=v; + do_controller(c,v); + return 0; +} + +int midi_init(char *appname, int channel) +{ + snd_seq_client_info_t *clientinfo; + int npfd; + struct pollfd *pfd; + + if(seq) + return 0; + + ch=channel; + + if(snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK) < 0) + { + g_warning("could not init ALSA sequencer\n"); + seq=0; + return -1; + } + + snd_seq_set_client_name(seq, appname); + snd_seq_client_info_alloca(&clientinfo); + snd_seq_get_client_info (seq, clientinfo); + client=snd_seq_client_info_get_client(clientinfo); + clientId = snd_seq_client_id(seq); + + portname=g_strdup_printf("%s Mixer Control", appname); + port=snd_seq_create_simple_port(seq, portname, + SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE|SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_APPLICATION); + if(port < 0) + { + g_warning("could not create ALSA sequencer port\n"); + midi_close(); + return -1; + } + + npfd=snd_seq_poll_descriptors_count(seq, POLLIN); + if(npfd<=0) + { + g_warning("could not get number of ALSA sequencer poll descriptors\n"); + midi_close(); + return -1; + } + + pfd=(struct pollfd*)alloca(npfd * sizeof(struct pollfd)); + if(pfd==0) + { + g_warning("could not alloc memory for ALSA sequencer poll descriptors\n"); + midi_close(); + return -1; + } + if(snd_seq_poll_descriptors(seq, pfd, npfd, POLLIN) != npfd) + { + g_warning("number of returned poll desc is not equal of request poll desc\n"); + midi_close(); + return -1; + } + + return pfd[0].fd; +} + +void mixer_adjust(GtkAdjustment *adj, gpointer data); + +void midi_process(gpointer data, gint source, GdkInputCondition condition) +{ + snd_seq_event_t *ev; + static GtkAdjustment *adj=0; + if(!adj) + adj=(GtkAdjustment*) gtk_adjustment_new(0, 0, 96, 1, 1, 10); + + do + { + snd_seq_event_input(seq, &ev); + if(!ev) continue; + switch(ev->type) + { + case SND_SEQ_EVENT_CONTROLLER: +#if 0 + fprintf(stderr, "Channel %02d: Controller %03d: Value:%d\n", + ev->data.control.channel, ev->data.control.param, ev->data.control.value); +#endif + if(ev->data.control.channel == ch && ev->data.control.param < maxstreams) + { + int stream=ev->data.control.param+1; + long data=((stream/2)<<16)|(stream&1); + int v=ev->data.control.value; v*=96; v/=127; + gtk_adjustment_set_value(adj, 96-v); + nofeedback=ev->data.control.param; + mixer_adjust(adj, (gpointer)data); + nofeedback=-1; + } + break; + + case SND_SEQ_EVENT_PORT_SUBSCRIBED: +#if 0 + fprintf(stderr, "event subscribed send.client:%i dest.client:%i clientId:%i\n", + (int)ev->data.connect.sender.client, (int)ev->data.connect.dest.client, clientId); +#endif + if(ev->data.connect.dest.client!=clientId) + { + int i; + for(i=0; i 0); +} diff --git a/envy24control/midi.h b/envy24control/midi.h new file mode 100644 index 0000000..e87eff9 --- /dev/null +++ b/envy24control/midi.h @@ -0,0 +1,12 @@ +#ifndef MIDI__H +#define MIDI__H + +#include + +int midi_init(char *appname, int channel); +int midi_close(); +void midi_maxstreams(int); +int midi_controller(int c, int v); +void midi_process(gpointer data, gint source, GdkInputCondition condition); + +#endif diff --git a/envy24control/mixer.c b/envy24control/mixer.c index 837d6b1..3870afa 100644 --- a/envy24control/mixer.c +++ b/envy24control/mixer.c @@ -18,6 +18,7 @@ ******************************************************************************/ #include "envy24control.h" +#include "midi.h" #define MULTI_PLAYBACK_SWITCH "Multi Playback Switch" #define MULTI_PLAYBACK_VOLUME "Multi Playback Volume" @@ -62,6 +63,8 @@ void mixer_update_stream(int stream, int vol_flag, int sw_flag) toggle_set(mixer_stereo_toggle[stream-1], FALSE); gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][0]), 96 - v[0]); gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][1]), 96 - v[1]); + midi_controller(stream*2-1, v[0]); + midi_controller(stream*2, v[1]); } if (sw_flag) { snd_ctl_elem_value_t *sw; @@ -139,10 +142,12 @@ static void set_volume1(int stream, int left, int right) if (left >= 0) { change |= (snd_ctl_elem_value_get_integer(vol, 0) != left); snd_ctl_elem_value_set_integer(vol, 0, left); + midi_controller(stream*2-1, left); } if (right >= 0) { change |= (snd_ctl_elem_value_get_integer(vol, 1) != right); snd_ctl_elem_value_set_integer(vol, 1, right); + midi_controller(stream*2, right); } if (change) { if ((err = snd_ctl_elem_write(ctl, vol)) < 0 && err != -EBUSY) @@ -176,6 +181,8 @@ void mixer_init(void) int nb_active_channels; snd_ctl_elem_value_t *val; + midi_maxstreams(sizeof(stream_is_active)/sizeof(stream_is_active[0])); + snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); memset (stream_is_active, 0, (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + MAX_SPDIF_CHANNELS) * sizeof(int));