├── data ├── art │ ├── 16x16 │ │ ├── jack_mixer.png │ │ └── jack_mixer.svg │ ├── 22x22 │ │ ├── jack_mixer.png │ │ └── jack_mixer.svg │ ├── 24x24 │ │ └── jack_mixer.png │ ├── 32x32 │ │ └── jack_mixer.png │ ├── 48x48 │ │ └── jack_mixer.png │ └── Makefile.am ├── Makefile.am └── jack_mixer.desktop ├── AUTHORS ├── .gitignore ├── m4 ├── jack.m4 ├── python.m4 └── swig.m4 ├── log.c ├── test.py ├── autogen.sh ├── scale.h ├── README ├── jack_compat.h ├── jack_mixer.schemas ├── abspeak.py ├── Makefile.am ├── NEWS ├── serialization_xml.py ├── log.h ├── memory_atomic.h ├── configure.ac ├── serialization.py ├── jack_mix_box.c ├── scale.c ├── scale.py ├── jack_mixer.h ├── meter.py ├── preferences.py ├── gui.py ├── slider.py ├── INSTALL ├── memory_atomic.c ├── COPYING └── jack_mixer_c.c /data/art/16x16/jack_mixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsheeler/jack_mixer/HEAD/data/art/16x16/jack_mixer.png -------------------------------------------------------------------------------- /data/art/22x22/jack_mixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsheeler/jack_mixer/HEAD/data/art/22x22/jack_mixer.png -------------------------------------------------------------------------------- /data/art/24x24/jack_mixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsheeler/jack_mixer/HEAD/data/art/24x24/jack_mixer.png -------------------------------------------------------------------------------- /data/art/32x32/jack_mixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsheeler/jack_mixer/HEAD/data/art/32x32/jack_mixer.png -------------------------------------------------------------------------------- /data/art/48x48/jack_mixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsheeler/jack_mixer/HEAD/data/art/48x48/jack_mixer.png -------------------------------------------------------------------------------- /data/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = art 2 | 3 | desktopdir = $(datadir)/applications 4 | desktop_DATA = jack_mixer.desktop 5 | 6 | EXTRA_DIST = $(desktop_DATA) 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Current Maintainer 2 | ================== 3 | 4 | Frederic Peters 5 | 6 | Authors 7 | ======= 8 | 9 | Nedko Arnaudov (original author) 10 | Daniel Sheeler 11 | -------------------------------------------------------------------------------- /data/jack_mixer.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Jack Mixer 3 | Comment=Jack Mixer 4 | Comment[fr]=Mixeur pour JACK 5 | Exec=jack_mixer 6 | Terminal=false 7 | Type=Application 8 | StartupNotify=true 9 | Categories=GTK;GNOME;AudioVideo;Player;Audio; 10 | Icon=jack_mixer 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /config 2 | /Makefile.in 3 | /Makefile 4 | /.deps 5 | /.libs 6 | /jack_mixer_c.so 7 | /config.h 8 | /libtool 9 | /stamp-h1 10 | /autom4te.cache 11 | /aclocal.m4 12 | /config.h.in 13 | /config.log 14 | /config.status 15 | /configure 16 | /jack_mixer_c.py 17 | /jack_mixer_wrap.c 18 | /data/Makefile 19 | /data/Makefile.in 20 | /data/art/Makefile 21 | /data/art/Makefile.in 22 | *.lo 23 | *.la 24 | *.pyc 25 | /m4/libtool.m4 26 | /m4/ltoptions.m4 27 | /m4/ltsugar.m4 28 | /m4/ltversion.m4 29 | /m4/lt~obsolete.m4 30 | *.o 31 | jack_mix_box 32 | -------------------------------------------------------------------------------- /m4/jack.m4: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2007 Nedko Arnaudov 2 | # This file is distributed under the same terms as the Autoconf macro files. 3 | 4 | AC_DEFUN([AC_JACK_MIDI_NFRAMES_CHECK], [ 5 | AC_MSG_CHECKING([whether JACK MIDI functions need nframes parameter]) 6 | AC_LANG_PUSH(C) 7 | AC_COMPILE_IFELSE(AC_LANG_PROGRAM([[ 8 | #include 9 | #include 10 | ]], [[ 11 | jack_midi_event_get(0, 0, 0, 0); 12 | ]]), [jackmidi_nframes='yes'], [jackmidi_nframes='no']) 13 | AC_MSG_RESULT([$jackmidi_nframes]) 14 | AC_LANG_POP() 15 | ]) 16 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C ; c-basic-offset: 2 -*- */ 2 | /***************************************************************************** 3 | * 4 | * Copyright (C) 2006,2007 Nedko Arnaudov 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; version 2 of the License 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | *****************************************************************************/ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "log.h" 27 | 28 | void jack_mixer_log(int level, const char * format, ...) 29 | { 30 | (void)level; 31 | va_list arglist; 32 | 33 | va_start(arglist, format); 34 | vprintf(format, arglist); 35 | va_end(arglist); 36 | } 37 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This file is part of jack_mixer 4 | # 5 | # Copyright (C) 2006 Nedko Arnaudov 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; version 2 of the License 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | import jack_mixer_c 21 | 22 | mixer = jack_mixer_c.Mixer("test") 23 | 24 | print("Channels count: %u" % mixer.channels_count) 25 | channel = mixer.add_channel("Channel 1", True) 26 | 27 | if channel.is_stereo: 28 | channel_type = "Stereo" 29 | else: 30 | channel_type = "Mono" 31 | 32 | channel_name = channel.name 33 | 34 | print("%s channel \"%s\"" % (channel_type, channel_name)) 35 | 36 | print("Channel meter read %s" % repr(channel.meter)) 37 | print("Channels count: %u" % mixer.channels_count) 38 | 39 | channel.remove() 40 | 41 | print("Channels count: %u" % mixer.channels_count) 42 | 43 | -------------------------------------------------------------------------------- /data/art/Makefile.am: -------------------------------------------------------------------------------- 1 | # ****************************************************************************** 2 | # Images and icon 3 | # ****************************************************************************** 4 | 5 | 16icondir = $(datadir)/icons/hicolor/16x16/apps 6 | 16icon_DATA = 16x16/jack_mixer.png 7 | 8 | 22icondir = $(datadir)/icons/hicolor/22x22/apps 9 | 22icon_DATA = 22x22/jack_mixer.png 10 | 11 | 24icondir = $(datadir)/icons/hicolor/24x24/apps 12 | 24icon_DATA = 24x24/jack_mixer.png 13 | 14 | 32icondir = $(datadir)/icons/hicolor/32x32/apps 15 | 32icon_DATA = 32x32/jack_mixer.png 16 | 17 | 48icondir = $(datadir)/icons/hicolor/48x48/apps 18 | 48icon_DATA = 48x48/jack_mixer.png 19 | 20 | svgicondir = $(datadir)/icons/hicolor/scalable/apps 21 | svgicon_DATA = scalable/jack_mixer.svg 22 | 23 | gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor 24 | 25 | install-data-hook: update-icon-cache 26 | uninstall-hook: update-icon-cache 27 | update-icon-cache: 28 | @-if test -z "$(DESTDIR)"; then \ 29 | echo "Updating Gtk icon cache."; \ 30 | $(gtk_update_icon_cache); \ 31 | else \ 32 | echo "*** Icon cache not updated. After (un)install, run this:"; \ 33 | echo "*** $(gtk_update_icon_cache)"; \ 34 | fi 35 | 36 | EXTRA_DIST = \ 37 | $(16icon_DATA) \ 38 | $(22icon_DATA) \ 39 | $(24icon_DATA) \ 40 | $(32icon_DATA) \ 41 | $(48icon_DATA) \ 42 | $(svgicon_DATA) \ 43 | 16x16/jack_mixer.svg \ 44 | 22x22/jack_mixer.svg \ 45 | 32x32/jack_mixer.svg \ 46 | 48x48/jack_mixer.svg 47 | 48 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # autotools bootstrap script for jack_mixer 4 | # 5 | # This file is part of jack_mixer 6 | # 7 | # Copyright (C) 2007 Nedko Arnaudov 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; version 2 of the License 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | test -n "$srcdir" || srcdir=`dirname "$0"` 24 | test -n "$srcdir" || srcdir=. 25 | 26 | olddir=`pwd` 27 | cd "$srcdir" 28 | 29 | if test x$1 = xclean 30 | then 31 | GENERATED="aclocal.m4 autom4te.cache config.h.in configure config Makefile.in" 32 | set -x 33 | if test -f Makefile; then make distclean; fi 34 | rm -rf ${GENERATED} 35 | else 36 | set -x 37 | mkdir -p config 38 | aclocal -I config -I m4 $ACLOCAL_FLAGS 39 | libtoolize --copy --force 40 | autoheader 41 | automake --foreign --add-missing --copy 42 | autoconf 43 | fi 44 | 45 | cd "$olddir" 46 | test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" 47 | -------------------------------------------------------------------------------- /scale.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C ; c-basic-offset: 2 -*- */ 2 | /***************************************************************************** 3 | * 4 | * This file is part of jack_mixer 5 | * 6 | * Copyright (C) 2006 Nedko Arnaudov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; version 2 of the License 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | *****************************************************************************/ 22 | 23 | #ifndef JACK_SCALE_H__DAEB51D8_5861_40F2_92E4_24CA495A384D__INCLUDED 24 | #define JACK_SCALE_H__DAEB51D8_5861_40F2_92E4_24CA495A384D__INCLUDED 25 | 26 | typedef void * jack_mixer_scale_t; 27 | 28 | jack_mixer_scale_t 29 | scale_create(); 30 | 31 | bool 32 | scale_add_threshold( 33 | jack_mixer_scale_t scale, 34 | float db, 35 | float scale_value); 36 | 37 | void 38 | scale_remove_thresholds( 39 | jack_mixer_scale_t scale); 40 | 41 | void 42 | scale_calculate_coefficients( 43 | jack_mixer_scale_t scale); 44 | 45 | double 46 | scale_db_to_scale( 47 | jack_mixer_scale_t scale, 48 | double db); 49 | 50 | double 51 | scale_scale_to_db( 52 | jack_mixer_scale_t scale, 53 | double scale_value); 54 | 55 | void 56 | scale_destroy( 57 | jack_mixer_scale_t scale); 58 | 59 | #endif /* #ifndef JACK_SCALE_H__DAEB51D8_5861_40F2_92E4_24CA495A384D__INCLUDED */ 60 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | jack_mixer -- Jack Audio Mixer 2 | ============================== 3 | 4 | jack_mixer is a GTK+ JACK audio mixer app with look similar to its hardware 5 | counterpart. It has lot of useful features, apart from being able to mix 6 | multiple JACK audio streams. 7 | 8 | It is licensed under GPL version 2 (or later), check COPYING for more info. 9 | 10 | Please visit project homepage is at https://rdio.space/jackmixer/ for more 11 | info. 12 | 13 | 14 | Installation 15 | ------------ 16 | 17 | Read INSTALL for information on installation. 18 | 19 | 20 | Using MIDI CCs to control jack_mixer 21 | ------------------------------------ 22 | 23 | MIDI CCs can be used to control volume and balance/panorama of channels. 24 | 25 | The default controller for main channel volume is 7, and the default for 26 | main channel balance is 8. Default controllers for added channels are 27 | chosen using predefined algorithm, first free controller starting from 11, 28 | first for volume, next for balance/panorama. 29 | 30 | So, if you don't delete channels, you will get CC#11 used for first input 31 | channel volume, CC#12 for first channel balance/panorama, CC#13 for second 32 | channel volume, CC#14 for second channel balance/panorama, etc. 33 | 34 | It is also possible to select other CCs when creating the channel, or 35 | afterwards from the channel properties dialog (accessible by double clicking 36 | on the channel name). 37 | 38 | MIDI CC values (0-127) are mapped to dBFS using current slider scale for 39 | corresponding channel. 40 | 41 | 42 | Feedback 43 | -------- 44 | 45 | If you have trouble getting jack_mixer working, find a bug or you miss some 46 | feature, please contact the maintainer by email. 47 | 48 | jack_mixer was initially written and supported by Nedko Arnaudov, it is now 49 | maintained by Frederic Peters. You can reach Frederic at fpeters (a.t) 0d 50 | (dot) be, and Nedko at nedko (a.t) arnaudov (dot) name. They can also be 51 | found in #lad on FreeNode (as fredp and nedko). 52 | -------------------------------------------------------------------------------- /m4/python.m4: -------------------------------------------------------------------------------- 1 | ## this one is commonly used with AM_PATH_PYTHONDIR ... 2 | dnl AM_CHECK_PYMOD(MODNAME [,SYMBOL [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]]]) 3 | dnl Check if a module containing a given symbol is visible to python. 4 | AC_DEFUN([AM_CHECK_PYMOD], 5 | [AC_REQUIRE([AM_PATH_PYTHON]) 6 | py_mod_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` 7 | AC_MSG_CHECKING(for ifelse([$2],[],,[$2 in ])python module $1) 8 | AC_CACHE_VAL(py_cv_mod_$py_mod_var, [ 9 | ifelse([$2],[], [prog=" 10 | import sys 11 | try: 12 | import $1 13 | except ImportError: 14 | sys.exit(1) 15 | except: 16 | sys.exit(0) 17 | sys.exit(0)"], [prog=" 18 | import $1 19 | $1.$2"]) 20 | if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC 21 | then 22 | eval "py_cv_mod_$py_mod_var=yes" 23 | else 24 | eval "py_cv_mod_$py_mod_var=no" 25 | fi 26 | ]) 27 | py_val=`eval "echo \`echo '$py_cv_mod_'$py_mod_var\`"` 28 | if test "x$py_val" != xno; then 29 | AC_MSG_RESULT(yes) 30 | ifelse([$3], [],, [$3 31 | ])dnl 32 | else 33 | AC_MSG_RESULT(no) 34 | ifelse([$4], [],, [$4 35 | ])dnl 36 | fi 37 | ]) 38 | 39 | dnl a macro to check for ability to create python extensions 40 | dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) 41 | dnl function also defines PYTHON_INCLUDES 42 | AC_DEFUN([AM_CHECK_PYTHON_HEADERS], 43 | [AC_REQUIRE([AM_PATH_PYTHON]) 44 | AC_MSG_CHECKING(for headers required to compile python extensions) 45 | dnl deduce PYTHON_INCLUDES 46 | py_prefix=`$PYTHON -c "import sys; print(sys.prefix)"` 47 | py_exec_prefix=`$PYTHON -c "import sys; print(sys.exec_prefix)"` 48 | PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" 49 | if test "$py_prefix" != "$py_exec_prefix"; then 50 | PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" 51 | fi 52 | AC_SUBST(PYTHON_INCLUDES) 53 | dnl check if the headers exist: 54 | save_CPPFLAGS="$CPPFLAGS" 55 | CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" 56 | AC_TRY_CPP([#include ],dnl 57 | [AC_MSG_RESULT(found) 58 | $1],dnl 59 | [AC_MSG_RESULT(not found) 60 | $2]) 61 | CPPFLAGS="$save_CPPFLAGS" 62 | ]) 63 | -------------------------------------------------------------------------------- /jack_compat.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C ; c-basic-offset: 2 -*- */ 2 | /***************************************************************************** 3 | * 4 | * This file is part of jack_nuke 5 | * 6 | * Copyright (C) 2007 Nedko Arnaudov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; version 2 of the License 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | *****************************************************************************/ 22 | 23 | #if defined(JACK_MIDI_NEEDS_NFRAMES) 24 | 25 | jack_nframes_t 26 | jack_midi_get_event_count_compat( 27 | void * port_buffer) 28 | { 29 | #if defined(HAVE_OLD_JACK_MIDI) 30 | return jack_midi_port_get_info(port_buffer, 0)->event_count; 31 | #else 32 | return jack_midi_get_event_count(port_buffer, 0); 33 | #endif 34 | } 35 | 36 | #define jack_midi_get_event_count jack_midi_get_event_count_compat 37 | 38 | int 39 | jack_midi_event_get_compat( 40 | jack_midi_event_t * event, 41 | void * port_buffer, 42 | jack_nframes_t event_index) 43 | { 44 | return jack_midi_event_get(event, port_buffer, event_index, 0); 45 | } 46 | 47 | #define jack_midi_event_get jack_midi_event_get_compat 48 | 49 | void 50 | jack_midi_clear_buffer_compat( 51 | void * port_buffer) 52 | { 53 | jack_midi_clear_buffer(port_buffer, 0); 54 | } 55 | 56 | #define jack_midi_clear_buffer jack_midi_clear_buffer_compat 57 | 58 | #else 59 | 60 | #if defined(HAVE_OLD_JACK_MIDI) 61 | #error "Old (0.102.20) JACK MIDI API needs nframes (autotools probably gone mad)" 62 | #endif 63 | 64 | #endif /* #if defined(JACK_MIDI_NEEDS_NFRAMES) */ 65 | -------------------------------------------------------------------------------- /jack_mixer.schemas: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /schemas/apps/jack_mixer/default_meter_scale 5 | /apps/jack_mixer/default_meter_scale 6 | jack_mixer 7 | string 8 | iec_268 9 | 10 | Default scale to use for peak meters 11 | Default scale to use for peak meters. "iec_268" uses 12 | scale close or maybe confirming to IEC 60268-18 13 | standard. "linear_70dB" is linear scale with range from -70 to 14 | 0 dBFS. 15 | 16 | 17 | 18 | 19 | /schemas/apps/jack_mixer/default_slider_scale 20 | /apps/jack_mixer/default_slider_scale 21 | jack_mixer 22 | string 23 | linear_30dB 24 | 25 | Default scale to use for volume siliders 26 | Default scale to use for volume siliders. "linear_30dB" 27 | is linear scale with range from -30 to +30 dBFS. "linear_70dB" 28 | is linear scale with range from -70 to 0 dBFS. 29 | 30 | 31 | 32 | 33 | /schemas/apps/jack_mixer/vumeter_color_scheme 34 | /apps/jack_mixer/vumeter_color_scheme 35 | jack_mixer 36 | string 37 | gradient 38 | 39 | Scheme used for the coloring of vumeters 40 | Gradient (default) or solid. 41 | 42 | 43 | 44 | /schemas/apps/jack_mixer/vumeter_color 45 | /apps/jack_mixer/vumeter_color 46 | jack_mixer 47 | string 48 | #ccb300 49 | 50 | Color for vumeters 51 | Color to be used in vumeters when configured to display a solid 52 | color. 53 | 54 | 55 | 56 | /schemas/apps/jack_mixer/use_custom_widgets 57 | /apps/jack_mixer/use_custom_widgets 58 | jack_mixer 59 | bool 60 | false 61 | 62 | Use custom widgets 63 | Whether to use custom widgets for the slider and balance. 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /abspeak.py: -------------------------------------------------------------------------------- 1 | # This file is part of jack_mixer 2 | # 3 | # Copyright (C) 2006 Nedko Arnaudov 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; version 2 of the License 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | from gi.repository import Gtk 19 | from gi.repository import Gdk 20 | from gi.repository import Pango 21 | from gi.repository import GObject 22 | import math 23 | 24 | class AbspeakWidget(Gtk.EventBox): 25 | def __init__(self): 26 | GObject.GObject.__init__(self) 27 | self.label = Gtk.Label() 28 | #attrs = Pango.AttrList() 29 | #font_attr = Pango.AttrFamily("monospace") 30 | #attrs.insert(font_attr) 31 | #self.label.set_attributes(attrs) 32 | self.add(self.label) 33 | self.connect("button-press-event", self.on_mouse) 34 | self.peak = -math.inf 35 | 36 | def on_mouse(self, widget, event): 37 | if event.type == Gdk.EventType.BUTTON_PRESS: 38 | if event.button == 1: 39 | self.emit("reset") 40 | elif event.button == 2: 41 | adjust = -self.peak 42 | if abs(adjust) < 30: # we better don't adjust more than +- 30 dB 43 | self.emit("volume-adjust", adjust) 44 | 45 | def set_peak(self, peak): 46 | self.peak = peak 47 | if math.isnan(peak): 48 | self.modify_bg(Gtk.StateType.NORMAL, Gdk.Color(int(65535 * 0.7), 0, 0)) 49 | self.label.set_text("NaN") 50 | else: 51 | text = "%+.1f" % peak 52 | 53 | if peak > 0: 54 | self.modify_bg(Gtk.StateType.NORMAL, Gdk.Color(int(65535 * 0.8), int(65535 * 0.3), 0)) 55 | else: 56 | pass 57 | #self.modify_bg(Gtk.StateType.NORMAL, self.label.style.bg[Gtk.StateType.NORMAL]) 58 | 59 | self.label.set_text(text) 60 | 61 | GObject.signal_new("reset", AbspeakWidget, 62 | GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, None, []) 63 | GObject.signal_new("volume-adjust", AbspeakWidget, 64 | GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, None, [GObject.TYPE_FLOAT]) 65 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of jack_mixer 3 | # 4 | # Copyright (C) 2007 Nedko Arnaudov 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; version 2 of the License 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | # 19 | 20 | AM_CFLAGS = $(JACKMIXER_CFLAGS) -D_GNU_SOURCE -Wall -fno-strict-aliasing 21 | if DEV_VERSION 22 | AM_CFLAGS += -Werror 23 | endif 24 | 25 | SUBDIRS = data 26 | 27 | AM_CPPFLAGS = $(PYTHON_INCLUDES) 28 | 29 | jack_mixerdir = $(pkgdatadir) 30 | pkgpyexecdir = $(pythondir) 31 | pkgpyexec_LTLIBRARIES = jack_mixer_c.la 32 | 33 | jack_mixer_c_la_LDFLAGS = -module -avoid-version 34 | 35 | jack_mixer_c_la_LIBADD = $(JACKMIXER_LIBS) 36 | 37 | jack_mixer_c_la_SOURCES = \ 38 | jack_mixer.c jack_mixer.h list.h memory_atomic.c memory_atomic.h log.h log.c scale.c scale.h jack_compat.h \ 39 | jack_mixer_c.c 40 | 41 | dist_jack_mixer_DATA = abspeak.py channel.py gui.py meter.py scale.py serialization.py serialization_xml.py slider.py preferences.py 42 | 43 | CLEANFILES = *.pyc 44 | EXTRA_DIST = test.py COPYING jack_mixer.schemas jack_mixer.py NEWS 45 | 46 | bin_SCRIPTS = $(srcdir)/jack_mixer.py 47 | 48 | bin_PROGRAMS = jack_mix_box 49 | 50 | jack_mixer_c.so: jack_mixer_c.la 51 | ln -nfs .libs/jack_mixer_c.so 52 | 53 | jack_mix_box_SOURCES = jack_mix_box.c jack_mixer.c scale.c log.c 54 | 55 | jack_mix_box_CFLAGS = $(JACKMIXER_CFLAGS) 56 | 57 | jack_mix_box_LDADD = $(JACKMIXER_LIBS) -lm 58 | 59 | test: _jack_mixer_c.so 60 | @./test.py 61 | 62 | schemadir = @GCONF_SCHEMA_FILE_DIR@ 63 | schema_DATA = jack_mixer.schemas 64 | 65 | install-exec-hook: 66 | ln -vfs jack_mixer.py $(DESTDIR)$(bindir)/jack_mixer 67 | 68 | if GCONF_SCHEMAS_INSTALL 69 | install-data-hook: 70 | GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$(schema_DATA) 71 | 72 | uninstall-hook: 73 | GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-uninstall-rule $(srcdir)/$(schema_DATA) 74 | rm -vf $(DESTDIR)$(bindir)/jack_mixer 75 | endif 76 | 77 | pacoinstall: 78 | test `whoami` == root # test for root 79 | -paco -rvB jack_mixer 80 | -paco -lE `pwd` -E /etc -p jack_mixer-`svnversion` "make install" 81 | -paco -i jack_mixer 82 | 83 | 84 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | ========== 2 | Version 11 3 | ========== 4 | 5 | * Spread out volume transition over a period of time to reduce 6 | discontinuities. 7 | * Port to pygobject and GTK3. 8 | * Port to Python 3. 9 | 10 | With contributions from Daniel Sheeler. 11 | 12 | ========== 13 | Version 10 14 | ========== 15 | 16 | * Fixed change of channel settings (#18299) 17 | * Added a MIDI out port for feeding back volume levels into motorized 18 | controllers 19 | * Added jack_mix_box, a minimalistic (no UI) jack mixer 20 | * Added a trayicon and minimize to tray feature 21 | 22 | With contributions from John Hedges, Sarah Mischke, and Nedko Arnaudov. 23 | 24 | ========= 25 | Version 9 26 | ========= 27 | 28 | * Changed to no longer appends PID to jack client name (#15006) 29 | * Added 'Edit .. channel' submenus 30 | * Set a default 'apply' button in channel properties 31 | * Fixed creation of mono channels 32 | * Removed bad crackling when changing the volume through MIDI 33 | * Moved back to polling for MIDI events, to avoid the need for threads 34 | * Changed to use backward compatible call to gobject.timeout_add (#14999) 35 | * Updated not to fail if we can't get lash server name 36 | * Added support for Ladish level 1 37 | * Improved SIGUSR1 handling 38 | 39 | With contributions from Nedko Arnaudov and Arnout Engelen. 40 | 41 | ========= 42 | Version 8 43 | ========= 44 | 45 | * Fix private modules lookup 46 | * Fix rotation of output channel colours 47 | * New menu items to remove output channels 48 | * New command line parameter to not connect to LASH 49 | 50 | ========= 51 | Version 7 52 | ========= 53 | 54 | * New maintainer, thanks Nedko for everything! 55 | * New icon by Lapo Calamandrei 56 | * Option to have a gradient in the vumeters 57 | * Option to use stock GtkScale widget for volume and balance 58 | * Rewrite of the C/Python binding (this removed the dependency on SWIG) 59 | * Improve performance when drawing vumeters 60 | * New menu items to load/save settings 61 | * New "Channel Properties" dialog, allowing to change assigned MIDI CCs 62 | * Automatic post fader outputs for input channels 63 | * Possibility to add new output channels, besides main mix 64 | * New "monitor" output, assignable to any output channel, or input channel 65 | (in which case it will take its prefader volume) 66 | * Removal of PyXML dependency 67 | 68 | With contributions from Nedko Arnaudov, Lapo Calamandrei, Arnout Engelen, 69 | and Krzysztof Foltman. 70 | 71 | ========= 72 | Version 6 73 | ========= 74 | 75 | * Fix building against jack 0.102.20 76 | * Handle python prefix different from install prefix 77 | * Fix LASH-less operation 78 | * Update install instructions after lash-0.5.3 and phat-0.4.1 releases 79 | * Apply Markus patch (thanks!) for sr #1698 (can't restore session using LASH) 80 | -------------------------------------------------------------------------------- /serialization_xml.py: -------------------------------------------------------------------------------- 1 | # This file is part of jack_mixer 2 | # 3 | # Copyright (C) 2006 Nedko Arnaudov 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; version 2 of the License 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | from serialization import * 19 | import xml.dom 20 | import xml.dom.minidom 21 | 22 | class XmlSerialization(SerializationBackend): 23 | def get_root_serialization_object(self, name): 24 | self.doc = xml.dom.getDOMImplementation().createDocument(xml.dom.EMPTY_NAMESPACE, name, None) 25 | return XmlSerializationObject(self.doc, self.doc.documentElement) 26 | 27 | def get_root_unserialization_object(self, name): 28 | if name != self.doc.documentElement.nodeName: 29 | return None 30 | return XmlSerializationObject(self.doc, self.doc.documentElement) 31 | 32 | def get_child_serialization_object(self, name, backend_object): 33 | child = self.doc.createElement(name) 34 | backend_object.element.appendChild(child) 35 | return XmlSerializationObject(self.doc, child) 36 | 37 | def save(self, file): 38 | file.write(self.doc.toprettyxml()) 39 | 40 | def load(self, file): 41 | self.doc = xml.dom.minidom.parse(file) 42 | 43 | class XmlSerializationObject: 44 | def __init__(self, doc, element): 45 | self.doc = doc 46 | self.element = element 47 | 48 | def add_property(self, name, value): 49 | self.add_property_as_attribute(name, value) 50 | 51 | def add_property_as_attribute(self, name, value): 52 | self.element.setAttribute(name, value) 53 | 54 | #def add_property_as_child_element(self, name, value): 55 | # child = self.doc.createElement(name) 56 | # value = self.doc.createTextNode(value) 57 | # child.appendChild(value) 58 | # self.element.appendChild(child) 59 | 60 | def get_childs(self): 61 | child_elements = self.element.childNodes 62 | childs = [] 63 | for child in child_elements: 64 | if child.nodeType == child.ELEMENT_NODE: 65 | childs.append(XmlSerializationObject(self.doc, child)) 66 | return childs 67 | 68 | def get_properties(self): 69 | properties = self.element.attributes 70 | dictionary = {} 71 | for i in range(properties.length): 72 | dictionary[properties.item(i).name] = properties.item(i).nodeValue 73 | return dictionary 74 | 75 | def serialization_name(self): 76 | return self.element.nodeName 77 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C ; c-basic-offset: 2 -*- */ 2 | /***************************************************************************** 3 | * 4 | * Copyright (C) 2006,2007 Nedko Arnaudov 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; version 2 of the License 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | *****************************************************************************/ 20 | 21 | #ifndef LOG_H__7097F6FE_4FEE_4962_9542_60375961F567__INCLUDED 22 | #define LOG_H__7097F6FE_4FEE_4962_9542_60375961F567__INCLUDED 23 | 24 | void jack_mixer_log(int level, const char * format, ...); 25 | 26 | #define LOG_LEVEL_DEBUG 0 27 | #define LOG_LEVEL_INFO 1 28 | #define LOG_LEVEL_WARNING 2 29 | #define LOG_LEVEL_NOTICE 3 30 | #define LOG_LEVEL_ERROR 4 31 | #define LOG_LEVEL_FATAL 5 32 | #define LOG_LEVEL_BLACK_HOLE 6 33 | 34 | #if !defined(LOG_LEVEL) 35 | #define LOG_LEVEL LOG_LEVEL_WARNING 36 | #endif 37 | 38 | #if LOG_LEVEL <= LOG_LEVEL_DEBUG 39 | # define LOG_DEBUG(format, ...) \ 40 | jack_mixer_log(LOG_LEVEL_DEBUG, \ 41 | format "\n", ## __VA_ARGS__) 42 | #else 43 | # define LOG_DEBUG(format, ...) 44 | #endif 45 | 46 | #if LOG_LEVEL <= LOG_LEVEL_INFO 47 | # define LOG_INFO(format, ...) \ 48 | jack_mixer_log(LOG_LEVEL_INFO, \ 49 | format "\n", ## __VA_ARGS__) 50 | #else 51 | # define LOG_INFO(format, ...) 52 | #endif 53 | 54 | #if LOG_LEVEL <= LOG_LEVEL_WARNING 55 | # define LOG_WARNING(format, ...) \ 56 | jack_mixer_log(LOG_LEVEL_WARNING, \ 57 | format "\n", ## __VA_ARGS__) 58 | #else 59 | # define LOG_WARNING(format, ...) 60 | #endif 61 | 62 | #if LOG_LEVEL <= LOG_LEVEL_NOTICE 63 | # define LOG_NOTICE(format, ...) \ 64 | jack_mixer_log(LOG_LEVEL_NOTICE, \ 65 | format "\n", ## __VA_ARGS__) 66 | #else 67 | # define LOG_NOTICE(format, ...) 68 | #endif 69 | 70 | #if LOG_LEVEL <= LOG_LEVEL_ERROR 71 | # define LOG_ERROR(format, ...) \ 72 | jack_mixer_log(LOG_LEVEL_ERROR, \ 73 | format "\n", ## __VA_ARGS__) 74 | #else 75 | # define LOG_ERROR(format, ...) 76 | #endif 77 | 78 | #if LOG_LEVEL <= LOG_LEVEL_FATAL 79 | # define LOG_FATAL(format, ...) \ 80 | jack_mixer_log(LOG_LEVEL_FATAL, \ 81 | format "\n", ## __VA_ARGS__) 82 | #else 83 | # define LOG_FATAL(format, ...) 84 | #endif 85 | 86 | #endif /* #ifndef LOG_H__7097F6FE_4FEE_4962_9542_60375961F567__INCLUDED */ 87 | -------------------------------------------------------------------------------- /memory_atomic.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C ; c-basic-offset: 2 -*- */ 2 | /***************************************************************************** 3 | * 4 | * Non-sleeping memory allocation 5 | * 6 | * Copyright (C) 2006,2007 Nedko Arnaudov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; version 2 of the License 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | *****************************************************************************/ 22 | 23 | #ifndef MEMORY_ATOMIC_H__7B572547_304D_4597_8808_990BE4476CC3__INCLUDED 24 | #define MEMORY_ATOMIC_H__7B572547_304D_4597_8808_990BE4476CC3__INCLUDED 25 | 26 | typedef void * rtsafe_memory_pool_handle; 27 | 28 | /* will sleep */ 29 | bool 30 | rtsafe_memory_pool_create( 31 | size_t data_size, /* chunk size */ 32 | size_t min_preallocated, /* min chunks preallocated */ 33 | size_t max_preallocated, /* max chunks preallocated */ 34 | bool enforce_thread_safety, /* true - enforce thread safety (internal mutex), 35 | false - assume caller code is already thread-safe */ 36 | rtsafe_memory_pool_handle * pool_ptr); 37 | 38 | /* will sleep */ 39 | void 40 | rtsafe_memory_pool_destroy( 41 | rtsafe_memory_pool_handle pool); 42 | 43 | /* may sleep */ 44 | void 45 | rtsafe_memory_pool_sleepy( 46 | rtsafe_memory_pool_handle pool); 47 | 48 | /* will not sleep, returns NULL if no memory is available */ 49 | void * 50 | rtsafe_memory_pool_allocate( 51 | rtsafe_memory_pool_handle pool); 52 | 53 | /* may sleep, will not fail */ 54 | void * 55 | rtsafe_memory_pool_allocate_sleepy( 56 | rtsafe_memory_pool_handle pool); 57 | 58 | /* will not sleep */ 59 | void 60 | rtsafe_memory_pool_deallocate( 61 | rtsafe_memory_pool_handle pool, 62 | void * data); 63 | 64 | typedef void * rtsafe_memory_handle; 65 | 66 | /* will sleep */ 67 | bool 68 | rtsafe_memory_init( 69 | size_t max_size, 70 | size_t prealloc_min, 71 | size_t prealloc_max, 72 | bool enforce_thread_safety, /* true - enforce thread safety (internal mutex), 73 | false - assume caller code is already thread-safe */ 74 | rtsafe_memory_handle * handle_ptr); 75 | 76 | /* will not sleep, returns NULL if no memory is available */ 77 | void * 78 | rtsafe_memory_allocate( 79 | rtsafe_memory_handle handle_ptr, 80 | size_t size); 81 | 82 | /* may sleep */ 83 | void 84 | rtsafe_memory_sleepy( 85 | rtsafe_memory_handle handle_ptr); 86 | 87 | /* will not sleep */ 88 | void 89 | rtsafe_memory_deallocate( 90 | void * data); 91 | 92 | void 93 | rtsafe_memory_uninit( 94 | rtsafe_memory_handle handle_ptr); 95 | 96 | #endif /* #ifndef MEMORY_ATOMIC_H__7B572547_304D_4597_8808_990BE4476CC3__INCLUDED */ 97 | -------------------------------------------------------------------------------- /m4/swig.m4: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2007 Nedko Arnaudov 2 | # This file is distributed under the same terms as the Autoconf macro files. 3 | 4 | AC_DEFUN([AC_PROG_SWIG],[ 5 | AC_PATH_PROG([SWIG],[swig]) 6 | if test -z "$SWIG" ; then 7 | AC_MSG_WARN([cannot find 'swig' program. You should look at http://www.swig.org]) 8 | SWIG='echo "Error: SWIG is not installed. You should look at http://www.swig.org" ; false' 9 | elif test -n "$1" ; then 10 | AC_MSG_CHECKING([for SWIG version]) 11 | [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`] 12 | AC_MSG_RESULT([$swig_version]) 13 | if test -n "$swig_version" ; then 14 | # Calculate the required version number components 15 | [required=$1] 16 | [required_major=`echo $required | sed 's/[^0-9].*//'`] 17 | if test -z "$required_major" ; then 18 | [required_major=0] 19 | fi 20 | [required=`echo $required | sed 's/[0-9]*[^0-9]//'`] 21 | [required_minor=`echo $required | sed 's/[^0-9].*//'`] 22 | if test -z "$required_minor" ; then 23 | [required_minor=0] 24 | fi 25 | [required=`echo $required | sed 's/[0-9]*[^0-9]//'`] 26 | [required_patch=`echo $required | sed 's/[^0-9].*//'`] 27 | if test -z "$required_patch" ; then 28 | [required_patch=0] 29 | fi 30 | # Calculate the available version number components 31 | [available=$swig_version] 32 | [available_major=`echo $available | sed 's/[^0-9].*//'`] 33 | if test -z "$available_major" ; then 34 | [available_major=0] 35 | fi 36 | [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] 37 | [available_minor=`echo $available | sed 's/[^0-9].*//'`] 38 | if test -z "$available_minor" ; then 39 | [available_minor=0] 40 | fi 41 | [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] 42 | [available_patch=`echo $available | sed 's/[^0-9].*//'`] 43 | if test -z "$available_patch" ; then 44 | [available_patch=0] 45 | fi 46 | if test $available_major -ne $required_major \ 47 | -o $available_minor -ne $required_minor \ 48 | -o $available_patch -lt $required_patch ; then 49 | AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version. You should look at http://www.swig.org]) 50 | SWIG='echo "Error: SWIG version >= $1 is required. You have '"$swig_version"'. You should look at http://www.swig.org" ; false' 51 | else 52 | AC_MSG_NOTICE([SWIG executable is '$SWIG']) 53 | SWIG_LIB=`$SWIG -swiglib` 54 | AC_MSG_NOTICE([SWIG library directory is '$SWIG_LIB']) 55 | fi 56 | else 57 | AC_MSG_WARN([cannot determine SWIG version]) 58 | SWIG='echo "Error: Cannot determine SWIG version. You should look at http://www.swig.org" ; false' 59 | fi 60 | fi 61 | AC_SUBST([SWIG_LIB]) 62 | ]) 63 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | # 4 | # This file is part of jack_mixer 5 | # 6 | # Copyright (C) 2007 Nedko Arnaudov 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; version 2 of the License 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | AC_PREREQ(2.61) 23 | AC_INIT(jack_mixer, 11) 24 | AC_CONFIG_AUX_DIR(config) 25 | AC_CONFIG_MACRO_DIR([m4]) 26 | AM_INIT_AUTOMAKE 27 | AC_CONFIG_HEADER([config.h]) 28 | 29 | # not real check but hack (because version is not parameter to configure script) to detect development version 30 | AM_CONDITIONAL(DEV_VERSION, test $VERSION = dev) 31 | 32 | AC_SUBST(DATADIR) 33 | 34 | # Checks for programs. 35 | AC_PROG_CC 36 | AC_DISABLE_STATIC 37 | AC_PROG_LIBTOOL 38 | AC_SUBST(ACLOCAL_AMFLAGS, "$ACLOCAL_FLAGS -I m4") 39 | 40 | # Checks for libraries. 41 | 42 | PKG_CHECK_MODULES(JACKMIXER, [glib-2.0 jack]) 43 | 44 | # JACK MIDI 45 | have_jackmidi="unknown" 46 | AC_ARG_ENABLE(jackmidi, [AS_HELP_STRING(--disable-jackmidi, [Force disable JACK MIDI support [default=no]])], [ have_jackmidi="no (disabled)" ]) 47 | if test "$have_jackmidi" = "unknown" 48 | then 49 | have_jackmidi="no" 50 | PKG_CHECK_MODULES(JACK_MIDI, jack >= 0.102.0, AC_DEFINE([HAVE_JACK_MIDI], [], [Defined if we have JACK MIDI support.]) have_jackmidi="yes") 51 | if test "$have_jackmidi" = "yes" 52 | then 53 | PKG_CHECK_MODULES(OLD_JACK_MIDI, jack < 0.102.27, have_jackmidi="yes (old one)" AC_DEFINE(HAVE_OLD_JACK_MIDI, 1, [whether or not we building against old JACK MIDI (0.102.20)]), echo -n) 54 | fi 55 | fi 56 | 57 | AM_CONDITIONAL(HAVE_JACK_MIDI, test "$have_jackmidi" = "yes" -o "$have_jackmidi" = "yes (old one)") 58 | 59 | if test "$have_jackmidi" = "no" 60 | then 61 | AC_MSG_WARN([Your JACK is missing MIDI support.]) 62 | fi 63 | 64 | if test "$have_jackmidi" = "yes" -o "$have_jackmidi" = "yes (old one)" 65 | then 66 | AC_JACK_MIDI_NFRAMES_CHECK() 67 | if test "$jackmidi_nframes" = "yes" 68 | then 69 | AC_DEFINE([JACK_MIDI_NEEDS_NFRAMES], 1, [Defined if we JACK MIDI functions need nframes parameter.]) 70 | fi 71 | fi 72 | 73 | # Python checking 74 | AM_PATH_PYTHON(3.5) 75 | AM_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(Could not find Python headers)]) 76 | #AS_AC_EXPAND(PYTHONDIR, $pythondir) 77 | #AC_SUBST(PYTHONDIR) 78 | 79 | AC_ARG_ENABLE(pymod-checks, [AS_HELP_STRING(--disable-pymod-checks, [Force disable checks for Python modules required at run time])], [ disable_pymod_checks="yes" ], [ disable_pymod_checks="no" ]) 80 | 81 | if test "$disable_pymod_checks" != "yes" 82 | then 83 | AM_CHECK_PYMOD(gi,,,[AC_MSG_ERROR(Could not find Pygi)]) 84 | AM_CHECK_PYMOD(cairo,,,[AC_MSG_ERROR(Could not find Pycairo)]) 85 | else 86 | AC_MSG_WARN([Checks for python modules required runtime have been force disabled]) 87 | fi 88 | 89 | AC_PATH_PROG(GCONFTOOL, gconftool-2, no) 90 | AM_GCONF_SOURCE_2 91 | AM_CONDITIONAL([GCONF_SCHEMAS_INSTALL], [test "x$ac_cv_path_GCONFTOOL" != "xno"]) 92 | 93 | AC_OUTPUT([ 94 | Makefile 95 | data/Makefile 96 | data/art/Makefile 97 | ]) 98 | 99 | AC_MSG_RESULT([]) 100 | AC_MSG_RESULT([**********************************************************************]) 101 | AC_MSG_RESULT([]) 102 | AC_MSG_RESULT([Prefix: $prefix]) 103 | AC_MSG_RESULT([Python dir: $pythondir]) 104 | #AC_MSG_RESULT([GConf schema dir: $GCONF_SCHEMA_FILE_DIR]) 105 | AC_MSG_RESULT([]) 106 | AC_MSG_RESULT([MIDI support: $have_jackmidi]) 107 | AC_MSG_RESULT([]) 108 | AC_MSG_RESULT([**********************************************************************]) 109 | AC_MSG_RESULT([]) 110 | -------------------------------------------------------------------------------- /serialization.py: -------------------------------------------------------------------------------- 1 | # This file is part of jack_mixer 2 | # 3 | # Copyright (C) 2006 Nedko Arnaudov 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; version 2 of the License 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | class SerializationBackend: 19 | '''Base class for serialization backends''' 20 | def get_root_serialization_object(self, name): 21 | '''Returns serialization object where properties of root object 22 | will be serialized to''' 23 | # this method should never be called for the base class 24 | raise NotImplementedError 25 | 26 | def get_child_serialization_object(self, name, backend_object): 27 | # this method should never be called for the base class 28 | raise NotImplementedError 29 | 30 | class SerializationObjectBackend: 31 | '''Base class for serialization backend objects where real object 32 | properties will be serialized to or unserialized from.''' 33 | def add_property(self, name, value): 34 | '''Serialize particular property''' 35 | pass 36 | 37 | def get_childs(self): 38 | pass 39 | 40 | def get_properties(self): 41 | pass 42 | 43 | def serialization_name(self): 44 | return None 45 | 46 | class SerializedObject: 47 | '''Base class for object supporting serialization''' 48 | def serialization_name(self): 49 | return None 50 | 51 | def serialize(self, object_backend): 52 | '''Serialize properties of called object into supplied serialization_object_backend''' 53 | pass 54 | 55 | def serialization_get_childs(self): 56 | '''Get child objects tha required and support serialization''' 57 | return [] 58 | 59 | def unserialize_property(self, name, value): 60 | pass 61 | 62 | def unserialize_child(self, name): 63 | return None 64 | 65 | class Serializator: 66 | def __init__(self): 67 | pass 68 | 69 | def serialize(self, root, backend): 70 | self.serialize_one(backend, root, backend.get_root_serialization_object(root.serialization_name())) 71 | 72 | def unserialize(self, root, backend): 73 | backend_object = backend.get_root_unserialization_object(root.serialization_name()) 74 | if backend_object == None: 75 | return False 76 | 77 | return self.unserialize_one(backend, root, backend_object) 78 | 79 | def unserialize_one(self, backend, object, backend_object): 80 | #print "Unserializing " + repr(object) 81 | properties = backend_object.get_properties() 82 | for name, value in properties.items(): 83 | #print "%s = %s" % (name, value) 84 | if not object.unserialize_property(name, value): 85 | return False 86 | 87 | backend_childs = backend_object.get_childs() 88 | for backend_child in backend_childs: 89 | name = backend_child.serialization_name() 90 | child = object.unserialize_child(name) 91 | if not child: 92 | return False 93 | if not self.unserialize_one(backend, child, backend_child): 94 | return False 95 | 96 | return True 97 | 98 | def serialize_one(self, backend, object, backend_object): 99 | object.serialize(backend_object) 100 | childs = object.serialization_get_childs() 101 | for child in childs: 102 | #print "serializing child " + repr(child) 103 | self.serialize_one(backend, child, 104 | backend.get_child_serialization_object( 105 | child.serialization_name(), backend_object)) 106 | -------------------------------------------------------------------------------- /jack_mix_box.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * 3 | * This file is part of jack_mixer 4 | * 5 | * Copyright (C) 2006 Nedko Arnaudov 6 | * Copyright (C) 2009-2011 Frederic Peters 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; version 2 of the License 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | *****************************************************************************/ 22 | 23 | /* 24 | * jack_mix_box is a most minimalistic jack mixer, a set of mono/sterero input 25 | * channels, mixed to a single output channel, with the volume of the 26 | * input channels controlled by MIDI control change (CC) codes. 27 | * 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "jack_mixer.h" 38 | 39 | jack_mixer_t mixer; 40 | bool keepRunning = true; 41 | 42 | void 43 | usage() 44 | { 45 | 46 | printf("Usage:\n"); 47 | printf("\tjack_mix_box [ -n|--name ] [ -s|--stereo ] [ -v|--volume ] MIDI_CC_1 MIDI_CC_2 ...\n"); 48 | printf("\tsend SIGUSR1 to the process to have the current columes reported per input channel\n\n"); 49 | } 50 | 51 | void 52 | reportVolume(int sig) 53 | { 54 | (void)sig; 55 | channels_volumes_read(mixer); 56 | } 57 | 58 | void 59 | triggerShutDown(int sig) 60 | { 61 | (void)sig; 62 | keepRunning = false; 63 | } 64 | 65 | int 66 | main(int argc, char *argv[]) 67 | { 68 | jack_mixer_scale_t scale; 69 | jack_mixer_channel_t main_mix_channel; 70 | char *jack_cli_name = NULL; 71 | int channel_index; 72 | bool bStereo = false; 73 | double initialVolume = 0.0f; //in dbFS 74 | 75 | while (1) { 76 | int c; 77 | static struct option long_options[] = 78 | { 79 | {"name", required_argument, 0, 'n'}, 80 | {"help", required_argument, 0, 'h'}, 81 | {"stereo", required_argument, 0, 's'}, 82 | {"volume", required_argument, 0, 'v'}, 83 | {0, 0, 0, 0} 84 | }; 85 | int option_index = 0; 86 | 87 | c = getopt_long (argc, argv, "shn:v:", long_options, &option_index); 88 | if (c == -1) 89 | break; 90 | 91 | switch (c) { 92 | case 'n': 93 | jack_cli_name = strdup(optarg); 94 | break; 95 | case 's': 96 | bStereo = true; 97 | break; 98 | case 'v': 99 | initialVolume = strtod(optarg, NULL); 100 | break; 101 | case 'h': 102 | usage(); 103 | exit(0); 104 | break; 105 | default: 106 | fprintf(stderr, "Unknown argument, aborting.\n"); 107 | exit(1); 108 | } 109 | } 110 | 111 | if (optind == argc) { 112 | fprintf(stderr, "You must specify at least one input channel\n"); 113 | exit(1); 114 | } 115 | 116 | scale = scale_create(); 117 | scale_add_threshold(scale, -70.0, 0.0); 118 | scale_add_threshold(scale, 0.0, 1.0); 119 | scale_calculate_coefficients(scale); 120 | 121 | if (jack_cli_name == NULL) { 122 | jack_cli_name = strdup("jack_mix_box"); 123 | } 124 | 125 | mixer = create(jack_cli_name, false); 126 | main_mix_channel = add_output_channel(mixer, "MAIN", true, false); 127 | channel_set_midi_scale(main_mix_channel, scale); 128 | channel_volume_write(main_mix_channel, 0.0); 129 | 130 | channel_index = 0; 131 | while (optind < argc) { 132 | char *channel_name; 133 | jack_mixer_channel_t channel; 134 | 135 | channel_index += 1; 136 | channel_name = malloc(15); 137 | if (snprintf(channel_name, 15, "Channel %d", channel_index) >= 15) { 138 | free(channel_name); 139 | abort(); 140 | } 141 | channel = add_channel(mixer, channel_name, bStereo); 142 | if (channel == NULL) { 143 | fprintf(stderr, "Failed to add channel %d, aborting\n", channel_index); 144 | exit(1); 145 | } 146 | channel_set_volume_midi_cc(channel, atoi(argv[optind++])); 147 | channel_set_midi_scale(channel, scale); 148 | channel_volume_write(channel, initialVolume); 149 | free(channel_name); 150 | } 151 | 152 | signal(SIGUSR1, reportVolume); 153 | signal(SIGTERM, triggerShutDown); 154 | signal(SIGHUP, triggerShutDown); 155 | signal(SIGINT, triggerShutDown); 156 | 157 | while (keepRunning) { 158 | usleep(500u * 1000u); //500msec 159 | } 160 | 161 | remove_channels(mixer); 162 | remove_output_channel(main_mix_channel); 163 | destroy(mixer); 164 | scale_destroy(scale); 165 | free(jack_cli_name); 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /scale.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C ; c-basic-offset: 2 -*- */ 2 | /***************************************************************************** 3 | * 4 | * This file is part of jack_mixer 5 | * 6 | * Copyright (C) 2006 Nedko Arnaudov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; version 2 of the License 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | *****************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "jack_mixer.h" 30 | //#define LOG_LEVEL LOG_LEVEL_DEBUG 31 | #include "log.h" 32 | #include "list.h" 33 | 34 | struct threshold 35 | { 36 | struct list_head scale_siblings; 37 | double db; 38 | double scale; 39 | double a; 40 | double b; 41 | }; 42 | 43 | struct scale 44 | { 45 | struct list_head thresholds; 46 | double max_db; 47 | }; 48 | 49 | jack_mixer_scale_t 50 | scale_create() 51 | { 52 | struct scale * scale_ptr; 53 | 54 | scale_ptr = malloc(sizeof(struct scale)); 55 | if (scale_ptr == NULL) 56 | { 57 | return NULL; 58 | } 59 | 60 | INIT_LIST_HEAD(&scale_ptr->thresholds); 61 | scale_ptr->max_db = -INFINITY; 62 | 63 | LOG_DEBUG("Scale %p created", scale_ptr); 64 | 65 | return (jack_mixer_scale_t)scale_ptr; 66 | } 67 | 68 | #define scale_ptr ((struct scale *)scale) 69 | 70 | void 71 | scale_destroy( 72 | jack_mixer_scale_t scale) 73 | { 74 | scale_remove_thresholds(scale); 75 | free(scale_ptr); 76 | } 77 | 78 | void 79 | scale_remove_thresholds( 80 | jack_mixer_scale_t scale) 81 | { 82 | 83 | struct threshold * threshold_ptr; 84 | struct threshold * node_ptr; 85 | 86 | list_for_each_entry_safe(threshold_ptr, node_ptr, &scale_ptr->thresholds, scale_siblings) 87 | { 88 | list_del(&(threshold_ptr->scale_siblings)); 89 | free(threshold_ptr); 90 | threshold_ptr = NULL; 91 | } 92 | } 93 | 94 | bool 95 | scale_add_threshold( 96 | jack_mixer_scale_t scale, 97 | float db, 98 | float scale_value) 99 | { 100 | struct threshold * threshold_ptr; 101 | 102 | LOG_DEBUG("Adding threshold (%f dBFS -> %f) to scale %p", db, scale, scale_ptr); 103 | 104 | threshold_ptr = malloc(sizeof(struct threshold)); 105 | LOG_DEBUG("Threshold %p created ", threshold_ptr, db, scale); 106 | 107 | if (threshold_ptr == NULL) 108 | { 109 | return false; 110 | } 111 | 112 | threshold_ptr->db = db; 113 | threshold_ptr->scale = scale_value; 114 | 115 | list_add_tail(&threshold_ptr->scale_siblings, &scale_ptr->thresholds); 116 | 117 | if (db > scale_ptr->max_db) 118 | { 119 | scale_ptr->max_db = db; 120 | } 121 | 122 | return true; 123 | } 124 | 125 | #undef threshold_ptr 126 | 127 | void 128 | scale_calculate_coefficients( 129 | jack_mixer_scale_t scale) 130 | { 131 | struct threshold * threshold_ptr; 132 | struct threshold * prev_ptr; 133 | struct list_head * node_ptr; 134 | 135 | prev_ptr = NULL; 136 | 137 | list_for_each(node_ptr, &scale_ptr->thresholds) 138 | { 139 | threshold_ptr = list_entry(node_ptr, struct threshold, scale_siblings); 140 | 141 | LOG_DEBUG("Calculating coefficients for threshold %p", threshold_ptr); 142 | 143 | if (prev_ptr != NULL) 144 | { 145 | threshold_ptr->a = (prev_ptr->scale - threshold_ptr->scale) / (prev_ptr->db - threshold_ptr->db); 146 | threshold_ptr->b = threshold_ptr->scale - threshold_ptr->a * threshold_ptr->db; 147 | LOG_DEBUG("%.0f dB - %.0f dB: scale = %f * dB + %f", prev_ptr->db, threshold_ptr->db, threshold_ptr->a, threshold_ptr->b); 148 | } 149 | 150 | prev_ptr = threshold_ptr; 151 | } 152 | } 153 | 154 | /* Convert dBFS value to number in range 0.0-1.0 */ 155 | double 156 | scale_db_to_scale( 157 | jack_mixer_scale_t scale, 158 | double db) 159 | { 160 | struct threshold * threshold_ptr; 161 | struct threshold * prev_ptr; 162 | struct list_head * node_ptr; 163 | 164 | prev_ptr = NULL; 165 | 166 | list_for_each(node_ptr, &scale_ptr->thresholds) 167 | { 168 | threshold_ptr = list_entry(node_ptr, struct threshold, scale_siblings); 169 | 170 | if (db < threshold_ptr->db) 171 | { 172 | LOG_DEBUG("Match at %f dB treshold", threshold_ptr->db); 173 | if (prev_ptr == NULL) 174 | { 175 | return 0.0; 176 | } 177 | 178 | return threshold_ptr->a * db + threshold_ptr->b; 179 | } 180 | 181 | prev_ptr = threshold_ptr; 182 | } 183 | 184 | return 1.0; 185 | } 186 | 187 | /* Convert number in range 0.0-1.0 to dBFS value */ 188 | double 189 | scale_scale_to_db( 190 | jack_mixer_scale_t scale, 191 | double scale_value) 192 | { 193 | struct threshold * threshold_ptr; 194 | struct threshold * prev_ptr; 195 | struct list_head * node_ptr; 196 | 197 | prev_ptr = NULL; 198 | 199 | list_for_each(node_ptr, &scale_ptr->thresholds) 200 | { 201 | threshold_ptr = list_entry(node_ptr, struct threshold, scale_siblings); 202 | 203 | if (scale_value <= threshold_ptr->scale) 204 | { 205 | if (prev_ptr == NULL) 206 | { 207 | return -INFINITY; 208 | } 209 | 210 | return (scale_value - threshold_ptr->b) / threshold_ptr->a; 211 | } 212 | 213 | prev_ptr = threshold_ptr; 214 | } 215 | 216 | return scale_ptr->max_db; 217 | } 218 | -------------------------------------------------------------------------------- /scale.py: -------------------------------------------------------------------------------- 1 | # This file is part of jack_mixer 2 | # 3 | # Copyright (C) 2006 Nedko Arnaudov 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; version 2 of the License 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | import math 19 | import jack_mixer_c 20 | 21 | class Mark: 22 | '''Encapsulates scale linear function edge and coefficients for scale = a * dB + b formula''' 23 | def __init__(self, db, scale): 24 | self.db = db 25 | self.scale = scale 26 | self.text = "%.0f" % math.fabs(db) 27 | 28 | class Base: 29 | '''Scale abstraction, various scale implementation derive from this class''' 30 | def __init__(self, scale_id, description): 31 | self.marks = [] 32 | self.scale_id = scale_id 33 | self.description = description 34 | self.scale = jack_mixer_c.Scale() 35 | 36 | def add_threshold(self, db, scale, is_mark): 37 | self.scale.add_threshold(db, scale) 38 | if is_mark: 39 | self.marks.append(Mark(db, scale)) 40 | 41 | def calculate_coefficients(self): 42 | self.scale.calculate_coefficients() 43 | 44 | def db_to_scale(self, db): 45 | '''Convert dBFS value to number in range 0.0-1.0 used in GUI''' 46 | #print "db_to_scale(%f)" % db 47 | return self.scale.db_to_scale(db) 48 | 49 | def scale_to_db(self, scale): 50 | '''Convert number in range 0.0-1.0 used in GUI to dBFS value''' 51 | return self.scale.scale_to_db(scale) 52 | 53 | def add_mark(self, db): 54 | self.marks.append(Mark(db, -1.0)) 55 | 56 | def get_marks(self): 57 | return self.marks 58 | 59 | def scale_marks(self): 60 | for i in self.marks: 61 | if i.scale == -1.0: 62 | i.scale = self.db_to_scale(i.db) 63 | 64 | # IEC 60268-18 Peak programme level meters - Digital audio peak level meter 65 | # Adapted from meterpridge, may be wrong, I'm not buying standards, event if they cost $45 66 | # If someone has the standart, please eighter share it with me or fix the code. 67 | class IEC268(Base): 68 | '''IEC 60268-18 Peak programme level meters - Digital audio peak level meter''' 69 | def __init__(self): 70 | Base.__init__(self, "iec_268", 71 | "IEC 60268-18 Peak programme level meters - Digital audio peak level meter") 72 | self.add_threshold(-70.0, 0.0, False) 73 | self.add_threshold(-60.0, 0.05, True) 74 | self.add_threshold(-50.0, 0.075, True) 75 | self.add_threshold(-40.0, 0.15, True) 76 | self.add_mark(-35.0) 77 | self.add_threshold(-30.0, 0.3, True) 78 | self.add_mark(-25.0) 79 | self.add_threshold(-20.0, 0.5, True) 80 | self.add_mark(-15.0) 81 | self.add_mark(-10.0) 82 | self.add_mark(-5.0) 83 | self.add_threshold(0.0, 1.0, True) 84 | self.calculate_coefficients() 85 | self.scale_marks() 86 | 87 | class IEC268Minimalistic(Base): 88 | '''IEC 60268-18 Peak programme level meters - Digital audio peak level meter, 89 | fewer marks''' 90 | def __init__(self): 91 | Base.__init__(self, 'iec_268_minimalistic', 92 | 'IEC 60268-18 Peak programme level meters - Digital audio peak level meter, fewer marks') 93 | self.add_threshold(-70.0, 0.0, False) 94 | self.add_threshold(-60.0, 0.05, True) 95 | self.add_threshold(-50.0, 0.075, False) 96 | self.add_threshold(-40.0, 0.15, True) 97 | self.add_threshold(-30.0, 0.3, True) 98 | self.add_threshold(-20.0, 0.5, True) 99 | self.add_mark(-10.0) 100 | self.add_threshold(0.0, 1.0, True) 101 | self.calculate_coefficients() 102 | self.scale_marks() 103 | 104 | class Linear70dB(Base): 105 | '''Linear scale with range from -70 to 0 dBFS''' 106 | def __init__(self): 107 | Base.__init__(self, "linear_70dB", "Linear scale with range from -70 to 0 dBFS") 108 | self.add_threshold(-70.0, 0.0, False) 109 | self.add_mark(-60.0) 110 | self.add_mark(-50.0) 111 | self.add_mark(-40.0) 112 | self.add_mark(-35.0) 113 | self.add_mark(-30.0) 114 | self.add_mark(-25.0) 115 | self.add_mark(-20.0) 116 | self.add_mark(-15.0) 117 | self.add_mark(-10.0) 118 | self.add_mark(-5.0) 119 | self.add_threshold(0.0, 1.0, True) 120 | self.calculate_coefficients() 121 | self.scale_marks() 122 | 123 | class Linear30dB(Base): 124 | '''Linear scale with range from -30 to +30 dBFS''' 125 | def __init__(self): 126 | Base.__init__(self, "linear_30dB", "Linear scale with range from -30 to +30 dBFS") 127 | self.add_threshold(-30.0, 0.0, False) 128 | self.add_threshold(+30.0, 1.0, True) 129 | self.calculate_coefficients() 130 | self.scale_marks() 131 | 132 | def scale_test1(scale): 133 | for i in range(-97 * 2, 1, 1): 134 | db = float(i)/2.0 135 | print("%5.1f dB maps to %f" % (db, scale.db_to_scale(db))) 136 | 137 | def scale_test2(scale): 138 | for i in range(101): 139 | s = float(i)/100.0 140 | print("%.2f maps to %.1f dB" % (s, scale.scale_to_db(s))) 141 | 142 | def print_db_to_scale(db): 143 | print("%-.1f dB maps to %f" % (db, scale.db_to_scale(db))) 144 | 145 | def scale_test3(scale): 146 | print_db_to_scale(+77.0) 147 | print_db_to_scale(+7.0) 148 | print_db_to_scale(0.0) 149 | print_db_to_scale(-107.0) 150 | 151 | #scale = linear_30dB() 152 | #scale_test2(scale) 153 | #scale_test3(scale) 154 | -------------------------------------------------------------------------------- /jack_mixer.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C ; c-basic-offset: 2 -*- */ 2 | /***************************************************************************** 3 | * 4 | * This file is part of jack_mixer 5 | * 6 | * Copyright (C) 2006 Nedko Arnaudov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; version 2 of the License 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | *****************************************************************************/ 22 | 23 | #ifndef JACK_MIXER_H__DAEB51D8_5861_40F2_92E4_24CA495A384D__INCLUDED 24 | #define JACK_MIXER_H__DAEB51D8_5861_40F2_92E4_24CA495A384D__INCLUDED 25 | 26 | #ifdef SWIG 27 | %module jack_mixer_c 28 | %include "typemaps.i" 29 | %apply double *OUTPUT { double * left_ptr, double * right_ptr, double * mono_ptr }; 30 | %{ 31 | #include 32 | #include "jack_mixer.h" 33 | %} 34 | #endif 35 | 36 | #include "scale.h" 37 | 38 | typedef void * jack_mixer_t; 39 | typedef void * jack_mixer_channel_t; 40 | typedef void * jack_mixer_output_channel_t; 41 | typedef void * jack_mixer_threshold_t; 42 | 43 | jack_mixer_t 44 | create( 45 | const char * jack_client_name_ptr, 46 | bool stereo); 47 | 48 | void 49 | destroy( 50 | jack_mixer_t mixer); 51 | 52 | unsigned int 53 | get_channels_count( 54 | jack_mixer_t mixer); 55 | 56 | const char* 57 | get_client_name( 58 | jack_mixer_t mixer); 59 | 60 | int 61 | get_last_midi_channel( 62 | jack_mixer_t mixer); 63 | 64 | unsigned int 65 | set_last_midi_channel( 66 | jack_mixer_t mixer, 67 | int new_channel); 68 | 69 | jack_mixer_channel_t 70 | add_channel( 71 | jack_mixer_t mixer, 72 | const char * channel_name, 73 | bool stereo); 74 | 75 | const char * 76 | channel_get_name( 77 | jack_mixer_channel_t channel); 78 | 79 | /* returned values are in dBFS */ 80 | void 81 | channel_stereo_meter_read( 82 | jack_mixer_channel_t channel, 83 | double * left_ptr, 84 | double * right_ptr); 85 | 86 | /* returned value is in dBFS */ 87 | void 88 | channel_mono_meter_read( 89 | jack_mixer_channel_t channel, 90 | double * mono_ptr); 91 | 92 | bool 93 | channel_is_stereo( 94 | jack_mixer_channel_t channel); 95 | 96 | void 97 | channel_set_midi_change_callback( 98 | jack_mixer_channel_t channel, 99 | void (*midi_change_callback) (void*), 100 | void *user_data); 101 | 102 | /* volume is in dBFS */ 103 | void 104 | channel_volume_write( 105 | jack_mixer_channel_t channel, 106 | double volume); 107 | 108 | double 109 | channel_volume_read( 110 | jack_mixer_channel_t channel); 111 | 112 | void 113 | channels_volumes_read(jack_mixer_t mixer_ptr); 114 | 115 | /* balance is from -1.0 (full left) to +1.0 (full right) */ 116 | void 117 | channel_balance_write( 118 | jack_mixer_channel_t channel, 119 | double balance); 120 | 121 | double 122 | channel_balance_read( 123 | jack_mixer_channel_t channel); 124 | 125 | int 126 | channel_get_balance_midi_cc( 127 | jack_mixer_channel_t channel); 128 | 129 | unsigned int 130 | channel_set_balance_midi_cc( 131 | jack_mixer_channel_t channel, 132 | int new_cc); 133 | 134 | int 135 | channel_get_volume_midi_cc( 136 | jack_mixer_channel_t channel); 137 | 138 | unsigned int 139 | channel_set_volume_midi_cc( 140 | jack_mixer_channel_t channel, 141 | int new_cc); 142 | 143 | int 144 | channel_get_mute_midi_cc( 145 | jack_mixer_channel_t channel); 146 | 147 | unsigned int 148 | channel_set_mute_midi_cc( 149 | jack_mixer_channel_t channel, 150 | int new_cc); 151 | 152 | int 153 | channel_get_solo_midi_cc( 154 | jack_mixer_channel_t channel); 155 | 156 | unsigned int 157 | channel_set_solo_midi_cc( 158 | jack_mixer_channel_t channel, 159 | int new_cc); 160 | 161 | void 162 | channel_autoset_midi_cc( 163 | jack_mixer_channel_t channel); 164 | 165 | void 166 | remove_channel( 167 | jack_mixer_channel_t channel); 168 | 169 | void 170 | remove_channels( 171 | jack_mixer_t mixer); 172 | 173 | /* returned value is in dBFS */ 174 | double 175 | channel_abspeak_read( 176 | jack_mixer_channel_t channel); 177 | 178 | void 179 | channel_abspeak_reset( 180 | jack_mixer_channel_t channel); 181 | 182 | void 183 | channel_out_mute( 184 | jack_mixer_channel_t channel); 185 | 186 | void 187 | channel_out_unmute( 188 | jack_mixer_channel_t channel); 189 | 190 | bool 191 | channel_is_out_muted( 192 | jack_mixer_channel_t channel); 193 | 194 | void 195 | channel_solo( 196 | jack_mixer_channel_t channel); 197 | 198 | void 199 | channel_unsolo( 200 | jack_mixer_channel_t channel); 201 | 202 | bool 203 | channel_is_soloed( 204 | jack_mixer_channel_t channel); 205 | 206 | void 207 | channel_rename( 208 | jack_mixer_channel_t channel, 209 | const char * name); 210 | 211 | void 212 | channel_set_midi_scale( 213 | jack_mixer_channel_t channel, 214 | jack_mixer_scale_t scale); 215 | 216 | bool 217 | channel_get_midi_in_got_events( 218 | jack_mixer_channel_t channel); 219 | 220 | jack_mixer_output_channel_t 221 | add_output_channel( 222 | jack_mixer_t mixer, 223 | const char * channel_name, 224 | bool stereo, 225 | bool system); 226 | 227 | void 228 | remove_output_channel( 229 | jack_mixer_output_channel_t output_channel); 230 | 231 | void 232 | output_channel_set_solo( 233 | jack_mixer_output_channel_t output_channel, 234 | jack_mixer_channel_t channel, 235 | bool solo_value); 236 | 237 | void 238 | output_channel_set_muted( 239 | jack_mixer_output_channel_t output_channel, 240 | jack_mixer_channel_t channel, 241 | bool muted_value); 242 | 243 | bool 244 | output_channel_is_muted( 245 | jack_mixer_output_channel_t output_channel, 246 | jack_mixer_channel_t channel); 247 | 248 | bool 249 | output_channel_is_solo( 250 | jack_mixer_output_channel_t output_channel, 251 | jack_mixer_channel_t channel); 252 | 253 | void 254 | output_channel_set_prefader( 255 | jack_mixer_output_channel_t output_channel, 256 | bool pfl_value); 257 | 258 | bool 259 | output_channel_is_prefader( 260 | jack_mixer_output_channel_t output_channel); 261 | 262 | #endif /* #ifndef JACK_MIXER_H__DAEB51D8_5861_40F2_92E4_24CA495A384D__INCLUDED */ 263 | -------------------------------------------------------------------------------- /meter.py: -------------------------------------------------------------------------------- 1 | # This file is part of jack_mixer 2 | # 3 | # Copyright (C) 2006 Nedko Arnaudov 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; version 2 of the License 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | from gi.repository import Gtk 19 | from gi.repository import Gdk 20 | from gi.repository import GObject 21 | import cairo 22 | 23 | class MeterWidget(Gtk.DrawingArea): 24 | def __init__(self, scale): 25 | GObject.GObject.__init__(self) 26 | 27 | self.scale = scale 28 | 29 | self.connect("draw", self.draw) 30 | #self.connect("size-request", self.on_size_request) 31 | self.connect("size-allocate", self.on_size_allocate) 32 | 33 | self.color_bg = Gdk.Color(0,0,0) 34 | self.color_value = None 35 | self.color_mark = Gdk.Color(int(65535 * 0.2), int(65535 * 0.2), int(65535 * 0.2)) 36 | self.width = 0 37 | self.height = 0 38 | self.cache_surface = None 39 | 40 | def get_preferred_width(self): 41 | print('get_preferred_width called') 42 | return 2 43 | 44 | def get_preferred_height(self): 45 | return 200 46 | 47 | def set_color(self, color): 48 | self.color_value = color 49 | self.cache_surface = None 50 | self.invalidate_all() 51 | 52 | def on_expose(self, widget, event): 53 | cairo_ctx = widget.window.cairo_create() 54 | 55 | # set a clip region for the expose event 56 | cairo_ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) 57 | cairo_ctx.clip() 58 | 59 | self.draw(cairo_ctx) 60 | 61 | return False 62 | 63 | def on_size_allocate(self, widget, allocation): 64 | self.width = float(allocation.width) 65 | self.height = float(allocation.height) 66 | self.font_size = 10 67 | self.cache_surface = None 68 | 69 | def on_size_request(self, widget, requisition): 70 | #print "size-request, %u x %u" % (requisition.width, requisition.height) 71 | requisition.width = 20 72 | return 73 | 74 | def invalidate_all(self): 75 | self.queue_draw_area(0, 0, int(self.width), int(self.height)) 76 | 77 | def draw_background(self, cairo_ctx): 78 | if not self.cache_surface: 79 | self.cache_surface = cairo.Surface.create_similar( 80 | cairo_ctx.get_target(), 81 | cairo.CONTENT_COLOR, 82 | int(self.width), 83 | int(self.height)) 84 | cache_cairo_ctx = cairo.Context(self.cache_surface) 85 | 86 | cache_cairo_ctx.set_source_rgba(0, 0, 0, 0) 87 | cache_cairo_ctx.rectangle(0, 0, self.width, self.height) 88 | cache_cairo_ctx.fill() 89 | 90 | cache_cairo_ctx.set_source_rgba(0.2, 0.2, 0.2, 1) 91 | cache_cairo_ctx.select_font_face("Fixed") 92 | cache_cairo_ctx.set_font_size(self.font_size) 93 | glyph_width = self.font_size * 3 / 5 # avarage glyph ratio 94 | for mark in self.scale.get_marks(): 95 | mark_position = int(self.height * (1 - mark.scale)) 96 | cache_cairo_ctx.move_to(0, mark_position) 97 | cache_cairo_ctx.line_to(self.width, mark_position) 98 | cache_cairo_ctx.stroke() 99 | x_correction = self.width / 2 - glyph_width * len(mark.text) / 2 100 | cache_cairo_ctx.move_to(x_correction, mark_position - 2) 101 | cache_cairo_ctx.show_text(mark.text) 102 | cairo_ctx.set_source_surface(self.cache_surface, 0, 0) 103 | cairo_ctx.paint() 104 | 105 | def draw_value(self, cairo_ctx, value, x, width): 106 | if self.color_value is not None: 107 | cairo_ctx.set_source_rgb(self.color_value.red/65535., 108 | self.color_value.green/65535., self.color_value.blue/65535.) 109 | else: 110 | height = self.height 111 | gradient = cairo.LinearGradient(1, 1, width-1, height-1) 112 | gradient.add_color_stop_rgb(0, 1, 0, 0) 113 | gradient.add_color_stop_rgb(0.2, 1, 1, 0) 114 | gradient.add_color_stop_rgb(1, 0, 1, 0) 115 | cairo_ctx.set_source(gradient) 116 | cairo_ctx.rectangle(x, self.height * (1 - value), width, self.height * value) 117 | cairo_ctx.fill() 118 | 119 | def set_scale(self, scale): 120 | self.scale = scale 121 | self.cache_surface = None 122 | self.invalidate_all() 123 | 124 | class MonoMeterWidget(MeterWidget): 125 | def __init__(self, scale): 126 | MeterWidget.__init__(self, scale) 127 | self.value = 0.0 128 | self.raw_value = 0.0 129 | 130 | def draw(self, widget, cairo_ctx): 131 | self.draw_background(cairo_ctx) 132 | self.draw_value(cairo_ctx, self.value, self.width/4.0, self.width/2.0) 133 | 134 | def set_value(self, value): 135 | if value != self.raw_value: 136 | self.raw_value = value 137 | self.value = self.scale.db_to_scale(value) 138 | self.invalidate_all() 139 | if value == self.raw_value: 140 | return 141 | self.raw_value = value 142 | old_value = self.value 143 | self.value = self.scale.db_to_scale(value) 144 | if (abs(old_value-self.value) * self.height) > 1: 145 | self.invalidate_all() 146 | 147 | class StereoMeterWidget(MeterWidget): 148 | def __init__(self, scale): 149 | MeterWidget.__init__(self, scale) 150 | self.left = 0.0 151 | self.right = 0.0 152 | 153 | self.raw_left = 0.0 154 | self.raw_right = 0.0 155 | 156 | def draw(self, widget, cairo_ctx): 157 | self.draw_background(cairo_ctx) 158 | self.draw_value(cairo_ctx, self.left, self.width/5.0, self.width/5.0) 159 | self.draw_value(cairo_ctx, self.right, self.width/5.0 * 3.0, self.width/5.0) 160 | 161 | def set_values(self, left, right): 162 | if left == self.raw_left and right == self.raw_right: 163 | return 164 | self.raw_left = left 165 | self.raw_right = right 166 | old_left = self.left 167 | old_right = self.right 168 | self.left = self.scale.db_to_scale(left) 169 | self.right = self.scale.db_to_scale(right) 170 | if (abs(old_left-self.left) * self.height) > 0.01 or (abs(old_right-self.right) * self.height) > 0.01: 171 | self.invalidate_all() 172 | -------------------------------------------------------------------------------- /preferences.py: -------------------------------------------------------------------------------- 1 | # This file is part of jack_mixer 2 | # 3 | # Copyright (C) 2006 Nedko Arnaudov 4 | # Copyright (C) 2009 Frederic Peters 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; version 2 of the License 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | 19 | from gi.repository import Gtk 20 | from gi.repository import Gdk 21 | from gi.repository import GObject 22 | 23 | try: 24 | from gi.repository import GConf 25 | except ImportError: 26 | gconf = None 27 | 28 | def lookup_scale(scales, scale_id): 29 | for scale in scales: 30 | if scale_id == scale.scale_id: 31 | return scale 32 | return None 33 | 34 | class PreferencesDialog(Gtk.Dialog): 35 | def __init__(self, parent): 36 | self.app = parent 37 | GObject.GObject.__init__(self) 38 | self.set_title( '') 39 | self.create_ui() 40 | self.connect('response', self.on_response_cb) 41 | self.connect('delete-event', self.on_response_cb) 42 | 43 | def create_frame(self, label, child): 44 | frame = Gtk.Frame() 45 | frame.set_label('') 46 | frame.set_border_width(3) 47 | frame.set_shadow_type(Gtk.ShadowType.NONE) 48 | frame.get_label_widget().set_markup('%s' % label) 49 | 50 | alignment = Gtk.Alignment.new(0, 0, 12, 0) 51 | frame.add(alignment) 52 | alignment.add(child) 53 | 54 | return frame 55 | 56 | def create_ui(self): 57 | vbox = Gtk.VBox() 58 | self.vbox.add(vbox) 59 | 60 | interface_vbox = Gtk.VBox() 61 | self.custom_widgets_checkbutton = Gtk.CheckButton('Use custom widgets') 62 | self.custom_widgets_checkbutton.set_active( 63 | self.app.gui_factory.get_use_custom_widgets()) 64 | self.custom_widgets_checkbutton.connect('toggled', 65 | self.on_custom_widget_toggled) 66 | interface_vbox.pack_start(self.custom_widgets_checkbutton, True, True, 0) 67 | 68 | self.vumeter_color_checkbutton = Gtk.CheckButton('Use custom vumeter color') 69 | self.vumeter_color_checkbutton.set_active( 70 | self.app.gui_factory.get_vumeter_color_scheme() == 'solid') 71 | self.vumeter_color_checkbutton.connect('toggled', 72 | self.on_vumeter_color_change) 73 | interface_vbox.pack_start(self.vumeter_color_checkbutton, True, True, 0) 74 | hbox = Gtk.HBox() 75 | interface_vbox.pack_start(hbox, True, True, 0) 76 | self.custom_color_box = hbox 77 | self.custom_color_box.set_sensitive( 78 | self.vumeter_color_checkbutton.get_active() == True) 79 | hbox.pack_start(Gtk.Label('Custom color:'), True, True, 0) 80 | self.vumeter_color_picker = Gtk.ColorButton() 81 | self.vumeter_color_picker.set_color(Gdk.color_parse( 82 | self.app.gui_factory.get_vumeter_color())) 83 | self.vumeter_color_picker.connect('color-set', 84 | self.on_vumeter_color_change) 85 | hbox.pack_start(self.vumeter_color_picker, True, True, 0) 86 | 87 | vbox.pack_start(self.create_frame('Interface', interface_vbox), True, True, 0) 88 | 89 | table = Gtk.Table(2, 2, False) 90 | table.set_row_spacings(5) 91 | table.set_col_spacings(5) 92 | 93 | table.attach(Gtk.Label(label='Meter scale'), 0, 1, 0, 1) 94 | self.meter_scale_combo = self.create_meter_store_and_combo() 95 | table.attach(self.meter_scale_combo, 1, 2, 0, 1) 96 | 97 | table.attach(Gtk.Label(label='Slider scale'), 0, 1, 1, 2) 98 | self.slider_scale_combo = self.create_slider_store_and_combo() 99 | table.attach(self.slider_scale_combo, 1, 2, 1, 2) 100 | 101 | vbox.pack_start(self.create_frame('Scales', table), True, True, 0) 102 | 103 | self.vbox.show_all() 104 | 105 | self.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE) 106 | 107 | def create_meter_store_and_combo(self): 108 | store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_PYOBJECT) 109 | for scale in self.app.meter_scales: 110 | row = scale.scale_id, scale 111 | current_iter = store.append(row) 112 | if scale is self.app.gui_factory.get_default_meter_scale(): 113 | active_iter = current_iter 114 | self.meter_store = store 115 | 116 | meter_scale_combo = Gtk.ComboBox.new_with_model(store) 117 | cell = Gtk.CellRendererText() 118 | meter_scale_combo.pack_start(cell, True) 119 | meter_scale_combo.add_attribute(cell, 'text', 0) 120 | meter_scale_combo.set_active_iter(active_iter) 121 | meter_scale_combo.connect('changed', 122 | self.on_meter_scale_combo_changed) 123 | 124 | return meter_scale_combo 125 | 126 | def create_slider_store_and_combo(self): 127 | store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_PYOBJECT) 128 | for scale in self.app.slider_scales: 129 | row = scale.scale_id, scale 130 | current_iter = store.append(row) 131 | if scale is self.app.gui_factory.get_default_slider_scale(): 132 | active_iter = current_iter 133 | self.slider_store = store 134 | 135 | slider_scale_combo = Gtk.ComboBox.new_with_model(store) 136 | cell = Gtk.CellRendererText() 137 | slider_scale_combo.pack_start(cell, True) 138 | slider_scale_combo.add_attribute(cell, 'text', 0) 139 | slider_scale_combo.set_active_iter(active_iter) 140 | slider_scale_combo.connect('changed', 141 | self.on_slider_scale_combo_changed) 142 | 143 | return slider_scale_combo 144 | 145 | def on_response_cb(self, dlg, response_id, *args): 146 | self.app.preferences_dialog = None 147 | self.destroy() 148 | 149 | def on_meter_scale_combo_changed(self, *args): 150 | active_iter = self.meter_scale_combo.get_active_iter() 151 | scale = self.meter_store.get(active_iter, 1)[0] 152 | self.app.gui_factory.set_default_meter_scale(scale) 153 | 154 | def on_slider_scale_combo_changed(self, *args): 155 | active_iter = self.slider_scale_combo.get_active_iter() 156 | scale = self.slider_store.get(active_iter, 1)[0] 157 | self.app.gui_factory.set_default_slider_scale(scale) 158 | 159 | def on_vumeter_color_change(self, *args): 160 | color_scheme = 'default' 161 | if self.vumeter_color_checkbutton.get_active(): 162 | color_scheme = 'solid' 163 | self.app.gui_factory.set_vumeter_color_scheme(color_scheme) 164 | 165 | color = self.vumeter_color_picker.get_color().to_string() 166 | self.app.gui_factory.set_vumeter_color(color) 167 | 168 | self.custom_color_box.set_sensitive( 169 | self.vumeter_color_checkbutton.get_active() == True) 170 | 171 | def on_custom_widget_toggled(self, *args): 172 | self.app.gui_factory.set_use_custom_widgets( 173 | self.custom_widgets_checkbutton.get_active()) 174 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | # This file is part of jack_mixer 2 | # 3 | # Copyright (C) 2006 Nedko Arnaudov 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; version 2 of the License 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | import gi 19 | gi.require_version('GConf', '2.0') 20 | from gi.repository import GObject 21 | 22 | try: 23 | from gi.repository import GConf 24 | except: 25 | print("Cannot load Python bindings for GConf, your preferences will not be preserved across jack_mixer invocations") 26 | GConf = None 27 | 28 | def lookup_scale(scales, scale_id): 29 | for scale in scales: 30 | if scale_id == scale.scale_id: 31 | return scale 32 | return None 33 | 34 | class Factory(GObject.GObject): 35 | def __init__(self, topwindow, meter_scales, slider_scales): 36 | GObject.GObject.__init__(self) 37 | self.topwindow = topwindow 38 | self.meter_scales = meter_scales 39 | self.slider_scales = slider_scales 40 | 41 | if GConf: 42 | self.gconf_client = GConf.Client.get_default() 43 | 44 | scale_id = self.gconf_client.get_string("/apps/jack_mixer/default_meter_scale") 45 | self.default_meter_scale = lookup_scale(meter_scales, scale_id) 46 | if not self.default_meter_scale: 47 | self.default_meter_scale = meter_scales[0] 48 | 49 | scale_id = self.gconf_client.get_string("/apps/jack_mixer/default_slider_scale") 50 | self.default_slider_scale = lookup_scale(slider_scales, scale_id) 51 | if not self.default_slider_scale: 52 | self.default_slider_scale = slider_scales[0] 53 | 54 | self.vumeter_color_scheme = self.gconf_client.get_string( 55 | '/apps/jack_mixer/vumeter_color_scheme') 56 | self.vumeter_color = self.gconf_client.get_string( 57 | '/apps/jack_mixer/vumeter_color') 58 | if not self.vumeter_color: 59 | self.vumeter_color = '#ccb300' 60 | 61 | self.use_custom_widgets = self.gconf_client.get_bool( 62 | '/apps/jack_mixer/use_custom_widgets') 63 | 64 | self.gconf_client.add_dir("/apps/jack_mixer", GConf.ClientPreloadType.PRELOAD_NONE) 65 | self.gconf_client.notify_add("/apps/jack_mixer/default_meter_scale", self.on_gconf_default_meter_scale_changed) 66 | self.gconf_client.notify_add("/apps/jack_mixer/default_slider_scale", self.on_gconf_default_slider_scale_changed) 67 | self.gconf_client.notify_add('/apps/jack_mixer/vumeter_color', self.on_gconf_vumeter_color_change) 68 | self.gconf_client.notify_add('/apps/jack_mixer/vumeter_color_scheme', self.on_gconf_vumeter_color_scheme_change) 69 | self.gconf_client.notify_add('/apps/jack_mixer/use_custom_widgets', self.on_gconf_use_custom_widgets_change) 70 | else: 71 | self.default_meter_scale = meter_scales[0] 72 | self.default_slider_scale = slider_scales[0] 73 | self.vumeter_color = '#ccb300' 74 | self.vumeter_color_scheme = 'default' 75 | self.use_custom_widgets = False 76 | 77 | def on_gconf_default_meter_scale_changed(self, client, connection_id, entry): 78 | #print "GConf default_meter_scale changed" 79 | scale_id = entry.get_value().get_string() 80 | scale = lookup_scale(self.meter_scales, scale_id) 81 | self.set_default_meter_scale(scale, from_gconf=True) 82 | 83 | def set_default_meter_scale(self, scale, from_gconf=False): 84 | if scale: 85 | if GConf and not from_gconf: 86 | self.gconf_client.set_string("/apps/jack_mixer/default_meter_scale", scale.scale_id) 87 | else: 88 | self.default_meter_scale = scale 89 | self.emit("default-meter-scale-changed", self.default_meter_scale) 90 | else: 91 | print("Ignoring GConf default_meter_scale setting, because \"%s\" scale is not known" % scale_id) 92 | 93 | def on_gconf_default_slider_scale_changed(self, client, connection_id, entry): 94 | #print "GConf default_slider_scale changed" 95 | scale_id = entry.get_value().get_string() 96 | scale = lookup_scale(self.slider_scales, scale_id) 97 | self.set_default_slider_scale(scale, from_gconf=True) 98 | 99 | def set_default_slider_scale(self, scale, from_gconf=False): 100 | if scale: 101 | if GConf and not from_gconf: 102 | self.gconf_client.set_string("/apps/jack_mixer/default_slider_scale", scale.scale_id) 103 | else: 104 | self.default_slider_scale = scale 105 | self.emit("default-slider-scale-changed", self.default_slider_scale) 106 | else: 107 | print("Ignoring GConf default_slider_scale setting, because \"%s\" scale is not known" % scale_id) 108 | 109 | def set_vumeter_color(self, color, from_gconf=False): 110 | if GConf and not from_gconf: 111 | self.gconf_client.set_string('/apps/jack_mixer/vumeter_color', color) 112 | else: 113 | self.vumeter_color = color 114 | self.emit('vumeter-color-changed', self.vumeter_color) 115 | 116 | def on_gconf_vumeter_color_change(self, client, connection_id, entry): 117 | color = entry.get_value().get_string() 118 | self.set_vumeter_color(color, from_gconf=True) 119 | 120 | def set_vumeter_color_scheme(self, color_scheme, from_gconf=False): 121 | if GConf and not from_gconf: 122 | self.gconf_client.set_string('/apps/jack_mixer/vumeter_color_scheme', color_scheme) 123 | else: 124 | self.vumeter_color_scheme = color_scheme 125 | self.emit('vumeter-color-scheme-changed', self.vumeter_color_scheme) 126 | 127 | def on_gconf_vumeter_color_scheme_change(self, client, connection_id, entry): 128 | color_scheme = entry.get_value().get_string() 129 | self.set_vumeter_color_scheme(color_scheme, from_gconf=True) 130 | 131 | def set_use_custom_widgets(self, use_custom, from_gconf=False): 132 | if GConf and not from_gconf: 133 | self.gconf_client.set_bool('/apps/jack_mixer/use_custom_widgets', use_custom) 134 | else: 135 | self.use_custom_widgets = use_custom 136 | self.emit('use-custom-widgets-changed', self.use_custom_widgets) 137 | 138 | def on_gconf_use_custom_widgets_change(self, client, connection_id, entry): 139 | use_custom = entry.get_value().get_bool() 140 | self.set_use_custom_widgets(use_custom, from_gconf=True) 141 | 142 | def get_default_meter_scale(self): 143 | return self.default_meter_scale 144 | 145 | def get_default_slider_scale(self): 146 | return self.default_slider_scale 147 | 148 | def get_vumeter_color(self): 149 | return self.vumeter_color 150 | 151 | def get_vumeter_color_scheme(self): 152 | return self.vumeter_color_scheme 153 | 154 | def get_use_custom_widgets(self): 155 | return self.use_custom_widgets 156 | 157 | 158 | GObject.signal_new("default-meter-scale-changed", Factory, 159 | GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, 160 | None, [GObject.TYPE_PYOBJECT]) 161 | GObject.signal_new("default-slider-scale-changed", Factory, 162 | GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, 163 | None, [GObject.TYPE_PYOBJECT]) 164 | GObject.signal_new('vumeter-color-changed', Factory, 165 | GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, 166 | None, [str]) 167 | GObject.signal_new('vumeter-color-scheme-changed', Factory, 168 | GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, 169 | None, [str]) 170 | GObject.signal_new('use-custom-widgets-changed', Factory, 171 | GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, 172 | None, [bool]) 173 | -------------------------------------------------------------------------------- /slider.py: -------------------------------------------------------------------------------- 1 | # This file is part of jack_mixer 2 | # 3 | # Copyright (C) 2006 Nedko Arnaudov 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; version 2 of the License 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | from gi.repository import Gtk 19 | from gi.repository import Gdk 20 | from gi.repository import GObject 21 | import cairo 22 | 23 | class AdjustmentdBFS(Gtk.Adjustment): 24 | def __init__(self, scale, default_db, step_inc): 25 | self.default_value = scale.db_to_scale(default_db) 26 | self.db = default_db 27 | self.scale = scale 28 | self.step_increment = step_inc 29 | Gtk.Adjustment.__init__(self, self.default_value, 0.0, 1.0, 0.02) 30 | self.connect("value-changed", self.on_value_changed) 31 | self.disable_value_notify = False 32 | 33 | def step_up(self): 34 | self.set_value(self.get_value() + self.step_increment) 35 | 36 | def step_down(self): 37 | self.set_value(self.get_value() - self.step_increment) 38 | 39 | def reset(self): 40 | self.set_value(self.default_value) 41 | 42 | def get_value_db(self): 43 | return self.db 44 | 45 | def set_value_db(self, db): 46 | self.db = db 47 | self.disable_value_notify = True 48 | self.set_value(self.scale.db_to_scale(db)) 49 | self.disable_value_notify = False 50 | self.emit("volume-changed") 51 | 52 | def on_value_changed(self, adjustment): 53 | if not self.disable_value_notify: 54 | self.db = self.scale.scale_to_db(self.get_value()) 55 | self.emit("volume-changed") 56 | 57 | def set_scale(self, scale): 58 | self.scale = scale 59 | self.disable_value_notify = True 60 | self.set_value(self.scale.db_to_scale(self.db)) 61 | self.disable_value_notify = False 62 | 63 | GObject.signal_new("volume-changed", AdjustmentdBFS, 64 | GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, None, []) 65 | 66 | 67 | class GtkSlider(Gtk.VScale): 68 | def __init__(self, adjustment): 69 | Gtk.VScale.__init__(self)#, adjustment) 70 | self.set_adjustment(adjustment) 71 | self.set_draw_value(False) 72 | self.set_inverted(True) 73 | 74 | # HACK: we want the behaviour you get with the middle button, so we 75 | # mangle the events. Clicking with other buttons moves the slider in 76 | # step increments, clicking with the middle button moves the slider 77 | # to the location of the click. 78 | self.connect('button-press-event', self.button_press_event) 79 | self.connect('button-release-event', self.button_release_event) 80 | 81 | def button_press_event(self, widget, event): 82 | print("button press", event.button) 83 | # event.button = 2 84 | return False 85 | 86 | def button_release_event(self, widget, event): 87 | #event.button = 2 88 | return False 89 | 90 | 91 | class CustomSliderWidget(Gtk.DrawingArea): 92 | def __init__(self, adjustment): 93 | Gtk.DrawingArea.__init__(self) 94 | 95 | self.adjustment = adjustment 96 | 97 | self.connect("draw", self.on_expose) 98 | self.connect("size_allocate", self.on_size_allocate) 99 | adjustment.connect("value-changed", self.on_value_changed) 100 | self.connect("button-press-event", self.on_mouse) 101 | self.connect("motion-notify-event", self.on_mouse) 102 | self.connect("scroll-event", self.on_scroll) 103 | self.set_events(Gdk.EventMask.BUTTON1_MOTION_MASK | 104 | Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.BUTTON_PRESS_MASK) 105 | 106 | def on_scroll(self, widget, event): 107 | delta = self.adjustment.step_increment 108 | value = self.adjustment.get_value() 109 | if event.direction == Gdk.ScrollDirection.UP: 110 | y = value + delta 111 | elif event.direction == Gdk.ScrollDirection.DOWN: 112 | y = value - delta 113 | if y >= 1: 114 | y = 1 115 | elif y <= 0: 116 | y = 0 117 | self.adjustment.set_value(y) 118 | return True 119 | 120 | def on_mouse(self, widget, event): 121 | if event.type == Gdk.EventType.BUTTON_PRESS: 122 | #print "mouse button %u pressed %u:%u" % (event.button, event.x, event.y) 123 | if event.button == 1: 124 | if event.y >= self.slider_rail_up and event.y < self.slider_rail_up + self.slider_rail_height: 125 | self.adjustment.set_value(1 - float(event.y - self.slider_rail_up)/float(self.slider_rail_height)) 126 | elif event.type == Gdk.EventType.MOTION_NOTIFY: 127 | #print "mouse motion %u:%u" % (event.x, event.y) 128 | if event.y < self.slider_rail_up: 129 | y = self.slider_rail_up 130 | elif event.y > self.slider_rail_up + self.slider_rail_height: 131 | y = self.slider_rail_up + self.slider_rail_height 132 | else: 133 | y = event.y 134 | self.adjustment.set_value(1 - float(y - self.slider_rail_up)/float(self.slider_rail_height)) 135 | 136 | return False 137 | 138 | def on_value_changed(self, adjustment): 139 | self.invalidate_all() 140 | 141 | def on_expose(self, widget, cairo_ctx): 142 | 143 | self.draw(cairo_ctx) 144 | 145 | return False 146 | 147 | def get_preferred_width(self, widget): 148 | minimal_width = natural_width = self.width 149 | return (minimal_width, natural_width) 150 | 151 | def get_preferred_height(self, widget): 152 | requisition = Gtk.Requisition() 153 | on_size_request(self, widget, requisition) 154 | minimal_height = natural_heigt = requisition.height 155 | return (minimal_height, natural_height) 156 | 157 | 158 | def on_size_allocate(self, widget, allocation): 159 | self.width = float(allocation.width) 160 | self.height = float(allocation.height) 161 | self.font_size = 10 162 | 163 | def on_size_request(self, widget, requisition): 164 | #print "size-request, %u x %u" % (requisition.width, requisition.height) 165 | requisition.width = 20 166 | return 167 | 168 | def invalidate_all(self): 169 | self.queue_draw_area(0, 0, int(self.width), int(self.height)) 170 | 171 | def draw(self, cairo_ctx): 172 | if self.has_focus(): 173 | state = Gtk.StateType.PRELIGHT 174 | else: 175 | state = Gtk.StateType.NORMAL 176 | 177 | #cairo_ctx.rectangle(0, 0, self.width, self.height) 178 | #cairo_ctx.set_source_color(self.style.bg[state]) 179 | #cairo_ctx.fill_preserve() 180 | #Gdk.cairo_set_source_color(cairo_ctx, 181 | # self.get_style_context().get_color(state).to_color()) 182 | #cairo_ctx.stroke() 183 | 184 | slider_knob_width = self.width * 3 / 4 185 | slider_knob_height = self.width * 3 / 2 186 | slider_knob_height -= slider_knob_height % 2 187 | slider_knob_height += 1 188 | 189 | slider_x = self.width/2 190 | 191 | cairo_ctx.set_line_width(1) 192 | 193 | # slider rail 194 | Gdk.cairo_set_source_color(cairo_ctx, 195 | self.get_style_context().get_color(state).to_color()) 196 | self.slider_rail_up = slider_knob_height/2 + (self.width - slider_knob_width)/2 197 | self.slider_rail_height = self.height - 2 * self.slider_rail_up 198 | cairo_ctx.move_to(slider_x, self.slider_rail_up) 199 | cairo_ctx.line_to(slider_x, self.slider_rail_height + self.slider_rail_up) 200 | cairo_ctx.stroke() 201 | 202 | # slider knob 203 | slider_y = round(self.slider_rail_up + self.slider_rail_height * (1 - self.adjustment.get_value())) 204 | lg = cairo.LinearGradient(slider_x - 205 | float(slider_knob_width)/2, slider_y - slider_knob_height/2, 206 | slider_x - float(slider_knob_width)/2, slider_y + 207 | slider_knob_height/2) 208 | slider_alpha = 1.0 209 | lg.add_color_stop_rgba(0, 0.55, 0.55, 0.55, slider_alpha) 210 | lg.add_color_stop_rgba(0.1, 0.65, 0.65, 0.65, slider_alpha) 211 | lg.add_color_stop_rgba(0.1, 0.75, 0.75, 0.75, slider_alpha) 212 | lg.add_color_stop_rgba(0.125, 0.75, 0.75, 0.75, slider_alpha) 213 | lg.add_color_stop_rgba(0.125, 0.15, 0.15, 0.15, slider_alpha) 214 | lg.add_color_stop_rgba(0.475, 0.35, 0.35, 0.35, slider_alpha) 215 | lg.add_color_stop_rgba(0.475, 0, 0, 0, slider_alpha) 216 | lg.add_color_stop_rgba(0.525, 0, 0, 0, slider_alpha) 217 | lg.add_color_stop_rgba(0.525, 0.35, 0.35, 0.35, slider_alpha) 218 | lg.add_color_stop_rgba(0.875, 0.65, 0.65, 0.65, slider_alpha) 219 | lg.add_color_stop_rgba(0.875, 0.75, 0.75, 0.75, slider_alpha) 220 | lg.add_color_stop_rgba(0.900, 0.75, 0.75, 0.75, slider_alpha) 221 | lg.add_color_stop_rgba(0.900, 0.15, 0.15, 0.15, slider_alpha) 222 | lg.add_color_stop_rgba(1.000, 0.10, 0.10, 0.10, slider_alpha) 223 | cairo_ctx.rectangle(slider_x - float(slider_knob_width)/2, 224 | slider_y - slider_knob_height/2, 225 | float(slider_knob_width), 226 | slider_knob_height) 227 | Gdk.cairo_set_source_color(cairo_ctx, 228 | self.get_style_context().get_background_color(state).to_color()) 229 | cairo_ctx.fill_preserve() 230 | cairo_ctx.set_source(lg) 231 | cairo_ctx.fill() 232 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | This program uses autotools. So you do the usual 5 | ./configure && make && make install 6 | 7 | If you're building directly from version control, you first need to: 8 | ./autogen.sh 9 | 10 | Build requirements: 11 | * You need gcc (3.4.3 should work) 12 | * You need Python headers (python3-dev package or something like that) 13 | * You need JACK headers (jack-dev package or something like that) 14 | 15 | Runtime requirements (checked by configure script): 16 | * You need Python (at least 3.5) 17 | * Pygobject 18 | 19 | Runtime optional things, that will enable you to get full functionality: 20 | * To get LASH (Linux audio session) support, you need LASH compiled 21 | with Python bindings. This means at least 0.5.3 release. 22 | * If you want your settings to be preserved in GConf you need gconf 23 | introspection data for gobject. It may be provided by a package 24 | named gir1.2-gconf-2.0. 25 | 26 | =================================================== 27 | These are the generic installation instructions. 28 | 29 | Installation Instructions 30 | ************************* 31 | 32 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 33 | 2006 Free Software Foundation, Inc. 34 | 35 | This file is free documentation; the Free Software Foundation gives 36 | unlimited permission to copy, distribute and modify it. 37 | 38 | Basic Installation 39 | ================== 40 | 41 | Briefly, the shell commands `./configure; make; make install' should 42 | configure, build, and install this package. The following 43 | more-detailed instructions are generic; see the `README' file for 44 | instructions specific to this package. 45 | 46 | The `configure' shell script attempts to guess correct values for 47 | various system-dependent variables used during compilation. It uses 48 | those values to create a `Makefile' in each directory of the package. 49 | It may also create one or more `.h' files containing system-dependent 50 | definitions. Finally, it creates a shell script `config.status' that 51 | you can run in the future to recreate the current configuration, and a 52 | file `config.log' containing compiler output (useful mainly for 53 | debugging `configure'). 54 | 55 | It can also use an optional file (typically called `config.cache' 56 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 57 | the results of its tests to speed up reconfiguring. Caching is 58 | disabled by default to prevent problems with accidental use of stale 59 | cache files. 60 | 61 | If you need to do unusual things to compile the package, please try 62 | to figure out how `configure' could check whether to do them, and mail 63 | diffs or instructions to the address given in the `README' so they can 64 | be considered for the next release. If you are using the cache, and at 65 | some point `config.cache' contains results you don't want to keep, you 66 | may remove or edit it. 67 | 68 | The file `configure.ac' (or `configure.in') is used to create 69 | `configure' by a program called `autoconf'. You need `configure.ac' if 70 | you want to change it or regenerate `configure' using a newer version 71 | of `autoconf'. 72 | 73 | The simplest way to compile this package is: 74 | 75 | 1. `cd' to the directory containing the package's source code and type 76 | `./configure' to configure the package for your system. 77 | 78 | Running `configure' might take a while. While running, it prints 79 | some messages telling which features it is checking for. 80 | 81 | 2. Type `make' to compile the package. 82 | 83 | 3. Optionally, type `make check' to run any self-tests that come with 84 | the package. 85 | 86 | 4. Type `make install' to install the programs and any data files and 87 | documentation. 88 | 89 | 5. You can remove the program binaries and object files from the 90 | source code directory by typing `make clean'. To also remove the 91 | files that `configure' created (so you can compile the package for 92 | a different kind of computer), type `make distclean'. There is 93 | also a `make maintainer-clean' target, but that is intended mainly 94 | for the package's developers. If you use it, you may have to get 95 | all sorts of other programs in order to regenerate files that came 96 | with the distribution. 97 | 98 | Compilers and Options 99 | ===================== 100 | 101 | Some systems require unusual options for compilation or linking that the 102 | `configure' script does not know about. Run `./configure --help' for 103 | details on some of the pertinent environment variables. 104 | 105 | You can give `configure' initial values for configuration parameters 106 | by setting variables in the command line or in the environment. Here 107 | is an example: 108 | 109 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 110 | 111 | *Note Defining Variables::, for more details. 112 | 113 | Compiling For Multiple Architectures 114 | ==================================== 115 | 116 | You can compile the package for more than one kind of computer at the 117 | same time, by placing the object files for each architecture in their 118 | own directory. To do this, you can use GNU `make'. `cd' to the 119 | directory where you want the object files and executables to go and run 120 | the `configure' script. `configure' automatically checks for the 121 | source code in the directory that `configure' is in and in `..'. 122 | 123 | With a non-GNU `make', it is safer to compile the package for one 124 | architecture at a time in the source code directory. After you have 125 | installed the package for one architecture, use `make distclean' before 126 | reconfiguring for another architecture. 127 | 128 | Installation Names 129 | ================== 130 | 131 | By default, `make install' installs the package's commands under 132 | `/usr/local/bin', include files under `/usr/local/include', etc. You 133 | can specify an installation prefix other than `/usr/local' by giving 134 | `configure' the option `--prefix=PREFIX'. 135 | 136 | You can specify separate installation prefixes for 137 | architecture-specific files and architecture-independent files. If you 138 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 139 | PREFIX as the prefix for installing programs and libraries. 140 | Documentation and other data files still use the regular prefix. 141 | 142 | In addition, if you use an unusual directory layout you can give 143 | options like `--bindir=DIR' to specify different values for particular 144 | kinds of files. Run `configure --help' for a list of the directories 145 | you can set and what kinds of files go in them. 146 | 147 | If the package supports it, you can cause programs to be installed 148 | with an extra prefix or suffix on their names by giving `configure' the 149 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 150 | 151 | Optional Features 152 | ================= 153 | 154 | Some packages pay attention to `--enable-FEATURE' options to 155 | `configure', where FEATURE indicates an optional part of the package. 156 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 157 | is something like `gnu-as' or `x' (for the X Window System). The 158 | `README' should mention any `--enable-' and `--with-' options that the 159 | package recognizes. 160 | 161 | For packages that use the X Window System, `configure' can usually 162 | find the X include and library files automatically, but if it doesn't, 163 | you can use the `configure' options `--x-includes=DIR' and 164 | `--x-libraries=DIR' to specify their locations. 165 | 166 | Specifying the System Type 167 | ========================== 168 | 169 | There may be some features `configure' cannot figure out automatically, 170 | but needs to determine by the type of machine the package will run on. 171 | Usually, assuming the package is built to be run on the _same_ 172 | architectures, `configure' can figure that out, but if it prints a 173 | message saying it cannot guess the machine type, give it the 174 | `--build=TYPE' option. TYPE can either be a short name for the system 175 | type, such as `sun4', or a canonical name which has the form: 176 | 177 | CPU-COMPANY-SYSTEM 178 | 179 | where SYSTEM can have one of these forms: 180 | 181 | OS KERNEL-OS 182 | 183 | See the file `config.sub' for the possible values of each field. If 184 | `config.sub' isn't included in this package, then this package doesn't 185 | need to know the machine type. 186 | 187 | If you are _building_ compiler tools for cross-compiling, you should 188 | use the option `--target=TYPE' to select the type of system they will 189 | produce code for. 190 | 191 | If you want to _use_ a cross compiler, that generates code for a 192 | platform different from the build platform, you should specify the 193 | "host" platform (i.e., that on which the generated programs will 194 | eventually be run) with `--host=TYPE'. 195 | 196 | Sharing Defaults 197 | ================ 198 | 199 | If you want to set default values for `configure' scripts to share, you 200 | can create a site shell script called `config.site' that gives default 201 | values for variables like `CC', `cache_file', and `prefix'. 202 | `configure' looks for `PREFIX/share/config.site' if it exists, then 203 | `PREFIX/etc/config.site' if it exists. Or, you can set the 204 | `CONFIG_SITE' environment variable to the location of the site script. 205 | A warning: not all `configure' scripts look for a site script. 206 | 207 | Defining Variables 208 | ================== 209 | 210 | Variables not defined in a site shell script can be set in the 211 | environment passed to `configure'. However, some packages may run 212 | configure again during the build, and the customized values of these 213 | variables may be lost. In order to avoid this problem, you should set 214 | them in the `configure' command line, using `VAR=value'. For example: 215 | 216 | ./configure CC=/usr/local2/bin/gcc 217 | 218 | causes the specified `gcc' to be used as the C compiler (unless it is 219 | overridden in the site shell script). 220 | 221 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 222 | an Autoconf bug. Until the bug is fixed you can use this workaround: 223 | 224 | CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash 225 | 226 | `configure' Invocation 227 | ====================== 228 | 229 | `configure' recognizes the following options to control how it operates. 230 | 231 | `--help' 232 | `-h' 233 | Print a summary of the options to `configure', and exit. 234 | 235 | `--version' 236 | `-V' 237 | Print the version of Autoconf used to generate the `configure' 238 | script, and exit. 239 | 240 | `--cache-file=FILE' 241 | Enable the cache: use and save the results of the tests in FILE, 242 | traditionally `config.cache'. FILE defaults to `/dev/null' to 243 | disable caching. 244 | 245 | `--config-cache' 246 | `-C' 247 | Alias for `--cache-file=config.cache'. 248 | 249 | `--quiet' 250 | `--silent' 251 | `-q' 252 | Do not print messages saying which checks are being made. To 253 | suppress all normal output, redirect it to `/dev/null' (any error 254 | messages will still be shown). 255 | 256 | `--srcdir=DIR' 257 | Look for the package's source code in directory DIR. Usually 258 | `configure' can determine that directory automatically. 259 | 260 | `configure' also accepts some other, not widely useful, options. Run 261 | `configure --help' for more details. 262 | 263 | -------------------------------------------------------------------------------- /memory_atomic.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C ; c-basic-offset: 2 -*- */ 2 | /***************************************************************************** 3 | * 4 | * Non-sleeping memory allocation 5 | * 6 | * Copyright (C) 2006,2007 Nedko Arnaudov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; version 2 of the License 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | *****************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "memory_atomic.h" 29 | #include "list.h" 30 | #include "log.h" 31 | 32 | struct rtsafe_memory_pool 33 | { 34 | size_t data_size; 35 | size_t min_preallocated; 36 | size_t max_preallocated; 37 | 38 | unsigned int used_count; 39 | struct list_head unused; 40 | unsigned int unused_count; 41 | 42 | bool enforce_thread_safety; 43 | /* next members are initialized/used only if enforce_thread_safety is true */ 44 | pthread_mutex_t mutex; 45 | unsigned int unused_count2; 46 | struct list_head pending; 47 | }; 48 | 49 | #define RTSAFE_GROUPS_PREALLOCATE 1024 50 | 51 | bool 52 | rtsafe_memory_pool_create( 53 | size_t data_size, 54 | size_t min_preallocated, 55 | size_t max_preallocated, 56 | bool enforce_thread_safety, 57 | rtsafe_memory_pool_handle * pool_handle_ptr) 58 | { 59 | int ret; 60 | struct rtsafe_memory_pool * pool_ptr; 61 | 62 | assert(min_preallocated <= max_preallocated); 63 | 64 | pool_ptr = malloc(sizeof(struct rtsafe_memory_pool)); 65 | if (pool_ptr == NULL) 66 | { 67 | return false; 68 | } 69 | 70 | pool_ptr->data_size = data_size; 71 | pool_ptr->min_preallocated = min_preallocated; 72 | pool_ptr->max_preallocated = max_preallocated; 73 | 74 | pool_ptr->used_count = 0; 75 | 76 | INIT_LIST_HEAD(&pool_ptr->unused); 77 | pool_ptr->unused_count = 0; 78 | 79 | pool_ptr->enforce_thread_safety = enforce_thread_safety; 80 | if (enforce_thread_safety) 81 | { 82 | ret = pthread_mutex_init(&pool_ptr->mutex, NULL); 83 | if (ret != 0) 84 | { 85 | free(pool_ptr); 86 | return false; 87 | } 88 | 89 | INIT_LIST_HEAD(&pool_ptr->pending); 90 | pool_ptr->unused_count2 = 0; 91 | } 92 | 93 | rtsafe_memory_pool_sleepy((rtsafe_memory_pool_handle)pool_ptr); 94 | *pool_handle_ptr = pool_ptr; 95 | 96 | return true; 97 | } 98 | 99 | #define pool_ptr ((struct rtsafe_memory_pool *)pool_handle) 100 | 101 | void 102 | rtsafe_memory_pool_destroy( 103 | rtsafe_memory_pool_handle pool_handle) 104 | { 105 | int ret; 106 | struct list_head * node_ptr; 107 | 108 | /* caller should deallocate all chunks prior releasing pool itself */ 109 | assert(pool_ptr->used_count == 0); 110 | 111 | while (pool_ptr->unused_count != 0) 112 | { 113 | assert(!list_empty(&pool_ptr->unused)); 114 | 115 | node_ptr = pool_ptr->unused.next; 116 | 117 | list_del(node_ptr); 118 | pool_ptr->unused_count--; 119 | 120 | free(node_ptr); 121 | } 122 | 123 | assert(list_empty(&pool_ptr->unused)); 124 | 125 | if (pool_ptr->enforce_thread_safety) 126 | { 127 | while (!list_empty(&pool_ptr->pending)) 128 | { 129 | node_ptr = pool_ptr->pending.next; 130 | 131 | list_del(node_ptr); 132 | 133 | free(node_ptr); 134 | } 135 | 136 | ret = pthread_mutex_destroy(&pool_ptr->mutex); 137 | assert(ret == 0); 138 | } 139 | 140 | free(pool_ptr); 141 | } 142 | 143 | /* adjust unused list size */ 144 | void 145 | rtsafe_memory_pool_sleepy( 146 | rtsafe_memory_pool_handle pool_handle) 147 | { 148 | struct list_head * node_ptr; 149 | unsigned int count; 150 | 151 | if (pool_ptr->enforce_thread_safety) 152 | { 153 | pthread_mutex_lock(&pool_ptr->mutex); 154 | 155 | count = pool_ptr->unused_count2; 156 | 157 | assert(pool_ptr->min_preallocated < pool_ptr->max_preallocated); 158 | 159 | while (count < pool_ptr->min_preallocated) 160 | { 161 | node_ptr = malloc(sizeof(struct list_head) + pool_ptr->data_size); 162 | if (node_ptr == NULL) 163 | { 164 | break; 165 | } 166 | 167 | list_add_tail(node_ptr, &pool_ptr->pending); 168 | 169 | count++; 170 | } 171 | 172 | while (count > pool_ptr->max_preallocated && !list_empty(&pool_ptr->pending)) 173 | { 174 | node_ptr = pool_ptr->pending.next; 175 | 176 | list_del(node_ptr); 177 | 178 | free(node_ptr); 179 | 180 | count--; 181 | } 182 | 183 | pthread_mutex_unlock(&pool_ptr->mutex); 184 | } 185 | else 186 | { 187 | while (pool_ptr->unused_count < pool_ptr->min_preallocated) 188 | { 189 | node_ptr = malloc(sizeof(struct list_head) + pool_ptr->data_size); 190 | if (node_ptr == NULL) 191 | { 192 | return; 193 | } 194 | 195 | list_add_tail(node_ptr, &pool_ptr->unused); 196 | pool_ptr->unused_count++; 197 | } 198 | 199 | while (pool_ptr->unused_count > pool_ptr->max_preallocated) 200 | { 201 | assert(!list_empty(&pool_ptr->unused)); 202 | 203 | node_ptr = pool_ptr->unused.next; 204 | 205 | list_del(node_ptr); 206 | pool_ptr->unused_count--; 207 | 208 | free(node_ptr); 209 | } 210 | } 211 | } 212 | 213 | /* find entry in unused list, fail if it is empty */ 214 | void * 215 | rtsafe_memory_pool_allocate( 216 | rtsafe_memory_pool_handle pool_handle) 217 | { 218 | struct list_head * node_ptr; 219 | 220 | if (list_empty(&pool_ptr->unused)) 221 | { 222 | return NULL; 223 | } 224 | 225 | node_ptr = pool_ptr->unused.next; 226 | list_del(node_ptr); 227 | pool_ptr->unused_count--; 228 | pool_ptr->used_count++; 229 | 230 | if (pool_ptr->enforce_thread_safety && 231 | pthread_mutex_trylock(&pool_ptr->mutex) == 0) 232 | { 233 | while (pool_ptr->unused_count < pool_ptr->min_preallocated && !list_empty(&pool_ptr->pending)) 234 | { 235 | node_ptr = pool_ptr->pending.next; 236 | 237 | list_del(node_ptr); 238 | list_add_tail(node_ptr, &pool_ptr->unused); 239 | pool_ptr->unused_count++; 240 | } 241 | 242 | pool_ptr->unused_count2 = pool_ptr->unused_count; 243 | 244 | pthread_mutex_unlock(&pool_ptr->mutex); 245 | } 246 | 247 | return (node_ptr + 1); 248 | } 249 | 250 | /* move from used to unused list */ 251 | void 252 | rtsafe_memory_pool_deallocate( 253 | rtsafe_memory_pool_handle pool_handle, 254 | void * data) 255 | { 256 | struct list_head * node_ptr; 257 | 258 | list_add_tail((struct list_head *)data - 1, &pool_ptr->unused); 259 | pool_ptr->used_count--; 260 | pool_ptr->unused_count++; 261 | 262 | if (pool_ptr->enforce_thread_safety && 263 | pthread_mutex_trylock(&pool_ptr->mutex) == 0) 264 | { 265 | while (pool_ptr->unused_count > pool_ptr->max_preallocated) 266 | { 267 | assert(!list_empty(&pool_ptr->unused)); 268 | 269 | node_ptr = pool_ptr->unused.next; 270 | 271 | list_del(node_ptr); 272 | list_add_tail(node_ptr, &pool_ptr->pending); 273 | pool_ptr->unused_count--; 274 | } 275 | 276 | pool_ptr->unused_count2 = pool_ptr->unused_count; 277 | 278 | pthread_mutex_unlock(&pool_ptr->mutex); 279 | } 280 | } 281 | 282 | void * 283 | rtsafe_memory_pool_allocate_sleepy( 284 | rtsafe_memory_pool_handle pool_handle) 285 | { 286 | void * data; 287 | 288 | do 289 | { 290 | rtsafe_memory_pool_sleepy(pool_handle); 291 | data = rtsafe_memory_pool_allocate(pool_handle); 292 | } 293 | while (data == NULL); 294 | 295 | return data; 296 | } 297 | 298 | /* max alloc is DATA_MIN * (2 ^ POOLS_COUNT) - DATA_SUB */ 299 | #define DATA_MIN 1024 300 | #define DATA_SUB 100 /* alloc slightly smaller chunks in hope to not allocating additional page for control data */ 301 | 302 | struct rtsafe_memory_pool_generic 303 | { 304 | size_t size; 305 | rtsafe_memory_pool_handle pool; 306 | }; 307 | 308 | struct rtsafe_memory 309 | { 310 | struct rtsafe_memory_pool_generic * pools; 311 | size_t pools_count; 312 | }; 313 | 314 | bool 315 | rtsafe_memory_init( 316 | size_t max_size, 317 | size_t prealloc_min, 318 | size_t prealloc_max, 319 | bool enforce_thread_safety, 320 | rtsafe_memory_handle * handle_ptr) 321 | { 322 | size_t i; 323 | size_t size; 324 | struct rtsafe_memory * memory_ptr; 325 | 326 | LOG_DEBUG("rtsafe_memory_init() called."); 327 | 328 | memory_ptr = malloc(sizeof(struct rtsafe_memory)); 329 | if (memory_ptr == NULL) 330 | { 331 | goto fail; 332 | } 333 | 334 | size = DATA_MIN; 335 | memory_ptr->pools_count = 1; 336 | 337 | while ((size << memory_ptr->pools_count) < max_size + DATA_SUB) 338 | { 339 | memory_ptr->pools_count++; 340 | 341 | if (memory_ptr->pools_count > sizeof(size_t) * 8) 342 | { 343 | assert(0); /* chances that caller really need such huge size are close to zero */ 344 | goto fail_free; 345 | } 346 | } 347 | 348 | memory_ptr->pools = malloc(memory_ptr->pools_count * sizeof(struct rtsafe_memory_pool_generic)); 349 | if (memory_ptr->pools == NULL) 350 | { 351 | goto fail_free; 352 | } 353 | 354 | size = DATA_MIN; 355 | 356 | for (i = 0 ; i < memory_ptr->pools_count ; i++) 357 | { 358 | memory_ptr->pools[i].size = size - DATA_SUB; 359 | 360 | if (!rtsafe_memory_pool_create( 361 | memory_ptr->pools[i].size, 362 | prealloc_min, 363 | prealloc_max, 364 | enforce_thread_safety, 365 | &memory_ptr->pools[i].pool)) 366 | { 367 | while (i > 0) 368 | { 369 | i--; 370 | rtsafe_memory_pool_destroy(memory_ptr->pools[i].pool); 371 | } 372 | 373 | goto fail_free_pools; 374 | } 375 | 376 | size = size << 1; 377 | } 378 | 379 | *handle_ptr = (rtsafe_memory_handle)memory_ptr; 380 | 381 | return true; 382 | 383 | fail_free_pools: 384 | free(memory_ptr->pools); 385 | 386 | fail_free: 387 | free(memory_ptr); 388 | 389 | fail: 390 | return false; 391 | } 392 | 393 | #define memory_ptr ((struct rtsafe_memory *)handle_ptr) 394 | void 395 | rtsafe_memory_uninit( 396 | rtsafe_memory_handle handle_ptr) 397 | { 398 | unsigned int i; 399 | 400 | LOG_DEBUG("rtsafe_memory_uninit() called."); 401 | 402 | for (i = 0 ; i < memory_ptr->pools_count ; i++) 403 | { 404 | LOG_DEBUG("Destroying pool for size %u", (unsigned int)memory_ptr->pools[i].size); 405 | rtsafe_memory_pool_destroy(memory_ptr->pools[i].pool); 406 | } 407 | 408 | free(memory_ptr->pools); 409 | 410 | free(memory_ptr); 411 | } 412 | 413 | void * 414 | rtsafe_memory_allocate( 415 | rtsafe_memory_handle handle_ptr, 416 | size_t size) 417 | { 418 | rtsafe_memory_pool_handle * data_ptr; 419 | size_t i; 420 | 421 | LOG_DEBUG("rtsafe_memory_allocate() called."); 422 | 423 | /* pool handle is stored just before user data to ease deallocation */ 424 | size += sizeof(rtsafe_memory_pool_handle); 425 | 426 | for (i = 0 ; i < memory_ptr->pools_count ; i++) 427 | { 428 | if (size <= memory_ptr->pools[i].size) 429 | { 430 | LOG_DEBUG("Using chunk with size %u.", (unsigned int)memory_ptr->pools[i].size); 431 | data_ptr = rtsafe_memory_pool_allocate(memory_ptr->pools[i].pool); 432 | if (data_ptr == NULL) 433 | { 434 | LOG_DEBUG("rtsafe_memory_pool_allocate() failed."); 435 | return NULL; 436 | } 437 | 438 | *data_ptr = memory_ptr->pools[i].pool; 439 | 440 | LOG_DEBUG("rtsafe_memory_allocate() returning %p", (data_ptr + 1)); 441 | return (data_ptr + 1); 442 | } 443 | } 444 | 445 | /* data size too big, increase POOLS_COUNT */ 446 | LOG_WARNING("Data size is too big"); 447 | return NULL; 448 | } 449 | 450 | void 451 | rtsafe_memory_sleepy( 452 | rtsafe_memory_handle handle_ptr) 453 | { 454 | unsigned int i; 455 | 456 | for (i = 0 ; i < memory_ptr->pools_count ; i++) 457 | { 458 | rtsafe_memory_pool_sleepy(memory_ptr->pools[i].pool); 459 | } 460 | } 461 | 462 | void 463 | rtsafe_memory_deallocate( 464 | void * data) 465 | { 466 | LOG_DEBUG("rtsafe_memory_deallocate(%p) called.", data); 467 | rtsafe_memory_pool_deallocate( 468 | *((rtsafe_memory_pool_handle *)data -1), 469 | (rtsafe_memory_pool_handle *)data - 1); 470 | } 471 | -------------------------------------------------------------------------------- /data/art/16x16/jack_mixer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 23 | 25 | 29 | 33 | 34 | 37 | 41 | 45 | 46 | 48 | 52 | 56 | 57 | 59 | 63 | 67 | 68 | 79 | 89 | 99 | 109 | 119 | 129 | 130 | 161 | 163 | 164 | 166 | image/svg+xml 167 | 169 | 170 | 171 | Lapo Calamandrei 172 | 173 | 174 | 175 | 177 | Jack MiXer 178 | 179 | 180 | mixer 181 | jack 182 | audio 183 | 184 | 185 | 186 | 187 | Jakub Steiner 188 | 189 | 190 | 191 | 193 | 195 | 197 | 199 | 201 | 203 | 205 | 206 | 207 | 208 | 212 | 215 | 217 | 226 | 231 | 236 | 245 | 252 | 259 | 266 | 269 | 271 | 278 | 285 | 286 | 287 | 290 | 292 | 299 | 306 | 307 | 308 | 311 | 313 | 320 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /data/art/22x22/jack_mixer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 23 | 26 | 30 | 34 | 35 | 38 | 42 | 46 | 47 | 50 | 54 | 58 | 59 | 62 | 66 | 70 | 71 | 74 | 78 | 82 | 83 | 85 | 89 | 93 | 94 | 97 | 101 | 105 | 106 | 109 | 113 | 117 | 118 | 121 | 125 | 129 | 130 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | 157 | 159 | 163 | 167 | 171 | 172 | 174 | 178 | 182 | 183 | 185 | 189 | 193 | 194 | 205 | 215 | 226 | 237 | 248 | 258 | 268 | 278 | 288 | 298 | 308 | 318 | 327 | 336 | 345 | 355 | 365 | 374 | 375 | 406 | 408 | 409 | 411 | image/svg+xml 412 | 414 | 415 | 416 | Lapo Calamandrei 417 | 418 | 419 | 420 | 422 | Jack MiXer 423 | 424 | 425 | mixer 426 | jack 427 | audio 428 | 429 | 430 | 431 | 432 | Jakub Steiner 433 | 434 | 435 | 436 | 438 | 440 | 442 | 444 | 446 | 448 | 450 | 451 | 452 | 453 | 457 | 460 | 470 | 473 | 477 | 484 | 489 | 494 | 495 | 504 | 509 | 514 | 516 | 523 | 530 | 537 | 538 | 541 | 543 | 550 | 557 | 558 | 559 | 568 | 571 | 573 | 580 | 587 | 588 | 589 | 592 | 594 | 601 | 608 | 609 | 610 | 611 | 618 | 627 | 631 | 641 | 648 | 649 | 658 | 665 | 675 | 683 | 687 | 688 | 689 | 690 | -------------------------------------------------------------------------------- /jack_mixer_c.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of jack_mixer 3 | * 4 | * Copyright (C) 2009 Frederic Peters 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; version 2 of the License 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "jack_mixer.h" 29 | 30 | 31 | /** Scale Type **/ 32 | 33 | typedef struct { 34 | PyObject_HEAD 35 | jack_mixer_scale_t scale; 36 | } ScaleObject; 37 | 38 | static void 39 | Scale_dealloc(ScaleObject *self) 40 | { 41 | if (self->scale) 42 | scale_destroy(self->scale); 43 | Py_TYPE(self)->tp_free((PyObject*)self); 44 | } 45 | 46 | static int 47 | Scale_init(ScaleObject *self, PyObject *args, PyObject *kwds) 48 | { 49 | self->scale = scale_create(); 50 | return 0; 51 | } 52 | 53 | static PyObject* 54 | Scale_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 55 | { 56 | ScaleObject *self; 57 | 58 | self = (ScaleObject*)type->tp_alloc(type, 0); 59 | 60 | return (PyObject*)self; 61 | } 62 | 63 | static PyObject* 64 | Scale_add_threshold(ScaleObject *self, PyObject *args) 65 | { 66 | float db, scale_value; 67 | 68 | if (! PyArg_ParseTuple(args, "ff", &db, &scale_value)) return NULL; 69 | 70 | scale_add_threshold(self->scale, db, scale_value); 71 | 72 | Py_INCREF(Py_None); 73 | return Py_None; 74 | } 75 | 76 | static PyObject* 77 | Scale_calculate_coefficients(ScaleObject *self, PyObject *args) 78 | { 79 | if (! PyArg_ParseTuple(args, "")) return NULL; 80 | scale_calculate_coefficients(self->scale); 81 | Py_INCREF(Py_None); 82 | return Py_None; 83 | } 84 | 85 | static PyObject* 86 | Scale_db_to_scale(ScaleObject *self, PyObject *args) 87 | { 88 | double db; 89 | if (! PyArg_ParseTuple(args, "d", &db)) return NULL; 90 | return PyFloat_FromDouble(scale_db_to_scale(self->scale, db)); 91 | } 92 | 93 | static PyObject* 94 | Scale_scale_to_db(ScaleObject *self, PyObject *args) 95 | { 96 | double scale_value; 97 | if (! PyArg_ParseTuple(args, "d", &scale_value)) return NULL; 98 | return PyFloat_FromDouble(scale_scale_to_db(self->scale, scale_value)); 99 | } 100 | 101 | static PyMethodDef Scale_methods[] = { 102 | {"add_threshold", (PyCFunction)Scale_add_threshold, METH_VARARGS, "Add threshold"}, 103 | {"calculate_coefficients", (PyCFunction)Scale_calculate_coefficients, 104 | METH_VARARGS, "Calculate coefficients"}, 105 | {"db_to_scale", (PyCFunction)Scale_db_to_scale, METH_VARARGS, "dB to scale"}, 106 | {"scale_to_db", (PyCFunction)Scale_scale_to_db, METH_VARARGS, "scale to dB"}, 107 | {NULL} 108 | }; 109 | 110 | static PyTypeObject ScaleType = { 111 | PyVarObject_HEAD_INIT(NULL, 0) 112 | "jack_mixer_c.Scale", /*tp_name*/ 113 | sizeof(ScaleObject), /*tp_basicsize*/ 114 | 0, /*tp_itemsize*/ 115 | (destructor)Scale_dealloc, /*tp_dealloc*/ 116 | 0, /*tp_print*/ 117 | 0, /*tp_getattr*/ 118 | 0, /*tp_setattr*/ 119 | 0, /*tp_compare*/ 120 | 0, /*tp_repr*/ 121 | 0, /*tp_as_number*/ 122 | 0, /*tp_as_sequence*/ 123 | 0, /*tp_as_mapping*/ 124 | 0, /*tp_hash */ 125 | 0, /*tp_call*/ 126 | 0, /*tp_str*/ 127 | 0, /*tp_getattro*/ 128 | 0, /*tp_setattro*/ 129 | 0, /*tp_as_buffer*/ 130 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 131 | "Scale objects", /* tp_doc */ 132 | 0, /* tp_traverse */ 133 | 0, /* tp_clear */ 134 | 0, /* tp_richcompare */ 135 | 0, /* tp_weaklistoffset */ 136 | 0, /* tp_iter */ 137 | 0, /* tp_iternext */ 138 | Scale_methods, /* tp_methods */ 139 | 0, /* tp_members */ 140 | 0, /* tp_getset */ 141 | 0, /* tp_base */ 142 | 0, /* tp_dict */ 143 | 0, /* tp_descr_get */ 144 | 0, /* tp_descr_set */ 145 | 0, /* tp_dictoffset */ 146 | (initproc)Scale_init, /* tp_init */ 147 | 0, /* tp_alloc */ 148 | Scale_new, /* tp_new */ 149 | }; 150 | 151 | 152 | /** Channel Type **/ 153 | 154 | typedef struct { 155 | PyObject_HEAD 156 | PyObject *midi_change_callback; 157 | jack_mixer_channel_t channel; 158 | } ChannelObject; 159 | 160 | static void 161 | Channel_dealloc(ChannelObject *self) 162 | { 163 | Py_XDECREF(self->midi_change_callback); 164 | Py_TYPE(self)->tp_free((PyObject*)self); 165 | } 166 | 167 | static int 168 | Channel_init(ChannelObject *self, PyObject *args, PyObject *kwds) 169 | { 170 | self->midi_change_callback = NULL; 171 | return 0; 172 | } 173 | 174 | static PyObject* 175 | Channel_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 176 | { 177 | ChannelObject *self; 178 | 179 | self = (ChannelObject*)type->tp_alloc(type, 0); 180 | 181 | if (self != NULL) { 182 | self->channel = NULL; 183 | self->midi_change_callback = NULL; 184 | } 185 | 186 | return (PyObject*)self; 187 | } 188 | 189 | static PyObject* 190 | Channel_get_is_stereo(ChannelObject *self, void *closure) 191 | { 192 | PyObject *result; 193 | 194 | bool is_stereo = channel_is_stereo(self->channel); 195 | if (is_stereo) { 196 | result = Py_True; 197 | } else { 198 | result = Py_False; 199 | } 200 | Py_INCREF(result); 201 | return result; 202 | } 203 | 204 | static PyObject* 205 | Channel_get_volume(ChannelObject *self, void *closure) 206 | { 207 | return PyFloat_FromDouble(channel_volume_read(self->channel)); 208 | } 209 | 210 | static int 211 | Channel_set_volume(ChannelObject *self, PyObject *value, void *closure) 212 | { 213 | if (self->channel == NULL) { 214 | PyErr_SetString(PyExc_RuntimeError, "unitialized channel"); 215 | return -1; 216 | } 217 | channel_volume_write(self->channel, PyFloat_AsDouble(value)); 218 | return 0; 219 | } 220 | 221 | static PyObject* 222 | Channel_get_balance(ChannelObject *self, void *closure) 223 | { 224 | return PyFloat_FromDouble(channel_balance_read(self->channel)); 225 | } 226 | 227 | static int 228 | Channel_set_balance(ChannelObject *self, PyObject *value, void *closure) 229 | { 230 | channel_balance_write(self->channel, PyFloat_AsDouble(value)); 231 | return 0; 232 | } 233 | 234 | static PyObject* 235 | Channel_get_out_mute(ChannelObject *self, void *closure) 236 | { 237 | PyObject *result; 238 | 239 | if (channel_is_out_muted(self->channel)) { 240 | result = Py_True; 241 | } else { 242 | result = Py_False; 243 | } 244 | Py_INCREF(result); 245 | return result; 246 | } 247 | 248 | static int 249 | Channel_set_out_mute(ChannelObject *self, PyObject *value, void *closure) 250 | { 251 | if (value == Py_True) { 252 | channel_out_mute(self->channel); 253 | } else { 254 | channel_out_unmute(self->channel); 255 | } 256 | return 0; 257 | } 258 | 259 | static PyObject* 260 | Channel_get_solo(ChannelObject *self, void *closure) 261 | { 262 | PyObject *result; 263 | 264 | if (channel_is_soloed(self->channel)) { 265 | result = Py_True; 266 | } else { 267 | result = Py_False; 268 | } 269 | Py_INCREF(result); 270 | return result; 271 | } 272 | 273 | static int 274 | Channel_set_solo(ChannelObject *self, PyObject *value, void *closure) 275 | { 276 | if (value == Py_True) { 277 | channel_solo(self->channel); 278 | } else { 279 | channel_unsolo(self->channel); 280 | } 281 | return 0; 282 | } 283 | 284 | static PyObject* 285 | Channel_get_meter(ChannelObject *self, void *closure) 286 | { 287 | PyObject *result; 288 | double left, right; 289 | 290 | if (channel_is_stereo(self->channel)) { 291 | result = PyTuple_New(2); 292 | channel_stereo_meter_read(self->channel, &left, &right); 293 | PyTuple_SetItem(result, 0, PyFloat_FromDouble(left)); 294 | PyTuple_SetItem(result, 1, PyFloat_FromDouble(right)); 295 | } else { 296 | result = PyTuple_New(1); 297 | channel_mono_meter_read(self->channel, &left); 298 | PyTuple_SetItem(result, 0, PyFloat_FromDouble(left)); 299 | } 300 | return result; 301 | } 302 | 303 | static PyObject* 304 | Channel_get_abspeak(ChannelObject *self, void *closure) 305 | { 306 | return PyFloat_FromDouble(channel_abspeak_read(self->channel)); 307 | } 308 | 309 | static int 310 | Channel_set_abspeak(ChannelObject *self, PyObject *value, void *closure) 311 | { 312 | if (value != Py_None) { 313 | fprintf(stderr, "abspeak can only be reset (set to None)\n"); 314 | return -1; 315 | } 316 | channel_abspeak_reset(self->channel); 317 | return 0; 318 | } 319 | 320 | static int 321 | Channel_set_midi_scale(ChannelObject *self, PyObject *value, void *closure) 322 | { 323 | ScaleObject *scale_object = (ScaleObject*)value; /* XXX: check */ 324 | 325 | channel_set_midi_scale(self->channel, scale_object->scale); 326 | return 0; 327 | } 328 | 329 | 330 | static PyObject* 331 | Channel_get_midi_change_callback(ChannelObject *self, void *closure) 332 | { 333 | if (self->midi_change_callback) { 334 | Py_INCREF(self->midi_change_callback); 335 | return self->midi_change_callback; 336 | } else { 337 | Py_INCREF(Py_None); 338 | return Py_None; 339 | } 340 | } 341 | 342 | static void 343 | channel_midi_callback(void *userdata) 344 | { 345 | ChannelObject *self = (ChannelObject*)userdata; 346 | PyGILState_STATE gstate; 347 | 348 | gstate = PyGILState_Ensure(); 349 | PyObject_CallObject(self->midi_change_callback, NULL); 350 | PyGILState_Release(gstate); 351 | } 352 | 353 | static int 354 | Channel_set_midi_change_callback(ChannelObject *self, PyObject *value, void *closure) 355 | { 356 | if (value == Py_None) { 357 | self->midi_change_callback = NULL; 358 | channel_set_midi_change_callback(self->channel, NULL, NULL); 359 | } else { 360 | if (!PyCallable_Check(value)) { 361 | PyErr_SetString(PyExc_TypeError, "value must be callable"); 362 | return -1; 363 | } 364 | if (self->midi_change_callback) { 365 | Py_XDECREF(self->midi_change_callback); 366 | } 367 | Py_INCREF(value); 368 | self->midi_change_callback = value; 369 | channel_set_midi_change_callback(self->channel, 370 | channel_midi_callback, self); 371 | } 372 | 373 | return 0; 374 | } 375 | 376 | static PyObject* 377 | Channel_get_name(ChannelObject *self, void *closure) 378 | { 379 | return PyUnicode_FromString(channel_get_name(self->channel)); 380 | } 381 | 382 | static int 383 | Channel_set_name(ChannelObject *self, PyObject *value, void *closure) 384 | { 385 | channel_rename(self->channel, PyUnicode_AsUTF8(value)); 386 | return 0; 387 | } 388 | 389 | static PyObject* 390 | Channel_get_balance_midi_cc(ChannelObject *self, void *closure) 391 | { 392 | return PyLong_FromLong(channel_get_balance_midi_cc(self->channel)); 393 | } 394 | 395 | static int 396 | Channel_set_balance_midi_cc(ChannelObject *self, PyObject *value, void *closure) 397 | { 398 | int new_cc; 399 | unsigned int result; 400 | 401 | new_cc = PyLong_AsLong(value); 402 | result = channel_set_balance_midi_cc(self->channel, new_cc); 403 | if (result == 0) { 404 | return 0; 405 | } 406 | if (result == 2) { 407 | PyErr_SetString(PyExc_RuntimeError, "value out of range"); 408 | } 409 | return -1; 410 | } 411 | 412 | static PyObject* 413 | Channel_get_volume_midi_cc(ChannelObject *self, void *closure) 414 | { 415 | return PyLong_FromLong(channel_get_volume_midi_cc(self->channel)); 416 | } 417 | 418 | static int 419 | Channel_set_volume_midi_cc(ChannelObject *self, PyObject *value, void *closure) 420 | { 421 | int new_cc; 422 | unsigned int result; 423 | 424 | new_cc = PyLong_AsLong(value); 425 | result = channel_set_volume_midi_cc(self->channel, new_cc); 426 | if (result == 0) { 427 | return 0; 428 | } 429 | if (result == 2) { 430 | PyErr_SetString(PyExc_RuntimeError, "value out of range"); 431 | } 432 | return -1; 433 | } 434 | 435 | static PyObject* 436 | Channel_get_mute_midi_cc(ChannelObject *self, void *closure) 437 | { 438 | return PyLong_FromLong(channel_get_mute_midi_cc(self->channel)); 439 | } 440 | 441 | static int 442 | Channel_set_mute_midi_cc(ChannelObject *self, PyObject *value, void *closure) 443 | { 444 | int new_cc; 445 | unsigned int result; 446 | 447 | new_cc = PyLong_AsLong(value); 448 | result = channel_set_mute_midi_cc(self->channel, new_cc); 449 | if (result == 0) { 450 | return 0; 451 | } 452 | if (result == 2) { 453 | PyErr_SetString(PyExc_RuntimeError, "value out of range"); 454 | } 455 | return -1; 456 | } 457 | 458 | static PyObject* 459 | Channel_get_solo_midi_cc(ChannelObject *self, void *closure) 460 | { 461 | return PyLong_FromLong(channel_get_solo_midi_cc(self->channel)); 462 | } 463 | 464 | static int 465 | Channel_set_solo_midi_cc(ChannelObject *self, PyObject *value, void *closure) 466 | { 467 | int new_cc; 468 | unsigned int result; 469 | 470 | new_cc = PyLong_AsLong(value); 471 | result = channel_set_solo_midi_cc(self->channel, new_cc); 472 | if (result == 0) { 473 | return 0; 474 | } 475 | if (result == 2) { 476 | PyErr_SetString(PyExc_RuntimeError, "value out of range"); 477 | } 478 | return -1; 479 | } 480 | 481 | static PyObject* 482 | Channel_get_midi_in_got_events(ChannelObject *self, void *closure) 483 | { 484 | PyObject *result; 485 | 486 | if (channel_get_midi_in_got_events(self->channel)) { 487 | result = Py_True; 488 | } else { 489 | result = Py_False; 490 | } 491 | Py_INCREF(result); 492 | return result; 493 | } 494 | 495 | static PyGetSetDef Channel_getseters[] = { 496 | {"is_stereo", 497 | (getter)Channel_get_is_stereo, NULL, 498 | "mono/stereo", NULL}, 499 | {"volume", 500 | (getter)Channel_get_volume, (setter)Channel_set_volume, 501 | "volume", NULL}, 502 | {"balance", 503 | (getter)Channel_get_balance, (setter)Channel_set_balance, 504 | "balance", NULL}, 505 | {"out_mute", 506 | (getter)Channel_get_out_mute, (setter)Channel_set_out_mute, 507 | "out_mute", NULL}, 508 | {"solo", 509 | (getter)Channel_get_solo, (setter)Channel_set_solo, 510 | "solo", NULL}, 511 | {"meter", 512 | (getter)Channel_get_meter, NULL, 513 | "meter", NULL}, 514 | {"abspeak", 515 | (getter)Channel_get_abspeak, (setter)Channel_set_abspeak, 516 | "balance", NULL}, 517 | {"midi_scale", 518 | NULL, (setter)Channel_set_midi_scale, 519 | "midi scale", NULL}, 520 | {"midi_change_callback", 521 | (getter)Channel_get_midi_change_callback, 522 | (setter)Channel_set_midi_change_callback, 523 | "midi change callback", NULL}, 524 | {"name", 525 | (getter)Channel_get_name, 526 | (setter)Channel_set_name, 527 | "name", NULL}, 528 | {"balance_midi_cc", 529 | (getter)Channel_get_balance_midi_cc, 530 | (setter)Channel_set_balance_midi_cc, 531 | "Balance MIDI CC", NULL}, 532 | {"volume_midi_cc", 533 | (getter)Channel_get_volume_midi_cc, 534 | (setter)Channel_set_volume_midi_cc, 535 | "Volume MIDI CC", NULL}, 536 | {"mute_midi_cc", 537 | (getter)Channel_get_mute_midi_cc, 538 | (setter)Channel_set_mute_midi_cc, 539 | "Mute MIDI CC", NULL}, 540 | {"solo_midi_cc", 541 | (getter)Channel_get_solo_midi_cc, 542 | (setter)Channel_set_solo_midi_cc, 543 | "Mute MIDI CC", NULL}, 544 | {"midi_in_got_events", 545 | (getter)Channel_get_midi_in_got_events, NULL, 546 | "Got new MIDI IN events", NULL}, 547 | {NULL} 548 | }; 549 | 550 | static PyObject* 551 | Channel_remove(ChannelObject *self, PyObject *args) 552 | { 553 | if (! PyArg_ParseTuple(args, "")) return NULL; 554 | remove_channel(self->channel); 555 | Py_INCREF(Py_None); 556 | return Py_None; 557 | } 558 | 559 | static PyObject* 560 | Channel_autoset_midi_cc(ChannelObject *self, PyObject *args) 561 | { 562 | if (! PyArg_ParseTuple(args, "")) return NULL; 563 | channel_autoset_midi_cc(self->channel); 564 | Py_INCREF(Py_None); 565 | return Py_None; 566 | } 567 | 568 | static PyMethodDef channel_methods[] = { 569 | {"remove", (PyCFunction)Channel_remove, METH_VARARGS, "Remove"}, 570 | {"autoset_midi_cc", (PyCFunction)Channel_autoset_midi_cc, METH_VARARGS, "Autoset MIDI CC"}, 571 | {NULL} 572 | }; 573 | 574 | static PyTypeObject ChannelType = { 575 | PyVarObject_HEAD_INIT(NULL, 0) 576 | "jack_mixer_c.Channel", /*tp_name*/ 577 | sizeof(ChannelObject), /*tp_basicsize*/ 578 | 0, /*tp_itemsize*/ 579 | (destructor)Channel_dealloc, /*tp_dealloc*/ 580 | 0, /*tp_print*/ 581 | 0, /*tp_getattr*/ 582 | 0, /*tp_setattr*/ 583 | 0, /*tp_compare*/ 584 | 0, /*tp_repr*/ 585 | 0, /*tp_as_number*/ 586 | 0, /*tp_as_sequence*/ 587 | 0, /*tp_as_mapping*/ 588 | 0, /*tp_hash */ 589 | 0, /*tp_call*/ 590 | 0, /*tp_str*/ 591 | 0, /*tp_getattro*/ 592 | 0, /*tp_setattro*/ 593 | 0, /*tp_as_buffer*/ 594 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 595 | "Channel objects", /* tp_doc */ 596 | 0, /* tp_traverse */ 597 | 0, /* tp_clear */ 598 | 0, /* tp_richcompare */ 599 | 0, /* tp_weaklistoffset */ 600 | 0, /* tp_iter */ 601 | 0, /* tp_iternext */ 602 | channel_methods, /* tp_methods */ 603 | 0, /* tp_members */ 604 | Channel_getseters, /* tp_getset */ 605 | 0, /* tp_base */ 606 | 0, /* tp_dict */ 607 | 0, /* tp_descr_get */ 608 | 0, /* tp_descr_set */ 609 | 0, /* tp_dictoffset */ 610 | (initproc)Channel_init, /* tp_init */ 611 | 0, /* tp_alloc */ 612 | Channel_new, /* tp_new */ 613 | }; 614 | 615 | static PyObject* 616 | Channel_New(jack_mixer_channel_t channel) 617 | { 618 | ChannelObject *self; 619 | self = (ChannelObject*)PyObject_NEW(ChannelObject, &ChannelType); 620 | if (self != NULL) { 621 | self->channel = channel; 622 | self->midi_change_callback = NULL; 623 | } 624 | return (PyObject*)self; 625 | } 626 | 627 | /** Output Channel Type **/ 628 | 629 | typedef struct { 630 | PyObject_HEAD 631 | PyObject *midi_change_callback; 632 | jack_mixer_output_channel_t *output_channel; 633 | } OutputChannelObject; 634 | 635 | static int 636 | OutputChannel_set_prefader(OutputChannelObject *self, PyObject *value, void *closure) 637 | { 638 | if (value == Py_True) { 639 | output_channel_set_prefader(self->output_channel, true); 640 | } else { 641 | output_channel_set_prefader(self->output_channel, false); 642 | } 643 | return 0; 644 | } 645 | 646 | static PyObject* 647 | OutputChannel_get_prefader(OutputChannelObject *self, void *closure) 648 | { 649 | PyObject *result; 650 | 651 | if (output_channel_is_prefader(self->output_channel)) { 652 | result = Py_True; 653 | } else { 654 | result = Py_False; 655 | } 656 | Py_INCREF(result); 657 | return result; 658 | } 659 | 660 | static PyGetSetDef OutputChannel_getseters[] = { 661 | {"prefader", 662 | (getter)OutputChannel_get_prefader, (setter)OutputChannel_set_prefader, 663 | "prefader", NULL}, 664 | {NULL} 665 | }; 666 | 667 | static PyObject* 668 | OutputChannel_remove(OutputChannelObject *self, PyObject *args) 669 | { 670 | if (! PyArg_ParseTuple(args, "")) return NULL; 671 | remove_output_channel(self->output_channel); 672 | Py_INCREF(Py_None); 673 | return Py_None; 674 | } 675 | 676 | static PyObject* 677 | OutputChannel_set_solo(OutputChannelObject *self, PyObject *args) 678 | { 679 | PyObject *channel; 680 | unsigned char solo; 681 | 682 | if (! PyArg_ParseTuple(args, "Ob", &channel, &solo)) return NULL; 683 | 684 | output_channel_set_solo(self->output_channel, 685 | ((ChannelObject*)channel)->channel, 686 | solo); 687 | 688 | Py_INCREF(Py_None); 689 | return Py_None; 690 | } 691 | 692 | static PyObject* 693 | OutputChannel_set_muted(OutputChannelObject *self, PyObject *args) 694 | { 695 | PyObject *channel; 696 | unsigned char muted; 697 | 698 | if (! PyArg_ParseTuple(args, "Ob", &channel, &muted)) return NULL; 699 | 700 | output_channel_set_muted(self->output_channel, 701 | ((ChannelObject*)channel)->channel, 702 | muted); 703 | 704 | Py_INCREF(Py_None); 705 | return Py_None; 706 | } 707 | 708 | static PyObject* 709 | OutputChannel_is_solo(OutputChannelObject *self, PyObject *args) 710 | { 711 | PyObject *channel; 712 | PyObject *result; 713 | 714 | if (! PyArg_ParseTuple(args, "O", &channel)) return NULL; 715 | 716 | if (output_channel_is_solo(self->output_channel, 717 | ((ChannelObject*)channel)->channel)) { 718 | result = Py_True; 719 | } else { 720 | result = Py_False; 721 | } 722 | 723 | Py_INCREF(result); 724 | return result; 725 | } 726 | 727 | static PyObject* 728 | OutputChannel_is_muted(OutputChannelObject *self, PyObject *args) 729 | { 730 | PyObject *channel; 731 | PyObject *result; 732 | 733 | if (! PyArg_ParseTuple(args, "O", &channel)) return NULL; 734 | 735 | if (output_channel_is_muted(self->output_channel, 736 | ((ChannelObject*)channel)->channel)) { 737 | result = Py_True; 738 | } else { 739 | result = Py_False; 740 | } 741 | 742 | Py_INCREF(result); 743 | return result; 744 | } 745 | 746 | static PyMethodDef output_channel_methods[] = { 747 | {"remove", (PyCFunction)OutputChannel_remove, METH_VARARGS, "Remove"}, 748 | {"set_solo", (PyCFunction)OutputChannel_set_solo, METH_VARARGS, "Set a channel as solo"}, 749 | {"set_muted", (PyCFunction)OutputChannel_set_muted, METH_VARARGS, "Set a channel as muted"}, 750 | {"is_solo", (PyCFunction)OutputChannel_is_solo, METH_VARARGS, "Is a channel set as solo"}, 751 | {"is_muted", (PyCFunction)OutputChannel_is_muted, METH_VARARGS, "Is a channel set as muted"}, 752 | {NULL} 753 | }; 754 | 755 | static PyTypeObject OutputChannelType = { 756 | PyVarObject_HEAD_INIT(NULL, 0) 757 | "jack_mixer_c.OutputChannel", /*tp_name*/ 758 | sizeof(OutputChannelObject), /*tp_basicsize*/ 759 | 0, /*tp_itemsize*/ 760 | 0, /*tp_dealloc*/ 761 | 0, /*tp_print*/ 762 | 0, /*tp_getattr*/ 763 | 0, /*tp_setattr*/ 764 | 0, /*tp_compare*/ 765 | 0, /*tp_repr*/ 766 | 0, /*tp_as_number*/ 767 | 0, /*tp_as_sequence*/ 768 | 0, /*tp_as_mapping*/ 769 | 0, /*tp_hash */ 770 | 0, /*tp_call*/ 771 | 0, /*tp_str*/ 772 | 0, /*tp_getattro*/ 773 | 0, /*tp_setattro*/ 774 | 0, /*tp_as_buffer*/ 775 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 776 | "Output Channel objects", /* tp_doc */ 777 | 0, /* tp_traverse */ 778 | 0, /* tp_clear */ 779 | 0, /* tp_richcompare */ 780 | 0, /* tp_weaklistoffset */ 781 | 0, /* tp_iter */ 782 | 0, /* tp_iternext */ 783 | output_channel_methods, /* tp_methods */ 784 | 0, /* tp_members */ 785 | OutputChannel_getseters, /* tp_getset */ 786 | &ChannelType, /* tp_base */ 787 | 0, /* tp_dict */ 788 | 0, /* tp_descr_get */ 789 | 0, /* tp_descr_set */ 790 | 0, /* tp_dictoffset */ 791 | 0, /* tp_init */ 792 | 0, /* tp_alloc */ 793 | 0, /* tp_new */ 794 | }; 795 | 796 | static PyObject* 797 | OutputChannel_New(jack_mixer_output_channel_t output_channel) 798 | { 799 | OutputChannelObject *self; 800 | self = (OutputChannelObject*)PyObject_NEW(OutputChannelObject, &OutputChannelType); 801 | if (self != NULL) { 802 | self->midi_change_callback = NULL; 803 | self->output_channel = output_channel; 804 | } 805 | return (PyObject*)self; 806 | } 807 | 808 | 809 | /** Mixer Type **/ 810 | 811 | typedef struct { 812 | PyObject_HEAD 813 | jack_mixer_t mixer; 814 | } MixerObject; 815 | 816 | static void 817 | Mixer_dealloc(MixerObject *self) 818 | { 819 | if (self->mixer) 820 | destroy(self->mixer); 821 | Py_TYPE(self)->tp_free((PyObject*)self); 822 | } 823 | 824 | static PyObject* 825 | Mixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 826 | { 827 | MixerObject *self; 828 | 829 | self = (MixerObject*)type->tp_alloc(type, 0); 830 | 831 | if (self != NULL) { 832 | self->mixer = NULL; 833 | } 834 | 835 | return (PyObject*)self; 836 | } 837 | 838 | static int 839 | Mixer_init(MixerObject *self, PyObject *args, PyObject *kwds) 840 | { 841 | static char *kwlist[] = {"name", "stereo", NULL}; 842 | char *name; 843 | int stereo = 1; 844 | 845 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|b", kwlist, &name, &stereo)) 846 | return -1; 847 | 848 | self->mixer = create(name, (bool)stereo); 849 | if (self->mixer == NULL) { 850 | PyErr_SetString(PyExc_RuntimeError, 851 | "error creating mixer, probably jack is not running"); 852 | return -1; 853 | } 854 | 855 | return 0; 856 | } 857 | 858 | static PyMemberDef Mixer_members[] = { 859 | {NULL} 860 | }; 861 | 862 | static PyObject* 863 | Mixer_get_channels_count(MixerObject *self, void *closure) 864 | { 865 | return PyLong_FromLong(get_channels_count(self->mixer)); 866 | } 867 | 868 | static PyObject* 869 | Mixer_get_client_name(MixerObject *self, void *closure) 870 | { 871 | return PyUnicode_FromString(get_client_name(self->mixer)); 872 | } 873 | 874 | static PyObject* 875 | Mixer_get_last_midi_channel(MixerObject *self, void *closure) 876 | { 877 | return PyLong_FromLong(get_last_midi_channel(self->mixer)); 878 | } 879 | 880 | static int 881 | Mixer_set_last_midi_channel(MixerObject *self, PyObject *value, void *closure) 882 | { 883 | int new_channel; 884 | unsigned int result; 885 | 886 | new_channel = PyLong_AsLong(value); 887 | result = set_last_midi_channel(self->mixer, new_channel); 888 | if (result == 0) { 889 | return 0; 890 | } 891 | return -1; 892 | } 893 | 894 | static PyGetSetDef Mixer_getseters[] = { 895 | {"channels_count", (getter)Mixer_get_channels_count, NULL, 896 | "channels count", NULL}, 897 | {"last_midi_channel", (getter)Mixer_get_last_midi_channel, (setter)Mixer_set_last_midi_channel, 898 | "last midi channel", NULL}, 899 | {NULL} 900 | }; 901 | 902 | static PyObject* 903 | Mixer_add_channel(MixerObject *self, PyObject *args) 904 | { 905 | char *name; 906 | int stereo; 907 | jack_mixer_channel_t channel; 908 | 909 | if (! PyArg_ParseTuple(args, "si", &name, &stereo)) return NULL; 910 | 911 | channel = add_channel(self->mixer, name, (bool)stereo); 912 | 913 | if (channel == NULL) { 914 | PyErr_SetString(PyExc_RuntimeError, "error adding channel"); 915 | return NULL; 916 | } 917 | 918 | return Channel_New(channel); 919 | } 920 | 921 | static PyObject* 922 | Mixer_add_output_channel(MixerObject *self, PyObject *args) 923 | { 924 | char *name; 925 | int stereo = 1; 926 | int system = 0; 927 | jack_mixer_output_channel_t channel; 928 | 929 | if (! PyArg_ParseTuple(args, "s|bb", &name, &stereo, &system)) return NULL; 930 | 931 | channel = add_output_channel(self->mixer, name, (bool)stereo, (bool)system); 932 | 933 | return OutputChannel_New(channel); 934 | } 935 | 936 | static PyObject* 937 | Mixer_destroy(MixerObject *self, PyObject *args) 938 | { 939 | if (self->mixer) { 940 | destroy(self->mixer); 941 | self->mixer = NULL; 942 | } 943 | Py_INCREF(Py_None); 944 | return Py_None; 945 | } 946 | 947 | static PyMethodDef Mixer_methods[] = { 948 | {"add_channel", (PyCFunction)Mixer_add_channel, METH_VARARGS, "Add a new channel"}, 949 | {"add_output_channel", (PyCFunction)Mixer_add_output_channel, METH_VARARGS, "Add a new output channel"}, 950 | {"destroy", (PyCFunction)Mixer_destroy, METH_VARARGS, "Destroy JACK Mixer"}, 951 | {"client_name", (PyCFunction)Mixer_get_client_name, METH_VARARGS, "Get jack client name"}, 952 | // {"remove_channel", (PyCFunction)Mixer_remove_channel, METH_VARARGS, "Remove a channel"}, 953 | {NULL} 954 | }; 955 | 956 | static PyTypeObject MixerType = { 957 | PyVarObject_HEAD_INIT(NULL, 0) 958 | "jack_mixer_c.Mixer", /*tp_name*/ 959 | sizeof(MixerObject), /*tp_basicsize*/ 960 | 0, /*tp_itemsize*/ 961 | (destructor)Mixer_dealloc, /*tp_dealloc*/ 962 | 0, /*tp_print*/ 963 | 0, /*tp_getattr*/ 964 | 0, /*tp_setattr*/ 965 | 0, /*tp_compare*/ 966 | 0, /*tp_repr*/ 967 | 0, /*tp_as_number*/ 968 | 0, /*tp_as_sequence*/ 969 | 0, /*tp_as_mapping*/ 970 | 0, /*tp_hash */ 971 | 0, /*tp_call*/ 972 | 0, /*tp_str*/ 973 | 0, /*tp_getattro*/ 974 | 0, /*tp_setattro*/ 975 | 0, /*tp_as_buffer*/ 976 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 977 | "Mixer objects", /* tp_doc */ 978 | 0, /* tp_traverse */ 979 | 0, /* tp_clear */ 980 | 0, /* tp_richcompare */ 981 | 0, /* tp_weaklistoffset */ 982 | 0, /* tp_iter */ 983 | 0, /* tp_iternext */ 984 | Mixer_methods, /* tp_methods */ 985 | Mixer_members, /* tp_members */ 986 | Mixer_getseters, /* tp_getset */ 987 | 0, /* tp_base */ 988 | 0, /* tp_dict */ 989 | 0, /* tp_descr_get */ 990 | 0, /* tp_descr_set */ 991 | 0, /* tp_dictoffset */ 992 | (initproc)Mixer_init, /* tp_init */ 993 | 0, /* tp_alloc */ 994 | Mixer_new, /* tp_new */ 995 | }; 996 | 997 | 998 | static PyMethodDef jack_mixer_methods[] = { 999 | {NULL, NULL, 0, NULL} /* Sentinel */ 1000 | }; 1001 | 1002 | 1003 | static struct PyModuleDef moduledef = { 1004 | PyModuleDef_HEAD_INIT, 1005 | "jack_mixer_c", /* m_name */ 1006 | "Jack Mixer C Helper Module", /* m_doc */ 1007 | -1, /* m_size */ 1008 | jack_mixer_methods, /* m_methods */ 1009 | NULL, /* m_reload */ 1010 | NULL, /* m_traverse */ 1011 | NULL, /* m_clear */ 1012 | NULL, /* m_free */ 1013 | }; 1014 | 1015 | PyMODINIT_FUNC PyInit_jack_mixer_c(void) 1016 | { 1017 | PyObject *m; 1018 | 1019 | if (PyType_Ready(&MixerType) < 0) 1020 | return NULL; 1021 | if (PyType_Ready(&ChannelType) < 0) 1022 | return NULL; 1023 | if (PyType_Ready(&OutputChannelType) < 0) 1024 | return NULL; 1025 | if (PyType_Ready(&ScaleType) < 0) 1026 | return NULL; 1027 | 1028 | m = PyModule_Create(&moduledef); 1029 | //m = Py_InitModule3("jack_mixer_c", jack_mixer_methods, "module doc"); 1030 | 1031 | Py_INCREF(&MixerType); 1032 | PyModule_AddObject(m, "Mixer", (PyObject*)&MixerType); 1033 | Py_INCREF(&ChannelType); 1034 | PyModule_AddObject(m, "Channel", (PyObject*)&ChannelType); 1035 | Py_INCREF(&OutputChannelType); 1036 | PyModule_AddObject(m, "OutputChannel", (PyObject*)&OutputChannelType); 1037 | Py_INCREF(&ScaleType); 1038 | PyModule_AddObject(m, "Scale", (PyObject*)&ScaleType); 1039 | 1040 | return m; 1041 | } 1042 | 1043 | --------------------------------------------------------------------------------