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 <doj@cubic.org>
This commit is contained in:
Dirk Jagdmann 2004-11-17 11:41:56 +00:00 committed by Takashi Iwai
parent 465acb88f4
commit 1be230856c
6 changed files with 269 additions and 41 deletions

View file

@ -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@

View file

@ -1,34 +1,33 @@
.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 <profiles file name>] [\fI\-v\fP] [<profile number>|<profile name>]
.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 <profiles file name>] [\fI\-v\fP] [<profile number>|<profile name>] [\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 <profiles file name>] [\fI\-v\fP] [<profile number>|<profile name>]
.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 <profiles file name>] [\fI\-v\fP] [<profile number>|<profile name>] [\fI\-m\fP midi\-channel]
.TP
If no control-name is given, then the first sound card is used.
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.
\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,
\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
\fI\-o\fP outputs
@ -58,15 +57,19 @@ doesn't exists SYS_PROFILERC will be used.
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 <perex@suse.cz>
This document is by James Tappin <james@xena.uklinux.net>.
Last updated by Dirk Kalis <dirk.kalis@t\-online.de>.

View file

@ -21,6 +21,7 @@
******************************************************************************/
#include "envy24control.h"
#include "midi.h"
#define _GNU_SOURCE
#include <getopt.h>
@ -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;
}

188
envy24control/midi.c Normal file
View file

@ -0,0 +1,188 @@
/*****************************************************************************
envy24control.c - Env24 chipset (ICE1712) control utility
midi controller code
(c) 2004 by Dirk Jagdmann <doj@cubic.org>
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 <string.h>
#include <alsa/asoundlib.h>
#include "midi.h"
#include <gtk/gtk.h>
#include <stdint.h>
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<sizeof(currentvalues); ++i)
do_controller(i, currentvalues[i]);
}
break;
}
snd_seq_free_event(ev);
}
while (snd_seq_event_input_pending(seq, 0) > 0);
}

12
envy24control/midi.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef MIDI__H
#define MIDI__H
#include <gdk/gdk.h>
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

View file

@ -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));