mudita24-1.0.3+svn13/0000755000175000017500000000000011704036465014000 5ustar alessioalessiomudita24-1.0.3+svn13/AUTHORS0000644000175000017500000001017711647357353015066 0ustar alessioalessioJaroslav Kysela volume.c: added by Takashi Iwai (2003/03/22) Changed to hbox/vbox layout. Copyright (C) 2003 by Søren Wedel Nielsen (16.12.2005) Re-worked user interface -digital mixer display permanently visible; pcms split to another page; controls re-arranged and all pages scrollable for min window size and greater flexibility; pop-up menu enabled. Changes to levelmeters.c to prevent invalid redraws. New options added: 'w' to set initial window pixel width and 't' for tall equal mixer height style. Copyright (C) 2005 by Alan Horstmann (Summer 2010) NPM: (1) Implemented "Peak Hold" functionality in meters; reimplemented meters to do away with inefficient "faux LED" peak-meter display, which causes thousands of rectangles per-second to be drawn, individually, for no good reason other than increasing the load. (2) Significantly reduced the number of timer interrupts generated by this program by slowing down all updates to 10 per second -- previously meters updated 25x/second! (3) All volumes are represented as decibels, including the 0 to -48dB range of the hardware peak-meters, the 0 -to- -48dB&off attenuation for all inputs to the digital mixer, the 0 -to- -63dB attenuation of the analog DAC, and the +18 -to- -63dB attenuation/amplification of the analog ADC. (4) All gtk "scale" widgets have dB legends; the "PageUp" "PageDown" keys allow rapid movement between the marked levels, and "UpArrow" and "DownArrow" allow fine-adjustment. (5) Added command-line options --no_scale_mark, --channel_group_modulus, --lights_color and --bg_color options; fixed --card and --device to allow valid ALSA card and CTL device names ( https://bugzilla.redhat.com/show_bug.cgi?id=602900 ). (6) Add display of "Delta IEC958 Input Status" under "Hardware Settings." Copyright (C) 2010 Niels Mayer ( http://nielsmayer.com ) (August 2010) TER: (1) Fixed bug - sliders with native markings snap to detents when using mouse. Removed native GtkScale markings and installed custom scale marks drawing, and custom page/step up/down key handling. (2) Fixed bug present even in original envy24control - sliders have bad jitter even without scale markings. (3) Allowed digital mixer controls to expand vertically. TODO: (1) Use ALSA functions to set analog volume sliders and markings, in order to support different DAC/ADC codec chips. (2) Shorten digital mixer meters to align with scale markings. (3) Use recent gtk functions to more accurately determine space between top of slider troughs and top of widget. (4) Add scale markings to master meters. (August 17 2010) TER: (1) Replaced hard-coded analog volume min/max and markings with ALSA derived values. Should now support different codec chip(s). 2) If gtk+ >= 2.6 use "change-value" signal for full control over sliders. 3) Many clean-ups and rewrites of my last changes. TODO: Still 2), 3), and 4) from last TODOs. Also, 5) try to extend mixer sliders to around -60dB, or a user selectable minimum, and 6) try to allow master meters to expand vertically. Copyright (C) 2010 Tim E. Real (Sourceforge username: terminator356) (September 23 2011) TER: 1) Moved to cmake build system. 2) Fixed wrong alsactl location. Now cmake looks for alsactl. Override with environment variable ALSACTL_PROG. 3) Added usage info. 4) Fixed serious fatal X error: height too big. Was far too many markings, brought down the system, due to lowest alsa db value now returing -9999999. Installed some limits. Copyright (C) 2011 Tim E. Real (Sourceforge username: terminator356) (October 18 2011) TER: Moved current code into branches/attic. This branch contains legacy deprecated code for supporting very old GTK+ versions. In trunk: Applied patch by James Morris to modernize and remove some deprecated code. TODO: Apply his suggested compiler flags to remove deprecated code. The code needs some more fixing before those flags will allow successful compilation. Removed all support for very old GTK+ versions. Copyright (C) 2011 Tim E. Real (terminator356 on sourceforge) mudita24-1.0.3+svn13/envy24control.10000644000175000017500000001556111434666733016631 0ustar alessioalessio.TH "envy24control" "1" "30 July 2005" "" "" .SH "NAME" envy24control \- GUI control tool for Envy24 (ice1712) based soundcards, under ALSA. .SH "SYNOPSIS" \fBenvy24control\fP [\fI\-c\fP card\-number] [\fI\-D\fP control\-name] [\fI\-o\fP 0\-num DACs max 8] [\fI\-i\fP 0\-num ADCs max 8] [\fI\-p\fP 0\-8] [\fI\-s\fP 0\-2] [\fI\-f\fP ] [\fI\-v\fP] [|] [\fI\-m\fP midi\-channel] [\fI\-M\fP] [\fI\-w\fP window\-width] [\fI\-t\fP 0\-9] [\fI\-n\fP] [\fI\-g\fP 1\-8] .SH "DESCRIPTION" \fBenvy24control\fP allows control of the digital mixer, channel gains and other hardware settings for sound cards based on the ice1712 chipset ( http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf ). It also displays a level meter for each input and output channel and maintains peak level indicators. This utility is preferable to \fBalsamixer(1)\fP for those with ice1712-based cards: M-Audio Delta 1010, Delta 1010LT, Delta DiO 2496, Delta 66, Delta 44, Delta 410 and Audiophile 2496. TerraTec EWS 88MT, EWS 88D, EWX 24/96, DMX 6Fire, Phase 88. Hoontech SoundTrack DSP 24, SoundTrack DSP 24 Value, SoundTrack DSP 24 Media 7.1. Event Electronics EZ8. Digigram VX442. Lionstracs, Mediastaton. Terrasoniq TS 88. Partial support for Roland/Edirol DA-2496. .SH "INVOKING" \fBenvy24control\fP [\fI\-c\fP card\-number] [\fI\-D\fP control\-name] [\fI\-o\fP 0\-num DACs max 8] [\fI\-i\fP 0\-num ADCs max 8] [\fI\-p\fP 0\-8] [\fI\-s\fP 0\-2] [\fI\-f\fP ] [\fI\-v\fP] [|] [\fI\-m\fP midi\-channel] [\fI\-M\fP] [\fI\-w\fP window\-width] [\fI\-t\fP 0\-9] [\fI\-n\fP] [\fI\-g\fP 1\-8] .TP If no control\-name is given, then the first sound card is used. .SS Options .TP \fI\-c\fP, \fI\--card\fP Use the card specified by card\-number rather than the first card. This is equivalent with \fI\-Dhw:n\fP option where \fIn\fP is the card number. May also use the card name, e.g. \fI\-cM66\fP . .TP \fI\-D\fP, \fI\--device\fP Use the card specified by control\-name rather than the first card, normally this will be of the form hw:\fIn\fP where \fIn\fP is the sound card number (zero\-based). May also use the card name, e.g. \fI\-Dhw:M66\fP . This is only needed if you have more than one Envy24\-based card or if your Envy24 card is not configured as the first card in your ALSA driver setup. .TP \fI\-o\fP, \fI\--outputs\fP Limit number of analog line outputs to display. Default is the number of DACs in the driver specification. Maximum is 8. The number of analog output lines can only be reduced from the available number of output lines. .TP \fI\-i\fP, \fI\--inputs\fP Limit number of analog line inputs to display. Default is the number of ADCs in the driver specification. Maximum is 8. The number of analog input lines can only be reduced from the available number of input lines. .TP \fI\-p\fP, \fI\--pcm_outputs\fP Limit number of PCM outputs to display. Default is 8. .TP \fI\-s\fP, \fI\--spdif\fP Limit number of SPDIF inputs/outputs to display. Default is 2. .TP \fI\-f\fP, \fI\--profiles_file\fP File to read/write the alsactl settings. If is not given DEFAULT_PROFILERC or for restore if DEFAULT_PROFILERC doesn't exists SYS_PROFILERC will be used. .TP \fI\-v\fP, \fI\--view_spdif_playback\fP View spdif playback channels in the mixer. It is not possible to manage something (muting, volume levels). It is only to view the levelmeters. Default is no view of spdif playback channels in the mixer. .TP \fI\-m\fP, \fI\--midichannel\fP Use MIDI controller values to control the Faders in the mixer view. The application will react to controllers on channel midi\-channel and send controllers on this channel when the user moves the GUI sliders. .TP \fI\-M\fP, \fI\--midienhanced\fP Use an enhanced mapping from midi controller values to db sliders. .TP \fI\-w\fP, \fI\--window_width\fP Specify the initial width of the envy24control window. Using window\-width in the range 0\-20 specifies approx number of mixer channels visible. Values >20 are interpreted as screen pixels. .TP \fI\-t\fP, \fI\--tall_equal_mixer_heights\fP Using \fI\-t1\fP enlarges the channel mixers to the same height as the digital mixer. With values >1 the height of all mixers is increased in stages. .TP \fI\-n\fP, \fI\--no_scale_mark\fP Disable scale marks, which may be incorrect on certain cards (?), or whose Gtk-detent at the mark position may be annoying. .TP \fI\-g\fP, \fI\--channel_group_modulus\fP Defaults to '2', which causes the application to pair input and output channels labeled scale marks to Left/Right sides - this behavior occurs in "Monitor Inputs" "Monitor PCMs" and "Analog Volume" panels. Should this behavior be inappropriate for 4, 5.1, 7.1, 8.1 and ambisonic setups: Using \fI\-g1\fP causes all channels to look the same. Other values up to the card's maximum number of input or output channels may produce layouts helpful for grouping channels. .TP \fI\-l\fP, \fI\--lights_color\fP Defaults to 'dodgerblue' or '#1e90ff'. Set to a different color to change the foreground color of the meters. .TP \fI\-b\fP, \fI\--bg_color\fP Defaults to '#304050'. Set to a different color to change the background color of the meters. .SH "SEE ALSO" \fB alsamixer(1), amixer(1), gamix(1), kmix(1), alsamixergui(1), alsamixer-qt4(1) \fP .SH "AUTHOR" \fBenvy24control\fP is originally by Jaroslav Kysela . with layout changes by Søren Wedel Nielsen, and volume control code by Takashi Iwai. Options \fI\-w\fP and \fI\-t\fP added Alan Horstmann . (Summer 2010) NPM: (1) Implemented "Peak Hold" functionality in meters; reimplemented meters to do away with inefficient "faux LED" peak-meter display, which causes thousands of rectangles per-second to be drawn, individually, for no good reason other than increasing the load. (2) Significantly reduced the number of timer interrupts generated by this program by slowing down all updates to 10 per second -- previously meters updated 25x/second! (3) All volumes are represented as decibels, including the 0 to -48dB range of the hardware peak-meters, the 0 -to- -48dB&off attenuation for all inputs to the digital mixer, the 0 -to- -63dB attenuation of the analog DAC, and the +18 -to- -63dB attenuation/amplification of the analog ADC. (4) All gtk "scale" widgets have dB legends; the "PageUp" "PageDown" keys allow rapid movement between the marked levels, and "UpArrow" and "DownArrow" allow fine-adjustment. (5) Added command-line options \fI\--no_scale_mark\fP, \fI\--channel_group_modulus\fP, \fI\--lights_color\fP and \fI\--bg_color options\fP; fixed \fI\--card\fP and \fI\--device\fP to allow valid ALSA card and CTL device names ( https://bugzilla.redhat.com/show_bug.cgi?id=602900 ). (6) Add display of "Delta IEC958 Input Status" under "Hardware Settings." This document is by James Tappin . Updated by Dirk Kalis . Further updated by Niels Mayer ( http://nielsmayer.com ). mudita24-1.0.3+svn13/config.h0000644000175000017500000000025311434666733015425 0ustar alessioalessio#ifndef CONFIG__H #define CONFIG__H void config_open(); void config_close(); void config_set_stereo(GtkWidget *but, gpointer data); void config_restore_stereo(); #endif mudita24-1.0.3+svn13/CMakeLists.txt0000755000175000017500000000676511647357353016571 0ustar alessioalessioinclude(FindPkgConfig) project(mudita24) CMAKE_MINIMUM_REQUIRED(VERSION 2.6.2) #if (COMMAND cmake_policy) #cmake_policy(SET CMP0003 NEW) #cmake_policy(SET CMP0011 NEW) #endif(COMMAND cmake_policy) IF(NOT DEFINED SHARE_INSTALL_PREFIX) SET(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share") ENDIF(NOT DEFINED SHARE_INSTALL_PREFIX) set(CMAKE_INCLUDE_CURRENT_DIR TRUE) set(CMAKE_SKIP_RULE_DEPENDENCY TRUE) SET(mudita24_INSTALL_NAME "mudita24-1.0.4") SET(mudita24_EXEC_NAME "mudita24") SET(mudita24_VERSION "1.0.4") FIND_PROGRAM(SVNVER svnversion) if (${SVNVER} STREQUAL "SVNVER-NOTFOUND") SET (mudita24_SVNVER 0) else (${SVNVER} STREQUAL "SVNVER-NOTFOUND") EXECUTE_PROCESS( COMMAND svnversion ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE mudita24_SVNVER OUTPUT_STRIP_TRAILING_WHITESPACE ) endif (${SVNVER} STREQUAL "SVNVER-NOTFOUND") ## Top documentation dir IF(NOT DEFINED mudita24_DOC_DIR) SET(mudita24_DOC_DIR ${SHARE_INSTALL_PREFIX}/doc/${mudita24_INSTALL_NAME}/) ENDIF(NOT DEFINED mudita24_DOC_DIR) #find_package(ALSA 0.9.0 REQUIRED alsa) # This didn't work. GTK2_INCLUDE_DIRS was blank. #find_package(GTK2 2.0 REQUIRED gtk) PKG_CHECK_MODULES(GTK2 REQUIRED gtk+-2.0>=2.20) # TODO What's the minimum required? PKG_CHECK_MODULES(ALSA REQUIRED alsa>=1.0.0) # TODO check for log10 ## NPM: version > 1.0.1's logarithmic display requires log10() and therefore ## "-lm". #PKG_CHECK_MODULES(M REQUIRED m) include_directories(${ALSA_INCLUDE_DIRS}) include_directories(${GTK2_INCLUDE_DIRS}) FIND_PROGRAM(mudita24_ALSACTL alsactl) # message(" alsactl location: " ${mudita24_ALSACTL} ) if (${mudita24_ALSACTL} STREQUAL "mudita24_ALSACTL-NOTFOUND") SET (mudita24_ALSACTL 0) endif (${mudita24_ALSACTL} STREQUAL "mudita24_ALSACTL-NOTFOUND") # # produce globaldefs.h file # configure_file ( ${PROJECT_SOURCE_DIR}/globaldefs.h.in ${PROJECT_BINARY_DIR}/globaldefs.h ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) set (mudita24_source_files envy24control.c # envy24control.h levelmeters.c midi.c mixer.c patchbay.c hardware.c driverevents.c volume.c profiles.c # profiles.h midi.h config.c # config.h ) add_executable( mudita24 ${mudita24_source_files} ) ## ## Compilation flags ## Oct 18, 2011: Suggested by James Morris along with supplied patch: ## -DGTK_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGSEAL_ENABLE ## Incomplete changes to source for now, causes errors. # # set_target_properties( mudita24 # PROPERTIES COMPILE_FLAGS "-DGTK_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGSEAL_ENABLE" # ) target_link_libraries(mudita24 ${ALSA_LIBRARIES} ${GTK2_LIBRARIES} #${M_LIBRARIES} m ) install( TARGETS mudita24 RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ ) ## Top documentation dir IF(NOT DEFINED mudita24_DOC_DIR) SET(mudita24_DOC_DIR ${SHARE_INSTALL_PREFIX}/doc/${mudita24_INSTALL_NAME}/) ENDIF(NOT DEFINED mudita24_DOC_DIR) ## Install doc files file (GLOB doc_files README.profiles ) INSTALL( FILES ${doc_files} DESTINATION ${mudita24_DOC_DIR}) # produce the manpages # TODO: # configure_file ( # mudita24.1.in # ${PROJECT_BINARY_DIR}/man/${mudita24_EXEC_NAME}.1 # ) #install( FILES # ${PROJECT_BINARY_DIR}/man/${mudita24_EXEC_NAME}.1 # DESTINATION ${SHARE_INSTALL_PREFIX}/man/man1/ # ) mudita24-1.0.3+svn13/profiles.c0000644000175000017500000013010711434666733016000 0ustar alessioalessio/* * Advanced Linux Sound Architecture part of envy24control * handle profiles for envy24control using alsactl * * Copyright (c) by Dirk Kalis * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define __PROFILES_C__ #include "envy24control.h" #undef __PROFILES_C__ /* include string search function */ #include "strstr_icase_blank.c" /* include forking process function */ #include "new_process.c" /* replace tilde with home directory */ static char filename_without_tilde[MAX_FILE_NAME_LENGTH]; static char profile_name[PROFILE_NAME_FIELD_LENGTH]; /* * check the environment and use the set variables */ char *check_environment(const char * const env_var, char * const var) { if ( (char *)getenv(env_var) == NULL ) return var; return (char *)getenv(env_var); } /* replace tilde with home directory by checking HOME environment variable */ void subst_tilde_in_filename(char * const filename) { char new_filename[MAX_FILE_NAME_LENGTH]; char *pos_after_tilde; if ((pos_after_tilde = strchr(filename, '~')) != NULL) { pos_after_tilde++; strncpy(new_filename, getenv("HOME"), MAX_FILE_NAME_LENGTH); strncpy(new_filename + strlen(new_filename), pos_after_tilde, MAX_FILE_NAME_LENGTH - strlen(new_filename)); new_filename[MAX_FILE_NAME_LENGTH - 1] = '\0'; strncpy(filename, new_filename, MAX_FILE_NAME_LENGTH); } } int which_cfgfile(char ** const cfgfile) { int res; FILE *inputFile; strncpy(filename_without_tilde, *cfgfile, MAX_FILE_NAME_LENGTH); filename_without_tilde[MAX_FILE_NAME_LENGTH - 1] = '\0'; subst_tilde_in_filename(filename_without_tilde); if ((inputFile = fopen(filename_without_tilde, "r")) == NULL) { if (strcmp(*cfgfile, DEFAULT_PROFILERC) && strcmp(*cfgfile, SYS_PROFILERC)) { res = -ENOENT; } else if (!strcmp(*cfgfile, DEFAULT_PROFILERC) && (inputFile = fopen(SYS_PROFILERC, "r")) == NULL) { res = -ENOENT; } else { fclose(inputFile); *cfgfile = SYS_PROFILERC; res = EXIT_SUCCESS; } } else { fclose(inputFile); *cfgfile = filename_without_tilde; res = EXIT_SUCCESS; } return res; } int get_file_size(const char * const filename) { struct stat file_status; strncpy(filename_without_tilde, filename, MAX_FILE_NAME_LENGTH); filename_without_tilde[MAX_FILE_NAME_LENGTH - 1] = '\0'; subst_tilde_in_filename(filename_without_tilde); if (stat(filename_without_tilde, &file_status) < 0) { fprintf(stderr, "Cannot find file '%s'.\n", filename); return -errno; } return (int)file_status.st_size; } int read_profiles_in_buffer(const char * const cfgfile, void * const buffer, const size_t max_length) { int res; int inputFile; if ((inputFile = open(cfgfile, O_RDONLY)) < 0) { fprintf(stderr, "warning: can't open profiles file '%s' for reading.\n", cfgfile); return -errno; } res = read(inputFile, buffer, max_length); close(inputFile); *(char *)(buffer + max_length - 1) = '\0'; if (res == max_length) { fprintf(stderr, "warning: profiles file '%s' has maximum size reached.\n", cfgfile); fprintf(stderr, "\tThe defined value for MAX_PROFILE_SIZE must be increased in 'profiles.h'.\n"); fprintf(stderr, "\tThen you must recompile the '%s' tool.\n", PROGRAM_NAME); fprintf(stderr, "\tThe current value is %d.\n", MAX_PROFILE_SIZE); } return res; } /* * write buffer with max_length to cfgfile * with introduction if with_intro != 0 */ int write_profiles_from_buffer(const char * const cfgfile, const void * const buffer, const size_t max_length, const int with_intro) { int res, length; time_t date_time[MAX_NUM_STR_LENGTH]; char introduction_part[MAX_SEARCH_FIELD_LENGTH]; int outputFile; if ((outputFile = open(cfgfile, O_WRONLY | O_CREAT | O_TRUNC | 0400000 /* O_NOFOLLOW */, FILE_CREA_MODE)) < 0) { fprintf(stderr, "warning: can't open profiles file '%s' for writing.\n", cfgfile); return -errno; } length = max_length > strlen(buffer) ? strlen(buffer) : max_length; time(date_time); snprintf(introduction_part, MAX_SEARCH_FIELD_LENGTH, "%s'%s'%s%s%s'%s'%s%s%s%s%s%s%s%s%s\n", \ "#\n" \ "# This file is automatically generated by ", PROGRAM_NAME, " at ", \ (char *) asctime(localtime(date_time)), \ "# Do not edit this file manually. This file will be permanently overwritten.\n" \ "# Use ", PROGRAM_NAME, " to modify and store settings.\n" \ "#\n" \ "# File-Structure:\n" \ "# ", PROFILE_HEADER_TEMPL, "\t\t- profile header\n" \ "#\t", CARD_HEADER_TEMPL, "\t- card header\n" \ "#\t", PROFILE_NAME_TEMPL, "\t\t- profile name - optional\n" \ "#\t\t***********************************\n" \ "#\t\t***** ALSA code for this card *****\n" \ "#\t\t***********************************\n" \ "#\t", CARD_FOOTER_TEMPL, "\t- card footer\n" \ "#\n"); introduction_part[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if (with_intro) res = write(outputFile, introduction_part, strlen(introduction_part)); res = write(outputFile, buffer, length); close(outputFile); if (res < 0) { fprintf(stderr, "warning: can't write profiles file '%s'.\n", cfgfile); } return res; } int create_dir_from_filename(const char * const filename) { int res; char pathname[MAX_FILE_NAME_LENGTH]; char * parameter[MAX_PARAM]; char *mkdir; mkdir = check_environment("MKDIR_PROG", MKDIR); strncpy(pathname, filename, MAX_FILE_NAME_LENGTH); pathname[MAX_FILE_NAME_LENGTH - 1] = '\0'; *strrchr(pathname, '/') = '\0'; subst_tilde_in_filename(pathname); parameter[0] = mkdir; parameter[1] = "-p"; parameter[2] = "-m"; parameter[3] = DIR_CREA_MODE; parameter[4] = pathname; parameter[5] = NULL; res = new_process(parameter); return res; } int compose_search_string(char * const search_string, const char * const templ, const char * const value, const char place_holder, const int max_length) { int res; char *pos_place_holder; memset(search_string, '\0', max_length); strncpy(search_string, templ, max_length); search_string[max_length - 1] = '\0'; if ((pos_place_holder = strchr(search_string, place_holder)) == NULL) { res = NOTFOUND; return res; } strncpy(pos_place_holder, value, max_length - strlen(search_string)); strncpy(pos_place_holder + strlen(value), \ strchr(templ, place_holder) \ + sizeof(char), \ max_length - strlen(search_string)); search_string[max_length - 1] = '\0'; res = EXIT_SUCCESS; return res; } /* search backwards first newline - start of line is position after newline */ int get_start_of_line(const char * const buffer, const int offset) { int begin_of_line; for (begin_of_line = offset; begin_of_line >= 0; begin_of_line--) { if (buffer[begin_of_line] == '\n') break; } begin_of_line++; return begin_of_line; } /* * search tokens sequential for the furthest token backwards from section_end * tokens should be specified from nearest token to the furthest */ int get_begin_of_section(const char * const buffer, char * const section_tokens, const char * const token_sep, const int section_end) { char *search_token; int pos_start_of_section, pos_end_of_section, pos_end_of_search; search_token = strtok(section_tokens, token_sep); pos_end_of_search = section_end; do { pos_start_of_section = 0; pos_end_of_section = 0; while ((pos_start_of_section = strstr_icase_blank(buffer + pos_end_of_section, search_token)) < pos_end_of_search - pos_end_of_section) { if (pos_start_of_section < 0) break; pos_end_of_section += pos_start_of_section; pos_end_of_section++; } pos_end_of_section--; pos_start_of_section = pos_end_of_section; pos_end_of_search = pos_end_of_section; if (pos_start_of_section < 0) { pos_start_of_section = NOTFOUND; // fprintf(stderr, "Begin of section for '%s' can't be found\n", search_token); return pos_start_of_section; } } while ((search_token = strtok(NULL, token_sep)) != NULL); return pos_start_of_section; } /* * search tokens sequential for the nearest token from section_begin * tokens should be specified from furthest token to the nearest */ int get_end_of_section(const char * const buffer, char * const section_tokens, const char * const token_sep, const int section_begin) { char *search_token; int pos_end_of_section, pos_end_of_search; search_token = strtok(section_tokens, token_sep); pos_end_of_section = strlen(buffer); do { pos_end_of_search = strstr_icase_blank(buffer + section_begin, search_token); if (!((pos_end_of_search < 0) || (pos_end_of_search > pos_end_of_section))) { pos_end_of_section = pos_end_of_search; } } while ((search_token = strtok(NULL, token_sep)) != NULL); return pos_end_of_section == strlen(buffer) ? pos_end_of_section : pos_end_of_section + section_begin; } int get_profile_begin(const char * const buffer, const int profile_number) { char header[MAX_SEARCH_FIELD_LENGTH], profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; /* compose profile header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", profile_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, PROFILE_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; return strstr_icase_blank(buffer, header); } int get_profile_end(const char * const buffer, const int profile_number) { char header[MAX_SEARCH_FIELD_LENGTH]; char place_holder; int pos_profile_begin; if ((pos_profile_begin = get_profile_begin(buffer, profile_number)) < 0) return pos_profile_begin; place_holder = PLACE_HOLDER_NUM; strncpy(header, PROFILE_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; *strchr(header, place_holder) = '\0'; return get_end_of_section(buffer, header, TOKEN_SEP, pos_profile_begin + sizeof(char)); } int get_card_begin(const char * const buffer, const int profile_number, const int card_number) { char header[MAX_SEARCH_FIELD_LENGTH], profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; int pos_profile_begin, pos_profile_end, pos_card_begin; if ((pos_profile_begin = get_profile_begin(buffer, profile_number)) < 0) return pos_profile_begin; pos_profile_end = get_profile_end(buffer, profile_number); /* compose card header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if ((pos_card_begin = strstr_icase_blank(buffer + pos_profile_begin, header)) < 0) return pos_card_begin; if ((pos_card_begin += pos_profile_begin) > pos_profile_end) return NOTFOUND; return pos_card_begin; } int get_card_end(const char * const buffer, const int profile_number, const int card_number) { char header[MAX_SEARCH_FIELD_LENGTH], profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; int pos_card_begin; if ((pos_card_begin = get_card_begin(buffer, profile_number, card_number)) < 0) return pos_card_begin; /* searching "[ profile | < card | < /card # >" */ /* add "[ profile |" to search_field */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; strncpy(header, PROFILE_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); *strchr(header, place_holder) = '\0'; strncpy(header + strlen(header), TOKEN_SEP, MAX_SEARCH_FIELD_LENGTH - strlen(header)); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; /* add "< card |" to header */ strncpy(header + strlen(header), CARD_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH - strlen(header)); *strchr(header, place_holder) = '\0'; strncpy(header + strlen(header), TOKEN_SEP, MAX_SEARCH_FIELD_LENGTH - strlen(header)); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; /* add "< /card # >" to header */ compose_search_string(header + strlen(header), CARD_FOOTER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH - strlen(header)); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; return get_end_of_section(buffer, header, TOKEN_SEP, pos_card_begin + sizeof(char)); } int get_pos_for_next_card(const char * const buffer, const int profile_number, const int card_number) { char header[MAX_SEARCH_FIELD_LENGTH], profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; int pos_next_card; if ((pos_next_card = get_card_end(buffer, profile_number, card_number)) < 0) return pos_next_card; /* add "< /card # >" to header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_FOOTER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if (strstr_icase_blank(buffer + pos_next_card, header) == 0) { while((buffer[pos_next_card] != '\n') && (buffer[pos_next_card] != '\0')) pos_next_card++; if (buffer[pos_next_card] == '\n') pos_next_card++; } return pos_next_card; } int get_number_from_header(const char * const header_string) { char string[MAX_SEARCH_FIELD_LENGTH]; char number_string[MAX_NUM_STR_LENGTH]; char *pos_number; strncpy(string, header_string, MAX_SEARCH_FIELD_LENGTH); string[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if ((pos_number = strpbrk(string, "0123456789")) == NULL) { return -EINVAL; } else { strncpy(number_string, pos_number, MAX_NUM_STR_LENGTH); number_string[MAX_NUM_STR_LENGTH - 1] = '\0'; } return atoi(number_string); } char *get_profile_name_from_header(const char * const header_string) { char string[MAX_SEARCH_FIELD_LENGTH]; char header_templ[MAX_SEARCH_FIELD_LENGTH]; char left_name_border; memset(profile_name, '\0', PROFILE_NAME_FIELD_LENGTH); strncpy(string, header_string, MAX_SEARCH_FIELD_LENGTH); string[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; strncpy(header_templ, PROFILE_NAME_TEMPL, MAX_SEARCH_FIELD_LENGTH); header_templ[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; string[strstr_icase_blank(string, strchr(header_templ, PLACE_HOLDER_STR) + sizeof(char))] = '\0'; left_name_border = *(strchr(header_templ, PLACE_HOLDER_STR) - sizeof(char)); strncpy(profile_name, strchr(string + sizeof(char), left_name_border) + sizeof(char), PROFILE_NAME_FIELD_LENGTH); profile_name[PROFILE_NAME_FIELD_LENGTH - 1] = '\0'; return profile_name; } /* search max card number in profile */ int get_max_card_number_in_profile(const char * const buffer, const int profile_number) { char header[MAX_SEARCH_FIELD_LENGTH]; char place_holder; int pos_card_next, pos_profile_end, pos_card_begin, card_number, card_number_max; place_holder = PLACE_HOLDER_NUM; card_number_max = NOTFOUND; strncpy(header, CARD_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; *strchr(header, place_holder) = '\0'; pos_card_next = get_profile_begin(buffer, profile_number); pos_profile_end = get_profile_end(buffer, profile_number); while ((pos_card_begin = strstr_icase_blank(buffer + pos_card_next, header)) >= 0) { if ((pos_card_begin += pos_card_next) > pos_profile_end) break; pos_card_next = pos_card_begin + sizeof(char); card_number = get_number_from_header(buffer + pos_card_begin); if (card_number > card_number_max) card_number_max = card_number; } return card_number_max; } int get_pos_name_header_from_card(const char * const buffer, const int profile_number, const int card_number) { char header[MAX_SEARCH_FIELD_LENGTH]; char place_holder; int pos_card_begin, pos_card_end, pos_name_header; pos_card_begin = get_card_begin(buffer, profile_number, card_number); pos_card_end = get_card_end(buffer, profile_number, card_number); place_holder = PLACE_HOLDER_STR; strncpy(header, PROFILE_NAME_TEMPL, MAX_SEARCH_FIELD_LENGTH); *strchr(header, place_holder) = '\0'; header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if ((pos_name_header = strstr_icase_blank(buffer + pos_card_begin, header)) >= 0) { if ((pos_name_header += pos_card_begin) < pos_card_end) { return pos_name_header; } } return NOTFOUND; } int get_begin_of_alsa_section(const char * const buffer, const int profile_number, const int card_number) { char search_string[MAX_SEARCH_FIELD_LENGTH]; int card_section_begin, card_section_end; int begin_of_alsa_section, pos_after_alsa_section; if ((card_section_begin = get_card_begin(buffer, profile_number, card_number)) < 0) return card_section_begin; card_section_end = get_card_end(buffer, profile_number, card_number); strncpy(search_string, PROFILE_NAME_TEMPL, MAX_SEARCH_FIELD_LENGTH); search_string[MAX_SEARCH_FIELD_LENGTH -1] = '\0'; *strchr(search_string, PLACE_HOLDER_STR) = '\0'; pos_after_alsa_section = get_start_of_line(buffer, card_section_end); begin_of_alsa_section = strstr_icase_blank(buffer + card_section_begin, search_string); begin_of_alsa_section = begin_of_alsa_section < 0 ? card_section_begin : begin_of_alsa_section + card_section_begin; if (begin_of_alsa_section > pos_after_alsa_section) begin_of_alsa_section = card_section_begin; while (begin_of_alsa_section < pos_after_alsa_section) { if (buffer[begin_of_alsa_section] == '\n') { begin_of_alsa_section++; break; } begin_of_alsa_section++; } if (begin_of_alsa_section >= pos_after_alsa_section) begin_of_alsa_section = NOTFOUND; return begin_of_alsa_section; } int reorganize_profiles(char * const buffer, const int max_length) { int profile_number, card_number, card_number_max; int res; int pos_profile_begin, pos_profile_end, pos_card_begin, pos_card_end, pos_name_header; int pos_alsa_section_begin, pos_after_alsa_section; char header[MAX_SEARCH_FIELD_LENGTH]; void *buffer_copy = NULL; char profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; if ((buffer_copy = malloc(max_length)) == NULL) { res = -ENOBUFS; profile_number = res; fprintf(stderr, "Cannot allocate memory for reorganize profiles.\n"); return res; } memset(buffer_copy, '\0', max_length); for (profile_number = 1; profile_number <= MAX_PROFILES; profile_number++) { if ((pos_profile_begin = get_profile_begin(buffer, profile_number)) < 0) continue; /* write profile header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", profile_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, PROFILE_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer_copy + strlen(buffer_copy), max_length - strlen(buffer_copy), "%s\n", header); pos_profile_end = get_profile_end(buffer, profile_number); /* search max card number in profile */ card_number_max = get_max_card_number_in_profile(buffer, profile_number); for (card_number = 0; card_number <= card_number_max; card_number++) { if ((pos_card_begin = get_card_begin(buffer, profile_number, card_number)) < 0) continue; /* write card header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer_copy + strlen(buffer_copy), max_length - strlen(buffer_copy), "%s\n", header); pos_card_end = get_card_end(buffer, profile_number, card_number); /* write profile name */ place_holder = PLACE_HOLDER_STR; if ((pos_name_header = get_pos_name_header_from_card(buffer, profile_number, card_number)) >= 0) { compose_search_string(header, PROFILE_NAME_TEMPL, get_profile_name_from_header(buffer + pos_name_header), place_holder, \ MAX_SEARCH_FIELD_LENGTH); snprintf(buffer_copy + strlen(buffer_copy), max_length - strlen(buffer_copy), "%s\n", header); } /* copy alsa section if exists */ if ((pos_alsa_section_begin = get_begin_of_alsa_section(buffer, profile_number, card_number)) >= 0) { pos_after_alsa_section = get_start_of_line(buffer, pos_card_end); strncpy(buffer_copy + strlen(buffer_copy), buffer + pos_alsa_section_begin, pos_after_alsa_section - pos_alsa_section_begin); } /* write card footer */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_FOOTER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer_copy + strlen(buffer_copy), max_length - strlen(buffer_copy), "%s\n", header); } } memset(buffer, '\0', max_length); strncpy(buffer, buffer_copy, max_length); buffer[max_length - 1] = '\0'; free(buffer_copy); buffer_copy = NULL; return EXIT_SUCCESS; } int delete_card_from_profile(char * const buffer, const int profile_number, const int card_number, const int max_length) { int pos_card_begin, pos_next_card; char *buffer_copy = NULL; if ((pos_card_begin = get_card_begin(buffer, profile_number, card_number)) < 0) return pos_card_begin; if ((buffer_copy = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot delete card '%d' from profile '%d'.\n", card_number, profile_number); return -ENOBUFS; } memset(buffer_copy, '\0', max_length); pos_next_card = get_pos_for_next_card(buffer, profile_number, card_number); strncpy(buffer_copy, buffer, pos_card_begin); strncpy(buffer_copy + pos_card_begin, buffer + pos_next_card, max_length - pos_card_begin); buffer_copy[max_length - 1] = '\0'; memset(buffer, '\0', max_length); strncpy(buffer, buffer_copy, max_length); buffer[max_length - 1] = '\0'; free(buffer_copy); buffer_copy = NULL; return EXIT_SUCCESS; } int save_restore_alsactl_settings(char * const tmpfile, const int card_number, char * const operation) { int res; char * parameter[MAX_PARAM]; char *alsactl; char profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH]; alsactl = check_environment("ALSACTL_PROG", ALSACTL); snprintf(profile_number_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; parameter[0] = alsactl; parameter[1] = "-f"; parameter[2] = tmpfile; parameter[3] = operation; parameter[4] = profile_number_or_card_number_as_str; parameter[5] = NULL; res = new_process(parameter); return res; } void compose_tmpfile_name(char * const tmpfile, const char * const cfgfile) { strncpy(tmpfile, cfgfile, MAX_FILE_NAME_LENGTH); tmpfile[MAX_FILE_NAME_LENGTH - 1] = '\0'; strncpy(tmpfile + strlen(tmpfile), "_alsactl_tmp", MAX_FILE_NAME_LENGTH - strlen(tmpfile)); tmpfile[MAX_FILE_NAME_LENGTH - 1] = '\0'; } /* * restore card settings * if profile_number < 0 profile_name must be given * if booth is given profile_number will be used profile_name will be ignored */ int restore_profile(const int profile_number, const int card_number, const char * profile_name, char * cfgfile) { int res, max_length; int begin_of_alsa_section, pos_after_alsa_section, profile_nr; char *buffer = NULL; char tmpfile[MAX_FILE_NAME_LENGTH]; int get_profile_number(const char * const profile_name_given, const int card_number, char * cfgfile); if ((profile_number < 0) && (profile_name == NULL)) { fprintf(stderr, "Without profile number - profile name for card '%d' must given.\n", card_number); return -EINVAL; } if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "Cannot get file size from '%s'.\n", cfgfile); return max_length; } max_length++; if ((buffer = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot read settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -ENOBUFS; } memset(buffer, '\0', max_length); if ((res = read_profiles_in_buffer(cfgfile, buffer, max_length)) <= 0) { if (profile_number < 0) { fprintf(stderr, "Cannot read settings for card '%d' in profile '%s'.\n", card_number, profile_name); } else { fprintf(stderr, "Cannot read settings for card '%d' in profile '%d'.\n", card_number, profile_number); } free(buffer); buffer = NULL; return -EINVAL; } profile_nr = profile_number; if (profile_number < 0) { if ((profile_nr = get_profile_number(profile_name, card_number, cfgfile)) < 0) { fprintf(stderr, "Cannot find profile '%s' for card '%d'.\n", profile_name, card_number); free(buffer); buffer = NULL; return profile_nr; } } if ((begin_of_alsa_section = get_begin_of_alsa_section(buffer, profile_nr, card_number)) < 0) { fprintf(stderr, "Cannot find alsa section for card '%d' in profile '%d'.\n", card_number, profile_nr); free(buffer); buffer = NULL; return begin_of_alsa_section; } pos_after_alsa_section = get_start_of_line(buffer, get_card_end(buffer, profile_nr, card_number)); compose_tmpfile_name(tmpfile, cfgfile); if ((res = write_profiles_from_buffer(tmpfile, buffer + begin_of_alsa_section, pos_after_alsa_section - begin_of_alsa_section, 0)) >= 0) { res = save_restore_alsactl_settings(tmpfile, card_number, ALSACTL_OP_RESTORE); unlink(tmpfile); } free(buffer); buffer = NULL; if (res > 0) res = EXIT_SUCCESS; return res; } int append_alsactl_settings(const char * const tmpfile, char * const buffer_position, const int max_length) { int res; res = read_profiles_in_buffer(tmpfile, buffer_position, max_length); if (res >= 0) return EXIT_SUCCESS; return res; } /* * insert entry for card in profile with alsactl settings * if profile_number < 0 no profile header is needed * if pos_end < 0 the new entry will be appended * if profile_name == NULL the profile name header will not be written */ int insert_card(char * const buffer, const int profile_number, const int card_number, const char * const profile_name, const int pos_begin, \ const int pos_end, char * const tmpfile, const int max_length) { int res; char *buffer_copy = NULL; char header[MAX_SEARCH_FIELD_LENGTH]; char profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char profile_name_copy[PROFILE_NAME_FIELD_LENGTH]; char place_holder; if ((res = save_restore_alsactl_settings(tmpfile, card_number, ALSACTL_OP_STORE)) < 0) return res; if (pos_end >= 0) { if ((buffer_copy = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); unlink(tmpfile); return -ENOBUFS; } memset(buffer_copy, '\0', max_length); strncpy(buffer_copy, buffer, max_length); buffer_copy[max_length - 1] = '\0'; memset(buffer + pos_begin, '\0', max_length - pos_begin); } if (profile_number > 0) { place_holder = PLACE_HOLDER_NUM; snprintf(profile_number_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", profile_number); profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, PROFILE_HEADER_TEMPL, profile_number_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer + strlen(buffer), max_length - strlen(buffer), "%s\n", header); buffer[max_length - 1] = '\0'; } /* compose card header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_number_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_HEADER_TEMPL, profile_number_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer + strlen(buffer), max_length - strlen(buffer), "%s\n", header); buffer[max_length - 1] = '\0'; /* compose profile name header if needed */ if (profile_name != NULL) { strncpy(profile_name_copy, profile_name, PROFILE_NAME_FIELD_LENGTH); profile_name_copy[PROFILE_NAME_FIELD_LENGTH - 1] = '\0'; place_holder = PLACE_HOLDER_STR; compose_search_string(header, PROFILE_NAME_TEMPL, profile_name_copy, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer + strlen(buffer), max_length - strlen(buffer), "%s\n", header); buffer[max_length - 1] = '\0'; } res = append_alsactl_settings(tmpfile, buffer + strlen(buffer), max_length - strlen(buffer)); buffer[max_length - 1] = '\0'; unlink(tmpfile); /* compose card footer */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_number_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_FOOTER_TEMPL, profile_number_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer + strlen(buffer), max_length - strlen(buffer), "%s\n", header); buffer[max_length - 1] = '\0'; if (pos_end >= 0) { strncpy(buffer + strlen(buffer), buffer_copy + pos_end, max_length - strlen(buffer)); free(buffer_copy); buffer_copy = NULL; } if (res >= 0) res = EXIT_SUCCESS; return res; } int save_profile(const int profile_number, const int card_number, const char * const profile_name, char *cfgfile) { int res, profile_begin, profile_end, profile_nr; int card_begin, pos_next_card, card_nr, card_number_max; const int no_profile_header = -1; const int append = -1; char *buffer = NULL; char tmpfile[MAX_FILE_NAME_LENGTH]; int max_length; if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "This operation will create a new profiles file '%s'.\n", cfgfile); max_length = 0; } max_length += MAX_PROFILE_SIZE; if ((buffer = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -ENOBUFS; } memset(buffer, '\0', max_length); compose_tmpfile_name(tmpfile, cfgfile); /* file found */ if ((res = open(cfgfile, O_RDONLY | 0400000 /* NOFOLLOW */)) >= 0) { close(res); res = read_profiles_in_buffer(cfgfile, buffer, max_length); if (res > 0) res = reorganize_profiles(buffer, max_length); } res = strlen(buffer); if (res > 0) { if ((profile_begin = get_profile_begin(buffer, profile_number)) < 0) { if (profile_number < MAX_PROFILES) { for (profile_nr = 1; profile_nr <= MAX_PROFILES; profile_nr++) { if (profile_nr > profile_number) { if ((profile_begin = get_profile_begin(buffer, profile_nr)) >= 0) break; } } if (profile_begin < 0) profile_begin = strlen(buffer); } else { profile_begin = strlen(buffer); } if (profile_begin < strlen(buffer)) { res = insert_card(buffer, profile_number, card_number, profile_name, profile_begin, profile_begin, tmpfile, max_length); } else { res = insert_card(buffer, profile_number, card_number, profile_name, profile_begin, append, tmpfile, max_length); } } else { if ((card_begin = get_card_begin(buffer, profile_number, card_number)) < 0) { card_number_max = get_max_card_number_in_profile(buffer, profile_number); profile_end = get_profile_end(buffer, profile_number); if (card_number_max > card_number) { for (card_nr = 0; card_nr <= card_number_max; card_nr++) { if (card_nr > card_number) { if ((card_begin = get_card_begin(buffer, profile_number, card_number)) >= 0) break; } } if (card_begin < 0) card_begin = profile_end; } else { card_begin = profile_end; } if (card_begin < strlen(buffer)) { res = insert_card(buffer, no_profile_header, card_number, profile_name, card_begin, card_begin, tmpfile, max_length); } else { res = insert_card(buffer, no_profile_header, card_number, profile_name, strlen(buffer), append, tmpfile, max_length); } } else { pos_next_card = get_pos_for_next_card(buffer, profile_number, card_number); res = insert_card(buffer, no_profile_header, card_number, profile_name, card_begin, pos_next_card, tmpfile, max_length); } } } else { res = insert_card(buffer, profile_number, card_number, profile_name, 0, -1, tmpfile, max_length); } if (res < 0) { fprintf(stderr, "Cannot store profile '%d' for card '%d'.\n", profile_number, card_number); } else { res = write_profiles_from_buffer(cfgfile, buffer, max_length, 1); } free(buffer); buffer = NULL; if (res > 0) res = EXIT_SUCCESS; return res; } int delete_card(const int card_number, char * cfgfile) { int res, profile_number, max_length; void *buffer = NULL; if (cfgfile == NULL) cfgfile = DEFAULT_PROFILERC; strncpy(filename_without_tilde, cfgfile, MAX_FILE_NAME_LENGTH); filename_without_tilde[MAX_FILE_NAME_LENGTH - 1] = '\0'; subst_tilde_in_filename(filename_without_tilde); cfgfile = filename_without_tilde; if ((res = open(cfgfile, O_RDWR | 0400000 /* O_NOFOLLOW */, FILE_CREA_MODE)) < 0) { fprintf(stderr, "Cannot open configuration file '%s' for writing.\n", cfgfile); fprintf(stderr, "Cannot save settings for card '%d'.\n", card_number); return -errno; } close(res); if (res >= 0) { if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "Cannot get file size for '%s'.\n", cfgfile); return max_length; } max_length++; if ((buffer = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot delete card '%d'.\n", card_number); return -ENOBUFS; } memset(buffer, '\0', max_length); res = read_profiles_in_buffer(cfgfile, buffer, max_length); if (res > 0) { for (profile_number = 1; profile_number <= MAX_PROFILES; profile_number++) delete_card_from_profile(buffer, profile_number, card_number, max_length); res = EXIT_SUCCESS; } else { res = NOTFOUND; } res = write_profiles_from_buffer(cfgfile, buffer, max_length, 1); free(buffer); buffer = NULL; } else { res = NOTFOUND; } return res; } /* * First search profile name. if profile name is found look that this profile * name is from the specified card number. * if not search next occurence from given profile name. */ int get_profile_number(const char * const profile_name_given, const int card_number, char * cfgfile) { int res, pos_name, pos_name_offset, pos_begin, pos_end, found; int profile_number, pos_profile; void *buffer = NULL; char search_field[MAX_SEARCH_FIELD_LENGTH]; char header_templ[MAX_SEARCH_FIELD_LENGTH]; char profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; int max_length; if (strlen(profile_name_given) == 0) { fprintf(stderr, "Profile name for card '%d' must be given.\n", card_number); return -EINVAL; } /* cut profile name to MAX_PROFILE_NAME_LENGTH */ strncpy(profile_name, profile_name_given, PROFILE_NAME_FIELD_LENGTH); profile_name[PROFILE_NAME_FIELD_LENGTH - 1] = '\0'; if (cfgfile == NULL) cfgfile = DEFAULT_PROFILERC; res = which_cfgfile(&cfgfile); if (res < 0) { profile_number = res; } else { if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "Cannot get file size from '%s'.\n", cfgfile); return max_length; } max_length++; if ((buffer = malloc(max_length)) == NULL) { res = -ENOBUFS; profile_number = res; fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); return profile_number; } memset(buffer, '\0', max_length); res = read_profiles_in_buffer(cfgfile, buffer, max_length); if (res > 0) { /* insert profile name in PROFILE_NAME_TEMPL */ place_holder = PLACE_HOLDER_STR; compose_search_string(search_field, PROFILE_NAME_TEMPL, profile_name, place_holder, MAX_SEARCH_FIELD_LENGTH); pos_name = 0; pos_name_offset = 0; pos_begin = NOTFOUND; found = 0; while ((pos_name = strstr_icase_blank(buffer + pos_name_offset, search_field)) >= 0) { pos_name += pos_name_offset; /* search begin of section for the given card from profile name pointer backward */ /* insert card number in CARD_HEADER_TEMPL */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(search_field, CARD_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if ((pos_begin = get_begin_of_section(buffer, search_field, TOKEN_SEP, pos_name)) < 0) break; /* searching "[ profile | < card | < /card # >" */ /* add "[ profile |" to search_field */ strncpy(header_templ, PROFILE_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); *strchr(header_templ, place_holder) = '\0'; strncpy(search_field, header_templ, MAX_SEARCH_FIELD_LENGTH); strncpy(search_field + strlen(search_field), TOKEN_SEP, MAX_SEARCH_FIELD_LENGTH - strlen(search_field)); /* add "< card |" to search_field */ strncpy(header_templ, CARD_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); *strchr(header_templ, place_holder) = '\0'; strncpy(search_field + strlen(search_field), header_templ, MAX_SEARCH_FIELD_LENGTH - strlen(search_field)); strncpy(search_field + strlen(search_field), TOKEN_SEP, MAX_SEARCH_FIELD_LENGTH - strlen(search_field)); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; /* add "< card # >" to search_field */ compose_search_string(header_templ, CARD_FOOTER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header_templ[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; strncpy(search_field + strlen(search_field), header_templ, MAX_SEARCH_FIELD_LENGTH - strlen(search_field)); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; pos_end = get_end_of_section(buffer, search_field, TOKEN_SEP, pos_begin + sizeof(char)); if ((pos_name > pos_begin) && (pos_name < pos_end) && (pos_begin >= 0) && (pos_end >= 0)) { found = 1; break; } pos_name_offset = pos_name + sizeof(char); place_holder = PLACE_HOLDER_STR; compose_search_string(search_field, PROFILE_NAME_TEMPL, profile_name, place_holder, MAX_SEARCH_FIELD_LENGTH); } if (found) { place_holder = PLACE_HOLDER_NUM; strncpy(search_field, PROFILE_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; *strchr(search_field, place_holder) = '\0'; if ((pos_profile = get_begin_of_section(buffer, search_field, TOKEN_SEP, pos_begin)) < 0) { profile_number = NOTFOUND; } else { profile_number = get_number_from_header(buffer + pos_profile); /* check profile header syntax */ if (get_profile_begin(buffer, profile_number) != pos_profile) { profile_number = -EINVAL; /* only the profile line */ strncpy(search_field, buffer + pos_profile, MAX_SEARCH_FIELD_LENGTH); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; *strchr(search_field, '\n') = '\0'; fprintf(stderr, "profile header '%s' has incorrect syntax.\n", search_field); fprintf(stderr, "profile header syntax is '%s'\n" \ "by replacing place holder '%c' with profile number.\n", \ PROFILE_HEADER_TEMPL, PLACE_HOLDER_NUM); /* check profile number */ } else if (profile_number < 1 || profile_number > MAX_PROFILES) { fprintf(stderr, "profile number '%d' is incorrect. the profile number will be in [1 ... %d].\n", \ profile_number, MAX_PROFILES); profile_number = -EINVAL; } } } else { profile_number = NOTFOUND; } } else { profile_number = NOTFOUND; } free(buffer); buffer = NULL; } return profile_number; } char *get_profile_name(const int profile_number, const int card_number, char * cfgfile) { int res, max_length; void *buffer = NULL; if (profile_number < 1 || profile_number > MAX_PROFILES) { fprintf(stderr, "profile number '%d' is incorrect. the profile number will be in [1 ... %d].\n", \ profile_number, MAX_PROFILES); return NULL; } if (cfgfile == NULL) cfgfile = DEFAULT_PROFILERC; res = which_cfgfile(&cfgfile); if (res < 0) { snprintf(profile_name, PROFILE_NAME_FIELD_LENGTH, "%d", profile_number); } else { if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "Cannot get file size from '%s'.\n", cfgfile); snprintf(profile_name, PROFILE_NAME_FIELD_LENGTH, "%d", profile_number); return profile_name; } max_length++; if ((buffer = malloc(max_length)) == NULL) { res = -ENOBUFS; snprintf(profile_name, PROFILE_NAME_FIELD_LENGTH, "%d", profile_number); fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); return profile_name; } memset(buffer, '\0', max_length); memset(profile_name, '\0', PROFILE_NAME_FIELD_LENGTH); res = read_profiles_in_buffer(cfgfile, buffer, max_length); if (res > 0) { if ((res = get_pos_name_header_from_card(buffer, profile_number, card_number)) >= 0) { get_profile_name_from_header(buffer + (res * sizeof(char))); profile_name[PROFILE_NAME_FIELD_LENGTH - 1] = '\0'; } } free(buffer); buffer = NULL; if (strlen(profile_name) == 0) { snprintf(profile_name, PROFILE_NAME_FIELD_LENGTH, "%d", profile_number); } } return profile_name; } int save_restore(const char * const operation, const int profile_number, const int card_number, char * cfgfile, const char * const profile_name) { int res; if (profile_number < 1 || profile_number > MAX_PROFILES) { fprintf(stderr, "profile number '%d' is incorrect. the profile number will be in [1 ... %d].\n", \ profile_number, MAX_PROFILES); return -EINVAL; } if (cfgfile == NULL) cfgfile = DEFAULT_PROFILERC; if (!strcmp(operation, ALSACTL_OP_STORE)) { strncpy(filename_without_tilde, cfgfile, MAX_FILE_NAME_LENGTH); filename_without_tilde[MAX_FILE_NAME_LENGTH - 1] = '\0'; subst_tilde_in_filename(filename_without_tilde); cfgfile = filename_without_tilde; if ((res = open(cfgfile, O_RDONLY | 0400000 /* O_NOFOLLOW */)) < 0) { if ((res = create_dir_from_filename(cfgfile)) < 0) { fprintf(stderr, "Cannot open configuration file '%s' for writing.\n", cfgfile); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -EACCES; } if ((res = open(cfgfile, O_RDWR | O_CREAT | 0400000 /* O_NOFOLLOW */, FILE_CREA_MODE)) < 0) { fprintf(stderr, "Cannot open configuration file '%s' for writing.\n", cfgfile); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -errno; } unlink(cfgfile); } else { if ((res = open(cfgfile, O_RDWR | 0400000 /* O_NOFOLLOW */, FILE_CREA_MODE)) < 0) { fprintf(stderr, "Cannot open configuration file '%s' for writing.\n", cfgfile); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -errno; } } res = save_profile(profile_number, card_number, profile_name, cfgfile); } else if (!strcmp(operation, ALSACTL_OP_RESTORE)) { res = which_cfgfile(&cfgfile); if (res < 0) { fprintf(stderr, "Cannot open profiles file '%s' ...\n", cfgfile); fprintf(stderr, "Use current settings.\n"); fprintf(stderr, "You can store this settings to profile no. %d in file '%s' by pressing save button.\n", profile_number, cfgfile); } else { if ((res = restore_profile(profile_number, card_number, profile_name, cfgfile)) < 0) { fprintf(stderr, "Cannot restore settings for card '%d' in profile '%d'.\n", card_number, profile_number); fprintf(stderr, "Use current settings.\n"); } } } else { fprintf(stderr, "%s: Unknown command '%s'...\n", PROGRAM_NAME, operation); res = -ENODEV; } return res < 0 ? -EXIT_FAILURE : EXIT_SUCCESS; } mudita24-1.0.3+svn13/strstr_icase_blank.c0000644000175000017500000000744511434666733020041 0ustar alessioalessio/* * Function to search string in string with ignoring case sensitivity * and times of blank * Copyright (c) by Dirk Kalis * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #ifndef MAX_SEARCH_FILED_LENGTH #define MAX_SEARCH_FIELD_LENGTH 1024 #endif #ifndef SEP_CHAR #define SEP_CHAR ' ' #endif #ifndef NOTFOUND #define NOTFOUND -1 #endif /* * Search string in string with ignoring case sensitivity and times of blank * and comment lines. * Blanks will be replaced with SEP_CHAR before compare. * Comment lines are lines beginning with first non blank character '#'. * Return value is the position in string1 relative to the begin of string1. */ int strstr_icase_blank(const char * const string1, const char * const string2) { int position, i, j; char line[MAX_SEARCH_FIELD_LENGTH]; char cmp_line[MAX_SEARCH_FIELD_LENGTH]; char search_string[MAX_SEARCH_FIELD_LENGTH]; char *pstr; int pos_first_non_blank; strncpy(search_string, string2, MAX_SEARCH_FIELD_LENGTH); search_string[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; pos_first_non_blank = -1; // convert search string in upper case for (i = 0; i < strlen(search_string); i++) { if ((pos_first_non_blank < 0) && (!isblank(search_string[i]))) pos_first_non_blank = i; search_string[i] = (char)toupper(search_string[i]); } // replace blanks in search string with SEP_CHAR to compare without blanks i = pos_first_non_blank; j = 0; while(i < strlen(search_string)) { if (j > 0) { cmp_line[j] = SEP_CHAR; j++; } sscanf(&search_string[i], "%s", cmp_line + j); i += strlen(cmp_line + j) + 1; j += strlen(cmp_line + j); for(; i < strlen(search_string); i++) { if (isblank(search_string[i])) { continue ; } else { break ; } } } strncpy(search_string, cmp_line, strlen(search_string)); position = 0; while (position < strlen(string1)) { strncpy(line, (string1 + (position * sizeof(char))), MAX_SEARCH_FIELD_LENGTH); line[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; pos_first_non_blank = -1; for (i = 0; i < strlen(line); i++) { if ((pos_first_non_blank < 0) && (!isblank(line[i]))) pos_first_non_blank = i; line[i] = (char)toupper(line[i]); if (line[i] == '\n') { line[i] = '\0'; break ; } } // no compare with comment lines or empty lines if ((line[pos_first_non_blank] != '#') && strlen(line) > 0) { // replace blanks between entities in input line with SEP_CHAR to compare without blanks i = pos_first_non_blank; j = 0; while(i < strlen(line)) { if (j > 0) { cmp_line[j] = SEP_CHAR; j++; } sscanf(&line[i], "%s", cmp_line + j); i += strlen(cmp_line + j) + 1; j += strlen(cmp_line + j); for(; i < strlen(line); i++) { if (isblank(line[i])) { continue ; } else { break ; } } } if ((pstr = strstr(cmp_line, search_string)) != NULL) { position += (pstr - cmp_line)/sizeof(char) + pos_first_non_blank; break ; } } position += strlen(line) + 1; } if (position >= strlen(string1)) { position = NOTFOUND; } return position; } mudita24-1.0.3+svn13/driverevents.c0000644000175000017500000000643211434666733016700 0ustar alessioalessio/***************************************************************************** driverevents.c - Events from the driver processing Copyright (C) 2000 by Jaroslav Kysela This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************/ #include "envy24control.h" void control_input_callback(gpointer data, gint source, GdkInputCondition condition) { snd_ctl_t *ctl = (snd_ctl_t *)data; snd_ctl_event_t *ev; const char *name; int index; unsigned int mask; snd_ctl_event_alloca(&ev); if (snd_ctl_read(ctl, ev) < 0) return; name = snd_ctl_event_elem_get_name(ev); index = snd_ctl_event_elem_get_index(ev); mask = snd_ctl_event_elem_get_mask(ev); if (! (mask & (SND_CTL_EVENT_MASK_VALUE | SND_CTL_EVENT_MASK_INFO))) return; switch (snd_ctl_event_elem_get_interface(ev)) { case SND_CTL_ELEM_IFACE_MIXER: if (!strcmp(name, "Word Clock Sync")) master_clock_update(); else if (!strcmp(name, "Multi Track Volume Rate")) volume_change_rate_update(); else if (!strcmp(name, "IEC958 Input Optical")) spdif_input_update(); else if (!strcmp(name, "Delta IEC958 Output Defaults")) spdif_output_update(); else if (!strcmp(name, "Multi Track Internal Clock")) master_clock_update(); else if (!strcmp(name, "Multi Track Internal Clock Default")) master_clock_update(); else if (!strcmp(name, "Multi Track Rate Locking")) rate_locking_update(); else if (!strcmp(name, "Multi Track Rate Reset")) rate_reset_update(); else if (!strcmp(name, "Multi Playback Volume")) mixer_update_stream(index + 1, 1, 0); else if (!strcmp(name, "H/W Multi Capture Volume")) mixer_update_stream(index + 11, 1, 0); else if (!strcmp(name, "IEC958 Multi Capture Volume")) mixer_update_stream(index + 19, 1, 0); else if (!strcmp(name, "Multi Playback Switch")) mixer_update_stream(index + 1, 0, 1); else if (!strcmp(name, "H/W Multi Capture Switch")) mixer_update_stream(index + 11, 0, 1); else if (!strcmp(name, "IEC958 Multi Capture Switch")) mixer_update_stream(index + 19, 0, 1); else if (!strcmp(name, "H/W Playback Route")) patchbay_update(); else if (!strcmp(name, "IEC958 Playback Route")) patchbay_update(); else if (!strcmp(name, "DAC Volume")) dac_volume_update(index); else if (!strcmp(name, "ADC Volume")) adc_volume_update(index); else if (!strcmp(name, "IPGA Analog Capture Volume")) ipga_volume_update(index); else if (!strcmp(name, "Output Sensitivity Switch")) dac_sense_update(index); else if (!strcmp(name, "Input Sensitivity Switch")) adc_sense_update(index); break; default: break; } } mudita24-1.0.3+svn13/README0000644000175000017500000001033611647357757014703 0ustar alessioalessiomudita24 - Control tool for Envy24 (ice1712) based soundcards Based on envy24control found in ALSA. Needs: ALSA-1.0.0 or later, GTK+-2.20 or later, an Envy24-based soundcard To build: cmake . (That's cmake 'dot') To change the default installation location (/usr/local) add: -DCMAKE_INSTALL_PREFIX=install_location To change the default build type (none, CMAKE_C_FLAGS used) add: -DCMAKE_BUILD_TYPE= where is Debug, Release, RelWithDebInfo or MinSizeRel. make su -c 'make install' or sudo make install The program 'alsactl' is automatically found and used for storing settings. Environment variable ALSACTL_PROG overrides its location. -------------------- Notes on the Envy24's hardware Digital Mixer and hardware Metering, by Niels Mayer ( http://nielsmayer.com ): -------------------- The "Monitor Inputs" and "Monitor PCMs" tabs contain multiple scale widgets grouped into L/R pairs and an associated peak-level meter. Each scale widget represents the 24 bit attenuation value of each input to the ice1712-based soundcard's digital mixer. This mixer is typically used for zero-latency monitoring of "live" inputs, alongside backing sounds and effects coming from the eight channels of PCM feeding the digital mixer. When many inputs are "hot" simultaneously these scale-widgets attenuate the inputs going into the digital mixer to prevent the output from clipping. For details see http://nielsmayer.com/npm/envy24mixer-architecture.png (from http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf ) This is what the above manual says about the Envy24's digital mixer: > 4.5.5 Multi-Track Digital Monitoring > > The Envy24 integrates a 36-bit resolution digital hardware mixer. The > width of the data path is strictly to ensure that during processing of > all the channels, under any condition, no resolution is lost. The > dynamic range of the end user system will be limited by the range of the > physical output devices used. In order to maintain identical gain to the > input stream (i.e. 0dB), the resulting 24-bit is not msb-aligned to the > 36-bit. The overflow bits correspond to the analog distortion due to > saturation. The user would need to reduce the overall attenuation of the > inputs to avoid clipping. Insertion of the digital mixer adds only a > single sample cycle delay with respect to the original data. This > extremely low latency all digital mixer provides monitoring > functionality and can replace a traditional external analog input > mixer. There are 20 independent audio data streams to mix and control > the volume. Adjustment of responsivity vs. "zipper noise" from the 1.5dB steps at the top-range of the digital mixer attenuators is achieved by the following control under "Hardware Settings": > MT3B: Volume Control Rate Register > ... > Volume update rate control (sampling rate, PSYNC) > This register allows gradual change of the digital mixer volume > setting. The value in MT3B specifies the number of samples to elapse (in > hex) between each 1.5dB increment/decrement in volume mixer. This gradual > volume update continues until the setting programmed into MT38 is > reached. The appropriate value to program may vary, but 00 or 01h are good > choices for most cases. The peak metering data is displayed as 0 to -48dBFS in envy24control's meters. This data is derived from the envy24's hardware peak metering: > MT3F: Peak Meter Data Register > [...] > Peak data derived from the absolute value of 9 msb. 00h min - FFh max > volume. Reading the register resets the meter to 00h. This resolution of the hardware metering is descibed by Fons Adriaensen in a mailing list discussion: http://lists.linuxaudio.org/pipermail/linux-audio-dev/2010-August/029009.html > [...] You have 128 steps between 0 and -6dB... > And even at -24dB the next step is 1.3dB lower. Below that > it gets worse radipdly. For a meter that is just supposed > to keep a check on peak levels it's OK. Note that hardware metering data is also available from the command-line: > amixer -c M66 cget iface=PCM,name='Multi Track Peak',numid=45 > numid=45,iface=PCM,name='Multi Track Peak' > ; type=INTEGER,access=r-------,values=22,min=0,max=255,step=0 > : values=63,62,51,49,56,60,63,62,59,54,0,0,0,0,0,0,0,0,0,0,113,112 mudita24-1.0.3+svn13/patchbay.c0000644000175000017500000001205411647357353015751 0ustar alessioalessio/***************************************************************************** patchbay.c - patchbay/router code Copyright (C) 2000 by Jaroslav Kysela This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************/ #include "envy24control.h" #define SPDIF_PLAYBACK_ROUTE_NAME "IEC958 Playback Route" #define ANALOG_PLAYBACK_ROUTE_NAME "H/W Playback Route" #define toggle_set(widget, state) \ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), state); static int stream_active[MAX_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS]; extern int output_channels, input_channels, pcm_output_channels, spdif_channels; static int is_active(GtkWidget *widget) { return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) ? 1 : 0; } static int get_toggle_index(int stream) { int err, out; snd_ctl_elem_value_t *val; stream--; if (stream < 0 || stream > 9) { g_print("get_toggle_index (1)\n"); return 0; } snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); if (stream >= MAX_OUTPUT_CHANNELS) { snd_ctl_elem_value_set_name(val, SPDIF_PLAYBACK_ROUTE_NAME); snd_ctl_elem_value_set_index(val, stream - MAX_OUTPUT_CHANNELS); } else { snd_ctl_elem_value_set_name(val, ANALOG_PLAYBACK_ROUTE_NAME); snd_ctl_elem_value_set_index(val, stream); } if ((err = snd_ctl_elem_read(ctl, val)) < 0) return 0; out = snd_ctl_elem_value_get_enumerated(val, 0); if (out >= MAX_INPUT_CHANNELS + MAX_SPDIF_CHANNELS + 1) { if (stream >= MAX_PCM_OUTPUT_CHANNELS || stream < MAX_SPDIF_CHANNELS) return 1; /* digital mixer */ } else if (out >= MAX_INPUT_CHANNELS + 1) return out - (MAX_INPUT_CHANNELS + 1) + 2; /* spdif left (=2) / right (=3) */ else if (out >= 1) return out + spdif_channels + 1; /* analog (4-) */ return 0; /* pcm */ } void patchbay_update(void) { int stream, tidx; for (stream = 1; stream <= (MAX_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS); stream++) { if (stream_active[stream - 1]) { tidx = get_toggle_index(stream); toggle_set(router_radio[stream - 1][tidx], TRUE); } } } static void set_routes(int stream, int idx) { int err; unsigned int out; snd_ctl_elem_value_t *val; stream--; if (stream < 0 || stream > 9) { g_print("set_routes (1)\n"); return; } if (! stream_active[stream]) return; out = 0; if (idx == 1) out = MAX_INPUT_CHANNELS + MAX_SPDIF_CHANNELS + 1; else if (idx == 2 || idx == 3) /* S/PDIF left & right */ out = idx + 7; /* 9-10 */ else if (idx >= 4) /* analog */ out = idx - 3; /* 1-8 */ snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); if (stream >= MAX_OUTPUT_CHANNELS) { snd_ctl_elem_value_set_name(val, SPDIF_PLAYBACK_ROUTE_NAME); snd_ctl_elem_value_set_index(val, stream - MAX_OUTPUT_CHANNELS); } else { snd_ctl_elem_value_set_name(val, ANALOG_PLAYBACK_ROUTE_NAME); snd_ctl_elem_value_set_index(val, stream); } snd_ctl_elem_value_set_enumerated(val, 0, out); if ((err = snd_ctl_elem_write(ctl, val)) < 0) g_print("Multi track route write error: %s\n", snd_strerror(err)); } void patchbay_toggled(GtkWidget *togglebutton, gpointer data) { int stream = (long)data >> 16; int what = (long)data & 0xffff; if (is_active(togglebutton)) set_routes(stream, what); } int patchbay_stream_is_active(int stream) { return stream_active[stream - 1]; } void patchbay_init(void) { int i; int nb_active_channels; snd_ctl_elem_value_t *val; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, ANALOG_PLAYBACK_ROUTE_NAME); memset (stream_active, 0, (MAX_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS) * sizeof(int)); nb_active_channels = 0; for (i = 0; i < output_channels; i++) { snd_ctl_elem_value_set_numid(val, 0); snd_ctl_elem_value_set_index(val, i); if (snd_ctl_elem_read(ctl, val) < 0) continue; stream_active[i] = 1; nb_active_channels++; } output_channels = nb_active_channels; snd_ctl_elem_value_set_name(val, SPDIF_PLAYBACK_ROUTE_NAME); nb_active_channels = 0; for (i = 0; i < spdif_channels; i++) { snd_ctl_elem_value_set_numid(val, 0); snd_ctl_elem_value_set_index(val, i); if (snd_ctl_elem_read(ctl, val) < 0) continue; stream_active[i + MAX_OUTPUT_CHANNELS] = 1; nb_active_channels++; } spdif_channels = nb_active_channels; } void patchbay_postinit(void) { patchbay_update(); } mudita24-1.0.3+svn13/envy24control.c0000644000175000017500000030661111647357353016713 0ustar alessioalessio/***************************************************************************** envy24control.c - Envy24 chipset (ICE1712) control utility Copyright (C) 2000 by Jaroslav Kysela volume.c: added by Takashi Iwai (2003/03/22) Changed to hbox/vbox layout. Copyright (C) 2003 by Søren Wedel Nielsen (16.12.2005) Re-worked user interface -digital mixer display permanently visible; pcms split to another page; controls re-arranged and all pages scrollable for min window size and greater flexibility; pop-up menu enabled. Changes to levelmeters.c to prevent invalid redraws. New options added: 'w' to set initial window pixel width and 't' for tall equal mixer height style. Copyright (C) 2005 by Alan Horstmann (Summer 2010) (0) After a decade, incremented version to 1.0. (1) Implemented "Peak Hold" functionality in meters; reimplemented meters to do away with inefficient "faux LED" peak-meter display, which causes thousands of rectangles per-second to be drawn, individually, for no good reason other than increasing the load. (2) Significantly reduced the number of timer interrupts generated by this program by slowing down all updates to 10 per second -- previously meters updated 25x/second! (3) All volumes are represented as decibels, including the 0 to -48dB range of the hardware peak-meters, the 0 -to- -48dB&off attenuation for all inputs to the digital mixer, the 0 -to- -63dB attenuation of the analog DAC, and the +18 -to- -63dB attenuation/amplification of the analog ADC. (4) All gtk "scale" widgets have dB legends; the "PageUp" "PageDown" keys allow rapid movement between the marked levels, and "UpArrow" and "DownArrow" allow fine-adjustment. (5) Added command-line options --no_scale_mark, --channel_group_modulus, --lights_color and --bg_color options; fixed --card and --device to allow valid ALSA card and CTL device names ( https://bugzilla.redhat.com/show_bug.cgi?id=602900 ). (6) Add display of "Delta IEC958 Input Status" under "Hardware Settings." Copyright (C) 2010 Niels Mayer ( http://nielsmayer.com ) Copyright (C) 2010 - 2011 Tim E. Real (terminator356 on sourceforge) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************/ #include "globaldefs.h" #include "envy24control.h" #include "midi.h" #include "config.h" #define _GNU_SOURCE #include int input_channels, output_channels, pcm_output_channels, spdif_channels, view_spdif_playback, card_number; int card_is_dmx6fire = FALSE, tall_equal_mixer_ht = FALSE; int no_scale_marks = FALSE, channel_group_modulus = 2; /* NPM added options */ GdkColor *meter_bg = NULL, *meter_fg = NULL; /* NPM added options */ int card_has_delta_iec958_input_status = FALSE; /* NPM added to support "Delta IEC958 Input Status" */ char *profiles_file_name, *default_profile; ice1712_eeprom_t card_eeprom; snd_ctl_t *ctl; GtkWidget *window; GtkWidget *mixer_mix_drawing; GtkWidget *mixer_clear_peaks_button; GtkWidget *mixer_drawing[20]; GtkObject *mixer_adj[20][2]; GtkWidget *mixer_vscale[20][2]; GtkWidget *mixer_label[20][2]; /* NPM: labels for L/R dB attenuation into digital mixer: see "Monitor Inputs" and "Monitor PCMs" panels */ GtkWidget *peak_label[MULTI_TRACK_PEAK_CHANNELS]; /* NPM: labels for dBFS peaks for all inputs/outputs of digital mixer: see "Monitor Inputs" and "Monitor PCMs" panels */ GtkWidget *adc_peak_label[MAX_INPUT_CHANNELS] = {NULL, }; /* NPM: labels for dBFS peak levels of ADC inputs: see "Analog Volume" panel */ GtkWidget *dac_peak_label[MAX_OUTPUT_CHANNELS] = {NULL, }; /* NPM: labels for dBFS peak levels of DAC outputs: see "Analog Volume" panel */ GtkWidget *mixer_mute_toggle[20][2]; GtkWidget *mixer_stereo_toggle[20]; GtkWidget *router_radio[10][12]; //GtkWidget *hw_master_clock_xtal_radio; GtkWidget *hw_master_clock_xtal_22050; GtkWidget *hw_master_clock_xtal_32000; GtkWidget *hw_master_clock_xtal_44100; GtkWidget *hw_master_clock_xtal_48000; GtkWidget *hw_master_clock_xtal_88200; GtkWidget *hw_master_clock_xtal_96000; GtkWidget *hw_master_clock_spdif_radio; GtkWidget *hw_master_clock_word_radio; GtkWidget *hw_master_clock_status_label; GtkWidget *hw_master_clock_actual_rate_label; GtkWidget *hw_clock_state_label; GtkWidget *hw_clock_state_locked; GtkWidget *hw_clock_state_reset; GtkWidget *hw_rate_locking_check; GtkWidget *hw_rate_reset_check; GtkObject *hw_volume_change_adj; GtkWidget *hw_volume_change_spin; GtkWidget *hw_iec958_input_status_label; /* NPM */ GtkWidget *hw_spdif_profi_nonaudio_radio; GtkWidget *hw_spdif_profi_audio_radio; GtkWidget *hw_profi_stream_stereo_radio; GtkWidget *hw_profi_stream_notid_radio; GtkWidget *hw_profi_emphasis_none_radio; GtkWidget *hw_profi_emphasis_5015_radio; GtkWidget *hw_profi_emphasis_ccitt_radio; GtkWidget *hw_profi_emphasis_notid_radio; GtkWidget *hw_consumer_copyright_on_radio; GtkWidget *hw_consumer_copyright_off_radio; GtkWidget *hw_consumer_copy_1st_radio; GtkWidget *hw_consumer_copy_original_radio; GtkWidget *hw_consumer_emphasis_none_radio; GtkWidget *hw_consumer_emphasis_5015_radio; GtkWidget *hw_consumer_category_dat_radio; GtkWidget *hw_consumer_category_pcm_radio; GtkWidget *hw_consumer_category_cd_radio; GtkWidget *hw_consumer_category_general_radio; GtkWidget *hw_spdif_professional_radio; GtkWidget *hw_spdif_consumer_radio; GtkWidget *hw_spdif_output_notebook; GtkWidget *hw_spdif_input_coaxial_radio; GtkWidget *hw_spdif_input_optical_radio; GtkWidget *hw_spdif_switch_off_radio; GtkWidget *input_interface_internal; GtkWidget *input_interface_front_input; GtkWidget *input_interface_rear_input; GtkWidget *input_interface_wavetable; GtkWidget *hw_phono_input_on_radio; GtkWidget *hw_phono_input_off_radio; GtkObject *av_dac_volume_adj[10]; GtkObject *av_adc_volume_adj[10]; GtkObject *av_ipga_volume_adj[10]; GtkWidget *av_dac_volume_label[10]; GtkWidget *av_adc_volume_label[10]; GtkWidget *av_ipga_volume_label[10]; GtkWidget *av_dac_sense_radio[10][4]; GtkWidget *av_adc_sense_radio[10][4]; // TER: Custom marker and page up/down snapping stuff. SliderScale mixer_volume_scales[20][2]; SliderScale dac_volume_scales[10]; SliderScale adc_volume_scales[10]; SliderScale ipga_volume_scales[10]; struct profile_button { GtkWidget *toggle_button; GtkWidget *entry; } profiles_toggle_buttons[MAX_PROFILES]; GtkWidget *active_button = NULL; GtkObject *card_number_adj; static void scale_mark_free(ScaleMark *mark) { g_free((gpointer)mark->markup); g_free(mark); } void clear_all_scale_marks(gboolean init) { int i, j; for(i = 0; i < 20; i++) { for(j = 0; j < 2; j++) { if(!init) { g_slist_foreach(mixer_volume_scales[i][j].marks, (GFunc)scale_mark_free, NULL); g_slist_free(mixer_volume_scales[i][j].marks); } mixer_volume_scales[i][j].marks = NULL; mixer_volume_scales[i][j].scale = NULL; mixer_volume_scales[i][j].type = MIXER_STRIP; mixer_volume_scales[i][j].idx = i; } } for(i = 0; i < 10; i++) { if(!init) { g_slist_foreach(dac_volume_scales[i].marks, (GFunc)scale_mark_free, NULL); g_slist_free(dac_volume_scales[i].marks); } dac_volume_scales[i].marks = NULL; dac_volume_scales[i].scale = NULL; dac_volume_scales[i].type = DAC_STRIP; dac_volume_scales[i].idx = i; } for(i = 0; i < 10; i++) { if(!init) { g_slist_foreach(adc_volume_scales[i].marks, (GFunc)scale_mark_free, NULL); g_slist_free(adc_volume_scales[i].marks); } adc_volume_scales[i].marks = NULL; adc_volume_scales[i].scale = NULL; adc_volume_scales[i].type = ADC_STRIP; adc_volume_scales[i].idx = i; } for(i = 0; i < 10; i++) { if(!init) { g_slist_foreach(ipga_volume_scales[i].marks, (GFunc)scale_mark_free, NULL); g_slist_free(ipga_volume_scales[i].marks); } ipga_volume_scales[i].marks = NULL; ipga_volume_scales[i].scale = NULL; ipga_volume_scales[i].type = ADC_STRIP; ipga_volume_scales[i].idx = i; } } static void create_mixer_frame(GtkWidget *box, int stream) { GtkWidget *vbox; GtkWidget *vbox1; GtkWidget *hbox; GtkWidget *frame; GtkObject *adj; GtkWidget *vscale; GtkWidget *drawing; GtkWidget *toggle; GtkWidget *sl_hbox; GtkWidget *sc_draw_area; char str[64], drawname[32]; if (stream <= MAX_PCM_OUTPUT_CHANNELS) { sprintf(str, "PCM Out %i", stream); } else if (stream <= (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS)) { sprintf(str, "SPDIF Out %s", stream & 1 ? "L": "R"); } else if (card_is_dmx6fire) { switch (stream) { case 11: sprintf(str, "CD In L");break; case 12: sprintf(str, "CD In R");break; case 13: sprintf(str, "Line In L");break; case 14: sprintf(str, "Line In R");break; case 15: sprintf(str, "Phono/Mic L");break; case 16: sprintf(str, "Phono/Mic R");break; case 19: sprintf(str, "Digital In L");break; case 20: sprintf(str, "Digital In R");break; default : sprintf(str, "????");break; } } else if (stream <= (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS)) { sprintf(str, "H/W In %i", stream - (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS)); } else if (stream <= (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + MAX_SPDIF_CHANNELS)) { sprintf(str, "SPDIF In %s", stream & 1 ? "L": "R"); } else { strcpy(str, "???"); } frame = gtk_frame_new(str); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), 2); vbox = gtk_vbox_new(FALSE, 6); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); // TER: Create a custom drawing area for the scale markings. sc_draw_area = gtk_drawing_area_new(); gtk_widget_show(sc_draw_area); /* * NPM: Left-channel scale widgets to adjust 24 bit attenuation of * each input into ice1712's on-chip digital mixer. */ adj = gtk_adjustment_new(LOW_MIXER_ATTENUATION_VALUE, 0, LOW_MIXER_ATTENUATION_VALUE, 1, MIXER_ATTENUATOR_STEP_SIZE, 0); /* NPM: using step size of 12 gives -12dB step-size */ mixer_adj[stream-1][0] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); /* NPM: don't draw value since printing dB values via mixer_adjust() */ mixer_vscale[stream-1][0] = vscale; /* NPM: above, set step size of 12 ==> -18dB step-size. Place dB-labelled markers at those locations */ // TER: Replaced with custom drawing. //draw_24bit_attenuator_scale_markings(GTK_SCALE(vscale), GTK_POS_LEFT, // (channel_group_modulus==1) // ? TRUE // : (stream%channel_group_modulus)); gtk_widget_show(vscale); // TER: Create list of scale marking positions, connect handlers, then pack. scale_add_marks(GTK_SCALE(vscale), &mixer_volume_scales[stream - 1][0], GTK_POS_LEFT, (channel_group_modulus==1) ? TRUE : (stream % channel_group_modulus)); g_signal_connect(G_OBJECT(sc_draw_area), "size-request", G_CALLBACK (scale_size_req_handler), (gpointer)&mixer_volume_scales[stream - 1][0]); //gtk_widget_set_events(sc_draw_area, GDK_STRUCTURE_MASK); // Needed ? g_signal_connect(G_OBJECT(sc_draw_area), "expose_event", G_CALLBACK (scale_expose_handler), (gpointer)&mixer_volume_scales[stream - 1][0]); gtk_widget_set_events(sc_draw_area, GDK_EXPOSURE_MASK); g_signal_connect(G_OBJECT(sc_draw_area), "button-press-event", G_CALLBACK (scale_btpress_handler), (gpointer)&mixer_volume_scales[stream - 1][0]); gtk_widget_set_events(sc_draw_area, GDK_BUTTON_PRESS_MASK); gtk_box_pack_start(GTK_BOX(hbox), sc_draw_area, TRUE, TRUE, 0); //gtk_box_pack_start(GTK_BOX(hbox), vscale, TRUE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), vscale, TRUE, TRUE, 0); // TER gtk_scale_set_digits(GTK_SCALE(vscale), 0); g_signal_connect(GTK_OBJECT(adj), "value_changed", G_CALLBACK(mixer_adjust), (gpointer)(long)((stream << 16) + 0)); // TER: Let us handle the page up/down snapping. g_signal_connect(GTK_OBJECT(vscale), "change-value", G_CALLBACK(slider_change_value_handler), (gpointer)&mixer_volume_scales[stream - 1][0]); vbox1 = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox1); gtk_box_pack_start(GTK_BOX(hbox), vbox1, FALSE, FALSE, 0); drawing = gtk_drawing_area_new(); mixer_drawing[stream-1] = drawing; sprintf(drawname, "Mixer%i", stream); gtk_widget_set_name(drawing, drawname); gtk_widget_show(drawing); g_signal_connect(GTK_OBJECT(drawing), "expose_event", G_CALLBACK(level_meters_expose_event), NULL); g_signal_connect(GTK_OBJECT(drawing), "configure_event", G_CALLBACK(level_meters_configure_event), NULL); gtk_widget_set_events(drawing, GDK_EXPOSURE_MASK); gtk_widget_set_usize(drawing, 24, (60 * tall_equal_mixer_ht + 204)); //gtk_box_pack_end(GTK_BOX(vbox1), drawing, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(vbox1), drawing, TRUE, TRUE, 0); // TER // TER: Create a custom drawing area for the scale markings. sc_draw_area = gtk_drawing_area_new(); gtk_widget_show(sc_draw_area); /* * NPM: Right-channel scale widgets to adjust 24bit attenuation of * each input into ice1712's on-chip digital mixer. */ adj = gtk_adjustment_new(LOW_MIXER_ATTENUATION_VALUE, 0, LOW_MIXER_ATTENUATION_VALUE, 1, MIXER_ATTENUATOR_STEP_SIZE, 0); mixer_adj[stream-1][1] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); /* NPM: don't draw value since printing dB values via mixer_adjust() */ mixer_vscale[stream-1][1] = vscale; // TER: Replaced with custom drawing. //draw_24bit_attenuator_scale_markings(GTK_SCALE(vscale), GTK_POS_RIGHT, // (channel_group_modulus==1) // ? FALSE // : ((stream-1)%channel_group_modulus)); gtk_widget_show(vscale); // TER: Create list of scale marking positions, connect handlers. scale_add_marks(GTK_SCALE(vscale), &mixer_volume_scales[stream - 1][1], GTK_POS_RIGHT, (channel_group_modulus==1) ? FALSE : ((stream - 1) % channel_group_modulus)); g_signal_connect(G_OBJECT(sc_draw_area), "size-request", G_CALLBACK (scale_size_req_handler), (gpointer)&mixer_volume_scales[stream - 1][1]); //gtk_widget_set_events(sc_draw_area, GDK_STRUCTURE_MASK); // Needed ? g_signal_connect(G_OBJECT(sc_draw_area), "expose_event", G_CALLBACK (scale_expose_handler), (gpointer)&mixer_volume_scales[stream - 1][1]); gtk_widget_set_events(sc_draw_area, GDK_EXPOSURE_MASK); g_signal_connect(G_OBJECT(sc_draw_area), "button-press-event", G_CALLBACK (scale_btpress_handler), (gpointer)&mixer_volume_scales[stream - 1][1]); gtk_widget_set_events(sc_draw_area, GDK_BUTTON_PRESS_MASK); gtk_box_pack_start(GTK_BOX(hbox), vscale, TRUE, FALSE, 0); gtk_scale_set_digits(GTK_SCALE(vscale), 0); g_signal_connect(GTK_OBJECT(adj), "value_changed", G_CALLBACK(mixer_adjust), (gpointer)(long)((stream << 16) + 1)); // TER: Let us handle the page up/down snapping. g_signal_connect(GTK_OBJECT(vscale), "change-value", G_CALLBACK(slider_change_value_handler), (gpointer)&mixer_volume_scales[stream - 1][0]); //gtk_widget_set_size_request(sc_draw_area, sc_width, -1); gtk_box_pack_start(GTK_BOX(hbox), sc_draw_area, TRUE, TRUE, 0); // TER /* NPM: Labels to display the retained peak levels gathered from ice1712's hardware metering */ peak_label[stream-1] = gtk_label_new("(Off) "); gtk_widget_modify_font(peak_label[stream-1], pango_font_description_from_string ("Monospace")); gtk_widget_show(peak_label[stream-1]); //gtk_box_pack_start(GTK_BOX(vbox), peak_label[stream-1], TRUE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), peak_label[stream-1], FALSE, FALSE, 0); // TER hbox = gtk_hbox_new(TRUE, 0); gtk_widget_show(hbox); //gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); // TER /* NPM: Labels to display attenuation of Left input into digital mixer: see mixer.c:mixer_adjust() */ mixer_label[stream-1][0] = gtk_label_new("(Off) "); /* NPM: note that all but the "(Off)" values get refreshed at startup */ gtk_misc_set_alignment(GTK_MISC(mixer_label[stream-1][0]), 0, 0.5); gtk_widget_modify_font(mixer_label[stream-1][0], pango_font_description_from_string ("Monospace")); gtk_widget_show(mixer_label[stream-1][0]); //gtk_box_pack_start(GTK_BOX(hbox), mixer_label[stream-1][0], FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), mixer_label[stream-1][0], FALSE, FALSE, 0); // TER /* NPM: Labels to display attenuation of Right input into digital mixer: see mixer.c:mixer_adjust() */ mixer_label[stream-1][1] = gtk_label_new("(Off) "); /* NPM: note that all but the "(Off)" values get refreshed at startup */ gtk_misc_set_alignment(GTK_MISC(mixer_label[stream-1][1]), 1, 0.5); gtk_widget_modify_font(mixer_label[stream-1][1], pango_font_description_from_string ("Monospace")); gtk_widget_show(mixer_label[stream-1][1]); //gtk_box_pack_start(GTK_BOX(hbox), mixer_label[stream-1][1], FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), mixer_label[stream-1][1], FALSE, FALSE, 0); // TER toggle = gtk_toggle_button_new_with_label("L/R Gang"); mixer_stereo_toggle[stream-1] = toggle; gtk_widget_show(toggle); gtk_box_pack_end(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); /* gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), TRUE); */ g_signal_connect(GTK_OBJECT(toggle), "toggled", G_CALLBACK(config_set_stereo), (gpointer)(long)(stream - 1)); /* NPM: use (long) to fix "envy24control.c:251: warning: cast to pointer from integer of different size" */ hbox = gtk_hbox_new(TRUE, 3); gtk_widget_show(hbox); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); toggle = gtk_toggle_button_new_with_label("Mute"); mixer_mute_toggle[stream-1][0] = toggle; gtk_widget_show(toggle); gtk_box_pack_start(GTK_BOX(hbox), toggle, FALSE, TRUE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), TRUE); g_signal_connect(GTK_OBJECT(toggle), "toggled", G_CALLBACK(mixer_toggled_mute), (gpointer)(long)((stream << 16) + 0)); toggle = gtk_toggle_button_new_with_label("Mute"); mixer_mute_toggle[stream-1][1] = toggle; gtk_widget_show(toggle); gtk_box_pack_start(GTK_BOX(hbox), toggle, FALSE, TRUE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), TRUE); g_signal_connect(GTK_OBJECT(toggle), "toggled", G_CALLBACK(mixer_toggled_mute), (gpointer)(long)((stream << 16) + 1)); } static void create_inputs_mixer(GtkWidget *main, GtkWidget *notebook, int page) { GtkWidget *hbox; GtkWidget *vbox; GtkWidget *label; GtkWidget *scrolledwindow; GtkWidget *viewport; int stream; hbox = gtk_hbox_new(FALSE, 3); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(notebook), hbox); label = gtk_label_new("Monitor Inputs"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook),page), label); /* build scrolling area */ scrolledwindow = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scrolledwindow); gtk_box_pack_start(GTK_BOX(hbox), scrolledwindow, TRUE, TRUE, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrolledwindow), viewport); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(viewport), vbox); hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); // TER: Changed. //gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); for(stream = (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + 1); \ stream <= input_channels + (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS); stream ++) { if (mixer_stream_is_active(stream)) create_mixer_frame(hbox, stream); } for(stream = (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + 1); \ stream <= spdif_channels + (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS); stream ++) { if (mixer_stream_is_active(stream)) create_mixer_frame(hbox, stream); } } static void create_pcms_mixer(GtkWidget *main, GtkWidget *notebook, int page) { GtkWidget *hbox; GtkWidget *vbox; GtkWidget *label; GtkWidget *scrolledwindow; GtkWidget *viewport; int stream; hbox = gtk_hbox_new(FALSE, 3); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(notebook), hbox); label = gtk_label_new("Monitor PCMs"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook),page), label); /* build scrolling area */ scrolledwindow = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scrolledwindow); gtk_box_pack_start(GTK_BOX(hbox), scrolledwindow, TRUE, TRUE, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrolledwindow), viewport); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(viewport), vbox); hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); // TER: Changed. //gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); for(stream = 1; stream <= pcm_output_channels; stream ++) { if (mixer_stream_is_active(stream)) create_mixer_frame(hbox, stream); } for(stream = (MAX_PCM_OUTPUT_CHANNELS + 1); \ stream <= spdif_channels + MAX_PCM_OUTPUT_CHANNELS; stream ++) { if (mixer_stream_is_active(stream) && view_spdif_playback) create_mixer_frame(hbox, stream); } } static void create_router_frame(GtkWidget *box, int stream, int pos) { GtkWidget *vbox; GtkWidget *frame; GtkWidget *radiobutton; GtkWidget *label; GtkWidget *hseparator; GSList *group = NULL; char str[64], str1[64]; int idx; static char *table[10] = { "S/PDIF In L", "S/PDIF In R", "H/W In 1", "H/W In 2", "H/W In 3", "H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", "H/W In 8" }; if (card_is_dmx6fire) { table[0] = "Digital In L"; table[1] = "Digital In R"; table[2] = "CD In L"; table[3] = "CD In R"; table[4] = "Line In L"; table[5] = "Line In R"; table[6] = "Phono/Mic L"; table[7] = "Phono/Mic R"; } if (stream <= MAX_OUTPUT_CHANNELS) { sprintf(str, "H/W Out %i (%s)", stream, stream & 1 ? "L" : "R"); } else if (stream == (MAX_OUTPUT_CHANNELS + 1)) { if (card_is_dmx6fire) { strcpy(str, "Digital Out (L)"); } else { strcpy(str, "S/PDIF Out (L)"); } } else if (stream == (MAX_OUTPUT_CHANNELS + 2)) { if (card_is_dmx6fire) { strcpy(str, "Digital Out (R)"); } else { strcpy(str, "S/PDIF Out (R)"); } } else { strcpy(str, "???"); } if ((stream == MAX_PCM_OUTPUT_CHANNELS + 1) || (stream == MAX_PCM_OUTPUT_CHANNELS + 2)) { sprintf(str1, "S/PDIF Out (%s)", stream & 1 ? "L" : "R"); } else { sprintf(str1, "PCM Out %i", stream); } frame = gtk_frame_new(str); gtk_widget_show(frame); gtk_box_pack_start (GTK_BOX(box), frame, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), 2); vbox = gtk_vbox_new(TRUE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, str1); router_radio[stream-1][0] = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(patchbay_toggled), (gpointer)(long)((stream << 16) + 0)); hseparator = gtk_hseparator_new(); gtk_widget_show(hseparator); gtk_box_pack_start(GTK_BOX(vbox), hseparator, FALSE, TRUE, 0); label = gtk_label_new(""); gtk_widget_show(label); /* the digital mixer can only be routed to HW1/2 or SPDIF1/2 */ if( (stream <= 2) /* hw1/2 */ || ((stream > MAX_OUTPUT_CHANNELS) && (stream <= MAX_OUTPUT_CHANNELS + 2)) /* spdif1/2 */ ) { radiobutton = gtk_radio_button_new_with_label(group, stream & 1 ? "Digital Mix L" : "Digital Mix R"); router_radio[stream-1][1] = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(patchbay_toggled), (gpointer)(long)((stream << 16) + 1)); } else { label = gtk_label_new(""); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); } hseparator = gtk_hseparator_new(); gtk_widget_show(hseparator); gtk_box_pack_start(GTK_BOX(vbox), hseparator, FALSE, TRUE, 0); for(idx = 2 - spdif_channels; idx < input_channels + 2; idx++) { radiobutton = gtk_radio_button_new_with_label(group, table[idx]); router_radio[stream-1][2+idx] = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(patchbay_toggled), (gpointer)(long)((stream << 16) + 2 + idx)); } } static void create_router(GtkWidget *main, GtkWidget *notebook, int page) { GtkWidget *hbox; GtkWidget *label; GtkWidget *scrolledwindow; GtkWidget *viewport; int stream, pos; scrolledwindow = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scrolledwindow); gtk_container_add(GTK_CONTAINER(notebook), scrolledwindow); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); label = gtk_label_new("Patchbay / Router"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page), label); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrolledwindow), viewport); hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(viewport), hbox); pos = 0; for (stream = 1; stream <= output_channels; stream++) { if (patchbay_stream_is_active(stream)) create_router_frame(hbox, stream, pos++); } for (stream = MAX_OUTPUT_CHANNELS + 1; stream <= MAX_OUTPUT_CHANNELS + spdif_channels; stream++) { if (patchbay_stream_is_active(stream)) create_router_frame(hbox, stream, pos++); } } static void create_master_clock(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GtkWidget *label; GSList *group = NULL; frame = gtk_frame_new("Master Clock"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "Int 22050"); hw_master_clock_xtal_22050 = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(internal_clock_toggled), (gpointer)"22050"); radiobutton = gtk_radio_button_new_with_label(group, "Int 32000"); hw_master_clock_xtal_32000 = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(internal_clock_toggled), (gpointer)"32000"); radiobutton = gtk_radio_button_new_with_label(group, "Int 44100"); hw_master_clock_xtal_44100 = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(internal_clock_toggled), (gpointer)"44100"); radiobutton = gtk_radio_button_new_with_label(group, "Int 48000"); hw_master_clock_xtal_48000 = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(internal_clock_toggled), (gpointer)"48000"); radiobutton = gtk_radio_button_new_with_label(group, "Int 88200"); hw_master_clock_xtal_88200 = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(internal_clock_toggled), (gpointer)"88200"); radiobutton = gtk_radio_button_new_with_label(group, "Int 96000"); hw_master_clock_xtal_96000 = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(internal_clock_toggled), (gpointer)"96000"); radiobutton = gtk_radio_button_new_with_label(group, "S/PDIF In"); hw_master_clock_spdif_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(internal_clock_toggled), (gpointer)"SPDIF"); if (card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTA1010 && card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTA1010LT) return; radiobutton = gtk_radio_button_new_with_label(group, "Word Clock"); hw_master_clock_word_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(internal_clock_toggled), (gpointer)"WordClock"); label = gtk_label_new("Locked"); hw_master_clock_status_label = label; gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); } static void create_rate_state(GtkWidget *box) { GtkWidget *frame; GtkWidget *hbox; GtkWidget *check; frame = gtk_frame_new("Rate State"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0); hbox = gtk_hbox_new(TRUE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(frame), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), 2); check = gtk_check_button_new_with_label("Multi Track\nRate Locking"); hw_rate_locking_check = check; gtk_widget_show(check); gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(check), "toggled", G_CALLBACK(rate_locking_toggled), (gpointer)"locked"); check = gtk_check_button_new_with_label("Multi Track\nRate Reset"); hw_rate_reset_check = check; gtk_widget_show(check); gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(check), "toggled", G_CALLBACK(rate_reset_toggled), (gpointer)"reset"); } static void create_actual_rate(GtkWidget *box) { GtkWidget *frame; GtkWidget *label; frame = gtk_frame_new("Actual Rate"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0); label = gtk_label_new(""); hw_master_clock_actual_rate_label = label; gtk_widget_show(label); gtk_container_add(GTK_CONTAINER(frame), label); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); gtk_misc_set_padding(GTK_MISC(label), 6, 6); } static void create_volume_change(GtkWidget *box) { GtkWidget *frame; GtkWidget *hbox; GtkObject *spinbutton_adj; GtkWidget *spinbutton; GtkWidget *label; frame = gtk_frame_new("Volume Change"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0); hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(frame), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), 2); label = gtk_label_new("Rate"); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, FALSE, 0); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); spinbutton_adj = gtk_adjustment_new(16, 0, 255, 1, 10, 0); /* NPM: last parm changed to 0, gets rid of 'Gtk-WARNING **: GtkSpinButton: setting an > adjustment with non-zero page size is deprecated' -- change suggested by James Morris on linux-audio-dev */ hw_volume_change_adj = spinbutton_adj; spinbutton = gtk_spin_button_new(GTK_ADJUSTMENT(spinbutton_adj), 1, 0); gtk_widget_show(spinbutton); gtk_box_pack_start(GTK_BOX(hbox), spinbutton, TRUE, FALSE, 0); gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spinbutton), TRUE); g_signal_connect(GTK_OBJECT(spinbutton_adj), "value_changed", G_CALLBACK(volume_change_rate_adj), NULL); } /* NPM: Put Label in "Hardware Settings" for value of IEC958 Input Status */ static void create_iec958_input_status(GtkWidget *box) { if (card_has_delta_iec958_input_status) { GtkWidget *frame; frame = gtk_frame_new("IEC958 Input Status"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0); hw_iec958_input_status_label = gtk_label_new("input: ()"); gtk_widget_show(hw_iec958_input_status_label); gtk_container_add(GTK_CONTAINER(frame), hw_iec958_input_status_label); } } static void create_spdif_output_settings_profi_data(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; frame = gtk_frame_new("Data Mode"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "Non-audio"); hw_spdif_profi_nonaudio_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(profi_data_toggled), (gpointer)"Non-audio"); radiobutton = gtk_radio_button_new_with_label(group, "Audio"); hw_spdif_profi_audio_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(profi_data_toggled), (gpointer)"Audio"); } static void create_spdif_output_settings_profi_stream(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; frame = gtk_frame_new("Stream"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "Stereophonic"); hw_profi_stream_stereo_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(profi_stream_toggled), (gpointer)"Stereo"); radiobutton = gtk_radio_button_new_with_label(group, "Not indicated"); hw_profi_stream_notid_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(profi_stream_toggled), (gpointer)"NOTID"); } static void create_spdif_output_settings_profi_emphasis(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; frame = gtk_frame_new("Emphasis"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "No emphasis"); hw_profi_emphasis_none_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(profi_emphasis_toggled), (gpointer)"No"); radiobutton = gtk_radio_button_new_with_label(group, "50/15us"); hw_profi_emphasis_5015_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(profi_emphasis_toggled), (gpointer)"5015"); radiobutton = gtk_radio_button_new_with_label(group, "CCITT J.17"); hw_profi_emphasis_ccitt_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(profi_emphasis_toggled), (gpointer)"CCITT"); radiobutton = gtk_radio_button_new_with_label(group, "Not indicated"); hw_profi_emphasis_notid_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(profi_emphasis_toggled), (gpointer)"NOTID"); } static void create_spdif_output_settings_profi(GtkWidget *notebook, int page) { GtkWidget *hbox; GtkWidget *vbox; GtkWidget *label; hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(notebook), hbox); label = gtk_label_new("Professional"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page), label); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); create_spdif_output_settings_profi_data(vbox); create_spdif_output_settings_profi_stream(vbox); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); create_spdif_output_settings_profi_emphasis(vbox); } static void create_spdif_output_settings_consumer_copyright(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; frame = gtk_frame_new("Copyright"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "Copyrighted"); hw_consumer_copyright_on_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_copyright_toggled), (gpointer)"Copyright"); radiobutton = gtk_radio_button_new_with_label(group, "Copy permitted"); hw_consumer_copyright_off_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_copyright_toggled), (gpointer)"Permitted"); } static void create_spdif_output_settings_consumer_copy(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; frame = gtk_frame_new("Copy"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "1-st generation"); hw_consumer_copy_1st_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_copy_toggled), (gpointer)"1st"); radiobutton = gtk_radio_button_new_with_label(group, "Original"); hw_consumer_copy_original_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_copy_toggled), (gpointer)"Original"); } static void create_spdif_output_settings_consumer_emphasis(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; frame = gtk_frame_new("Emphasis"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "No emphasis"); hw_consumer_emphasis_none_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_emphasis_toggled), (gpointer)"No"); radiobutton = gtk_radio_button_new_with_label(group, "50/15us"); hw_consumer_emphasis_5015_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_emphasis_toggled), (gpointer)"5015"); } static void create_spdif_output_settings_consumer_category(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; frame = gtk_frame_new("Category"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "DAT"); hw_consumer_category_dat_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_category_toggled), (gpointer)"DAT"); radiobutton = gtk_radio_button_new_with_label(group, "PCM encoder"); hw_consumer_category_pcm_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_category_toggled), (gpointer)"PCM"); radiobutton = gtk_radio_button_new_with_label(group, "CD (ICE-908)"); hw_consumer_category_cd_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_category_toggled), (gpointer)"CD"); radiobutton = gtk_radio_button_new_with_label(group, "General"); hw_consumer_category_general_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(consumer_category_toggled), (gpointer)"General"); } static void create_spdif_output_settings_consumer(GtkWidget *notebook, int page) { GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(notebook), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), 2); label = gtk_label_new("Consumer"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page), label); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); create_spdif_output_settings_consumer_copyright(vbox); create_spdif_output_settings_consumer_copy(vbox); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); create_spdif_output_settings_consumer_emphasis(vbox); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); create_spdif_output_settings_consumer_category(vbox); } static void create_spdif_output_settings(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *radiobutton; GtkWidget *notebook; GSList *group = NULL; frame = gtk_frame_new("S/PDIF Output Settings"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), 2); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); radiobutton = gtk_radio_button_new_with_label(NULL, "Professional"); hw_spdif_professional_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(radiobutton), 2); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(spdif_output_toggled), (gpointer)"Professional"); radiobutton = gtk_radio_button_new_with_label(group, "Consumer"); hw_spdif_consumer_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(radiobutton), 2); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(spdif_output_toggled), (gpointer)"Consumer"); notebook = gtk_notebook_new(); hw_spdif_output_notebook = notebook; gtk_widget_show(notebook); gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); create_spdif_output_settings_profi(notebook, 0); create_spdif_output_settings_consumer(notebook, 1); } static void create_spdif_input_select(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; int hide = 1; if((card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTADIO2496) || (card_is_dmx6fire)) hide = 0; frame = gtk_frame_new("Digital Input"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), 2); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "Coaxial"); hw_spdif_input_coaxial_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(spdif_input_toggled), (gpointer)"Coaxial"); radiobutton = gtk_radio_button_new_with_label(group, "Optical"); hw_spdif_input_optical_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(spdif_input_toggled), (gpointer)"Optical"); radiobutton = gtk_radio_button_new_with_label(group, "Internal CD"); hw_spdif_switch_off_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); if(card_is_dmx6fire) gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(spdif_input_toggled), (gpointer)"Off"); if(hide) gtk_widget_hide_all(frame); } static void create_phono_input(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; int hide = 1; if(card_is_dmx6fire) hide = 0; frame = gtk_frame_new("Phono Input Switch"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 2); gtk_container_set_border_width(GTK_CONTAINER(frame), 2); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "Phono"); hw_phono_input_on_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(phono_input_toggled), (gpointer)"Phono"); radiobutton = gtk_radio_button_new_with_label(group, "Mic"); hw_phono_input_off_radio = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(phono_input_toggled), (gpointer)"Mic"); if(hide) gtk_widget_hide_all(frame); } static void create_input_interface(GtkWidget *box) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *radiobutton; GSList *group = NULL; int hide = 1; if (card_is_dmx6fire) hide = 0; frame = gtk_frame_new("Line In Selector"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 4); //gtk_container_set_border_width(GTK_CONTAINER(frame), 6); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); radiobutton = gtk_radio_button_new_with_label(group, "Internal"); input_interface_internal = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(analog_input_select_toggled), (gpointer)"Internal"); radiobutton = gtk_radio_button_new_with_label(group, "Front Input"); input_interface_front_input = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(analog_input_select_toggled), (gpointer)"Front Input"); radiobutton = gtk_radio_button_new_with_label(group, "Rear Input"); input_interface_rear_input = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(analog_input_select_toggled), (gpointer)"Rear Input"); radiobutton = gtk_radio_button_new_with_label(group, "Wavetable"); input_interface_wavetable = radiobutton; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(analog_input_select_toggled), (gpointer)"Wave Table"); if(hide) gtk_widget_hide_all(frame); } static void create_hardware(GtkWidget *main, GtkWidget *notebook, int page) { GtkWidget *label; GtkWidget *hbox; GtkWidget *hbox1; GtkWidget *hbox2; GtkWidget *vbox; GtkWidget *vbox1; GtkWidget *scrolledwindow; GtkWidget *viewport; hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(notebook), hbox); label = gtk_label_new("Hardware Settings"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page), label); /* Build scrolling area */ scrolledwindow = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scrolledwindow); gtk_box_pack_start(GTK_BOX(hbox), scrolledwindow, TRUE, TRUE, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrolledwindow), viewport); /* Outer box */ hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(viewport), hbox); /* Create boxes for controls */ vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 6); hbox1 = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox1); gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 0); // hseparator = gtk_hseparator_new(); // gtk_widget_show(hseparator); // gtk_box_pack_start(GTK_BOX(vbox), hseparator, FALSE, FALSE, 2); hbox2 = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox2); gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 0); create_master_clock(hbox1); vbox1 = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox1); gtk_box_pack_start(GTK_BOX(hbox1), vbox1, FALSE, FALSE, 20); create_rate_state(vbox1); create_actual_rate(vbox1); create_volume_change(vbox1); create_iec958_input_status(vbox1); create_input_interface(hbox2); create_phono_input(hbox2); create_spdif_input_select(hbox2); create_spdif_output_settings(hbox); } static void create_about(GtkWidget *main, GtkWidget *notebook, int page) { GtkWidget *label; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *scrolledwindow; GtkWidget *viewport; hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(notebook), hbox); label = gtk_label_new("About"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page), label); /* build scrolling area */ scrolledwindow = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scrolledwindow); gtk_box_pack_start(GTK_BOX(hbox), scrolledwindow, TRUE, TRUE, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrolledwindow), viewport); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(viewport), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); char temp_text[1024]; sprintf(temp_text, "Envy24 Control Utility ( %s %s)\n\n\ A GTK Tool for Envy24 PCI Audio Chip\n\n\ Copyright (C) 2000 by Jaroslav Kysela <perex@perex.cz>\n\ Copyright (C) 2003 by Søren Wedel Nielsen\n\ Copyright (C) 2005 by Alan Horstmann\n\ Copyright (C) 2010 Niels Mayer ( http://nielsmayer.com )\n\ Copyright (C) 2010 - 2011 Tim E. Real ( terminator356 on sourceforge)\n\ ", VERSION, SVNVERSION); /* Create text as label */ label = gtk_label_new(""); gtk_label_set_markup(GTK_LABEL(label), temp_text); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 6); } static void create_analog_volume(GtkWidget *main, GtkWidget *notebook, int page) { GtkWidget *label; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *frame; GtkObject *adj; GtkWidget *vscale; GtkWidget *radiobutton; GSList *group; GtkWidget *scrolledwindow; GtkWidget *viewport; GtkWidget *sl_hbox; GtkWidget *sc_draw_area; gdouble mn, mx; int i, j; static char* dmx6fire_inputs[6] = { "CD In (L)", "CD In (R)", "Line (L)", "Line (R)", "Phono (L)", "Phono (R)" }; static char* dmx6fire_outputs[6] = { "Front (L)", "Front (R)", "Rear (L)", "Rear (R)", "Centre", "LFE" }; scrolledwindow = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scrolledwindow); gtk_container_add(GTK_CONTAINER(notebook), scrolledwindow); label = gtk_label_new("Analog Volume"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page), label); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrolledwindow), viewport); hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(viewport), hbox); /* create DAC */ for(i = 0; i < envy_dac_volumes(); i++) { char name[32]; sprintf(name, "DAC %d", i+1); /* NPM: for consistency w/ other panels, start w/ "DAC 1" */ frame = gtk_frame_new(name); gtk_widget_show(frame); //gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), 3); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); /* Add friendly labels for DMX 6Fires */ if(card_is_dmx6fire && (i < 6)){ label = gtk_label_new(dmx6fire_outputs[i]); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); } /* NPM: display peak levels on DAC's in "Analog Volume" panel */ if (i < MAX_OUTPUT_CHANNELS) { /* make sure within bounds of dac_peak_label[] */ dac_peak_label[i] = label = gtk_label_new("(Off)"); gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); } // TER: Create hbox for scale drawing area + slider. sl_hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(sl_hbox); gtk_box_pack_start(GTK_BOX(vbox), sl_hbox, TRUE, TRUE, 0); if(!get_alsa_control_range(&dac_volume_scales[i], &mn, &mx)) // TER { mn = 0; mx = 127; } //adj = gtk_adjustment_new(0, -(envy_dac_max()), 0, 1, ANALOG_GAIN_STEP_SIZE, 0); /* NPM: using step size of 12 gives -6dB step-size */ //adj = gtk_adjustment_new(0, mn, mx, 1, ANALOG_GAIN_STEP_SIZE, 0); // TER adj = gtk_adjustment_new(0, -mx, mn, 1, ANALOG_GAIN_STEP_SIZE, 0); // TER av_dac_volume_adj[i] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); /* NPM: above, set step size of 12 ==> -6dB step-size. Place dB-labelled markers at those locations */ // TER: Replaced with custom drawing. //draw_dac_scale_markings(GTK_SCALE(vscale), (i%channel_group_modulus) ? GTK_POS_RIGHT : GTK_POS_LEFT); gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); /* NPM: don't draw scale value since we're displaying in dB's */ gtk_widget_show(vscale); // TER: Create list of scale marking positions. scale_add_marks(GTK_SCALE(vscale), &dac_volume_scales[i], (i % channel_group_modulus) ? GTK_POS_RIGHT : GTK_POS_LEFT, TRUE); // Create a drawing area for the scale markings. sc_draw_area = gtk_drawing_area_new(); gtk_widget_show(sc_draw_area); // Connect size requests. g_signal_connect(G_OBJECT(sc_draw_area), "size-request", G_CALLBACK (scale_size_req_handler), (gpointer)&dac_volume_scales[i]); //gtk_widget_set_events(sc_draw_area, GDK_STRUCTURE_MASK); // Needed ? // Handle the expose event. g_signal_connect(G_OBJECT(sc_draw_area), "expose_event", G_CALLBACK (scale_expose_handler), (gpointer)&dac_volume_scales[i]); gtk_widget_set_events(sc_draw_area, GDK_EXPOSURE_MASK); // Needed? g_signal_connect(G_OBJECT(sc_draw_area), "button-press-event", G_CALLBACK (scale_btpress_handler), (gpointer)&dac_volume_scales[i]); gtk_widget_set_events(sc_draw_area, GDK_BUTTON_PRESS_MASK); // Now pack the drawing area into the box. if(i % channel_group_modulus) gtk_box_pack_end(GTK_BOX(sl_hbox), sc_draw_area, TRUE, TRUE, 0); else gtk_box_pack_start(GTK_BOX(sl_hbox), sc_draw_area, TRUE, TRUE, 0); //gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 0); // TER: Changed. if(i % channel_group_modulus) gtk_box_pack_start(GTK_BOX(sl_hbox), vscale, TRUE, TRUE, 0); else gtk_box_pack_end(GTK_BOX(sl_hbox), vscale, TRUE, TRUE, 0); gtk_scale_set_digits(GTK_SCALE(vscale), 0); g_signal_connect(GTK_OBJECT(adj), "value_changed", G_CALLBACK(dac_volume_adjust), (gpointer)(long)(i)); // TER: Let us handle the page up/down snapping. g_signal_connect(GTK_OBJECT(vscale), "change-value", G_CALLBACK(slider_change_value_handler), (gpointer)&dac_volume_scales[i]); av_dac_volume_label[i] = label = gtk_label_new("-63.5"); gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); if (i >= envy_dac_senses()) continue; group = NULL; for (j = 0; j < envy_dac_sense_items(); j++) { radiobutton = gtk_radio_button_new_with_label(group, envy_dac_sense_enum_name(j)); av_dac_sense_radio[i][j] = radiobutton; gtk_widget_show(radiobutton); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(dac_sense_toggled), (gpointer)(long)((i << 8) + j)); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, TRUE, 0); group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); } } /* create ADC */ for (i = 0; i < envy_adc_volumes(); i++) { char name[32]; sprintf(name, "ADC %d", i+1); /* NPM: for consistency w/ other panels, start w/ "ADC 1" */ frame = gtk_frame_new(name); gtk_widget_show(frame); //gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), 3); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); /* Add friendly labels for DMX 6Fires */ if(card_is_dmx6fire && (i < 6)){ label = gtk_label_new(dmx6fire_inputs[i]); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); } /* NPM: display peak levels on ADC's in "Analog Volume" panel */ if (i < MAX_INPUT_CHANNELS) { /* make sure within bounds of adc_peak_label[] */ adc_peak_label[i] = label = gtk_label_new("(Off)"); gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); } // TER: Create hbox for scale drawing area + slider. sl_hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(sl_hbox); gtk_box_pack_start(GTK_BOX(vbox), sl_hbox, TRUE, TRUE, 0); if(!get_alsa_control_range(&adc_volume_scales[i], &mn, &mx)) // TER { mn = 0; mx = 164; } //adj = gtk_adjustment_new(0, -(envy_adc_max()), 0, 1, ANALOG_GAIN_STEP_SIZE, 0); /* using step size of 12 gives -6dB step-size */ //adj = gtk_adjustment_new(0, mn, mx, 1, ANALOG_GAIN_STEP_SIZE, 0); // TER adj = gtk_adjustment_new(0, -mx, mn, 1, ANALOG_GAIN_STEP_SIZE, 0); // TER av_adc_volume_adj[i] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); /* NPM: above, set step size of 12 ==> -6dB step-size. Place dB-labelled markers at those locations */ // TER: Replaced with custom drawing. //draw_adc_scale_markings(GTK_SCALE(vscale), (i%channel_group_modulus) ? GTK_POS_RIGHT : GTK_POS_LEFT); gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); /* NPM: don't draw scale value since we're displaying in dB's */ gtk_widget_show(vscale); // TER: Create list of scale marking positions. scale_add_marks(GTK_SCALE(vscale), &adc_volume_scales[i], (i % channel_group_modulus) ? GTK_POS_RIGHT : GTK_POS_LEFT, TRUE); // Create a drawing area for the scale markings. sc_draw_area = gtk_drawing_area_new(); gtk_widget_show(sc_draw_area); // Handle size requests. g_signal_connect(G_OBJECT(sc_draw_area), "size-request", G_CALLBACK (scale_size_req_handler), (gpointer)&adc_volume_scales[i]); //gtk_widget_set_events(sc_draw_area, GDK_STRUCTURE_MASK); // Needed ? // Handle the expose event. g_signal_connect(G_OBJECT(sc_draw_area), "expose_event", G_CALLBACK (scale_expose_handler), (gpointer)&adc_volume_scales[i]); gtk_widget_set_events(sc_draw_area, GDK_EXPOSURE_MASK); g_signal_connect(G_OBJECT(sc_draw_area), "button-press-event", G_CALLBACK (scale_btpress_handler), (gpointer)&adc_volume_scales[i]); gtk_widget_set_events(sc_draw_area, GDK_BUTTON_PRESS_MASK); // Now pack the drawing area into the box. if(i % channel_group_modulus) gtk_box_pack_end(GTK_BOX(sl_hbox), sc_draw_area, TRUE, TRUE, 0); else gtk_box_pack_start(GTK_BOX(sl_hbox), sc_draw_area, TRUE, TRUE, 0); //gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 0); // TER: Changed if(i % channel_group_modulus) gtk_box_pack_start(GTK_BOX(sl_hbox), vscale, TRUE, TRUE, 0); else gtk_box_pack_end(GTK_BOX(sl_hbox), vscale, TRUE, TRUE, 0); gtk_scale_set_digits(GTK_SCALE(vscale), 0); g_signal_connect(GTK_OBJECT(adj), "value_changed", G_CALLBACK(adc_volume_adjust), (gpointer)(long)(i)); // TER: Let us handle the page up/down snapping. g_signal_connect(GTK_OBJECT(vscale), "change-value", G_CALLBACK(slider_change_value_handler), (gpointer)&adc_volume_scales[i]); /* NPM */ av_adc_volume_label[i] = label = gtk_label_new("-63.5"); gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); if (i >= envy_adc_senses()) continue; group = NULL; for (j = 0; j < envy_adc_sense_items(); j++) { radiobutton = gtk_radio_button_new_with_label(group, envy_adc_sense_enum_name(j)); av_adc_sense_radio[i][j] = radiobutton; gtk_widget_show(radiobutton); g_signal_connect(GTK_OBJECT(radiobutton), "toggled", G_CALLBACK(adc_sense_toggled), (gpointer)(long)((i << 8) + j)); gtk_box_pack_start(GTK_BOX(vbox), radiobutton, FALSE, TRUE, 0); group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton)); } } /* create IPGA */ for (i = 0; i < envy_ipga_volumes(); i++) { char name[32]; sprintf(name, "IPGA %d", i); frame = gtk_frame_new(name); gtk_widget_show(frame); //gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), 3); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); /* Add friendly labels for DMX 6Fires */ if(card_is_dmx6fire && (i < 6)){ label = gtk_label_new(dmx6fire_inputs[i]); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3); } // TODO: Added ipga scale code needs TESTING on old ALSA around pre- 1.0.15. TER. // Create hbox for scale drawing area + slider. sl_hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(sl_hbox); //gtk_container_add(GTK_CONTAINER(viewport), sl_hbox); gtk_box_pack_start(GTK_BOX(vbox), sl_hbox, TRUE, TRUE, 0); if(!get_alsa_control_range(&ipga_volume_scales[i], &mn, &mx)) // TER { mn = 0; mx = 36; } //adj = gtk_adjustment_new(0, -36, 0, 1, 6, 0); //adj = gtk_adjustment_new(0, mn, mx, 1, 6, 0); // TER adj = gtk_adjustment_new(0, -mx, mn, 1, 6, 0); // TER av_ipga_volume_adj[i] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); gtk_widget_show(vscale); // TER: Create list of scale marking positions. scale_add_marks(GTK_SCALE(vscale), &ipga_volume_scales[i], (i % channel_group_modulus) ? GTK_POS_RIGHT : GTK_POS_LEFT, TRUE); // Create a drawing area for the scale markings. sc_draw_area = gtk_drawing_area_new(); gtk_widget_show(sc_draw_area); // Handle size requests. g_signal_connect(G_OBJECT(sc_draw_area), "size-request", G_CALLBACK (scale_size_req_handler), (gpointer)&ipga_volume_scales[i]); //gtk_widget_set_events(sc_draw_area, GDK_STRUCTURE_MASK); // Needed ? // Handle the expose event. g_signal_connect(G_OBJECT(sc_draw_area), "expose_event", G_CALLBACK (scale_expose_handler), (gpointer)&ipga_volume_scales[i]); gtk_widget_set_events(sc_draw_area, GDK_EXPOSURE_MASK); g_signal_connect(G_OBJECT(sc_draw_area), "button-press-event", G_CALLBACK (scale_btpress_handler), (gpointer)&ipga_volume_scales[i]); gtk_widget_set_events(sc_draw_area, GDK_BUTTON_PRESS_MASK); // Now pack the drawing area into the box. if(i % channel_group_modulus) gtk_box_pack_end(GTK_BOX(sl_hbox), sc_draw_area, TRUE, TRUE, 0); else gtk_box_pack_start(GTK_BOX(sl_hbox), sc_draw_area, TRUE, TRUE, 0); //gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 3); // TER: Changed if(i % channel_group_modulus) gtk_box_pack_start(GTK_BOX(sl_hbox), vscale, TRUE, TRUE, 0); else gtk_box_pack_end(GTK_BOX(sl_hbox), vscale, TRUE, TRUE, 0); gtk_scale_set_digits(GTK_SCALE(vscale), 0); g_signal_connect(GTK_OBJECT(adj), "value_changed", G_CALLBACK(ipga_volume_adjust), (gpointer)(long)(i)); // TER: Let us handle the page up/down snapping. g_signal_connect(GTK_OBJECT(vscale), "change-value", G_CALLBACK(slider_change_value_handler), (gpointer)&ipga_volume_scales[i]); av_ipga_volume_label[i] = label = gtk_label_new("-63.5"); gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3); } } int index_active_profile() { gint index; gboolean found; found = FALSE; for (index = 0; index < MAX_PROFILES; index++) { if (active_button == profiles_toggle_buttons[index].toggle_button) { found = TRUE; break; } } if (found) return index; return NOTFOUND; } int delete_card_number(GtkWidget *delete_button) { gint res; gint card_nr; gint index; if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (delete_button))) return EXIT_SUCCESS; // TER: Changed. Value property is new since 2.4 //card_nr = GTK_ADJUSTMENT (card_number_adj)->value; card_nr = gtk_adjustment_get_value(GTK_ADJUSTMENT(card_number_adj)); if ((card_nr < 0) || (card_nr >= MAX_CARD_NUMBERS)) { fprintf(stderr, "card number not in [0 ... %d]\n", MAX_CARD_NUMBERS - 1); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (delete_button), FALSE); return -EINVAL; } res = delete_card(card_number, profiles_file_name); if (res < 0) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (delete_button), FALSE); return res; } if (card_nr == card_number) { for (index = 0; index < MAX_PROFILES; index++) { gtk_entry_set_text(GTK_ENTRY (profiles_toggle_buttons[index].entry), get_profile_name(index + 1, card_number, profiles_file_name)); } } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (delete_button), FALSE); return EXIT_SUCCESS; } int restore_active_profile(const gint profile_number) { gint res; res = save_restore(ALSACTL_OP_RESTORE, profile_number, card_number, profiles_file_name, NULL); return res; } int save_active_profile(GtkWidget *save_button) { gint res; gint index; if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (save_button))) return EXIT_SUCCESS; if ((index = index_active_profile()) >= 0) { res = save_restore(ALSACTL_OP_STORE, index + 1, card_number, profiles_file_name, \ gtk_entry_get_text(GTK_ENTRY (profiles_toggle_buttons[index].entry))); } else { fprintf(stderr, "No active profile found.\n"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (save_button), FALSE); return -EXIT_FAILURE; } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (save_button), FALSE); return res; } void entry_toggle_editable(GtkWidget *toggle_button, GtkWidget *entry) { gint index; gint profile_number; if (active_button == toggle_button) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (toggle_button), TRUE); gtk_editable_set_editable(GTK_EDITABLE (entry), TRUE); gtk_widget_grab_focus(entry); return; } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle_button))) { active_button = toggle_button; } gtk_editable_set_editable(GTK_EDITABLE (entry), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle_button))); if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle_button))) { gtk_widget_grab_focus(entry); profile_number = NOTFOUND; for (index = 0; index < MAX_PROFILES; index++) { if (profiles_toggle_buttons[index].toggle_button != toggle_button) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (profiles_toggle_buttons[index].toggle_button), FALSE); } else { profile_number = index + 1; } } if (profile_number >= 0) restore_active_profile(profile_number); } } void enter_callback( const GtkWidget *widget, const GtkWidget *entry ) { const gchar *entry_text; entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); printf("Inhalt : %s\n", entry_text); } static GtkWidget *toggle_button_entry(const GtkWidget *parent, const gchar *profile_name, const gint index) { GtkWidget *box; GtkWidget *entry_label; GtkWidget *toggle_button; box = gtk_hbox_new(FALSE, 0); toggle_button = gtk_toggle_button_new(); gtk_container_set_border_width(GTK_CONTAINER(toggle_button), 3); profiles_toggle_buttons[index].entry = entry_label = gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY (entry_label), MAX_PROFILE_NAME_LENGTH); gtk_entry_set_text(GTK_ENTRY (entry_label), profile_name); /* only the active profile can be modified */ gtk_editable_set_editable(GTK_EDITABLE (entry_label), FALSE); g_signal_connect(GTK_OBJECT (entry_label), "activate", G_CALLBACK (enter_callback), (gpointer) entry_label); g_signal_connect(GTK_OBJECT (toggle_button), "toggled", G_CALLBACK (entry_toggle_editable), (gpointer) entry_label); gtk_box_pack_start(GTK_BOX (box), entry_label, FALSE, FALSE, 20); gtk_widget_show(entry_label); gtk_widget_show(box); gtk_container_add(GTK_CONTAINER (toggle_button), box); gtk_widget_show(toggle_button); return (toggle_button); } static void create_profiles(GtkWidget *main, GtkWidget *notebook, int page) { GtkWidget *label; GtkWidget *label_card_nr; GtkWidget *vbox1; GtkWidget *vbox2; GtkWidget *hbox; GtkWidget *hbox1; GtkWidget *save_button; GtkWidget *delete_button; GtkObject *card_button_adj; GtkWidget *card_button; GtkWidget *scrolledwindow; GtkWidget *viewport; gint index; gint profile_number; gchar *profile_name; gint max_profiles; gint max_digits; hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(notebook), hbox); label = gtk_label_new("Profiles"); gtk_widget_show(label); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page), label); /* build scrolling area */ scrolledwindow = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scrolledwindow); gtk_box_pack_start(GTK_BOX(hbox), scrolledwindow, TRUE, TRUE, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrolledwindow), viewport); hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(viewport), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), 0); /* Create button boxes */ vbox1 = gtk_vbutton_box_new(); gtk_vbutton_box_set_spacing_default(0); for (index = 0; index < MAX_PROFILES; index++) { profile_name = get_profile_name(index + 1, card_number, profiles_file_name); profiles_toggle_buttons[index].toggle_button = toggle_button_entry(window, profile_name, index); gtk_box_pack_start(GTK_BOX (vbox1), profiles_toggle_buttons[index].toggle_button, FALSE, FALSE, 0); } gtk_widget_show(vbox1); gtk_container_set_border_width(GTK_CONTAINER(vbox1), 6); vbox2 = gtk_vbutton_box_new(); gtk_widget_show(vbox2); gtk_container_set_border_width(GTK_CONTAINER(vbox2), 50); hbox1 = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox1); gtk_box_pack_start(GTK_BOX(vbox2), hbox1, FALSE, FALSE, 20); label_card_nr = gtk_label_new("Card Number:"); gtk_widget_show(label_card_nr); gtk_box_pack_start(GTK_BOX(hbox1), label_card_nr, FALSE, FALSE, 20); gtk_label_set_justify(GTK_LABEL(label_card_nr), GTK_JUSTIFY_LEFT); card_button_adj = gtk_adjustment_new(16, 0, MAX_CARD_NUMBERS - 1, 1, 10, 0); /* NPM: set last parm to 0 to get rid of 'Gtk-WARNING **: GtkSpinButton: setting an > adjustment with non-zero page size is deprecated' -- change suggested by James Morris on linux-audio-dev */ card_number_adj = card_button_adj; card_button = gtk_spin_button_new(GTK_ADJUSTMENT (card_button_adj), 1, 0); gtk_widget_show(card_button); gtk_box_pack_start(GTK_BOX (hbox1), card_button, TRUE, FALSE, 0); gtk_spin_button_set_numeric(GTK_SPIN_BUTTON (card_button), TRUE); gtk_adjustment_set_value(GTK_ADJUSTMENT (card_button_adj), card_number); delete_button = gtk_toggle_button_new_with_label("Delete card from profiles"); gtk_widget_show(delete_button); gtk_box_pack_start(GTK_BOX (vbox2), delete_button, FALSE, FALSE, 20); g_signal_connect(GTK_OBJECT (delete_button), "toggled", G_CALLBACK (delete_card_number), NULL); save_button = gtk_toggle_button_new_with_label("Save active profile"); gtk_widget_show(save_button); gtk_box_pack_end(GTK_BOX (vbox2), save_button, FALSE, FALSE, 20); g_signal_connect(GTK_OBJECT (save_button), "toggled", G_CALLBACK (save_active_profile), NULL); gtk_container_add(GTK_CONTAINER(hbox), vbox1); gtk_container_add(GTK_CONTAINER(hbox), vbox2); if (default_profile != NULL) { /* * only if default_profile is numerical and lower or equal than MAX_PROFILES it will be a profile_number * otherwise it will be a profile name */ if (strcspn(default_profile, "0123456789") == 0) { for (max_profiles = MAX_PROFILES, max_digits = 0; max_profiles > 9; max_digits++, max_profiles /= 10) ; max_digits++; if (strlen(default_profile) <= max_digits) { profile_number = atoi(default_profile); if (profile_number < 1 || profile_number > MAX_PROFILES) profile_number = get_profile_number(default_profile, card_number, profiles_file_name); } else { profile_number = get_profile_number(default_profile, card_number, profiles_file_name); } } else { profile_number = get_profile_number(default_profile, card_number, profiles_file_name); } if ((profile_number > 0) && (profile_number <= MAX_PROFILES)) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (profiles_toggle_buttons[profile_number - 1].toggle_button), TRUE); } else { fprintf(stderr, "Cannot find profile '%s' for card '%d'.\n", default_profile, card_number); } } } static void create_outer(GtkWidget *main) { GtkWidget *hbox1; GtkWidget *vbox; GtkWidget *label; GtkWidget *frame; GtkWidget *drawing; /* Create digital mixer frame */ vbox = gtk_vbox_new(FALSE, 1); gtk_widget_show(vbox); gtk_box_pack_start(GTK_BOX(main), vbox, FALSE, FALSE, 0); label = gtk_label_new("(Rt Menu) >>"); //gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 3); frame = gtk_frame_new("Digital Mixer"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), 1); /* Create controls in the digital mixer frame */ vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); hbox1 = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox1); gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 6); drawing = gtk_drawing_area_new(); mixer_mix_drawing = drawing; gtk_widget_set_name(drawing, "DigitalMixer"); gtk_box_pack_start(GTK_BOX(hbox1), drawing, TRUE, FALSE, 3); if (tall_equal_mixer_ht > 1 ) { gtk_widget_set_usize(drawing, 60, 264 + 60 * (tall_equal_mixer_ht - 1)); } else { gtk_widget_set_usize(drawing, 60, 264); } g_signal_connect(GTK_OBJECT(drawing), "expose_event", G_CALLBACK(level_meters_expose_event), NULL); g_signal_connect(GTK_OBJECT(drawing), "configure_event", G_CALLBACK(level_meters_configure_event), NULL); gtk_widget_set_events(drawing, GDK_EXPOSURE_MASK); gtk_widget_show(drawing); hbox1 = gtk_hbox_new(TRUE, 0); gtk_widget_show(hbox1); gtk_box_pack_start(GTK_BOX(vbox), hbox1, TRUE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox1), 6); peak_label[IDX_LMIX] = label = gtk_label_new("(Off)"); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, TRUE, 0); peak_label[IDX_RMIX] = label = gtk_label_new("(Off)"); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, TRUE, 0); mixer_clear_peaks_button = gtk_button_new_with_label("Reset Peaks"); gtk_widget_show(mixer_clear_peaks_button); gtk_box_pack_start(GTK_BOX(vbox), mixer_clear_peaks_button, TRUE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(mixer_clear_peaks_button), 4); g_signal_connect(GTK_OBJECT(mixer_clear_peaks_button), "clicked", G_CALLBACK(level_meters_reset_peaks), NULL); }/* End create_outer */ static void create_blank(GtkWidget *main, GtkWidget *notebook, int page) { /* This is a little workaround for a problem with the pop-up menu. For some reason the label of the last page is not accessed by the menu so all it shows is 'page 7'. Here a blank extra page is created, unseen, which seems to satisfy gtk, and we see the menu last page label correct. AH 12.7.2005 */ GtkWidget *label; GtkWidget *hbox; hbox = gtk_hbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(notebook), hbox); label = gtk_label_new("Blank"); gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page), label); } static void usage(void) { fprintf(stderr, "usage: mudita24 [-c card#] [-D control-name] [-o num-outputs] [-i num-inputs] [-p num-pcm-outputs] [-s num-spdif-in/outs] [-v] [-f profiles-file] [profile name|profile id] [-m channel-num] [-w initial-window-width] [-t height-num] [-n]\n"); fprintf(stderr, "\t-c, --card\tAlsa card number to control\n"); fprintf(stderr, "\t-D, --device\tcontrol-name\n"); fprintf(stderr, "\t-o, --outputs\tLimit number of analog line outputs to display\n"); fprintf(stderr, "\t-i, --input\tLimit number of analog line inputs to display\n"); fprintf(stderr, "\t-p, --pcm_output\tLimit number of PCM outputs to display\n"); fprintf(stderr, "\t-s, --spdif\tLimit number of spdif inputs/outputs to display\n"); fprintf(stderr, "\t-v, --view_spdif_playback\tshows the spdif playback channels in the mixer\n"); fprintf(stderr, "\t-f, --profiles_file\tuse file as profiles file\n"); fprintf(stderr, "\t-m, --midichannel\tmidi channel number for controller control\n"); fprintf(stderr, "\t-M, --midienhanced\tUse an enhanced mapping from midi controller to db slider\n"); fprintf(stderr, "\t-w, --window_width\tSet initial window width (try 2,6 or 8; 280,626, or 968)\n"); fprintf(stderr, "\t-t, --tall_eq_mixer_heights\tSet taller height mixer displays (1-9)\n"); fprintf(stderr, "\t-n, --no_scale_mark\tDisable scale marks, which may be incorrect on certain cards (?),\n\t\t or whose Gtk-detent at the mark position may be annoying\n"); fprintf(stderr, "\n\tThe program 'alsactl' is automatically found and used.\n\tEnvironment variable ALSACTL_PROG overrides its location.\n"); } /* NPM for efficiency&power-savings, replaced multiple 40ms&100ms timeouts for each of the callbacks contained here, with a single 100ms one which calls gtk_timeout_add(100, (GtkFunction)envy24control_poll, ...) */ void envy24control_poll() { level_meters_timeout_callback(NULL); master_clock_status_timeout_callback(NULL); internal_clock_status_timeout_callback(NULL); rate_locking_status_timeout_callback(NULL); rate_reset_status_timeout_callback(NULL); if (card_has_delta_iec958_input_status) iec958_input_status_timeout_callback(NULL); /* NPM */ } int main(int argc, char **argv) { GtkWidget *notebook; GtkWidget *outerbox; char *name, tmpname[8], title[128]; int i, c, err; snd_ctl_card_info_t *hw_info; snd_ctl_elem_value_t *val; int npfds; struct pollfd *pfds; int midi_fd = -1, midi_channel = -1, midi_enhanced = 0; int page; int input_channels_set = 0; int output_channels_set = 0; int pcm_output_channels_set = 0; int width_val; int wwidth_set =FALSE; int wwidth = 796; const int chanwidth = 86; const int fixwidth = 108; static struct option long_options[] = { {"device", 1, 0, 'D'}, {"card", 1, 0, 'c'}, {"profiles_file", 1, 0, 'f'}, {"inputs", 1, 0, 'i'}, {"midichannel", 1, 0, 'm'}, {"midienhanced", 0, 0, 'M'}, {"outputs", 1, 0, 'o'}, {"pcm_outputs", 1, 0, 'p'}, {"spdif", 1, 0, 's'}, {"window_width", 1, 0, 'w'}, {"view_spdif_playback", 0, 0, 'v'}, {"tall_eq_mixer_heights", 1, 0, 't'}, {"no_scale_mark", 1, 0, 'n'}, /* NPM: add option to disable scale marks, which may be incorrect on certain cards (?), or whose Gtk-detent behavior annoys users */ {"channel_group_modulus", 1, 0, 'g'}, /* NPM: add optional count to control grouping behavior of labels */ {"bg_color", 1, 0, 'b'}, /* NPM: add optional 'bg_color' for peak level metering */ {"lights_color", 1, 0, 'l'}, /* NPM: add optional 'lights_color' for peak level metering */ { NULL } }; snd_ctl_card_info_alloca(&hw_info); snd_ctl_elem_value_alloca(&val); /* Go through gtk initialization */ gtk_init(&argc, &argv); name = NULL; /* probe */ card_number = 0; input_channels = MAX_INPUT_CHANNELS; output_channels = MAX_OUTPUT_CHANNELS; pcm_output_channels = MAX_PCM_OUTPUT_CHANNELS; spdif_channels = MAX_SPDIF_CHANNELS; view_spdif_playback = 0; profiles_file_name = DEFAULT_PROFILERC; default_profile = NULL; clear_all_scale_marks(TRUE); // TER while ((c = getopt_long(argc, argv, "D:c:f:i:m:Mo:p:s:w:vt:ng:b:l:", long_options, NULL)) != -1) { switch (c) { case 'D': /* * NPM: use ALSA code to fix/validate * https://bugzilla.redhat.com/show_bug.cgi?id=602900 * The old code assumed ':' present, e.g. "-Dhw:66", (w/ * .asoundrc "ctl.66 {type hw, card M66}") and would * coredump if given "-D66". Here, by not worrying about * "hw:" and passing "optarg" as-is via 'name' to * snd_ctl_open() below, resolves the issue, giving similar * behavior as 'amixer'. However, letting snd_ctl_open() * validate gives less helpful error messages on * failure: "envy24control -Dhw:foo" * ==> "snd_ctl_open: No such device" * So validate the arg as ALSA CTL, providing * a more specific error message and fail first if not valid. */ name = optarg; /* NPM: e.g. pass "-D66" unaltered to snd_ctl_open() and let it validate valid ALSA CTL name */ if (index(optarg, ':')) { /* NPM: handle e.g. optarg == "hw:M66" */ card_number = snd_card_get_index(strchr(optarg, ':') + sizeof(char)); if (card_number < 0) { fprintf(stderr, "mudita24: invalid ALSA audio device, invalid index or name for card: %s\n", optarg); exit(1); } } break; case 'c': /* * NPM: use snd_card_get_index() to fix/validate * https://bugzilla.redhat.com/show_bug.cgi?id=602900 * nb:"The accepted format is an integer value in ASCII * representation or the card identifier (the id parameter * for sound-card drivers). The control device name like * /dev/snd/controlC0 is accepted, too." */ card_number = snd_card_get_index(optarg); if (card_number < 0) { /* NPM: code orig from alsa-utils/alsamixer/cli.c */ fprintf(stderr, "mudita24: invalid ALSA index or name for audio card: %s\n", optarg); exit(1); } sprintf(tmpname, "hw:%d", card_number); /* e.g. "hw:M66" for arg "-cM66" passed to snd_ctl_open() below */ name = tmpname; break; case 'f': profiles_file_name = optarg; break; case 'i': input_channels = atoi(optarg); if (input_channels < 0 || input_channels > MAX_INPUT_CHANNELS) { fprintf(stderr, "mudita24: must have 0-%i inputs\n", MAX_INPUT_CHANNELS); exit(1); } input_channels_set = 1; break; case 'm': midi_channel = atoi(optarg); if (midi_channel < 1 || midi_channel > 16) { fprintf(stderr, "mudita24: invalid midi channel number %i\n", midi_channel); exit(1); } --midi_channel; break; case 'M': midi_enhanced = 1; break; case 'o': output_channels = atoi(optarg); if (output_channels < 0 || output_channels > MAX_OUTPUT_CHANNELS) { fprintf(stderr, "mudita24: must have 0-%i outputs\n", MAX_OUTPUT_CHANNELS); exit(1); } output_channels_set = 1; break; case 'p': pcm_output_channels = atoi(optarg); if (pcm_output_channels < 0 || pcm_output_channels > MAX_PCM_OUTPUT_CHANNELS) { fprintf(stderr, "mudita24: must have 0-%i pcm outputs\n", MAX_PCM_OUTPUT_CHANNELS); exit(1); } pcm_output_channels_set = 1; break; case 's': spdif_channels = atoi(optarg); if (spdif_channels < 0 || spdif_channels > MAX_SPDIF_CHANNELS) { fprintf(stderr, "mudita24: must have 0-%i spdifs\n", MAX_SPDIF_CHANNELS); exit(1); } break; case 'w': width_val = atoi(optarg); if ((width_val >= 1) && (width_val <= 20)) { wwidth = (width_val * chanwidth + fixwidth); } else { wwidth = width_val; } wwidth_set = TRUE; break; case 'v': view_spdif_playback = 1; break; case 't': tall_equal_mixer_ht = atoi(optarg); if ((tall_equal_mixer_ht < 0) || (tall_equal_mixer_ht >= 10)) tall_equal_mixer_ht = 0; break; case 'n': /* NPM */ no_scale_marks = TRUE; break; case 'g': /* NPM */ channel_group_modulus = atoi(optarg); if (channel_group_modulus <= 0) channel_group_modulus = 1; if (channel_group_modulus >= MAX_INPUT_CHANNELS) channel_group_modulus = MAX_INPUT_CHANNELS; break; case 'b': /* NPM */ meter_bg = (GdkColor *)g_malloc(sizeof(GdkColor)); /* free()'d on exit() */ if (!gdk_color_parse(optarg, meter_bg)) { fprintf(stderr, "mudita24: unable to parse arguments --bg_color '%s'\n", optarg); exit(1); } break; case 'l': /* NPM */ meter_fg = (GdkColor *)g_malloc(sizeof(GdkColor)); /* free()'d on exit() */ if (!gdk_color_parse(optarg, meter_fg)) { fprintf(stderr, "mudita24: unable to parse arguments --lights_color '%s'\n", optarg); exit(1); } break; default: usage(); exit(1); break; } } if (optind < argc) { default_profile = argv[optind]; } if (! name) { /* probe cards */ static char cardname[8]; /* FIXME: hardcoded max number of cards */ for (card_number = 0; card_number < 8; card_number++) { sprintf(cardname, "hw:%d", card_number); if (snd_ctl_open(&ctl, cardname, 0) < 0) continue; if (snd_ctl_card_info(ctl, hw_info) < 0 || strcmp(snd_ctl_card_info_get_driver(hw_info), "ICE1712")) { snd_ctl_close(ctl); continue; } /* found */ name = cardname; break; } if (! name) { fprintf(stderr, "No ICE1712 cards found\n"); exit(EXIT_FAILURE); } } else { if ((err = snd_ctl_open(&ctl, name, 0)) < 0) { fprintf(stderr, "snd_ctl_open: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if ((err = snd_ctl_card_info(ctl, hw_info)) < 0) { fprintf(stderr, "snd_ctl_card_info: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if (strcmp(snd_ctl_card_info_get_driver(hw_info), "ICE1712")) { fprintf(stderr, "invalid card type (driver is %s)\n", snd_ctl_card_info_get_driver(hw_info)); exit(EXIT_FAILURE); } } snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_CARD); snd_ctl_elem_value_set_name(val, "ICE1712 EEPROM"); if ((err = snd_ctl_elem_read(ctl, val)) < 0) { fprintf(stderr, "Unable to read EEPROM contents: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } memcpy(&card_eeprom, snd_ctl_elem_value_get_bytes(val), 32); if(card_eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE) card_is_dmx6fire = TRUE; /* NPM: determine if "Delta IEC958 Input Status" available excluding ICE1712_SUBDEVICE_DELTA44 since it has no IEC958 in */ if ((card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 || card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010LT || card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTA66 || card_eeprom.subvendor == ICE1712_SUBDEVICE_AUDIOPHILE || card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTADIO2496 || card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTA410)) card_has_delta_iec958_input_status = TRUE; /* Set a better default for input_channels and output_channels */ if(!input_channels_set) if(card_is_dmx6fire) input_channels = 6; if(!output_channels_set) if(card_is_dmx6fire) output_channels = 6; if(!pcm_output_channels_set) if(card_is_dmx6fire) pcm_output_channels = 6; /* PCMs 7&8 can be used -set using option -p8 */ if (!wwidth_set) if (card_is_dmx6fire) wwidth = 626; /* Initialize code */ config_open(); level_meters_init(); mixer_init(); patchbay_init(); hardware_init(); analog_volume_init(); if (midi_channel >= 0) midi_fd = midi_init(argv[0], midi_channel, midi_enhanced); g_timeout_add(100, (GSourceFunc)envy24control_poll, NULL); /* NPM for efficiency&power-savings, replaced multiple 40ms&100ms timeouts with this single one */ fprintf(stderr, "using\t --- input_channels: %i\n\t --- output_channels: %i\n\t --- pcm_output_channels: %i\n\t --- spdif in/out channels: %i\n", \ input_channels, output_channels, pcm_output_channels, spdif_channels); /* Make the title */ sprintf(title, "Envy24 Control Utility %s (%s)", VERSION, snd_ctl_card_info_get_longname(hw_info)); /* Create the main window */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), title); g_signal_connect(GTK_OBJECT (window), "delete_event", G_CALLBACK( gtk_main_quit), NULL); signal(SIGINT, (void *)gtk_main_quit); gtk_window_set_default_size(GTK_WINDOW(window), wwidth, 300); outerbox = gtk_hbox_new(FALSE, 3); gtk_widget_show(outerbox); gtk_container_add(GTK_CONTAINER(window), outerbox); create_outer(outerbox); /* Create the notebook */ notebook = gtk_notebook_new(); gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE); gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook)); gtk_widget_show(notebook); gtk_container_add(GTK_CONTAINER(outerbox), notebook); page = 0; create_inputs_mixer(outerbox, notebook, page++); create_pcms_mixer(outerbox, notebook, page++); create_router(outerbox, notebook, page++); create_hardware(outerbox, notebook, page++); if (envy_analog_volume_available()) create_analog_volume(outerbox, notebook, page++); create_profiles(outerbox, notebook, page++); create_about(outerbox, notebook, page++); create_blank(outerbox, notebook, page++); npfds = snd_ctl_poll_descriptors_count(ctl); if (npfds > 0) { pfds = alloca(sizeof(*pfds) * npfds); npfds = snd_ctl_poll_descriptors(ctl, pfds, npfds); for (i = 0; i < npfds; i++) gdk_input_add(pfds[i].fd, GDK_INPUT_READ, control_input_callback, ctl); snd_ctl_subscribe_events(ctl, 1); } if (midi_fd >= 0) { gdk_input_add(midi_fd, GDK_INPUT_READ, midi_process, NULL); } gtk_widget_show(window); level_meters_postinit(); mixer_postinit(); patchbay_postinit(); hardware_postinit(); analog_volume_postinit(); gtk_main(); snd_ctl_close(ctl); midi_close(); config_close(); clear_all_scale_marks(FALSE); // TER return EXIT_SUCCESS; } mudita24-1.0.3+svn13/midi.h0000644000175000017500000000047011434666733015103 0ustar alessioalessio#ifndef MIDI__H #define MIDI__H #include int midi_init(char *appname, int channel, int midi_enhanced); int midi_close(); void midi_maxstreams(int); int midi_controller(int c, int v); void midi_process(gpointer data, gint source, GdkInputCondition condition); int midi_button(int b, int v); #endif mudita24-1.0.3+svn13/COPYING0000644000175000017500000004313111434666733015044 0ustar alessioalessio GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. mudita24-1.0.3+svn13/profiles.h0000644000175000017500000000412311637240346015774 0ustar alessioalessio#ifndef __PROFILES_H__ #define __PROFILES_H__ #include #include #include #include #include #include #include #include #include "globaldefs.h" #ifndef PROGRAM_NAME #define PROGRAM_NAME "mudita24" #endif #ifndef MAX_PROFILES #define MAX_PROFILES 4 #endif #ifndef MAX_PROFILE_NAME_LENGTH #define MAX_PROFILE_NAME_LENGTH 20 #endif #ifndef DEFAULT_PROFILERC #define DEFAULT_PROFILERC "~/"PROGRAM_NAME"/profiles.conf" #endif #ifndef SYS_PROFILERC #define SYS_PROFILERC "/etc/"PROGRAM_NAME"/profiles.conf" #endif #define PROFILE_NAME_FIELD_LENGTH MAX_PROFILE_NAME_LENGTH + 1 #define PROFILE_HEADER_TEMPL "[ Profile # ]" #define CARD_HEADER_TEMPL "< Card # >" #define CARD_FOOTER_TEMPL "< /CARD # >" #define PROFILE_NAME_TEMPL "{ /$/ }" #define PLACE_HOLDER_NUM '#' #define PLACE_HOLDER_STR '$' /* max 32k for every profile */ #define MAX_PROFILE_SIZE 32768 #define MAX_SEARCH_FIELD_LENGTH 1024 #define MAX_FILE_NAME_LENGTH 1024 #define MAX_NUM_STR_LENGTH 10 #define TOKEN_SEP "|" #define SEP_CHAR ' ' #ifndef NOTFOUND #define NOTFOUND -1 #endif #define ALSACTL_OP_STORE "store" #define ALSACTL_OP_RESTORE "restore" #define DIR_CREA_MODE "0755" // this must be a string #define FILE_CREA_MODE 0644 // this must be a octal number /* max count of parameters for new_process * !first parameter will be the name of the external programm * - last parameter will be NULL */ #define MAX_PARAM 10 /* the place from mkdir */ #ifndef MKDIR #define MKDIR "/bin/mkdir" #endif /* the place from alsactl */ #ifndef ALSACTL #define ALSACTL "/usr/sbin/alsactl" #endif #ifndef __PROFILES_C__ extern int save_restore(const char * const operation, const int profile_number, const int card_number, char * cfgfile, const char * const profile_name); extern char *get_profile_name(const int profile_number, const int card_number, char * cfgfile); extern int get_profile_number(const char * const profile_name, const int card_number, char * cfgfile); extern int delete_card(const int card_number, char * const cfgfile); #endif #endif /* __PROFILES_H__ */ mudita24-1.0.3+svn13/envy24control.h0000644000175000017500000003401411647357353016713 0ustar alessioalessio#include #include #include #include #include #include #include #include #include "globaldefs.h" /* Profiles */ #ifdef PACKAGE #define PROGRAM_NAME PACKAGE #else #define PROGRAM_NAME "envy24control" #endif #define MAX_PROFILES 8 #define MAX_PROFILE_NAME_LENGTH 20 #define DEFAULT_PROFILERC "~/.envy24control/profiles.conf" /* NPM: use hidden directory for profiles */ #define SYS_PROFILERC "/etc/envy24control/profiles.conf" #ifndef MKDIR #define MKDIR "/bin/mkdir" #endif #ifndef ALSACTL #define ALSACTL "/usr/sbin/alsactl" #endif #include "profiles.h" /* MidiMan */ #define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 #define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6 #define ICE1712_SUBDEVICE_DELTA66 0x121432d6 #define ICE1712_SUBDEVICE_DELTA44 0x121433d6 #define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6 #define ICE1712_SUBDEVICE_DELTA410 0x121438d6 #define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6 /* Terratec */ #define ICE1712_SUBDEVICE_EWX2496 0x3b153011 #define ICE1712_SUBDEVICE_EWS88MT 0x3b151511 #define ICE1712_SUBDEVICE_EWS88D 0x3b152b11 #define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811 /* Hoontech */ #define ICE1712_SUBDEVICE_STDSP24 0x12141217 /* Hoontech SoundTrack Audio DSP 24 */ /* max number of cards for alsa */ #define MAX_CARD_NUMBERS 8 /* max number of HW input/output channels (analog lines) * the number of available HW input/output channels is defined * at 'adcs/dacs' in the driver */ /* max number of HW input channels (analog lines) */ #define MAX_INPUT_CHANNELS 8 /* max number of HW output channels (analog lines) */ #define MAX_OUTPUT_CHANNELS 8 /* max number of spdif input/output channels */ #define MAX_SPDIF_CHANNELS 2 /* max number of PCM output channels */ #define MAX_PCM_OUTPUT_CHANNELS 8 /* NPM: the digital mixer max-attenuation in dB, per snd ice1712 architecture diagram http://nielsmayer.com/npm/envy24mixer-architecture.png taken from http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf */ /* NPM: the control values for the digital mixer are 0-96 and not the dB attenuation values of MAX_MIXER_ATTENUATION_DB... The following values comprise all the signals mixed in the ice1712's digital mixer: MULTI_PLAYBACK_VOLUME, HW_MULTI_CAPTURE_VOLUME, IEC958_MULTI_CAPTURE_VOLUME: > amixer -c M66 cget iface=MIXER,name='Multi Playback Volume' numid=11,iface=MIXER,name='Multi Playback Volume' ; type=INTEGER,access=rw---R--,values=2,min=0,max=96,step=0 : values=78,67 | dBscale-min=-144.00dB,step=1.50dB,mute=0 > amixer -c M66 cget iface=MIXER,name="H/W Multi Capture Volume" numid=27,iface=MIXER,name='H/W Multi Capture Volume' ; type=INTEGER,access=rw---R--,values=2,min=0,max=96,step=0 : values=0,0 | dBscale-min=-144.00dB,step=1.50dB,mute=0 > amixer -c M66 cget iface=MIXER,name="IEC958 Multi Capture Volume" numid=31,iface=MIXER,name='IEC958 Multi Capture Volume' ; type=INTEGER,access=rw------,values=2,min=0,max=96,step=0 : values=16,0 */ #define MAX_MIXER_ATTENUATION_VALUE 96 /* for -144dB */ #define LOW_MIXER_ATTENUATION_VALUE 33 /* for -49.5dB nb: 64-->-48dB where 96-64=32 */ #define MIN_MIXER_ATTENUATION_VALUE 0 /* for 0dB */ /* * NPM: DAC and ADC constants */ #define MIN_ADC_GAIN -63 #define MAX_ADC_GAIN 18 #define MIN_DAC_GAIN -63 #define MAX_DAC_GAIN 0 #define ANALOG_GAIN_STEP_SIZE 12 /* this value gives a known -18dB step size for 24 bit attenuators, -6dB step for 16 bit attenuators */ // TER: Changed. //#define MIXER_ATTENUATOR_STEP_SIZE 8 /* this value gives a known -12dB step size for 24 bit attenuators, -6dB step for 16 bit attenuators */ #define MIXER_ATTENUATOR_STEP_SIZE 4 /* this value gives a known -6dB step size for 24 bit attenuators, -6dB step for 16 bit attenuators */ /* * NPM: For peak meters */ #define MULTI_TRACK_PEAK_CHANNELS 22 #define IDX_LMIX 20 #define IDX_RMIX 21 #define MAX_METERING_LEVEL 255 /* level corresponding to 0dB output for ice1712 hardware peak meters */ // #define MIN_METERING_LEVEL_DB -48.164799306 /* == 20*log10(1/(MAX_METERING_LEVEL+1)) */ // #define MIN_METERING_LEVEL_DB −48.130803609 /* == 20*log10(1/MAX_METERING_LEVEL) */ /* * NPM: */ typedef struct { unsigned int subvendor; /* PCI[2c-2f] */ unsigned char size; /* size of EEPROM image in bytes */ unsigned char version; /* must be 1 */ unsigned char codec; /* codec configuration PCI[60] */ unsigned char aclink; /* ACLink configuration PCI[61] */ unsigned char i2sID; /* PCI[62] */ unsigned char spdif; /* S/PDIF configuration PCI[63] */ unsigned char gpiomask; /* GPIO initial mask, 0 = write, 1 = don't */ unsigned char gpiostate; /* GPIO initial state */ unsigned char gpiodir; /* GPIO direction state */ unsigned short ac97main; unsigned short ac97pcm; unsigned short ac97rec; unsigned char ac97recsrc; unsigned char dacID[4]; /* I2S IDs for DACs */ unsigned char adcID[4]; /* I2S IDs for ADCs */ unsigned char extra[4]; } ice1712_eeprom_t; extern snd_ctl_t *ctl; extern ice1712_eeprom_t card_eeprom; extern GtkWidget *mixer_mix_drawing; extern GtkWidget *mixer_clear_peaks_button; extern GtkWidget *mixer_drawing[20]; extern GtkObject *mixer_adj[20][2]; extern GtkWidget *mixer_vscale[20][2]; extern GtkWidget *mixer_label[20][2]; /* NPM */ extern GtkWidget *peak_label[MULTI_TRACK_PEAK_CHANNELS]; /* NPM */ extern GtkWidget *adc_peak_label[MAX_INPUT_CHANNELS]; /* NPM */ extern GtkWidget *dac_peak_label[MAX_OUTPUT_CHANNELS]; /* NPM */ extern GtkWidget *mixer_solo_toggle[20][2]; extern GtkWidget *mixer_mute_toggle[20][2]; extern GtkWidget *mixer_stereo_toggle[20]; extern GtkWidget *router_radio[10][12]; //extern GtkWidget *hw_master_clock_xtal_radio; extern GtkWidget *hw_master_clock_xtal_22050; extern GtkWidget *hw_master_clock_xtal_32000; extern GtkWidget *hw_master_clock_xtal_44100; extern GtkWidget *hw_master_clock_xtal_48000; extern GtkWidget *hw_master_clock_xtal_88200; extern GtkWidget *hw_master_clock_xtal_96000; extern GtkWidget *hw_master_clock_spdif_radio; extern GtkWidget *hw_master_clock_word_radio; extern GtkWidget *hw_master_clock_status_label; extern GtkWidget *hw_master_clock_actual_rate_label; extern GtkWidget *hw_rate_locking_check; extern GtkWidget *hw_rate_reset_check; extern GtkObject *hw_volume_change_adj; extern GtkWidget *hw_volume_change_spin; extern GtkWidget *hw_iec958_input_status_label; /* NPM */ extern GtkWidget *hw_spdif_profi_nonaudio_radio; extern GtkWidget *hw_spdif_profi_audio_radio; extern GtkWidget *hw_profi_stream_stereo_radio; extern GtkWidget *hw_profi_stream_notid_radio; extern GtkWidget *hw_profi_emphasis_none_radio; extern GtkWidget *hw_profi_emphasis_5015_radio; extern GtkWidget *hw_profi_emphasis_ccitt_radio; extern GtkWidget *hw_profi_emphasis_notid_radio; extern GtkWidget *hw_consumer_copyright_on_radio; extern GtkWidget *hw_consumer_copyright_off_radio; extern GtkWidget *hw_consumer_copy_1st_radio; extern GtkWidget *hw_consumer_copy_original_radio; extern GtkWidget *hw_consumer_emphasis_none_radio; extern GtkWidget *hw_consumer_emphasis_5015_radio; extern GtkWidget *hw_consumer_category_dat_radio; extern GtkWidget *hw_consumer_category_pcm_radio; extern GtkWidget *hw_consumer_category_cd_radio; extern GtkWidget *hw_consumer_category_general_radio; extern GtkWidget *hw_spdif_professional_radio; extern GtkWidget *hw_spdif_consumer_radio; extern GtkWidget *hw_spdif_output_notebook; extern GtkWidget *hw_spdif_input_coaxial_radio; extern GtkWidget *hw_spdif_input_optical_radio; extern GtkWidget *hw_spdif_switch_off_radio; extern GtkWidget *hw_phono_input_on_radio; extern GtkWidget *hw_phono_input_off_radio; extern GtkWidget *input_interface_internal; extern GtkWidget *input_interface_front_input; extern GtkWidget *input_interface_rear_input; extern GtkWidget *input_interface_wavetable; extern GtkObject *av_dac_volume_adj[]; extern GtkObject *av_adc_volume_adj[]; extern GtkObject *av_ipga_volume_adj[]; extern GtkWidget *av_dac_volume_label[]; extern GtkWidget *av_adc_volume_label[]; extern GtkWidget *av_ipga_volume_label[]; extern GtkWidget *av_dac_sense_radio[][4]; extern GtkWidget *av_adc_sense_radio[][4]; /* flags */ extern int card_is_dmx6fire; extern int no_scale_marks; /* NPM: --no_scale_marks option */ extern GdkColor *meter_bg, *meter_fg; /* NPM: --bg_color --lights_color options */ extern int card_has_delta_iec958_input_status; /* NPM added to support "Delta IEC958 Input Status" */ gint level_meters_configure_event(GtkWidget *widget, GdkEventConfigure *event); gint level_meters_expose_event(GtkWidget *widget, GdkEventExpose *event); gint level_meters_timeout_callback(gpointer data); void level_meters_reset_peaks(GtkButton *button, gpointer data); void level_meters_init(void); void level_meters_postinit(void); int mixer_stream_is_active(int stream); void mixer_update_stream(int stream, int vol_flag, int sw_flag); void mixer_toggled_solo(GtkWidget *togglebutton, gpointer data); void mixer_toggled_mute(GtkWidget *togglebutton, gpointer data); void mixer_adjust(GtkAdjustment *adj, gpointer data); void mixer_init(void); void mixer_postinit(void); int patchbay_stream_is_active(int stream); void patchbay_update(void); void patchbay_toggled(GtkWidget *togglebutton, gpointer data); void patchbay_init(void); void patchbay_postinit(void); void master_clock_update(void); gint master_clock_status_timeout_callback(gpointer data); gint internal_clock_status_timeout_callback(gpointer data); gint rate_locking_status_timeout_callback(gpointer data); gint rate_reset_status_timeout_callback(gpointer data); gint iec958_input_status_timeout_callback(gpointer data); void internal_clock_toggled(GtkWidget *togglebutton, gpointer data); void rate_locking_update(void); void rate_locking_toggled(GtkWidget *togglebutton, gpointer data); void rate_reset_update(void); void rate_reset_toggled(GtkWidget *togglebutton, gpointer data); void volume_change_rate_update(void); void volume_change_rate_adj(GtkAdjustment *adj, gpointer data); void profi_data_toggled(GtkWidget *togglebutton, gpointer data); void profi_stream_toggled(GtkWidget *togglebutton, gpointer data); void profi_emphasis_toggled(GtkWidget *togglebutton, gpointer data); void consumer_copyright_toggled(GtkWidget *togglebutton, gpointer data); void consumer_copy_toggled(GtkWidget *togglebutton, gpointer data); void consumer_emphasis_toggled(GtkWidget *togglebutton, gpointer data); void consumer_category_toggled(GtkWidget *togglebutton, gpointer data); void spdif_output_update(void); void spdif_output_toggled(GtkWidget *togglebutton, gpointer data); void spdif_input_update(void); void spdif_input_toggled(GtkWidget *togglebutton, gpointer data); void analog_input_select_toggled(GtkWidget *togglebutton, gpointer data); void phono_input_toggled(GtkWidget *togglebutton, gpointer data); void iec958_input_status_update(void); /* NPM */ void hardware_init(void); void hardware_postinit(void); void analog_volume_init(void); void analog_volume_postinit(void); int envy_dac_volumes(void); //int envy_dac_max(void); // TER int envy_adc_volumes(void); //int envy_adc_max(void); // TER int envy_ipga_volumes(void); int envy_dac_senses(void); int envy_adc_senses(void); int envy_dac_sense_items(void); int envy_adc_sense_items(void); const char *envy_dac_sense_enum_name(int i); const char *envy_adc_sense_enum_name(int i); int envy_analog_volume_available(void); // // Custom marker and page up/down snapping stuff by TER. // typedef enum { MIXER_STRIP, DAC_STRIP, ADC_STRIP, IPGA_STRIP } StripType; typedef struct _SliderScale SliderScale; typedef struct _ScaleMark ScaleMark; struct _ScaleMark { gdouble value; const gchar *markup; GtkPositionType position; }; struct _SliderScale { GtkScale *scale; StripType type; gint idx; GSList *marks; }; extern SliderScale mixer_volume_scales[20][2]; extern SliderScale dac_volume_scales[10]; extern SliderScale adc_volume_scales[10]; extern SliderScale ipga_volume_scales[10]; gboolean get_alsa_control_range(SliderScale *sl_scale, gdouble *min, gdouble *max); void scale_add_mark(SliderScale *sl_scale, gdouble value, GtkPositionType position, const gchar *markup); void scale_add_analog_marks(SliderScale *sl_scale, GtkPositionType position, gboolean draw_legend_p); void scale_add_marks(GtkScale *scale, SliderScale *sl_scale, GtkPositionType position, gboolean draw_legend_p); void clear_all_scale_marks(gboolean init); gboolean scale_btpress_handler(GtkWidget *widget, GdkEventButton *event, gpointer data); gboolean scale_expose_handler(GtkWidget *widget, GdkEventExpose *event, gpointer data); void scale_size_req_handler(GtkWidget *widget, GtkRequisition *requisition, gpointer data); gboolean slider_change_value_handler(GtkRange *range, GtkScrollType scroll, gdouble value, gpointer data); void dac_volume_update(int idx); void adc_volume_update(int idx); void ipga_volume_update(int idx); void dac_sense_update(int idx); void adc_sense_update(int idx); void dac_volume_adjust(GtkAdjustment *adj, gpointer data); void adc_volume_adjust(GtkAdjustment *adj, gpointer data); void ipga_volume_adjust(GtkAdjustment *adj, gpointer data); void dac_sense_toggled(GtkWidget *togglebutton, gpointer data); void adc_sense_toggled(GtkWidget *togglebutton, gpointer data); void control_input_callback(gpointer data, gint source, GdkInputCondition condition); void mixer_input_callback(gpointer data, gint source, GdkInputCondition condition); /* NPM: volume/db-related stuff added to volume.c */ char* peak_level_to_db(int ival); // TER: Replaced with custom drawing. //void draw_24bit_attenuator_scale_markings(GtkScale *scale, GtkPositionType position, int draw_legend_p); //void draw_dac_scale_markings(GtkScale *scale, GtkPositionType position); //void draw_adc_scale_markings(GtkScale *scale, GtkPositionType position); mudita24-1.0.3+svn13/new_process.c0000644000175000017500000000355311434666733016510 0ustar alessioalessio#include #include #include #include #include #include #ifndef MAX_PARAM #define MAX_PARAM 10 #endif /* * start child process */ int new_process(char * const cmd_line[MAX_PARAM]) { int proc_status; pid_t pid; pid_t w; struct stat file_status; /* memory for storage of function pointers from the signal handling routines */ void (*int_stat)(); void (*quit_stat)(); void (*usr2_stat)(); /* * check command file */ /* search file */ if (stat(cmd_line[0], &file_status) < 0) { fprintf(stderr, "Cannot find program '%s'.\n", cmd_line[0]); fprintf(stderr, "You must specify path for '%s'.\n", cmd_line[0]); return -errno; } proc_status = 0; /* check file status and permissions */ if (file_status.st_mode & S_IFREG) { if (!(file_status.st_mode & S_IXOTH)) { if (!(file_status.st_mode & S_IXGRP)) { if (!(file_status.st_mode & S_IXUSR)) { proc_status = -EACCES; } else if (file_status.st_uid != getuid()) { proc_status = -EACCES; } } else if ((file_status.st_gid != getgid()) && (file_status.st_uid != getuid())) { proc_status = -EACCES; } } } else { proc_status = -EACCES; } if (proc_status != 0) { fprintf(stderr, "No permissions to execute program '%s'.\n", cmd_line[0]); return proc_status; } if ( (pid = fork() ) == 0) { execv(cmd_line[0], cmd_line); } /* for waiting ingnoring special interrupts */ int_stat = signal(SIGINT, SIG_IGN); quit_stat = signal(SIGQUIT, SIG_IGN); usr2_stat = signal(SIGUSR2, SIG_IGN); /* waiting for the end of the child process */ while ( ( (w = wait(&proc_status)) != pid ) && (w != -1) ) ; if (w == -1) { proc_status = -errno; } /* restore pointers from signal handling routines */ signal(SIGINT, int_stat); signal(SIGQUIT, quit_stat); signal(SIGUSR2, usr2_stat); return proc_status; } mudita24-1.0.3+svn13/volume.c0000644000175000017500000013222411647357353015467 0ustar alessioalessio/* * volume.c - analog volume settings * * This code is added by Takashi Iwai * * Copyright (c) 2000 Jaroslav Kysela * Copyright (C) 2011 Tim E. Real (terminator356 on sourceforge) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include // TER: For key defs. #include #include "envy24control.h" #define toggle_set(widget, state) \ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), state); #define DAC_VOLUME_NAME "DAC Volume" #define ADC_VOLUME_NAME "ADC Volume" #define IPGA_VOLUME_NAME "IPGA Analog Capture Volume" #define DAC_SENSE_NAME "Output Sensitivity Switch" #define ADC_SENSE_NAME "Input Sensitivity Switch" static int dac_volumes; //static int dac_max = 127; // TER //static int adc_max = 127; // TER static int adc_volumes; static int ipga_volumes; static int dac_senses; static int adc_senses; static int dac_sense_items; static int adc_sense_items; static char *dac_sense_name[4]; static char *adc_sense_name[4]; static const int mark_width = 7; static const int mark_height = 4; static const int mark_pad = 1; static char str_tmp[128]; extern int input_channels, output_channels; int envy_dac_volumes(void) { return dac_volumes; } //int envy_dac_max(void) //{ // return dac_max; //} int envy_adc_volumes(void) { return adc_volumes; } //int envy_adc_max(void) //{ // return adc_max; //} int envy_ipga_volumes(void) { return ipga_volumes; } int envy_dac_senses(void) { return dac_senses; } int envy_adc_senses(void) { return adc_senses; } int envy_dac_sense_items(void) { return dac_sense_items; } int envy_adc_sense_items(void) { return adc_sense_items; } const char *envy_dac_sense_enum_name(int i) { return dac_sense_name[i]; } const char *envy_adc_sense_enum_name(int i) { return adc_sense_name[i]; } int envy_analog_volume_available(void) { return dac_volumes > 0 || adc_volumes > 0 || ipga_volumes > 0; } /* */ gboolean get_alsa_control_range(SliderScale *sl_scale, gdouble *min, gdouble *max) { const char *cname; switch(sl_scale->type) { case DAC_STRIP: cname = DAC_VOLUME_NAME; break; case ADC_STRIP: cname = ADC_VOLUME_NAME; break; case IPGA_STRIP: cname = IPGA_VOLUME_NAME; break; default: return FALSE; } snd_ctl_elem_info_t *elem_info; snd_ctl_elem_info_alloca(&elem_info); snd_ctl_elem_info_set_interface(elem_info, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_info_set_name(elem_info, cname); snd_ctl_elem_info_set_numid(elem_info, 0); snd_ctl_elem_info_set_index(elem_info, sl_scale->idx); int err; if((err = snd_ctl_elem_info(ctl, elem_info)) < 0) { g_print("get_alsa_control_range: Error reading control info: %s\n", snd_strerror(err)); return FALSE; } *min = (gdouble)snd_ctl_elem_info_get_min(elem_info); *max = (gdouble)snd_ctl_elem_info_get_max(elem_info); return TRUE; } void scale_add_analog_marks(SliderScale *sl_scale, GtkPositionType position, gboolean draw_legend_p) { const char *cname; switch(sl_scale->type) { case DAC_STRIP: cname = DAC_VOLUME_NAME; break; case ADC_STRIP: cname = ADC_VOLUME_NAME; break; case IPGA_STRIP: cname = IPGA_VOLUME_NAME; break; default: return; } snd_ctl_elem_id_t *elem_id; snd_ctl_elem_id_alloca(&elem_id); snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_name(elem_id, cname); snd_ctl_elem_id_set_index(elem_id, sl_scale->idx); long dbminl, dbmaxl; if(snd_ctl_get_dB_range(ctl, elem_id, &dbminl, &dbmaxl) < 0) return; // Get the nearest max 6dB value below or equal. dbmaxl = (dbmaxl / 600) * 600; // Get the nearest min 6dB value above or equal. dbminl = (dbminl / 600) * 600; if((dbminl % 600) != 0) dbminl += 600; // 09/23/2011 TER. Crashes due to too many marks. Seems db range has now changed such that // a very low value means -infinity. snd_ctl_get_dB_range returned a dbminl of -9999999, // which gives 16666 marks! So put some kind of limits: if(dbminl < -12000) // Enough for say ten marks, down to -120db? dbminl = -12000; //if(dbmaxl > 6000) // dbmaxl = 6000; long i; long ival; long lastival; long first = 1; for(i = dbminl; i <= dbmaxl; i+= 600) { if(snd_ctl_convert_from_dB(ctl, elem_id, i, &ival, 0) < 0) continue; if(!first && ival == lastival) // Keep going until we find a change. continue; first = 0; lastival = ival; if(draw_legend_p) { const char *col; if(i > 0) col = "red"; else if (i == 0) col = "green"; else col = "blue"; if(i <= -12000) // Say <= -120db = -infinity? sprintf(str_tmp, "~", col); else sprintf(str_tmp, "%+ld", col, i/100); } //printf("scale_add_analog_marks i:%d max:%d ival:%d\n", i, max, ival); scale_add_mark(sl_scale, (float)(-ival), position, draw_legend_p ? str_tmp : NULL); } } void scale_add_mark(SliderScale *sl_scale, gdouble value, GtkPositionType position, const gchar *markup) { ScaleMark *mark = g_new(ScaleMark, 1); mark->value = value; if(markup) mark->markup = g_strdup(markup); else mark->markup = NULL; mark->position = position; sl_scale->marks = g_slist_prepend(sl_scale->marks, mark); } void scale_add_marks(GtkScale *scale, SliderScale *sl_scale, GtkPositionType position, gboolean draw_legend_p) { switch(sl_scale->type) { case MIXER_STRIP: sl_scale->scale = scale; if(no_scale_marks) return; // We know it's an ice1712 envy24 chip. So we can hard-code these markings... scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE, position, draw_legend_p ? "+0": NULL); scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE+1*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-6" : NULL); scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE+2*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-12" : NULL); scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE+3*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-18" : NULL); scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE+4*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-24" : NULL); scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE+5*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-30" : NULL); scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE+6*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-36" : NULL); scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE+7*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-42" : NULL); scale_add_mark(sl_scale, (float) MIN_MIXER_ATTENUATION_VALUE+8*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-48" : NULL); //scale_add_mark(sl_scale, (float) LOW_MIXER_ATTENUATION_VALUE, // position, draw_legend_p ? "off" : NULL); break; // Ask ALSA for analog volume info, because the chip(s) may not be known, can't hard-code markings... case DAC_STRIP: case ADC_STRIP: case IPGA_STRIP: sl_scale->scale = scale; if(no_scale_marks) return; scale_add_analog_marks(sl_scale, position, draw_legend_p); break; default: return; /* case DAC_STRIP: sl_scale->scale = scale; scale_add_mark(sl_scale, (float) -envy_dac_max(), position, draw_legend_p ? "+0" : NULL); scale_add_mark(sl_scale, (float) -envy_dac_max()+1*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-6" : NULL); scale_add_mark(sl_scale, (float) -envy_dac_max()+2*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-12" : NULL); scale_add_mark(sl_scale, (float) -envy_dac_max()+3*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-18" : NULL); scale_add_mark(sl_scale, (float) -envy_dac_max()+4*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-24" : NULL); scale_add_mark(sl_scale, (float) -envy_dac_max()+5*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-30" : NULL); scale_add_mark(sl_scale, (float) -envy_dac_max()+6*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-36" : NULL); // scale_add_mark(sl_scale, (float) -envy_dac_max()+7*ANALOG_GAIN_STEP_SIZE, // position, draw_legend_p ? "-42" : NULL); scale_add_mark(sl_scale, (float) -envy_dac_max()+8*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-48" : NULL); // scale_add_mark(sl_scale, (float) -envy_dac_max()+9*ANALOG_GAIN_STEP_SIZE, // position, draw_legend_p ? "-54" : NULL); scale_add_mark(sl_scale, (float) -envy_dac_max()+10*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-60" : NULL); //scale_add_mark(sl_scale, (float) 0, // position, draw_legend_p ? "off" : NULL); break; case ADC_STRIP: sl_scale->scale = scale; scale_add_mark(sl_scale, (float) -envy_adc_max(), position, draw_legend_p ? "+18" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+1*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "+12" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+2*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "+6" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+3*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "+0" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+4*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-6" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+5*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-12" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+6*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-18" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+7*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-24" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+8*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-30" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+9*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-36" : NULL); // scale_add_mark(sl_scale, (float) -envy_adc_max()+10*ANALOG_GAIN_STEP_SIZE, // position, draw_legend_p ? "-42" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+11*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-48" : NULL); // scale_add_mark(sl_scale, (float) -envy_adc_max()+12*ANALOG_GAIN_STEP_SIZE, // position, draw_legend_p ? "-54" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+13*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "-60" : NULL); //scale_add_mark(sl_scale, (float) 0, // position, draw_legend_p ? "off" : NULL); break; case IPGA_STRIP: sl_scale->scale = scale; scale_add_mark(sl_scale, (float) -envy_adc_max(), position, draw_legend_p ? "+18" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+1*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "+12" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+2*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "+6" : NULL); scale_add_mark(sl_scale, (float) -envy_adc_max()+3*ANALOG_GAIN_STEP_SIZE, position, draw_legend_p ? "+0" : NULL); break; default: return; */ } } void scale_size_req_handler(GtkWidget *widget, GtkRequisition *requisition, gpointer data) { //printf("scale_size_req_handler\n"); SliderScale* sscale = (SliderScale*)data; gint w = 0, h = 0; if(sscale->marks) { PangoLayout *layout; PangoRectangle layout_rect; GSList *m; layout = gtk_widget_create_pango_layout(widget, NULL); for(m = sscale->marks; m; m = m->next) { ScaleMark *mark = m->data; if(h + mark_height > h) h += mark_height; if(mark_width + mark_pad > w) w = mark_width + mark_pad; if(mark->markup) { pango_layout_set_markup(layout, mark->markup, -1); pango_layout_get_pixel_extents(layout, NULL, &layout_rect); if(h + layout_rect.height > h) h += layout_rect.height; //printf("scale_size_req_handler item w:%d\n", layout_rect.width); if(layout_rect.width + mark_width + mark_pad > w) w = layout_rect.width + mark_width + mark_pad; } } g_object_unref(layout); } //printf("scale_size_req_handler w:%d h:%d\n", w, h); requisition->width = w; requisition->height = h; return; } gboolean scale_expose_handler(GtkWidget *widget, GdkEventExpose *event, gpointer data) { //printf("scale_expose_handler\n"); SliderScale* sscale = (SliderScale*)data; GtkStateType state_type; state_type = GTK_STATE_NORMAL; if(!gtk_widget_is_sensitive(widget)) state_type = GTK_STATE_INSENSITIVE; if(sscale->marks) { gint x1, x2, x3, y1, y2; PangoLayout *layout; PangoRectangle layout_rect; GSList *m; GtkScale *scale = sscale->scale; GtkRange *rng = GTK_RANGE(scale); GtkAdjustment *adj = gtk_range_get_adjustment(rng); if(!adj) return FALSE; gdouble min = gtk_adjustment_get_lower(adj); gdouble max = gtk_adjustment_get_upper(adj); gint sl_w; gtk_widget_style_get(GTK_WIDGET(scale), "slider-length", &sl_w, NULL); //printf("scale_expose_handler sl_w:%d\n", sl_w); gdouble z = max - min; if(z == 0.0) return FALSE; // Extra space between top of slider thumb track and top of allocation. (Also at bottom). // Just a manual amount for now, measured on my machine. // TODO: Make use of gtk 2.20 funcs for more accuracy, if available. gint h_extra = 2; //gint slider_start = 0; //gint slider_end = 0; // These two funcs are new since gtk+-2.20 Try to avoid using them for now. //gtk_range_get_slider_range(rng, &slider_start, &slider_end); //GdkRectangle rng_rect; //gtk_range_get_range_rect(rng, &rng_rect); gdouble h1 = (gdouble)(GTK_WIDGET(scale)->allocation.height - sl_w - 2*h_extra) / z; layout = gtk_widget_create_pango_layout(widget, NULL); //gdouble rtmp; for(m = sscale->marks; m; m = m->next) { ScaleMark *mark = m->data; if(mark->position == GTK_POS_LEFT) { x1 = widget->allocation.width - mark_width; x2 = widget->allocation.width - 1; } else { x1 = 0; x2 = mark_width - 1; } // These compile, but I get pre-c99 warning: // " warning: incompatible implicit declaration of built-in function ‘round’ ". // Close visual inspection: Seems less accurate anyway, with round, than without. //y1 = (gint) round(mark->value * h1 + (gdouble)sl_w/2.0) + h_extra; //y1 = (gint) nearbyint(mark->value * h1 + (gdouble)sl_w/2.0) + h_extra; //y1 = lrint(mark->value * h1 + (gdouble)sl_w/2.0) + widget->allocation.y + h_extra; //y1 = (gint)(mark->value * h1 + (gdouble)sl_w/2.0) + widget->allocation.y + h_extra; // y1 = (gint)((mark->value - min) * h1 + (gdouble)sl_w/2.0) + h_extra; // Works OK. //rtmp = (mark->value - min) * h1 + (gdouble)sl_w/2.0; //rtmp = (rtmp > 0.0) ? floor(rtmp + 0.5) : ceil(rtmp - 0.5); // No C99 required. //y1 = (gint)rtmp + h_extra; gtk_paint_hline (widget->style, widget->window, state_type, NULL, widget, "range-mark", x1, x2, y1); if(mark->markup) { pango_layout_set_markup(layout, mark->markup, -1); pango_layout_get_pixel_extents(layout, NULL, &layout_rect); x3 = mark->position == GTK_POS_LEFT ? x1 - layout_rect.width - mark_pad : mark_width + mark_pad; y2 = y1 - layout_rect.height / 2; if(y2 < 0) y2 = 0; //gdk_draw_layout(layout); gtk_paint_layout(widget->style, widget->window, state_type, FALSE, NULL, widget, "scale-mark", x3, y2, layout); } } g_object_unref(layout); } return TRUE; } // // Event handler to give focus to a slider when scale area left-clicked. // gboolean scale_btpress_handler(GtkWidget *widget, GdkEventButton *event, gpointer data) { //printf("scale_btpress_handler\n"); if(event->button != 1) // 1 is normally left mouse button. return FALSE; SliderScale* sscale = (SliderScale*)data; if(!sscale->scale) return FALSE; gtk_widget_grab_focus(GTK_WIDGET(sscale->scale)); return TRUE; } // // Handle all types of slider scroll changes. gboolean slider_change_value_handler(GtkRange *range, GtkScrollType scroll, gdouble value, gpointer data) { SliderScale* sscale = (SliderScale*)data; GtkAdjustment *adj = gtk_range_get_adjustment(range); if(!adj) return FALSE; gboolean is_pg = TRUE; gdouble min = gtk_adjustment_get_lower(adj); gdouble max = gtk_adjustment_get_upper(adj); gdouble inc = gtk_adjustment_get_page_increment(adj); gdouble curv = gtk_adjustment_get_value(adj); // Tested, GtkScrollType values observed: // GTK_SCROLL_NONE // GTK_SCROLL_JUMP // Drag the slider knob, or middle click trough. // GTK_SCROLL_STEP_BACKWARD // Up key. // GTK_SCROLL_STEP_FORWARD // Down key. // GTK_SCROLL_PAGE_BACKWARD // Page up key, or left click upper trough. // GTK_SCROLL_PAGE_FORWARD // Page down key, or left click lower trough. // GTK_SCROLL_STEP_UP // GTK_SCROLL_STEP_DOWN // GTK_SCROLL_PAGE_UP // GTK_SCROLL_PAGE_DOWN // GTK_SCROLL_STEP_LEFT // GTK_SCROLL_STEP_RIGHT // GTK_SCROLL_PAGE_LEFT // GTK_SCROLL_PAGE_RIGHT // GTK_SCROLL_START // GTK_SCROLL_END gboolean up = TRUE; switch(scroll) { case GTK_SCROLL_JUMP: { //printf("slider_change_value_handler GTK_SCROLL_JUMP value:%f\n", value); // Drag the slider knob, or middle click trough. // Round required. //value = round(value); // C99 required value = (value > 0.0) ? floor(value + 0.5) : ceil(value - 0.5); // Clamp required. if(value < min) value = min; else if(value > max) value = max; gdouble newv = (gint)value; if(curv != newv) { //gtk_adjustment_set_value(adj, newv); //gtk_range_set_value(range, newv); adj->value = newv; gtk_adjustment_value_changed(adj); } return TRUE; } case GTK_SCROLL_STEP_BACKWARD: //printf("slider_change_value_handler GTK_SCROLL_STEP_BACKWARD\n"); // Up key. inc = adj->step_increment; is_pg = FALSE; break; case GTK_SCROLL_STEP_FORWARD: //printf("slider_change_value_handler GTK_SCROLL_STEP_FORWARD\n"); // Down key. inc = adj->step_increment; up = FALSE; is_pg = FALSE; break; case GTK_SCROLL_PAGE_BACKWARD: //printf("slider_change_value_handler GTK_SCROLL_PAGE_BACKWARD\n"); // Page up key, or left click upper trough. break; case GTK_SCROLL_PAGE_FORWARD: //printf("slider_change_value_handler GTK_SCROLL_PAGE_FORWARD\n"); // Page down key, or left click lower trough. up = FALSE; break; default: //printf("slider_change_value_handler: unhandled scroll type:%d\n", (int)scroll); return FALSE; } gdouble newv = up ? min : max; // If it's a page, and we want scale marks, and there are actually some marks, use them... if(is_pg && !no_scale_marks && sscale->marks) { GSList *m; for(m = sscale->marks; m; m = m->next) { ScaleMark *mark = m->data; if(up) { if(mark->value < curv && mark->value > newv) newv = mark->value; } else if(mark->value > curv && mark->value < newv) newv = mark->value; } if(curv != newv) { //gtk_adjustment_set_value(adj, newv); //gtk_range_set_value(range, newv); adj->value = newv; gtk_adjustment_value_changed(adj); } } else // ...it's a step, or scale marks not wanted, or no scale marks. Use slider properties instead. { //if(inc == 0.0) // return FALSE; gdouble pg = (curv - min) / inc; gint pgint = (gint)pg; gdouble newv = pgint * inc + min; if(up) { // Page up. Are we already right on the page stop? Go to prev page. if(curv == newv) newv -= inc; } else // Page down. Go to next page no matter what. newv += inc; // Clamp required. if(newv < min) newv = min; else if(newv > max) newv = max; if(curv != newv) { adj->value = newv; gtk_adjustment_value_changed(adj); } } return TRUE; } void dac_volume_update(int idx) { snd_ctl_elem_value_t *val; int err; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, DAC_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); if ((err = snd_ctl_elem_read(ctl, val)) < 0) { g_print("Unable to read dac volume: %s\n", snd_strerror(err)); return; } // TER: Stop jitter when adjusting sliders. //printf("dac_volume_update cur val:%f new val:%d\n", gtk_adjustment_get_value(GTK_ADJUSTMENT(av_dac_volume_adj[idx])), -snd_ctl_elem_value_get_integer(val, 0)); if((int)gtk_adjustment_get_value(GTK_ADJUSTMENT(av_dac_volume_adj[idx])) != -snd_ctl_elem_value_get_integer(val, 0)) gtk_adjustment_set_value(GTK_ADJUSTMENT(av_dac_volume_adj[idx]), -snd_ctl_elem_value_get_integer(val, 0)); } void adc_volume_update(int idx) { snd_ctl_elem_value_t *val; int err; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, ADC_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); if ((err = snd_ctl_elem_read(ctl, val)) < 0) { g_print("Unable to read adc volume: %s\n", snd_strerror(err)); return; } // TER: Stop jitter when adjusting sliders. //printf("adc_volume_update cur val:%f new val:%d\n", GTK_ADJUSTMENT(av_adc_volume_adj[idx])->value, -snd_ctl_elem_value_get_integer(val, 0)); if((int)gtk_adjustment_get_value(GTK_ADJUSTMENT(av_adc_volume_adj[idx])) != -snd_ctl_elem_value_get_integer(val, 0)) gtk_adjustment_set_value(GTK_ADJUSTMENT(av_adc_volume_adj[idx]), -snd_ctl_elem_value_get_integer(val, 0)); snd_ctl_elem_value_set_name(val, IPGA_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); if ((err = snd_ctl_elem_read(ctl, val)) < 0) { g_print("Unable to read ipga volume: %s\n", snd_strerror(err)); return; } if (ipga_volumes > 0) { // TER: Stop jitter when adjusting sliders. //printf("adc_volume_update ipga cur val:%f new val:%d\n", GTK_ADJUSTMENT(av_ipga_volume_adj[idx])->value, -0); //if((int)gtk_adjustment_get_value(GTK_ADJUSTMENT(av_ipga_volume_adj[idx])) != -0) // gtk_adjustment_set_value(GTK_ADJUSTMENT(av_ipga_volume_adj[idx]), // -0); GtkAdjustment *adj = GTK_ADJUSTMENT(av_ipga_volume_adj[idx]); if((gint)adj->value != gtk_adjustment_get_upper(adj)) gtk_adjustment_set_value(adj, gtk_adjustment_get_upper(adj)); } } void ipga_volume_update(int idx) { snd_ctl_elem_value_t *val; int err, ipga_vol; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, IPGA_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); if ((err = snd_ctl_elem_read(ctl, val)) < 0) { g_print("Unable to read ipga volume: %s\n", snd_strerror(err)); return; } // TER: Stop jitter when adjusting sliders. //printf("ipga_volume_update cur val:%f new val:%d\n", GTK_ADJUSTMENT(av_ipga_volume_adj[idx])->value, -ipga_vol); ipga_vol = snd_ctl_elem_value_get_integer(val, 0); if((int)gtk_adjustment_get_value(GTK_ADJUSTMENT(av_ipga_volume_adj[idx])) != -ipga_vol) gtk_adjustment_set_value(GTK_ADJUSTMENT(av_ipga_volume_adj[idx]), //-(ipga_vol = snd_ctl_elem_value_get_integer(val, 0))); -ipga_vol); snd_ctl_elem_value_set_name(val, ADC_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); if ((err = snd_ctl_elem_read(ctl, val)) < 0) { g_print("Unable to read adc volume: %s\n", snd_strerror(err)); return; } // set ADC volume to max if IPGA volume greater 0 if (ipga_vol) { // TER: Stop jitter when adjusting sliders. //printf("ipga_volume_update adc cur val:%f new val:%d\n", GTK_ADJUSTMENT(av_adc_volume_adj[idx])->value, -ipga_vol); //if((int)gtk_adjustment_get_value(GTK_ADJUSTMENT(av_adc_volume_adj[idx])) != -adc_max) // gtk_adjustment_set_value(GTK_ADJUSTMENT(av_adc_volume_adj[idx]), // -adc_max); GtkAdjustment *adj = GTK_ADJUSTMENT(av_adc_volume_adj[idx]); if((gint)adj->value != gtk_adjustment_get_lower(adj)) gtk_adjustment_set_value(adj, gtk_adjustment_get_lower(adj)); } } void dac_sense_update(int idx) { snd_ctl_elem_value_t *val; int err; int state; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, DAC_SENSE_NAME); snd_ctl_elem_value_set_index(val, idx); if ((err = snd_ctl_elem_read(ctl, val)) < 0) { g_print("Unable to read dac sense: %s\n", snd_strerror(err)); return; } state = snd_ctl_elem_value_get_enumerated(val, 0); toggle_set(av_dac_sense_radio[idx][state], TRUE); } void adc_sense_update(int idx) { snd_ctl_elem_value_t *val; int err; int state; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, ADC_SENSE_NAME); snd_ctl_elem_value_set_index(val, idx); if ((err = snd_ctl_elem_read(ctl, val)) < 0) { g_print("Unable to read adc sense: %s\n", snd_strerror(err)); return; } state = snd_ctl_elem_value_get_enumerated(val, 0); toggle_set(av_adc_sense_radio[idx][state], TRUE); } /* */ static char temp_label[16]; /* * NPM: Per Fons Adriaensen''s message to linux-audio-user's list * July 2010 ( http://www.linuxaudio.org/mailarchive/lad/2010/7/13/171540 ) * "L dB (L < 0) corresponds to 255 * (10 ^ L / 20)" * Alternately: http://en.wikipedia.org/wiki/Decibel#Field_quantities * 20 log10(V1/V0) where V1 is voltage measured and V0 is reference. * ... of course, the value of the digital peak meters isn't the "voltage" * for example an output of 1/255 --> −48.130803609dB * whereas 255/255 --> 0dB * 254/255 --> −0.034129276 * 250/255 --> −0.172003435 * http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf states * "Peak data derived from the absolute value of 9 msb. * 00h min - FFh max volume. Reading the register * resets the meter to 00h." */ char* peak_level_to_db(int ival) { if (ival != 0) { double value = 20.0 * log10((double)ival/(double)MAX_METERING_LEVEL); //"(Off)" //"0dBFS" <-- seems to cause a resize oscillation, use 0.0dB instead //"0.0dB" //"-0.10" //"-9.90" //"-48.0" if (value == 0.0) sprintf(temp_label, "0.0dB"); else if (value > -10.0) sprintf(temp_label, "%+1.2f", value); else sprintf(temp_label, "%+2.1f", value); return (temp_label); } else return ("(Off)"); } // TER: For native GtkScale marks. Removed and replaced with custom drawing. /* // // NPM: "subroutine" used twice in create_mixer_frame() to draw markings // for each digital mixer input attenuation slider. Each slider is drawn // optionally with or without a legend describing the dBs attenuation at // the given level. Each slider controls attentuation of its input from // +0dB to -144.0dB (and "Off"). // void draw_24bit_attenuator_scale_markings(GtkScale *scale, GtkPositionType position, int draw_legend_p) { if (!no_scale_marks) { gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE, position, draw_legend_p ? "+0": NULL); gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+1*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-12" : NULL); gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+2*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-24" : NULL); gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+3*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-36" : NULL); gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+4*MIXER_ATTENUATOR_STEP_SIZE, position, draw_legend_p ? "-48" : NULL); gtk_scale_add_mark(scale, (float) LOW_MIXER_ATTENUATION_VALUE, position, NULL); // NPM: last marker needs to not be a label, else the last level gets placed incorrectly (gtk2-2.18.9-3.fc12.x86_64 bug?) // gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE, // position, draw_legend_p ? "+0": NULL); // gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+1*MIXER_ATTENUATOR_STEP_SIZE, // position, draw_legend_p ? "-18" : NULL); // gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+2*MIXER_ATTENUATOR_STEP_SIZE, // position, draw_legend_p ? "-36" : NULL); // gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+3*MIXER_ATTENUATOR_STEP_SIZE, // position, draw_legend_p ? "-54" : NULL); // // gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+4*MIXER_ATTENUATOR_STEP_SIZE, // // position, draw_legend_p ? "-72" : NULL); // gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+5*MIXER_ATTENUATOR_STEP_SIZE, // position, draw_legend_p ? "-90" : NULL); // // gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+6*MIXER_ATTENUATOR_STEP_SIZE, // // position, draw_legend_p ? "-108" : NULL); // gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+7*MIXER_ATTENUATOR_STEP_SIZE, // position, draw_legend_p ? "-126" : NULL); // gtk_scale_add_mark(scale, (float) MAX_MIXER_ATTENUATION_VALUE, // position, NULL); // NPM: last marker needs to not be a label, else the last level gets placed incorrectly (gtk2-2.18.9-3.fc12.x86_64 bug?) } } // NPM: used in create_analog_volume() to draw dB markings on DAC attenuators void draw_dac_scale_markings(GtkScale *scale, GtkPositionType position) { if (!no_scale_marks) { gtk_scale_add_mark(scale, (float) -envy_dac_max(), position, "+0"); gtk_scale_add_mark(scale, (float) -envy_dac_max()+1*ANALOG_GAIN_STEP_SIZE, position, "-6"); gtk_scale_add_mark(scale, (float) -envy_dac_max()+2*ANALOG_GAIN_STEP_SIZE, position, "-12"); gtk_scale_add_mark(scale, (float) -envy_dac_max()+3*ANALOG_GAIN_STEP_SIZE, position, "-18"); gtk_scale_add_mark(scale, (float) -envy_dac_max()+4*ANALOG_GAIN_STEP_SIZE, position, "-24"); gtk_scale_add_mark(scale, (float) -envy_dac_max()+5*ANALOG_GAIN_STEP_SIZE, position, "-30"); gtk_scale_add_mark(scale, (float) -envy_dac_max()+6*ANALOG_GAIN_STEP_SIZE, position, "-36"); // gtk_scale_add_mark(scale, (float) -envy_dac_max()+7*ANALOG_GAIN_STEP_SIZE, // position, "-42"); gtk_scale_add_mark(scale, (float) -envy_dac_max()+8*ANALOG_GAIN_STEP_SIZE, position, "-48"); // gtk_scale_add_mark(scale, (float) -envy_dac_max()+9*ANALOG_GAIN_STEP_SIZE, // position, "-54"); gtk_scale_add_mark(scale, (float) -envy_dac_max()+10*ANALOG_GAIN_STEP_SIZE, position, "-60"); gtk_scale_add_mark(scale, (float) 0, position, NULL); // NPM: last marker needs to not be a label, else the last level gets placed incorrectly (gtk2-2.18.9-3.fc12.x86_64 bug?) } } // NPM: used in create_analog_volume() to draw dB markings on ADC attenuators/amplifiers void draw_adc_scale_markings(GtkScale *scale, GtkPositionType position) { if (!no_scale_marks) { gtk_scale_add_mark(scale, (float) -envy_adc_max(), position, "+18"); gtk_scale_add_mark(scale, (float) -envy_adc_max()+1*ANALOG_GAIN_STEP_SIZE, position, "+12"); gtk_scale_add_mark(scale, (float) -envy_adc_max()+2*ANALOG_GAIN_STEP_SIZE, position, "+6"); gtk_scale_add_mark(scale, (float) -envy_adc_max()+3*ANALOG_GAIN_STEP_SIZE, position, "+0"); gtk_scale_add_mark(scale, (float) -envy_adc_max()+4*ANALOG_GAIN_STEP_SIZE, position, "-6"); gtk_scale_add_mark(scale, (float) -envy_adc_max()+5*ANALOG_GAIN_STEP_SIZE, position, "-12"); // gtk_scale_add_mark(scale, (float) -envy_adc_max()+6*ANALOG_GAIN_STEP_SIZE, // position, "-18"); gtk_scale_add_mark(scale, (float) -envy_adc_max()+7*ANALOG_GAIN_STEP_SIZE, position, "-24"); // gtk_scale_add_mark(scale, (float) -envy_adc_max()+8*ANALOG_GAIN_STEP_SIZE, // position, "-30"); gtk_scale_add_mark(scale, (float) -envy_adc_max()+9*ANALOG_GAIN_STEP_SIZE, position, "-36"); // gtk_scale_add_mark(scale, (float) -envy_adc_max()+10*ANALOG_GAIN_STEP_SIZE, // position, "-42"); gtk_scale_add_mark(scale, (float) -envy_adc_max()+11*ANALOG_GAIN_STEP_SIZE, position, "-48"); // gtk_scale_add_mark(scale, (float) -envy_adc_max()+12*ANALOG_GAIN_STEP_SIZE, // position, "-54"); // gtk_scale_add_mark(scale, (float) -envy_adc_max()+13*ANALOG_GAIN_STEP_SIZE, // position, "-60"); gtk_scale_add_mark(scale, (float) 0, position, NULL); // NPM: last marker needs to not be a label, else the last level gets placed incorrectly (gtk2-2.18.9-3.fc12.x86_64 bug?) } } */ void dac_volume_adjust(GtkAdjustment *adj, gpointer data) { int idx = (int)(long)data; snd_ctl_elem_value_t *val; int err; //, ival = -(int)adj->value; // TER int ival = -(int)gtk_adjustment_get_value(adj); //printf("dac_volume_adjust cur val:%f new val:%d\n", gtk_adjustment_get_value(adj), ival); snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, DAC_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); snd_ctl_elem_value_set_integer(val, 0, ival); if ((err = snd_ctl_elem_write(ctl, val)) < 0) { g_print("Unable to write dac volume: %s\n", snd_strerror(err)); sprintf(temp_label, "(Err)"); } else if (ival == 0) { sprintf(temp_label, "(Off)"); } else { /* NPM: changed to output dB values. Use of proper ALSA API snd_ctl_convert_to_dB() to return dB values suggested by Tim E. Real on linux-audio-devel list. */ snd_ctl_elem_id_t *elem_id; snd_ctl_elem_id_alloca(&elem_id); //snd_ctl_elem_id_clear(elem_id); snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_name(elem_id, DAC_VOLUME_NAME); snd_ctl_elem_id_set_index(elem_id, idx); long db_gain = 0; snd_ctl_convert_to_dB(ctl, elem_id, ival, &db_gain); /* convert ival integer to dB */ float fval = ((float)db_gain / 100.0); if (fval <= -10) sprintf(temp_label, "%+2.1f", fval); else sprintf(temp_label, "%+2.1f ", fval); } gtk_label_set_text(GTK_LABEL(av_dac_volume_label[idx]), temp_label); } void adc_volume_adjust(GtkAdjustment *adj, gpointer data) { int idx = (int)(long)data; snd_ctl_elem_value_t *val; int err; //, ival = -(int)adj->value; // TER int ival = -(int)gtk_adjustment_get_value(adj); snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, ADC_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); snd_ctl_elem_value_set_integer(val, 0, ival); if ((err = snd_ctl_elem_write(ctl, val)) < 0) { g_print("Unable to write adc volume: %s\n", snd_strerror(err)); sprintf(temp_label, "(Err)"); } else if (ival == 0) { sprintf(temp_label, "(Off)"); } else { /* NPM: changed to output dB values. Use of proper ALSA API snd_ctl_convert_to_dB() to return dB values suggested by Tim E. Real on linux-audio-devel list. */ snd_ctl_elem_id_t *elem_id; snd_ctl_elem_id_alloca(&elem_id); //snd_ctl_elem_id_clear(elem_id); snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_name(elem_id, ADC_VOLUME_NAME); snd_ctl_elem_id_set_index(elem_id, idx); long db_gain = 0; snd_ctl_convert_to_dB(ctl, elem_id, ival, &db_gain); float fval = ((float)db_gain / 100.0); if (fval >= 10) sprintf(temp_label, "%+2.1f", fval); else if (fval > 0) sprintf(temp_label, "%+2.1f ", fval); else if (fval <= -10) sprintf(temp_label, "%+2.1f", fval); else sprintf(temp_label, "%+2.1f ", fval); } gtk_label_set_text(GTK_LABEL(av_adc_volume_label[idx]), temp_label); } void ipga_volume_adjust(GtkAdjustment *adj, gpointer data) { int idx = (int)(long)data; snd_ctl_elem_value_t *val; int err;// , ival = -(int)adj->value; // TER int ival = gtk_adjustment_get_value(adj); char text[16]; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, IPGA_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); snd_ctl_elem_value_set_integer(val, 0, ival); sprintf(text, "%03i", ival); gtk_label_set_text(GTK_LABEL(av_ipga_volume_label[idx]), text); if ((err = snd_ctl_elem_write(ctl, val)) < 0) g_print("Unable to write ipga volume: %s\n", snd_strerror(err)); } void dac_sense_toggled(GtkWidget *togglebutton, gpointer data) { int idx = (long)data >> 8; int state = (long)data & 0xff; snd_ctl_elem_value_t *val; int err; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, DAC_SENSE_NAME); snd_ctl_elem_value_set_index(val, idx); snd_ctl_elem_value_set_enumerated(val, 0, state); if ((err = snd_ctl_elem_write(ctl, val)) < 0) g_print("Unable to write dac sense: %s\n", snd_strerror(err)); } void adc_sense_toggled(GtkWidget *togglebutton, gpointer data) { int idx = (long)data >> 8; int state = (long)data & 0xff; snd_ctl_elem_value_t *val; int err; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, ADC_SENSE_NAME); snd_ctl_elem_value_set_index(val, idx); snd_ctl_elem_value_set_enumerated(val, 0, state); if ((err = snd_ctl_elem_write(ctl, val)) < 0) g_print("Unable to write adc sense: %s\n", snd_strerror(err)); } /* */ void analog_volume_init(void) { snd_ctl_elem_info_t *info; int i; snd_ctl_elem_info_alloca(&info); snd_ctl_elem_info_set_interface(info, SND_CTL_ELEM_IFACE_MIXER); for (i = 0; i < 10; i++) { snd_ctl_elem_info_set_name(info, DAC_VOLUME_NAME); snd_ctl_elem_info_set_numid(info, 0); snd_ctl_elem_info_set_index(info, i); if (snd_ctl_elem_info(ctl, info) < 0) break; //dac_max = snd_ctl_elem_info_get_max(info); // TER } if (i < output_channels - 1) dac_volumes = i; else dac_volumes = output_channels; snd_ctl_elem_info_set_name(info, DAC_SENSE_NAME); for (i = 0; i < dac_volumes; i++) { snd_ctl_elem_info_set_numid(info, 0); snd_ctl_elem_info_set_index(info, i); if (snd_ctl_elem_info(ctl, info) < 0) break; } dac_senses = i; if (dac_senses > 0) { snd_ctl_elem_info_set_numid(info, 0); snd_ctl_elem_info_set_index(info, 0); snd_ctl_elem_info(ctl, info); dac_sense_items = snd_ctl_elem_info_get_items(info); for (i = 0; i < dac_sense_items; i++) { snd_ctl_elem_info_set_item(info, i); snd_ctl_elem_info(ctl, info); dac_sense_name[i] = strdup(snd_ctl_elem_info_get_item_name(info)); } } for (i = 0; i < 10; i++) { snd_ctl_elem_info_set_name(info, ADC_VOLUME_NAME); snd_ctl_elem_info_set_numid(info, 0); snd_ctl_elem_info_set_index(info, i); if (snd_ctl_elem_info(ctl, info) < 0) break; //adc_max = snd_ctl_elem_info_get_max(info); // TER } if (i < input_channels - 1) adc_volumes = i; else adc_volumes = input_channels; snd_ctl_elem_info_set_name(info, ADC_SENSE_NAME); for (i = 0; i < adc_volumes; i++) { snd_ctl_elem_info_set_numid(info, 0); snd_ctl_elem_info_set_index(info, i); if (snd_ctl_elem_info(ctl, info) < 0) break; } adc_senses = i; if (adc_senses > 0) { snd_ctl_elem_info_set_numid(info, 0); snd_ctl_elem_info_set_index(info, 0); snd_ctl_elem_info(ctl, info); adc_sense_items = snd_ctl_elem_info_get_items(info); for (i = 0; i < adc_sense_items; i++) { snd_ctl_elem_info_set_item(info, i); snd_ctl_elem_info(ctl, info); adc_sense_name[i] = strdup(snd_ctl_elem_info_get_item_name(info)); } } for (i = 0; i < 10; i++) { snd_ctl_elem_info_set_name(info, IPGA_VOLUME_NAME); snd_ctl_elem_info_set_numid(info, 0); snd_ctl_elem_info_set_index(info, i); if (snd_ctl_elem_info(ctl, info) < 0) break; } if (i < input_channels - 1) ipga_volumes = i; else ipga_volumes = input_channels; } void analog_volume_postinit(void) { int i; for (i = 0; i < dac_volumes; i++) { dac_volume_update(i); dac_volume_adjust((GtkAdjustment *)av_dac_volume_adj[i], (gpointer)(long)i); } for (i = 0; i < adc_volumes; i++) { adc_volume_update(i); adc_volume_adjust((GtkAdjustment *)av_adc_volume_adj[i], (gpointer)(long)i); } for (i = 0; i < ipga_volumes; i++) { ipga_volume_update(i); ipga_volume_adjust((GtkAdjustment *)av_ipga_volume_adj[i], (gpointer)(long)i); } for (i = 0; i < dac_senses; i++) dac_sense_update(i); for (i = 0; i < adc_senses; i++) adc_sense_update(i); } mudita24-1.0.3+svn13/midi.c0000644000175000017500000002206411434666733015101 0ustar alessioalessio/***************************************************************************** envy24control.c - Env24 chipset (ICE1712) control utility midi controller code (c) 2004, 2005 by Dirk Jagdmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************/ #include #include #include "midi.h" #include #include static const int midi2slider_lin[128] = { 96, 96, 95, 94, 93, 93, 92, 91, 90, 90, 89, 88, 87, 87, 86, 85, 84, 84, 83, 82, 81, 81, 80, 79, 78, 78, 77, 76, 75, 75, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67, 66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 59, 58, 57, 56, 56, 55, 54, 53, 53, 52, 51, 50, 50, 49, 48, 47, 47, 46, 45, 44, 44, 43, 42, 41, 41, 40, 39, 38, 38, 37, 36, 35, 35, 34, 33, 32, 31, 31, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 22, 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 13, 13, 12, 11, 10, 10, 9, 8, 7, 7, 6, 5, 4, 4, 3, 2, 1, 0, }; static const int slider2midi_lin[97] = { 0, 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15, 17, 18, 19, 21, 22, 23, 25, 26, 27, 29, 30, 31, 33, 34, 35, 37, 38, 39, 41, 42, 43, 44, 46, 47, 48, 50, 51, 52, 54, 55, 56, 58, 59, 60, 62, 63, 64, 66, 67, 68, 70, 71, 72, 74, 75, 76, 78, 79, 80, 82, 83, 84, 85, 87, 88, 89, 91, 92, 93, 95, 96, 97, 99, 100, 101, 103, 104, 105, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120, 121, 123, 124, 125, 127, }; static const int midi2slider_enh[128] = { 96, 95, 94, 93, 91, 90, 89, 87, 86, 85, 83, 82, 81, 79, 78, 77, 75, 74, 73, 72, 70, 69, 68, 66, 65, 64, 62, 61, 60, 58, 57, 56, 54, 53, 52, 51, 49, 48, 47, 45, 44, 43, 41, 40, 39, 37, 36, 35, 33, 32, 31, 30, 28, 27, 26, 24, 23, 22, 20, 19, 18, 16, 15, 14, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, }; static const int slider2midi_enh[97] = { 0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 12, 13, 13, 14, 15, 16, 16, 17, 18, 19, 20, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 39, 40, 41, 42, 42, 43, 44, 45, 45, 46, 47, 48, 48, 49, 50, 51, 52, 52, 53, 54, 55, 55, 56, 57, 58, 58, 59, 60, 61, 61, 62, 63, 68, 68, 73, 78, 83, 88, 93, 98, 103, 108, 113, 118, 123, 127, }; static const int *midi2slider, *slider2midi; static snd_seq_t *seq=0; static int client, clientId, port, ch; static char *portname=0, *appname=0; static int maxstreams=0; static int currentvalue[128]; void midi_maxstreams(int m) { maxstreams=m*2; } int midi_close() { int i=0; if(seq) i=snd_seq_close(seq); seq=0; client=port=0; if(portname) free(portname), portname=0; if(appname) free(appname), appname=0; return i; } static void do_controller(int c, int v) { snd_seq_event_t ev; if(!seq) return; if(currentvalue[c]==v) return; #if 0 fprintf(stderr, "do_controller(%i,%i)\n",c,v); #endif snd_seq_ev_clear(&ev); snd_seq_ev_set_source(&ev, port); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); snd_seq_ev_set_controller(&ev,ch,c,v); snd_seq_event_output(seq, &ev); snd_seq_drain_output(seq); currentvalue[c]=v; } int midi_controller(int c, int v) { int v2; if (! seq) return 0; if(c<0 || c>127) return 0; if(v<0) v=0; else if(v>96) v=96; v2=slider2midi[v]; #if 0 fprintf(stderr, "midi_controller(%i,%i)->%i\n",c,v,v2); #endif do_controller(c,v2); return 0; } int midi_button(int b, int v) { if(b<0) return 0; b+=maxstreams; if(b>127) return 0; do_controller(b, v?127:0); return 0; } int midi_init(char *appname, int channel, int midi_enhanced) { snd_seq_client_info_t *clientinfo; int npfd; struct pollfd *pfd; if(seq) return 0; for(npfd=0; npfd!=128; ++npfd) currentvalue[npfd]=-1; ch=channel; if(midi_enhanced) { midi2slider=midi2slider_enh; slider2midi=slider2midi_enh; } else { midi2slider=midi2slider_lin; slider2midi=slider2midi_lin; } if(snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK) < 0) { g_warning("could not init ALSA sequencer\n"); seq=0; return -1; } snd_seq_set_client_name(seq, appname); snd_seq_client_info_alloca(&clientinfo); snd_seq_get_client_info (seq, clientinfo); client=snd_seq_client_info_get_client(clientinfo); clientId = snd_seq_client_id(seq); portname=g_strdup_printf("%s Mixer Control", appname); port=snd_seq_create_simple_port(seq, portname, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE|SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION); if(port < 0) { g_warning("could not create ALSA sequencer port\n"); midi_close(); return -1; } npfd=snd_seq_poll_descriptors_count(seq, POLLIN); if(npfd<=0) { g_warning("could not get number of ALSA sequencer poll descriptors\n"); midi_close(); return -1; } pfd=(struct pollfd*)alloca(npfd * sizeof(struct pollfd)); if(pfd==0) { g_warning("could not alloc memory for ALSA sequencer poll descriptors\n"); midi_close(); return -1; } if(snd_seq_poll_descriptors(seq, pfd, npfd, POLLIN) != npfd) { g_warning("number of returned poll desc is not equal of request poll desc\n"); midi_close(); return -1; } return pfd[0].fd; } void mixer_adjust(GtkAdjustment *adj, gpointer data); void mixer_set_mute(int stream, int left, int right); void midi_process(gpointer data, gint source, GdkInputCondition condition) { snd_seq_event_t *ev; static GtkAdjustment *adj=0; if(!adj) adj=(GtkAdjustment*) gtk_adjustment_new(0, 0, 96, 1, 1, 10); do { snd_seq_event_input(seq, &ev); if(!ev) continue; switch(ev->type) { case SND_SEQ_EVENT_CONTROLLER: #if 0 fprintf(stderr, "Channel %02d: Controller %03d: Value:%d\n", ev->data.control.channel, ev->data.control.param, ev->data.control.value); #endif if(ev->data.control.channel == ch) { currentvalue[ev->data.control.param]=ev->data.control.value; if(ev->data.control.param < maxstreams) { int stream=ev->data.control.param; long data=((stream/2+1)<<16)|(stream&1); gtk_adjustment_set_value(adj, midi2slider[ev->data.control.value]); mixer_adjust(adj, (gpointer)data); } else if(ev->data.control.param < maxstreams*2) { int b=ev->data.control.param-maxstreams; int left=-1, right=-1; if(b&1) right=ev->data.control.value; else left=ev->data.control.value; mixer_set_mute(b/2+1, left, right); } } break; case SND_SEQ_EVENT_PORT_SUBSCRIBED: #if 0 fprintf(stderr, "event subscribed send.client:%i dest.client:%i clientId:%i\n", (int)ev->data.connect.sender.client, (int)ev->data.connect.dest.client, clientId); #endif if(ev->data.connect.dest.client!=clientId) { int i; for(i=0; i!=128; ++i) if(currentvalue[i] >= 0) { /* set currentvalue[i] to a fake value, so the check in do_controller does not trigger */ int v=currentvalue[i]; currentvalue[i]=-1; do_controller(i, v); } } break; } snd_seq_free_event(ev); } while (snd_seq_event_input_pending(seq, 0) > 0); } /* ************************************************* */ /* C++ code to help calculating midi<->slider tables */ #if 0 #include #include #include using namespace std; int main() { int i; int midi2slider[128]; ///// your midi to slider conversion should be calculated here for(i=0; i<64; ++i) midi2slider[i]=(i*84)/64; for(i=0; i<64; ++i) midi2slider[i+64]=(i*13)/64+84; ///// end of your calculation // print map map m; int z=-1; cout << "static const int midi2slider_enh[128] = {" << endl; for(i=0; i<128; ++i) { int v=96-midi2slider[i]; cout << v << ", "; if(((++z)%8)==7) cout << endl; m[v]=z; } cout << "};" << endl; // now generate the reverse map vector rm; cout << "static const int slider2midi_enh[97] = {" << endl; int last=0; for(i=0; i<97; ++i) { int v=m[i]; if(v==0) v=last; last=v; rm.push_back(v); } z=-1; for(i=96; i>=0; --i) { cout << rm[i] << ", "; if(((++z)%8)==7) cout << endl; } cout << "};" << endl; return 0; } #endif mudita24-1.0.3+svn13/mixer.c0000644000175000017500000003244311647357353015306 0ustar alessioalessio/***************************************************************************** mixer.c - mixer code Copyright (C) 2000 by Jaroslav Kysela This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************/ #include "envy24control.h" #include "midi.h" #include "config.h" #define MULTI_PLAYBACK_SWITCH "Multi Playback Switch" #define MULTI_PLAYBACK_VOLUME "Multi Playback Volume" #define HW_MULTI_CAPTURE_SWITCH "H/W Multi Capture Switch" #define IEC958_MULTI_CAPTURE_SWITCH "IEC958 Multi Capture Switch" #define HW_MULTI_CAPTURE_VOLUME "H/W Multi Capture Volume" #define IEC958_MULTI_CAPTURE_VOLUME "IEC958 Multi Capture Volume" #define toggle_set(widget, state) \ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), state); static int stream_is_active[MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + \ MAX_INPUT_CHANNELS + MAX_SPDIF_CHANNELS]; extern int input_channels, output_channels, pcm_output_channels, spdif_channels, view_spdif_playback; static int is_active(GtkWidget *widget) { return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) ? 1 : 0; } void mixer_update_stream(int stream, int vol_flag, int sw_flag) { int err; //printf("mixer_update_stream stream:%d vol_flag:%d sw_flag:%d\n", stream, vol_flag, sw_flag); if (! stream_is_active[stream - 1]) return; if (vol_flag) { snd_ctl_elem_value_t *vol; int v[2]; snd_ctl_elem_value_alloca(&vol); snd_ctl_elem_value_set_interface(vol, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(vol, stream <= 10 ? MULTI_PLAYBACK_VOLUME : (stream <= 18 ? HW_MULTI_CAPTURE_VOLUME : IEC958_MULTI_CAPTURE_VOLUME)); snd_ctl_elem_value_set_index(vol, stream <= 18 ? (stream - 1) % 10 : (stream - 1) % 18 ); if ((err = snd_ctl_elem_read(ctl, vol)) < 0) g_print("Unable to read multi playback volume: %s\n", snd_strerror(err)); v[0] = snd_ctl_elem_value_get_integer(vol, 0); v[1] = snd_ctl_elem_value_get_integer(vol, 1); if (v[0] != v[1]) toggle_set(mixer_stereo_toggle[stream-1], FALSE); // TER: Stop jitter when adjusting sliders. //printf("mixer_update_stream cur val:%f new val:%d\n", GTK_ADJUSTMENT(mixer_adj[stream-1][0])->value, MAX_MIXER_ATTENUATION_VALUE - v[0]); if((gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(mixer_adj[stream-1][0])) != MAX_MIXER_ATTENUATION_VALUE - v[0]) gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][0]), MAX_MIXER_ATTENUATION_VALUE - v[0]); if((gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(mixer_adj[stream-1][1])) != MAX_MIXER_ATTENUATION_VALUE - v[1]) gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][1]), MAX_MIXER_ATTENUATION_VALUE - v[1]); midi_controller((stream-1)*2, v[0]); midi_controller((stream-1)*2+1, v[1]); } if (sw_flag) { snd_ctl_elem_value_t *sw; int v[2]; snd_ctl_elem_value_alloca(&sw); snd_ctl_elem_value_set_interface(sw, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(sw, stream <= 10 ? MULTI_PLAYBACK_SWITCH : (stream <= 18 ? HW_MULTI_CAPTURE_SWITCH : IEC958_MULTI_CAPTURE_SWITCH)); snd_ctl_elem_value_set_index(sw, stream <= 18 ? (stream - 1) % 10 : (stream - 1) % 18 ); if ((err = snd_ctl_elem_read(ctl, sw)) < 0) g_print("Unable to read multi playback switch: %s\n", snd_strerror(err)); v[0] = snd_ctl_elem_value_get_boolean(sw, 0); v[1] = snd_ctl_elem_value_get_boolean(sw, 1); if (v[0] != v[1]) toggle_set(mixer_stereo_toggle[stream-1], FALSE); toggle_set(mixer_mute_toggle[stream-1][0], !v[0] ? TRUE : FALSE); toggle_set(mixer_mute_toggle[stream-1][1], !v[1] ? TRUE : FALSE); midi_button((stream-1)*2, v[0]); midi_button((stream-1)*2+1, v[1]); } } static void set_switch1(int stream, int left, int right) { snd_ctl_elem_value_t *sw; int err, changed = 0; snd_ctl_elem_value_alloca(&sw); snd_ctl_elem_value_set_interface(sw, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(sw, stream <= 10 ? MULTI_PLAYBACK_SWITCH : (stream <= 18 ? HW_MULTI_CAPTURE_SWITCH : IEC958_MULTI_CAPTURE_SWITCH)); snd_ctl_elem_value_set_index(sw, stream <= 18 ? (stream - 1) % 10 : (stream - 1) % 18 ); if ((err = snd_ctl_elem_read(ctl, sw)) < 0) g_print("Unable to read multi switch: %s\n", snd_strerror(err)); if (left >= 0 && left != snd_ctl_elem_value_get_boolean(sw, 0)) { snd_ctl_elem_value_set_boolean(sw, 0, left); changed = 1; midi_button((stream-1)*2, left); } if (right >= 0 && right != snd_ctl_elem_value_get_boolean(sw, 1)) { snd_ctl_elem_value_set_boolean(sw, 1, right); changed = 1; midi_button((stream-1)*2+1, right); } if (changed) { err = snd_ctl_elem_write(ctl, sw); if (err < 0) g_print("Unable to write multi switch: %s\n", snd_strerror(err)); } } void mixer_toggled_mute(GtkWidget *togglebutton, gpointer data) { int stream = (long)data >> 16; int button = (long)data & 1; int stereo = is_active(mixer_stereo_toggle[stream-1]) ? 1 : 0; int mute; int vol[2] = { -1, -1 }; mute = is_active(mixer_mute_toggle[stream-1][button]); vol[button] = !mute; if (stereo) { toggle_set(mixer_mute_toggle[stream-1][button^1], mute); vol[button^1] = !mute; } set_switch1(stream, vol[0], vol[1]); } void mixer_set_mute(int stream, int left, int right) { int stereo = is_active(mixer_stereo_toggle[stream-1]) ? 1 : 0; if (left >= 0 || stereo) { toggle_set(mixer_mute_toggle[stream-1][0], left ? TRUE : FALSE); if(stereo && left<0) left=right; } if (right >= 0 || stereo) { toggle_set(mixer_mute_toggle[stream-1][1], right ? TRUE : FALSE); if(stereo && right<0) right=left; } set_switch1(stream, left, right); } static void set_volume1(int stream, int left, int right) { snd_ctl_elem_value_t *vol; int change = 0; int err; snd_ctl_elem_value_alloca(&vol); snd_ctl_elem_value_set_interface(vol, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(vol, stream <= 10 ? MULTI_PLAYBACK_VOLUME : (stream <= 18 ? HW_MULTI_CAPTURE_VOLUME : IEC958_MULTI_CAPTURE_VOLUME)); snd_ctl_elem_value_set_index(vol, stream <= 18 ? (stream - 1) % 10 : (stream - 1) % 18 ); if ((err = snd_ctl_elem_read(ctl, vol)) < 0) g_print("Unable to read multi volume: %s\n", snd_strerror(err)); if (left >= 0) { change |= (snd_ctl_elem_value_get_integer(vol, 0) != left); snd_ctl_elem_value_set_integer(vol, 0, left); midi_controller((stream-1)*2, left); } if (right >= 0) { change |= (snd_ctl_elem_value_get_integer(vol, 1) != right); snd_ctl_elem_value_set_integer(vol, 1, right); midi_controller((stream-1)*2+1, right); } if (change) { if ((err = snd_ctl_elem_write(ctl, vol)) < 0 && err != -EBUSY) g_print("Unable to write multi volume: %s\n", snd_strerror(err)); } } /* * NPM: mixer_volume_to_db() -- called out of mixer_adjust(). Use of proper * ALSA API snd_ctl_convert_to_dB() to return dB values suggested by * Tim E. Real on linux-audio-devel list. */ static char temp_label[16]; static char* mixer_volume_to_db(int stream, int ival) { if (ival != 0) { // g_print("mixer_volume_to_db(%i, %i)\n", stream, ival); snd_ctl_elem_id_t *elem_id; snd_ctl_elem_id_alloca(&elem_id); snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER); /* NPM: IEC958_MULTI_CAPTURE_VOLUME, for stream=19 or 20 gives incorrect * results, use HW_MULTI_CAPTURE_VOLUME for all. * Verified by TER. Those two controls have no dB values, but they * should, they're just part of the same mixer ! */ //snd_ctl_elem_id_set_name(elem_id, (stream <= 10) ? MULTI_PLAYBACK_VOLUME : (stream <= 18 ? HW_MULTI_CAPTURE_VOLUME : IEC958_MULTI_CAPTURE_VOLUME)); snd_ctl_elem_id_set_name(elem_id, (stream <= 10) ? MULTI_PLAYBACK_VOLUME : HW_MULTI_CAPTURE_VOLUME); /* TER: First line is 'proper' way, but second line is corrected * workaround for lack of dB values for IEC958 controls. */ //snd_ctl_elem_id_set_index(elem_id, stream <= 18 ? (stream - 1) % 10 : (stream - 1) % 18 ); snd_ctl_elem_id_set_index(elem_id, stream <= 18 ? (stream - 1) % 10 : 0 ); long db_gain = 0; snd_ctl_convert_to_dB(ctl, elem_id, ival, &db_gain); /* convert 'ival' attenuation to mixer from integer to dB for display */ float fval = ((float)db_gain / 100.0); if (fval > -10) sprintf(temp_label, "%+2.1f ", fval); if (fval < -100) sprintf(temp_label, "%+2.1f", fval); else sprintf(temp_label, "%+2.1f ", fval); } else sprintf(temp_label, "(Off) "); return (temp_label); } void mixer_adjust(GtkAdjustment *adj, gpointer data) { int stream = (long)data >> 16; int button = (long)data & 1; int stereo = is_active(mixer_stereo_toggle[stream-1]) ? 1 : 0; int vol[2] = { -1, -1 }; // TER: Stop jitter when adjusting sliders. int ival = (int)gtk_adjustment_get_value(adj); //printf("mixer_adjust stream:%d button:%d stereo:%d val:%f newval:%d ival:%d\n", // stream, button, stereo, gtk_adjustment_get_value(adj), (int)(MAX_MIXER_ATTENUATION_VALUE - gtk_adjustment_get_value(adj)), ival); //vol[button] = MAX_MIXER_ATTENUATION_VALUE - adj->value; vol[button] = MAX_MIXER_ATTENUATION_VALUE - ival; // TER if (stereo) { // TER: Changed //gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][button ^ 1]), adj->value); //vol[button ^ 1] = MAX_MIXER_ATTENUATION_VALUE - adj->value; gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][button ^ 1]), gtk_adjustment_get_value(adj)); vol[button ^ 1] = MAX_MIXER_ATTENUATION_VALUE - ival; } if (vol[0] != -1) { if (vol[0] <= (MAX_MIXER_ATTENUATION_VALUE - LOW_MIXER_ATTENUATION_VALUE)) vol[0] = MIN_MIXER_ATTENUATION_VALUE; /* reset to "off" if at bottom of shortened mixer scale */ gtk_label_set_text(GTK_LABEL(mixer_label[stream-1][0]), (gchar*)mixer_volume_to_db(stream, vol[0])); } if (vol[1] != -1) { if (vol[1] <= (MAX_MIXER_ATTENUATION_VALUE - LOW_MIXER_ATTENUATION_VALUE)) vol[1] = MIN_MIXER_ATTENUATION_VALUE; /* reset to "off" if at bottom of shortened mixer scale */ gtk_label_set_text(GTK_LABEL(mixer_label[stream-1][1]), (gchar*)mixer_volume_to_db(stream, vol[1])); } set_volume1(stream, vol[0], vol[1]); } int mixer_stream_is_active(int stream) { return stream_is_active[stream - 1]; } void mixer_init(void) { int i; int nb_active_channels; snd_ctl_elem_value_t *val; midi_maxstreams(sizeof(stream_is_active)/sizeof(stream_is_active[0])); snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); memset (stream_is_active, 0, (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + MAX_SPDIF_CHANNELS) * sizeof(int)); snd_ctl_elem_value_set_name(val, MULTI_PLAYBACK_SWITCH); nb_active_channels = 0; for (i = 0; i < pcm_output_channels; i++) { snd_ctl_elem_value_set_numid(val, 0); snd_ctl_elem_value_set_index(val, i); if (snd_ctl_elem_read(ctl, val) < 0) continue; stream_is_active[i] = 1; nb_active_channels++; } pcm_output_channels = nb_active_channels; for (i = MAX_PCM_OUTPUT_CHANNELS; i < MAX_PCM_OUTPUT_CHANNELS + spdif_channels; i++) { snd_ctl_elem_value_set_numid(val, 0); snd_ctl_elem_value_set_index(val, i); if (snd_ctl_elem_read(ctl, val) < 0) continue; stream_is_active[i] = 1; } snd_ctl_elem_value_set_name(val, HW_MULTI_CAPTURE_SWITCH); nb_active_channels = 0; for (i = 0; i < input_channels; i++) { snd_ctl_elem_value_set_numid(val, 0); snd_ctl_elem_value_set_index(val, i); if (snd_ctl_elem_read(ctl, val) < 0) continue; stream_is_active[i + MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS] = 1; nb_active_channels++; } input_channels = nb_active_channels; snd_ctl_elem_value_set_name(val, IEC958_MULTI_CAPTURE_SWITCH); for (i = 0; i < spdif_channels; i++) { snd_ctl_elem_value_set_numid(val, 0); snd_ctl_elem_value_set_index(val, i); if (snd_ctl_elem_read(ctl, val) < 0) continue; stream_is_active[i + MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS] = 1; } } void mixer_postinit(void) { int stream; for (stream = 1; stream <= pcm_output_channels; stream++) { if (stream_is_active[stream - 1]) mixer_update_stream(stream, 1, 1); } for (stream = MAX_PCM_OUTPUT_CHANNELS + 1; \ stream <= MAX_PCM_OUTPUT_CHANNELS + spdif_channels; stream++) { if (stream_is_active[stream - 1] && view_spdif_playback) mixer_update_stream(stream, 1, 1); } for (stream = MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + 1; \ stream <= MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + input_channels; stream++) { if (stream_is_active[stream - 1]) mixer_update_stream(stream, 1, 1); } for (stream = MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + 1; \ stream <= MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + spdif_channels; stream++) { if (stream_is_active[stream - 1]) mixer_update_stream(stream, 1, 1); } config_restore_stereo(); } mudita24-1.0.3+svn13/globaldefs.h.in0000755000175000017500000000057011647357353016675 0ustar alessioalessio //#cmakedefine HAVE_XXX //#cmakedefine XXX_SUPPORT #define VERSION "${mudita24_VERSION}" #define ALSACTL "${mudita24_ALSACTL}" #define SVNVERSION "${mudita24_SVNVER}" #define PACKAGE_NAME "mudita24" #define DOCDIR "${mudita24_DOC_DIR}" //#define SHAREDIR "${mudita24_SHARE_DIR}" //#define LIBDIR "${mudita24_LIB_DIR}" mudita24-1.0.3+svn13/hardware.c0000644000175000017500000007047211647357353015763 0ustar alessioalessio/***************************************************************************** hardware.c - Hardware Settings Copyright (C) 2000 by Jaroslav Kysela This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************/ #include "envy24control.h" static snd_ctl_elem_value_t *internal_clock; static snd_ctl_elem_value_t *internal_clock_default; static snd_ctl_elem_value_t *word_clock_sync; static snd_ctl_elem_value_t *rate_locking; static snd_ctl_elem_value_t *rate_reset; static snd_ctl_elem_value_t *volume_rate; static snd_ctl_elem_value_t *spdif_input; static snd_ctl_elem_value_t *spdif_output; static snd_ctl_elem_value_t *analog_input_select; static snd_ctl_elem_value_t *breakbox_led; static snd_ctl_elem_value_t *spdif_on_off; static snd_ctl_elem_value_t *phono_input; static snd_ctl_elem_value_t *iec958_in_status; /* NPM: add feature to display "Delta IEC958 Input Status" */ static inline int is_update_needed(void); #define toggle_set(widget, state) \ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), state); static int is_active(GtkWidget *widget) { return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) ? 1 : 0; } void master_clock_update(void) { int err, rate, need_default_update; if ((err = snd_ctl_elem_read(ctl, internal_clock)) < 0) g_print("Unable to read Internal Clock state: %s\n", snd_strerror(err)); if ((err = snd_ctl_elem_read(ctl, internal_clock_default)) < 0) g_print("Unable to read Internal Clock Default state: %s\n", snd_strerror(err)); if (card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 || card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010LT) { if ((err = snd_ctl_elem_read(ctl, word_clock_sync)) < 0) g_print("Unable to read word clock sync selection: %s\n", snd_strerror(err)); } if (snd_ctl_elem_value_get_enumerated(internal_clock, 0) == 13) { if (snd_ctl_elem_value_get_boolean(word_clock_sync, 0)) { toggle_set(hw_master_clock_word_radio, TRUE); } else { toggle_set(hw_master_clock_spdif_radio, TRUE); } } else { // toggle_set(hw_master_clock_xtal_radio, TRUE); need_default_update = !is_update_needed() ? 1 : 0; if (need_default_update) { rate = snd_ctl_elem_value_get_enumerated(internal_clock_default, 0); } else { rate = snd_ctl_elem_value_get_enumerated(internal_clock, 0); } switch (rate) { case 5: toggle_set(hw_master_clock_xtal_22050, TRUE); break; case 7: toggle_set(hw_master_clock_xtal_32000, TRUE); break; case 8: toggle_set(hw_master_clock_xtal_44100, TRUE); break; case 9: toggle_set(hw_master_clock_xtal_48000, TRUE); break; case 11: toggle_set(hw_master_clock_xtal_88200, TRUE); break; case 12: toggle_set(hw_master_clock_xtal_96000, TRUE); break; default: g_print("Error in rate: %d\n", rate); break; } } internal_clock_status_timeout_callback(NULL); master_clock_status_timeout_callback(NULL); } static void master_clock_word_select(int on) { int err; if (card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTA1010 && card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTA1010LT) return; snd_ctl_elem_value_set_boolean(word_clock_sync, 0, on ? 1 : 0); if ((err = snd_ctl_elem_write(ctl, word_clock_sync)) < 0) g_print("Unable to write word clock sync selection: %s\n", snd_strerror(err)); } static void internal_clock_set(int xrate) { int err; master_clock_word_select(0); snd_ctl_elem_value_set_enumerated(internal_clock, 0, xrate); if ((err = snd_ctl_elem_write(ctl, internal_clock)) < 0) g_print("Unable to write internal clock rate: %s\n", snd_strerror(err)); } void internal_clock_toggled(GtkWidget *togglebutton, gpointer data) { char *what = (char *) data; if (!is_active(togglebutton)) return; if (!strcmp(what, "22050")) { internal_clock_set(5); } else if (!strcmp(what, "32000")) { internal_clock_set(7); } else if (!strcmp(what, "44100")) { internal_clock_set(8); } else if (!strcmp(what, "48000")) { internal_clock_set(9); } else if (!strcmp(what, "88200")) { internal_clock_set(11); } else if (!strcmp(what, "96000")) { internal_clock_set(12); } else if (!strcmp(what, "SPDIF")) { internal_clock_set(13); } else if (!strcmp(what, "WordClock")) { internal_clock_set(13); // 13 should be considered '!internal' master_clock_word_select(1); } else { g_print("internal_clock_toggled: %s ???\n", what); } } static int is_rate_locked(void) { int err; if ((err = snd_ctl_elem_read(ctl, rate_locking)) < 0) g_print("Unable to read rate locking state: %s\n", snd_strerror(err)); return snd_ctl_elem_value_get_boolean(rate_locking, 0) ? 1 : 0; } static int is_rate_reset(void) { int err; if ((err = snd_ctl_elem_read(ctl, rate_reset)) < 0) g_print("Unable to read rate reset state: %s\n", snd_strerror(err)); return snd_ctl_elem_value_get_boolean(rate_reset, 0) ? 1 : 0; } static inline int is_update_needed(void) { return (is_rate_locked() || !is_rate_reset()); } gint master_clock_status_timeout_callback(gpointer data) { snd_ctl_elem_value_t *sw; int err; if (card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTA1010 && card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTA1010LT) return FALSE; snd_ctl_elem_value_alloca(&sw); snd_ctl_elem_value_set_interface(sw, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(sw, "Word Clock Status"); if ((err = snd_ctl_elem_read(ctl, sw)) < 0) g_print("Unable to determine word clock status: %s\n", snd_strerror(err)); gtk_label_set_text(GTK_LABEL(hw_master_clock_status_label), snd_ctl_elem_value_get_boolean(sw, 0) ? "No signal" : "Locked"); return TRUE; } gint internal_clock_status_timeout_callback(gpointer data) { int err, rate, need_update; char *label; if ((err = snd_ctl_elem_read(ctl, internal_clock)) < 0) g_print("Unable to read Internal Clock state: %s\n", snd_strerror(err)); if ((err = snd_ctl_elem_read(ctl, internal_clock_default)) < 0) g_print("Unable to read Internal Clock Default state: %s\n", snd_strerror(err)); if (card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 || card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010LT) { if ((err = snd_ctl_elem_read(ctl, word_clock_sync)) < 0) g_print("Unable to read word clock sync selection: %s\n", snd_strerror(err)); } need_update = is_update_needed() ? 1 : 0; if (snd_ctl_elem_value_get_enumerated(internal_clock, 0) == 13) { if (snd_ctl_elem_value_get_boolean(word_clock_sync, 0)) { label = "Word Clock"; } else { label = "S/PDIF"; } } else { // toggle_set(hw_master_clock_xtal_radio, TRUE); rate = snd_ctl_elem_value_get_enumerated(internal_clock, 0); // g_print("Rate: %d need_update: %d\n", rate, need_update); // for debug switch (rate) { case 0: label = "8000"; break; case 1: label = "9600"; break; case 2: label = "11025"; break; case 3: label = "12000"; break; case 4: label = "16000"; break; case 5: label = "22050"; if (need_update) toggle_set(hw_master_clock_xtal_22050, TRUE); break; case 6: label = "24000"; break; case 7: label = "32000"; if (need_update) toggle_set(hw_master_clock_xtal_32000, TRUE); break; case 8: label = "44100"; if (need_update) toggle_set(hw_master_clock_xtal_44100, TRUE); break; case 9: label = "48000"; if (need_update) toggle_set(hw_master_clock_xtal_48000, TRUE); break; case 10: label = "64000"; break; case 11: label = "88200"; if (need_update) toggle_set(hw_master_clock_xtal_88200, TRUE); break; case 12: label = "96000"; if (need_update) toggle_set(hw_master_clock_xtal_96000, TRUE); break; default: label = "ERROR"; g_print("Error in rate: %d\n", rate); break; } if (!need_update) { //default clock need update rate = snd_ctl_elem_value_get_enumerated(internal_clock_default, 0); switch (rate) { case 5: toggle_set(hw_master_clock_xtal_22050, TRUE); break; case 7: toggle_set(hw_master_clock_xtal_32000, TRUE); break; case 8: toggle_set(hw_master_clock_xtal_44100, TRUE); break; case 9: toggle_set(hw_master_clock_xtal_48000, TRUE); break; case 11: toggle_set(hw_master_clock_xtal_88200, TRUE); break; case 12: toggle_set(hw_master_clock_xtal_96000, TRUE); break; default: g_print("Error in rate: %d\n", rate); break; } } } gtk_label_set_text(GTK_LABEL(hw_master_clock_actual_rate_label), label); return TRUE; } gint rate_locking_status_timeout_callback(gpointer data) { int state; if (is_active(hw_rate_locking_check) != (state = is_rate_locked())) { toggle_set(hw_rate_locking_check, state ? TRUE : FALSE); } return TRUE; } gint rate_reset_status_timeout_callback(gpointer data) { int state; if (is_active(hw_rate_reset_check) != (state = is_rate_reset())) { toggle_set(hw_rate_reset_check, state ? TRUE : FALSE); } return TRUE; } /* NPM: add feature to display "Delta IEC958 Input Status" */ static int iec958_input_status_enabled = TRUE; gint iec958_input_status_timeout_callback(gpointer data) { if (gtk_widget_get_visible(hw_iec958_input_status_label) && iec958_input_status_enabled) { int err; if ((err = snd_ctl_elem_read(ctl, iec958_in_status)) < 0) { char temp_text[1024]; sprintf(temp_text, "Unable to read IEC958 Input Status:\n %s", snd_strerror(err)); gtk_label_set_markup(GTK_LABEL(hw_iec958_input_status_label), temp_text); iec958_input_status_enabled = FALSE; /* NPM: to prevent constant retries on HW that doesn't support this feature, if it fails the first time it tries, assume it won't succeed later */ } else if (snd_ctl_elem_value_get_boolean(iec958_in_status, 0)) gtk_label_set_markup(GTK_LABEL(hw_iec958_input_status_label), "Input Active"); else gtk_label_set_markup(GTK_LABEL(hw_iec958_input_status_label), "No Signal Detected"); } return TRUE; } void rate_locking_update(void) { int err; if ((err = snd_ctl_elem_read(ctl, rate_locking)) < 0) g_print("Unable to read rate locking state: %s\n", snd_strerror(err)); if (snd_ctl_elem_value_get_boolean(rate_locking, 0)) toggle_set(hw_rate_locking_check, TRUE); } void rate_reset_update(void) { int err; if ((err = snd_ctl_elem_read(ctl, rate_reset)) < 0) g_print("Unable to read rate reset state: %s\n", snd_strerror(err)); if (snd_ctl_elem_value_get_boolean(rate_reset, 0)) toggle_set(hw_rate_reset_check, TRUE); } static void rate_locking_set(int on) { int err; snd_ctl_elem_value_set_boolean(rate_locking, 0, on ? 1 : 0); if ((err = snd_ctl_elem_write(ctl, rate_locking)) < 0) g_print("Unable to write rate locking state: %s\n", snd_strerror(err)); } static void rate_reset_set(int on) { int err; snd_ctl_elem_value_set_boolean(rate_reset, 0, on ? 1 : 0); if ((err = snd_ctl_elem_write(ctl, rate_reset)) < 0) g_print("Unable to write rate reset state: %s\n", snd_strerror(err)); } void rate_locking_toggled(GtkWidget *togglebutton, gpointer data) { char *what = (char *) data; if (!is_active(togglebutton)) { rate_locking_set(0); return; } if (!strcmp(what, "locked")) { rate_locking_set(1); internal_clock_status_timeout_callback(NULL); } else { g_print("rate_locking_toggled: %s ???\n", what); } } void rate_reset_toggled(GtkWidget *togglebutton, gpointer data) { char *what = (char *) data; if (!is_active(togglebutton)) { rate_reset_set(0); internal_clock_status_timeout_callback(NULL); return; } if (!strcmp(what, "reset")) { rate_reset_set(1); } else { g_print("rate_reset_toggled: %s ???\n", what); } } void volume_change_rate_update(void) { int err; if ((err = snd_ctl_elem_read(ctl, volume_rate)) < 0) g_print("Unable to read volume change rate: %s\n", snd_strerror(err)); gtk_adjustment_set_value(GTK_ADJUSTMENT(hw_volume_change_adj), snd_ctl_elem_value_get_integer(volume_rate, 0)); } void volume_change_rate_adj(GtkAdjustment *adj, gpointer data) { int err; snd_ctl_elem_value_set_integer(volume_rate, 0, gtk_adjustment_get_value(adj)); if ((err = snd_ctl_elem_write(ctl, volume_rate)) < 0) g_print("Unable to write volume change rate: %s\n", snd_strerror(err)); } void spdif_output_update(void) { int err; snd_aes_iec958_t iec958; if ((err = snd_ctl_elem_read(ctl, spdif_output)) < 0) { if (err == -ENOENT) return; g_print("Unable to read Delta S/PDIF output state: %s\n", snd_strerror(err)); } snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!(iec958.status[0] & IEC958_AES0_PROFESSIONAL)) { /* consumer */ toggle_set(hw_spdif_consumer_radio, TRUE); if (iec958.status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) { toggle_set(hw_consumer_copyright_off_radio, TRUE); } else { toggle_set(hw_consumer_copyright_on_radio, TRUE); } if ((iec958.status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) { toggle_set(hw_consumer_emphasis_none_radio, TRUE); } else { toggle_set(hw_consumer_emphasis_5015_radio, TRUE); } switch (iec958.status[1] & IEC958_AES1_CON_CATEGORY) { case IEC958_AES1_CON_MAGNETIC_ID: toggle_set(hw_consumer_category_dat_radio, TRUE); break; case IEC958_AES1_CON_DIGDIGCONV_ID: toggle_set(hw_consumer_category_pcm_radio, TRUE); break; case IEC958_AES1_CON_GENERAL: toggle_set(hw_consumer_category_general_radio, TRUE); break; case IEC958_AES1_CON_LASEROPT_ID: default: toggle_set(hw_consumer_category_cd_radio, TRUE); break; } if (iec958.status[1] & IEC958_AES1_CON_ORIGINAL) { toggle_set(hw_consumer_copy_original_radio, TRUE); } else { toggle_set(hw_consumer_copy_1st_radio, TRUE); } } else { toggle_set(hw_spdif_professional_radio, TRUE); if (!(iec958.status[0] & IEC958_AES0_NONAUDIO)) { toggle_set(hw_spdif_profi_audio_radio, TRUE); } else { toggle_set(hw_spdif_profi_nonaudio_radio, TRUE); } switch (iec958.status[0] & IEC958_AES0_PRO_EMPHASIS) { case IEC958_AES0_PRO_EMPHASIS_CCITT: toggle_set(hw_profi_emphasis_ccitt_radio, TRUE); break; case IEC958_AES0_PRO_EMPHASIS_NONE: toggle_set(hw_profi_emphasis_none_radio, TRUE); break; case IEC958_AES0_PRO_EMPHASIS_5015: toggle_set(hw_profi_emphasis_5015_radio, TRUE); break; case IEC958_AES0_PRO_EMPHASIS_NOTID: default: toggle_set(hw_profi_emphasis_notid_radio, TRUE); break; } if ((iec958.status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) { toggle_set(hw_profi_stream_stereo_radio, TRUE); } else { toggle_set(hw_profi_stream_notid_radio, TRUE); } } } static void spdif_output_write(void) { int err; if ((err = snd_ctl_elem_write(ctl, spdif_output)) < 0) g_print("Unable to write Delta S/PDIF Output Defaults: %s\n", snd_strerror(err)); } void profi_data_toggled(GtkWidget *togglebutton, gpointer data) { char *str = (char *)data; snd_aes_iec958_t iec958; snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!is_active(togglebutton)) return; if (!(iec958.status[0] & IEC958_AES0_PROFESSIONAL)) return; if (!strcmp(str, "Audio")) { iec958.status[0] &= ~IEC958_AES0_NONAUDIO; } else if (!strcmp(str, "Non-audio")) { iec958.status[0] |= IEC958_AES0_NONAUDIO; } snd_ctl_elem_value_set_iec958(spdif_output, &iec958); spdif_output_write(); } void profi_stream_toggled(GtkWidget *togglebutton, gpointer data) { char *str = (char *)data; snd_aes_iec958_t iec958; if (!is_active(togglebutton)) return; snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!(iec958.status[0] & IEC958_AES0_PROFESSIONAL)) return; iec958.status[1] &= ~IEC958_AES1_PRO_MODE; if (!strcmp(str, "NOTID")) { iec958.status[0] |= IEC958_AES1_PRO_MODE_STEREOPHONIC; } else if (!strcmp(str, "Stereo")) { iec958.status[0] |= IEC958_AES1_PRO_MODE_NOTID; } snd_ctl_elem_value_set_iec958(spdif_output, &iec958); spdif_output_write(); } void profi_emphasis_toggled(GtkWidget *togglebutton, gpointer data) { char *str = (char *)data; snd_aes_iec958_t iec958; snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!is_active(togglebutton)) return; if (!(iec958.status[0] & IEC958_AES0_PROFESSIONAL)) return; iec958.status[0] &= ~IEC958_AES0_PRO_EMPHASIS; if (!strcmp(str, "CCITT")) { iec958.status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; } else if (!strcmp(str, "No")) { iec958.status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; } else if (!strcmp(str, "5015")) { iec958.status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; } else if (!strcmp(str, "NOTID")) { iec958.status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; } snd_ctl_elem_value_set_iec958(spdif_output, &iec958); spdif_output_write(); } void consumer_copyright_toggled(GtkWidget *togglebutton, gpointer data) { char *str = (char *)data; snd_aes_iec958_t iec958; snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!is_active(togglebutton)) return; if (iec958.status[0] & IEC958_AES0_PROFESSIONAL) return; if (!strcmp(str, "Copyright")) { iec958.status[0] &= ~IEC958_AES0_CON_NOT_COPYRIGHT; } else if (!strcmp(str, "Permitted")) { iec958.status[1] |= IEC958_AES0_CON_NOT_COPYRIGHT; } snd_ctl_elem_value_set_iec958(spdif_output, &iec958); spdif_output_write(); } void consumer_copy_toggled(GtkWidget *togglebutton, gpointer data) { char *str = (char *)data; snd_aes_iec958_t iec958; snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!is_active(togglebutton)) return; if (iec958.status[0] & IEC958_AES0_PROFESSIONAL) return; if (!strcmp(str, "1st")) { iec958.status[0] |= IEC958_AES1_CON_ORIGINAL; } else if (!strcmp(str, "Original")) { iec958.status[1] &= ~IEC958_AES1_CON_ORIGINAL; } snd_ctl_elem_value_set_iec958(spdif_output, &iec958); spdif_output_write(); } void consumer_emphasis_toggled(GtkWidget *togglebutton, gpointer data) { char *str = (char *)data; snd_aes_iec958_t iec958; snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!is_active(togglebutton)) return; if (iec958.status[0] & IEC958_AES0_PROFESSIONAL) return; iec958.status[0] &= ~IEC958_AES0_CON_EMPHASIS; if (!strcmp(str, "No")) { iec958.status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; } else if (!strcmp(str, "5015")) { iec958.status[1] |= ~IEC958_AES0_CON_EMPHASIS_5015; } snd_ctl_elem_value_set_iec958(spdif_output, &iec958); spdif_output_write(); } void consumer_category_toggled(GtkWidget *togglebutton, gpointer data) { char *str = (char *)data; snd_aes_iec958_t iec958; snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!is_active(togglebutton)) return; if (iec958.status[0] & IEC958_AES0_PROFESSIONAL) return; iec958.status[0] &= ~IEC958_AES1_CON_CATEGORY; if (!strcmp(str, "DAT")) { iec958.status[0] |= IEC958_AES1_CON_DAT; } else if (!strcmp(str, "PCM")) { iec958.status[0] |= IEC958_AES1_CON_PCM_CODER; } else if (!strcmp(str, "CD")) { iec958.status[0] |= IEC958_AES1_CON_IEC908_CD; } else if (!strcmp(str, "General")) { iec958.status[0] |= IEC958_AES1_CON_GENERAL; } snd_ctl_elem_value_set_iec958(spdif_output, &iec958); spdif_output_write(); } void spdif_output_toggled(GtkWidget *togglebutton, gpointer data) { char *str = (char *)data; snd_aes_iec958_t iec958; int page; if (is_active(togglebutton)) { snd_ctl_elem_value_get_iec958(spdif_output, &iec958); if (!strcmp(str, "Professional")) { if (!(iec958.status[0] & IEC958_AES0_PROFESSIONAL)) { /* default setup: audio, no emphasis */ memset(&iec958, 0, sizeof(iec958)); iec958.status[0] = IEC958_AES0_PROFESSIONAL | IEC958_AES0_PRO_EMPHASIS_NONE | IEC958_AES0_PRO_FS_48000; iec958.status[1] = IEC958_AES1_PRO_MODE_STEREOPHONIC; snd_ctl_elem_value_set_iec958(spdif_output, &iec958); } page = 0; } else { if (iec958.status[0] & IEC958_AES0_PROFESSIONAL) { /* default setup: no emphasis, PCM encoder */ memset(&iec958, 0, sizeof(iec958)); iec958.status[0] = IEC958_AES0_CON_EMPHASIS_NONE; iec958.status[1] = IEC958_AES1_CON_PCM_CODER | IEC958_AES1_CON_ORIGINAL; iec958.status[3] = IEC958_AES3_CON_FS_48000; snd_ctl_elem_value_set_iec958(spdif_output, &iec958); } page = 1; } spdif_output_write(); gtk_notebook_set_current_page(GTK_NOTEBOOK(hw_spdif_output_notebook), page); spdif_output_update(); } } void spdif_input_update(void) { int err; int digoptical = FALSE; int diginternal = FALSE; if ((card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTADIO2496) && ! card_is_dmx6fire) return; if ((err = snd_ctl_elem_read(ctl, spdif_input)) < 0) g_print("Unable to read S/PDIF input switch: %s\n", snd_strerror(err)); if (snd_ctl_elem_value_get_boolean(spdif_input, 0)) digoptical = TRUE; if (card_is_dmx6fire) { if ((err = snd_ctl_elem_read(ctl, spdif_on_off)) < 0) g_print("Unable to read S/PDIF on/off switch: %s\n", snd_strerror(err)); if (!(snd_ctl_elem_value_get_boolean(spdif_on_off, 0))) diginternal = TRUE; } if (digoptical) { toggle_set(hw_spdif_input_optical_radio, TRUE); } else { toggle_set(hw_spdif_input_coaxial_radio, TRUE); } if (diginternal) toggle_set(hw_spdif_switch_off_radio, TRUE); } void spdif_input_toggled(GtkWidget *togglebutton, gpointer data) { int err; char *str = (char *)data; if (!is_active(togglebutton)) return; if (!strcmp(str, "Off")) snd_ctl_elem_value_set_boolean(spdif_on_off, 0, 0); else { snd_ctl_elem_value_set_boolean(spdif_on_off, 0, 1); if (!strcmp(str, "Optical")) snd_ctl_elem_value_set_boolean(spdif_input, 0, 1); else if (!strcmp(str, "Coaxial")) snd_ctl_elem_value_set_boolean(spdif_input, 0, 0); } if ((err = snd_ctl_elem_write(ctl, spdif_on_off)) < 0) g_print("Unable to write S/PDIF on/off switch: %s\n", snd_strerror(err)); if ((err = snd_ctl_elem_write(ctl, spdif_input)) < 0) g_print("Unable to write S/PDIF input switch: %s\n", snd_strerror(err)); } void analog_input_select_update(void) { int err, input_interface; if (! card_is_dmx6fire) return; if ((err = snd_ctl_elem_read(ctl, analog_input_select)) < 0) g_print("Unable to read analog input switch: %s\n", snd_strerror(err)); input_interface = snd_ctl_elem_value_get_enumerated(analog_input_select, 0); switch (input_interface) { case 0: toggle_set(input_interface_internal, TRUE); break; case 1: toggle_set(input_interface_front_input, TRUE); break; case 2: toggle_set(input_interface_rear_input, TRUE); break; case 3: toggle_set(input_interface_wavetable, TRUE); break; default: g_print("Error in analogue input: %d\n", input_interface); break; } } void analog_input_select_set(int value) { int err; snd_ctl_elem_value_set_enumerated(analog_input_select, 0, value); if ((err = snd_ctl_elem_write(ctl, analog_input_select)) < 0) g_print("Unable to write analog input selection: %s\n", snd_strerror(err)); } void analog_input_select_toggled(GtkWidget *togglebutton, gpointer data) { char *what = (char *) data; int err; if (!is_active(togglebutton)) return; if (!strcmp(what, "Internal")) { analog_input_select_set(0); snd_ctl_elem_value_set_boolean(breakbox_led, 0, 0); } else if (!strcmp(what, "Front Input")) { analog_input_select_set(1); snd_ctl_elem_value_set_boolean(breakbox_led, 0, 1); } else if (!strcmp(what, "Rear Input")) { analog_input_select_set(2); snd_ctl_elem_value_set_boolean(breakbox_led, 0, 0); } else if (!strcmp(what, "Wave Table")) { analog_input_select_set(3); snd_ctl_elem_value_set_boolean(breakbox_led, 0, 0); } else { g_print("analog_input_select_toggled: %s ???\n", what); } if ((err = snd_ctl_elem_write(ctl, breakbox_led)) < 0) g_print("Unable to write breakbox LED switch: %s\n", snd_strerror(err)); } void phono_input_update(void) { int err; if (! card_is_dmx6fire) return; if ((err = snd_ctl_elem_read(ctl, phono_input)) < 0) g_print("Unable to read phono input switch: %s\n", snd_strerror(err)); if (snd_ctl_elem_value_get_boolean(phono_input, 0)) { toggle_set(hw_phono_input_on_radio, TRUE); } else { toggle_set(hw_phono_input_off_radio, TRUE); } } void phono_input_toggled(GtkWidget *togglebutton, gpointer data) { int err; char *str = (char *) data; if (!is_active(togglebutton)) return; if (!strcmp(str, "Phono")) snd_ctl_elem_value_set_boolean(phono_input, 0, 1); else snd_ctl_elem_value_set_boolean(phono_input, 0, 0); if ((err = snd_ctl_elem_write(ctl, phono_input)) < 0) g_print("Unable to write phono input switch: %s\n", snd_strerror(err)); } void hardware_init(void) { if (snd_ctl_elem_value_malloc(&internal_clock) < 0 || snd_ctl_elem_value_malloc(&internal_clock_default) < 0 || snd_ctl_elem_value_malloc(&word_clock_sync) < 0 || snd_ctl_elem_value_malloc(&rate_locking) < 0 || snd_ctl_elem_value_malloc(&rate_reset) < 0 || snd_ctl_elem_value_malloc(&volume_rate) < 0 || snd_ctl_elem_value_malloc(&spdif_input) < 0 || snd_ctl_elem_value_malloc(&spdif_output) < 0 || snd_ctl_elem_value_malloc(&analog_input_select) < 0 || snd_ctl_elem_value_malloc(&breakbox_led) < 0 || snd_ctl_elem_value_malloc(&spdif_on_off) < 0 || snd_ctl_elem_value_malloc(&phono_input) < 0 || snd_ctl_elem_value_malloc(&iec958_in_status) < 0) { /* NPM: add feature to display "Delta IEC958 Input Status" */ g_print("Cannot allocate memory\n"); exit(1); } snd_ctl_elem_value_set_interface(internal_clock, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(internal_clock, "Multi Track Internal Clock"); snd_ctl_elem_value_set_interface(internal_clock_default, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(internal_clock_default, "Multi Track Internal Clock Default"); snd_ctl_elem_value_set_interface(word_clock_sync, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(word_clock_sync, "Word Clock Sync"); snd_ctl_elem_value_set_interface(rate_locking, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(rate_locking, "Multi Track Rate Locking"); snd_ctl_elem_value_set_interface(rate_reset, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(rate_reset, "Multi Track Rate Reset"); snd_ctl_elem_value_set_interface(volume_rate, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(volume_rate, "Multi Track Volume Rate"); if (card_is_dmx6fire) { snd_ctl_elem_value_set_interface(spdif_input, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(spdif_input, "Optical Digital Input Switch"); } else { snd_ctl_elem_value_set_interface(spdif_input, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(spdif_input, "IEC958 Input Optical"); } snd_ctl_elem_value_set_interface(spdif_output, SND_CTL_ELEM_IFACE_PCM); snd_ctl_elem_value_set_name(spdif_output, "IEC958 Playback Default"); snd_ctl_elem_value_set_interface(analog_input_select, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(analog_input_select, "Analog Input Select"); snd_ctl_elem_value_set_interface(breakbox_led, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(breakbox_led, "Breakbox LED"); snd_ctl_elem_value_set_interface(spdif_on_off, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(spdif_on_off, "Front Digital Input Switch"); snd_ctl_elem_value_set_interface(phono_input, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(phono_input, "Phono Analog Input Switch"); snd_ctl_elem_value_set_interface(iec958_in_status, SND_CTL_ELEM_IFACE_MIXER); /* NPM: add feature to display "Delta IEC958 Input Status" */ snd_ctl_elem_value_set_name(iec958_in_status, "Delta IEC958 Input Status"); /* NPM: add feature to display "Delta IEC958 Input Status" */ } void hardware_postinit(void) { master_clock_update(); rate_locking_update(); rate_reset_update(); volume_change_rate_update(); spdif_input_update(); spdif_output_update(); analog_input_select_update(); phono_input_update(); if (card_has_delta_iec958_input_status) iec958_input_status_timeout_callback(NULL); /* NPM */ } mudita24-1.0.3+svn13/README.profiles0000644000175000017500000001106111647357353016511 0ustar alessioalessio Profiles management as part of alsa 2004-05-16 Dirk Kalis General ======= Profiles management can be used from all applications like mixers or hardware control programs. It uses always alsactl and if directorys for the profiles file doesn't exists - mkdir. profiles file means the file in which the profiles will be stored. For other application the following files are needed: profiles.h - header file with the exported functions profiles.c - profiles implementation new_process.c - used to start external programs (alsactl and mkdir) strstr_icase_blank.c - string search function with ignoring case sensitivity, number of blanks, empty and comment lines (first non blank character '#') Profile numbers beginning with number 1 not 0 ! Introduction ============ Profiles management stores and restores alsactl settings in a profiles file. Every card is stored in the profiles and every card profile can have a profile name. The profiles file has the following structure: [ profile ] < Card > { / > -----next card or next profile or end of file----- The header for profile name and card footer are optional. The functions in profile.c write always a card footer. DO NOT EDIT THIS FILE MANUALLY BECAUSE EVERY WRITE ACCESS MAKE A REORGANIZATION AND COMMENTS NOT WRITTEN IN THE ALSACTL SECTION WILL BE REMOVED! ALSO THE STRUCTURE OF THE FILE WILL BE MODIFIED! With the environment variables ALACTL_PROG and MKDIR_PROG can the compiled defaults for this programs be overwritten. e.g.: export ALSACTL_PROG=;export MKDIR_PROG=; This pathes must not be a link ! The profiles file name and path can explicit given. If is not given (by value NULL) two defined defaults will be used. Two variables can be set in the profiles header file for the default profiles file and for the system profiles file. The default for DEFAULT_PROFILERC is ~//profiles.conf. The default for SYS_PROFILERC is /etc//profiles.conf The profiles management search for profiles file if no profiles file name is given. Search order for restore: if not -> if doesn't exists -> Search order for save: if not -> The SYS_PROFILERC can only be written as root by explicit given profiles file name or you make a copy as root from one of the DEFAULT_PROFILERC. This files must not be a link! The delete_card function can be used to remove card profiles from profiles file if a card is removed from your pc. Miscellaneous cards can have same profile names - thats no problem. One card with equal profile names is also no problem if you use profile number for searching. If you use profile name for searching you will allways get the least profile number with this profile name. Profiles management in mudita24 ==================================== In mudita24 you can find a new map named "Profiles". In this map you can store 8 profiles for every envy24 card. You can change/give name by activating this profile and then click in the name entry. You can only change the name for the active profile. If all settings in the active profile are done you can save settings by clicking button "Save active profile". Error messages will only displayed on stderr in the text window if you start mudita24 from xterm. To activate a profile click only the profile button. You can start mudita24 with given profiles file and a default profile. if a default profile is given after initialisation of mudita24 this profile will be activated. Default profile can be given by profile number or profile name. The accuracy of discrimination works in the following way: - if the given default profile contains a letter -> profile name - if the given default profile has a number outside the range [1 ... MAX_PROFILES] -> profile name - the given default profile is interpreted as profile number. Profile names should have at least one letter. For "Delete card from profiles" and equal profile names read above. One card with equal names is only a problem if you start mudita24 with default profile name. You activate allways the profile with the least profile number with this profile name. Copyright ========= Copyright Dirk Kalis Modified for mudita24 (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge). This is free and can be distributed under GPL. mudita24-1.0.3+svn13/levelmeters.c0000644000175000017500000006226411647357353016515 0ustar alessioalessio/***************************************************************************** levelmeters.c - Stereo level meters Copyright (C) 2000 by Jaroslav Kysela Missing peak-level meter feature, and associated rewrite of metering for lower X resource usage, along with using a logarithmic display that attempts correspondence to mixer labels; updating text display of these levels in dBFS appears in their associated panels. Copyright (C) 2010 by Niels Mayer ( http://nielsmayer.com ). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************/ #include #include "envy24control.h" static GdkGC *penWhiteLight[21] = { NULL, }; static GdkGC *penGreenLight[21] = { NULL, }; static GdkGC *penForeground[21] = { NULL, }; static GdkGC *penBackground[21] = { NULL, }; static GdkGC *penOrangeLight[21] = { NULL, }; static GdkGC *penRedLight[21] = { NULL, }; static GdkColor *peak_label_color = NULL; /* NPM set in level_meters_configure_event() */ static GdkPixmap *pixmap[21] = { NULL, }; static snd_ctl_elem_value_t *peaks; extern int input_channels, output_channels, pcm_output_channels, spdif_channels, view_spdif_playback; static void update_peak_switch(void) { int err; if ((err = snd_ctl_elem_read(ctl, peaks)) < 0) g_print("Unable to read peaks: %s\n", snd_strerror(err)); } /* * Niels Mayer (NPM) Jul-11-10: Fixing https://bugzilla.redhat.com/show_bug.cgi?id=602903 * by implementing peak-level meters. The http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf * soundchip provides hardware level meters and these are returned via ALSA as such: * >> amixer -c M66 cget iface=PCM,name='Multi Track Peak',numid=45 * ; type=INTEGER,access=r-------,values=22,min=0,max=255,step=0 * : values=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,198,255,198 * These globals store the peak levels of the meters across callbacks. */ #define RESET -1 // NPM: Note special case in original envy24control code, which puts stereo mix track at // "index 0" which is actually "(peaks, 20)" and "(peaks, 21)" cases below // thus peak_lmix <--> peak_levels[IDX_LMIX] ; peak_rmix <--> peak_levels[IDX_RMIX] static int peak_levels[MULTI_TRACK_PEAK_CHANNELS] = {0,}; static int peak_changed[MULTI_TRACK_PEAK_CHANNELS] = {0,}; static int previous_levels[MULTI_TRACK_PEAK_CHANNELS] = {0,}; /* NPM: changed for https://bugzilla.redhat.com/show_bug.cgi?id=602903 */ static void get_levels(int idx, int *l1, int *l2) { *l1 = *l2 = 0; if (idx == 0) { /* "if (stereo)" -- special case idx=0 as digital mix pair */ if ((peak_changed[IDX_LMIX] != RESET) && (peak_changed[IDX_RMIX] != RESET)) { /* don't change values if doing "Reset Peaks" */ if ((*l1 = snd_ctl_elem_value_get_integer(peaks, IDX_LMIX)) > peak_levels[IDX_LMIX]) { peak_levels[IDX_LMIX] = (*l1); peak_changed[IDX_LMIX] = TRUE; } if ((*l2 = snd_ctl_elem_value_get_integer(peaks, IDX_RMIX)) > peak_levels[IDX_RMIX]) { peak_levels[IDX_RMIX] = (*l2); peak_changed[IDX_RMIX] = TRUE; } } } else { if (peak_changed[idx-1] != RESET) { if ((*l1 = snd_ctl_elem_value_get_integer(peaks, idx - 1)) > peak_levels[idx - 1]) { peak_levels[idx - 1] = (*l1); peak_changed[idx - 1] = TRUE; } } } } static GdkGC *get_pen(int idx, int nRed, int nGreen, int nBlue) { GdkColor *c; GdkGC *gc; c = (GdkColor *)g_malloc(sizeof(GdkColor)); c->red = nRed; c->green = nGreen; c->blue = nBlue; gdk_color_alloc(gdk_colormap_get_system(), c); gc = gdk_gc_new(pixmap[idx]); gdk_gc_set_foreground(gc, c); return gc; } static int get_index(const gchar *name) { int result; if (!strcmp(name, "DigitalMixer")) return 0; result = atoi(name + 5); if (result < 1 || result > 20) { g_print("Wrong drawing area ID: %s\n", name); gtk_main_quit(); } return result; } /* * NPM: per http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf the * the values in each entry of iface=PCM,name='Multi Track Peak',numid=45 * represent: "Peak data derived from the absolute value of 9 msb. 00h * min - FFh max volume. Reading the register resets the meter to 00h." */ //NPM difft colors for -1dB, -3dB, and -6dB peak levels #define GET_COLOR_FOR_PEAKLEVEL(level) \ (level > 228) \ ? (penRedLight[idx]) \ : ((level > 181) \ ? (penOrangeLight[idx]) \ : ((level > 128) \ ? (penWhiteLight[idx]) \ : (penGreenLight[idx]))) /* * NPM: Called by redraw_meters(), this is a special case for when "Reset * Peaks" is clicked: then just refresh meters, ignore value, clear RESET * and let the next pass-through draw for the first time in the cleared * meter... */ static void draw_meters_reset(int idx, int width, int height, int stereo, int segment_width) { // GdkColor color; GtkWidget* lbl; // if (!gdk_color_parse("bg", &color)) // g_print("gdk_color_parse('bg') fail\n"); gdk_draw_rectangle(pixmap[idx], penBackground[idx], TRUE, //X 6, //Y 0, //WIDTH segment_width, //HEIGHT height ); /* NPM: Reset peak labels for "Monitor Inputs" and "Monitor PCMs" panels */ gtk_label_set_text(GTK_LABEL((stereo) ? peak_label[IDX_LMIX] : peak_label[idx-1]), peak_level_to_db((stereo) ? peak_levels[IDX_LMIX] : peak_levels[idx-1])); /* put new value in label */ gtk_widget_modify_fg((stereo) ? peak_label[IDX_LMIX] : peak_label[idx-1], GTK_STATE_NORMAL, NULL); /* NPM: Reset "Analog Volume" panel's peak levels -- never happens for "stereo" case */ if (!stereo) { /* NPM: Reset peak labels for DACs in "Analog Volume" panel */ if (((idx-1) >= 0) && ((idx-1) < envy_dac_volumes())) { /* index 0-8 corresponds to one of the eight DAC's */ if (((idx-1) < MAX_OUTPUT_CHANNELS) /* make sure within bounds of dac_peak_label[] */ && (lbl = dac_peak_label[idx-1]) != NULL) { gtk_label_set_text(GTK_LABEL(lbl), peak_level_to_db(peak_levels[idx-1])); /* put new value in label */ gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, NULL); /* reset fg color */ } } /* NPM: Reset peak labels for ADCs in "Analog Volume" panel */ else if (((idx-1) >= (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) /* ADC channels begin at 11 = MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS */ && ((idx-1) < (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS + envy_adc_volumes()))) { /* ADC channels end at 19 */ if ((((idx-1) - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) < MAX_INPUT_CHANNELS) /* make sure within bounds of adc_peak_label[] */ && (lbl = adc_peak_label[(idx-1) - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)]) != NULL) { gtk_label_set_text(GTK_LABEL(lbl), peak_level_to_db(peak_levels[idx-1])); /* put new value in label */ gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, NULL); /* reset fg color */ } } } if (stereo) { gdk_draw_rectangle(pixmap[idx], penBackground[idx], TRUE, //X 2 + (width / 2), //Y 0, //WIDTH segment_width, //HEIGHT height ); /* put new value in label */ gtk_label_set_text(GTK_LABEL(peak_label[IDX_RMIX]), peak_level_to_db(peak_levels[IDX_RMIX])); gtk_widget_modify_fg(peak_label[IDX_RMIX], GTK_STATE_NORMAL, NULL); peak_changed[IDX_LMIX] = FALSE; peak_changed[IDX_RMIX] = FALSE; } else { peak_changed[idx-1] = FALSE; } } /* * NPM: Called through redraw_meters(), via draw_meters_and_peaks(), * this handles updating gtk labels/colors related to updated peak values. * Note 'index' [0 to MULTI_TRACK_PEAK_CHANNELS-1] into global peak_label[] * which is initialized by envy24control.c:create_mixer_frame(). */ static void draw_peak_labels(int index, int stereo, int peak1_level, int peak2_level) { GtkWidget* lbl; gtk_label_set_text(GTK_LABEL((stereo) ? peak_label[IDX_LMIX] : peak_label[index]), peak_level_to_db(peak1_level)); /* put new value in label */ if (peak1_level >= MAX_METERING_LEVEL) { /* if at 0dB, make label red; RESET reverts to normal color */ lbl = (stereo) ? peak_label[IDX_LMIX] : peak_label[index]; gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, peak_label_color); } /* NPM: Update "Analog Volume" panel's peak levels: the normal non-"stereo" case of single channel peak monitors of input or PCMs */ if (!stereo) { /* NPM: Update peak labels for DACs in "Analog Volume" panel */ if ((index >= 0) && (index < envy_dac_volumes())) { /* index 0-8 corresponds to one of the eight DAC's */ if ((index < MAX_OUTPUT_CHANNELS) /* make sure within bounds of dac_peak_label[] */ && (lbl = dac_peak_label[index]) != NULL) { gtk_label_set_text(GTK_LABEL(lbl), peak_level_to_db(peak1_level)); /* put new value in label */ if (peak1_level >= MAX_METERING_LEVEL) gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, peak_label_color); } } /* NPM: Update peak labels for ADCs in "Analog Volume" panel */ else if ((index >= (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) /* ADC channels begin at 11 = MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS */ && (index <= (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS + envy_adc_volumes()))) { /* ADC channels end at 19 */ if (((index - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) < MAX_INPUT_CHANNELS) /* make sure within bounds of adc_peak_label[] */ && (lbl = adc_peak_label[index - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)]) != NULL) { gtk_label_set_text(GTK_LABEL(lbl), peak_level_to_db(peak1_level)); /* put new value in label */ if (peak1_level >= MAX_METERING_LEVEL) gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, peak_label_color); } } } /* Handle special "stereo" case for right-channel of digital mixer */ else if (peak_changed[IDX_RMIX]) { //for stereo, draw RMIX, but skip redraw if same gtk_label_set_text(GTK_LABEL(peak_label[IDX_RMIX]), peak_level_to_db(peak2_level)); /* put new value in label */ if (peak2_level >= MAX_METERING_LEVEL) /* if at 0dB, make label "selected"; RESET reverts to normal color */ gtk_widget_modify_fg(peak_label[IDX_RMIX], GTK_STATE_NORMAL, peak_label_color); } } //NPM: below, fudging value 51.0 instead of 48.164799306 = 20*log10(1/256) // or 48.130803609 = 20*log10(1/255) so as to offset meter in direction of // scale markings. #define METER_LEVEL(level, height) \ ((level==0) \ ? 0 \ : ((int) (((double)(height)) \ * ((20.0 * log10(((double)(level)) \ / ((double)MAX_METERING_LEVEL)) \ + 51.0) \ / 51.0) ))-1 \ ) /* * NPM: Called by redraw_meters(), this is the normal case * where we draw the meter and peaks, if changed. */ static void draw_meters_and_peaks(int idx, int width, int height, int level1, int level2, int stereo, int segment_width) { int meter1 = (stereo) ? METER_LEVEL(peak_levels[IDX_LMIX], height) : METER_LEVEL(peak_levels[idx - 1], height); int meter2 = (stereo) ? METER_LEVEL(peak_levels[IDX_RMIX], height) : 0; int peak1 = height - meter1; int peak2 = (stereo) ? height - meter2: 0; /* * draw the peak(s), only if changed */ if ( (stereo && (peak_changed[IDX_LMIX] || peak_changed[IDX_RMIX])) || peak_changed[idx-1]) { int peak2_level, peak1_level = (stereo) ? peak_levels[IDX_LMIX] : peak_levels[idx-1]; /* Draw the peak as a single line */ gdk_draw_line(pixmap[idx], GET_COLOR_FOR_PEAKLEVEL(peak1_level), //X1 6, //Y1 peak1 - 1, //X2 5 + segment_width, //Y2 peak1 - 1 ); /* Handle special "stereo" case for right-channel of digital mixer */ if (stereo && peak_changed[IDX_RMIX]) { //for stereo, draw RMIX, but skip redraw if same peak2_level = peak_levels[IDX_RMIX]; gdk_draw_line(pixmap[idx], GET_COLOR_FOR_PEAKLEVEL(peak2_level), //X1 2 + (width / 2), //Y1 peak2 - 1, //X2 1 + (width / 2) + segment_width, //Y2 peak2 - 1 ); } else peak2_level = -1; /* not used unless above case, which is also in draw_peak_labels()... but initialize anyways */ draw_peak_labels(idx-1, stereo, peak1_level, peak2_level); /* reset peak_changed[] status now that new peak values rendered */ if (stereo) { if (peak_changed[IDX_LMIX]) peak_changed[IDX_LMIX] = FALSE; if (peak_changed[IDX_RMIX]) peak_changed[IDX_RMIX] = FALSE; } else { peak_changed[idx-1] = FALSE; } } /* * draw the meters * level1 at 0 --> no dspl * level1 at 1 --> draw rectange at (1/255)*height * level1 at 255 --> draw rectangle (255/255)*height */ meter1 = METER_LEVEL(level1, height); if (stereo) meter2 = METER_LEVEL(level2, height); if (level1 != (stereo ? previous_levels[IDX_LMIX] : previous_levels[idx-1]) ) { //skip redraw if same gdk_draw_rectangle(pixmap[idx], penBackground[idx], TRUE, //X 6, // draw black downward from peak //Y peak1, //WIDTH segment_width, //HEIGHT height - meter1 - peak1 ); gdk_draw_rectangle(pixmap[idx], penForeground[idx], TRUE, //X 6, //Y height - meter1, //WIDTH segment_width, //HEIGHT meter1 ); /* save current level value, skip redraw next time if no change */ if (stereo) previous_levels[IDX_LMIX] = level1; else previous_levels[idx-1] = level1; } if (stereo && (level2 != previous_levels[IDX_RMIX])) { //for stereo, draw RMIX, but skip redraw if same gdk_draw_rectangle(pixmap[idx], penBackground[idx], TRUE, //X 2 + (width / 2), //Y peak2, //WIDTH segment_width, //HEIGHT height - meter2 - peak2 ); gdk_draw_rectangle(pixmap[idx], penForeground[idx], TRUE, //X 2 + (width / 2), //Y height - meter2, //WIDTH segment_width, //HEIGHT meter2 ); /* save current level value, skip redraw next time if no change */ previous_levels[IDX_RMIX] = level2; } } static void redraw_meters(int idx, int width, int height, int level1, int level2) { int stereo = (idx == 0); int segment_width = (stereo) ? (width / 2) - 8 : width - 12; if ( (stereo && ((peak_changed[IDX_LMIX] == RESET) || (peak_changed[IDX_RMIX] == RESET))) || peak_changed[idx-1] == RESET) //needs full refresh, reset peaks button was clicked draw_meters_reset(idx, width, height, stereo, segment_width); else draw_meters_and_peaks(idx, width, height, level1, level2, stereo, segment_width); } /* NPM: called out of level_meters_configure_event() at initialization to pickup foreground color for level metering from --lights_color command-line, or create default. */ static void levelmeters_init_fg(GtkWidget* widget, GdkGC *gc) { if (meter_fg == NULL) { /* if --lights_color command-line argument not provided */ meter_fg = (GdkColor *)g_malloc(sizeof(GdkColor)); /* free()'d on exit() */ if (!gdk_color_parse("#1e90ff", meter_fg)) { /* "dodgerblue" per http://en.wikipedia.org/wiki/X11_color_names */ free((void*) meter_fg); /* if for some impossible reason above fails, continue on valiantly */ meter_fg = &(gtk_widget_get_style(widget)->text_aa[GTK_STATE_ACTIVE]); /* should never happen, but this would pick up color from existing gtk style w/o needing any addl color allocs */ } else gdk_color_alloc(gdk_colormap_get_system(), meter_fg); } gdk_gc_set_foreground(gc, meter_fg); } /* NPM: called out of level_meters_configure_event() at initialization to pickup background color for level metering from --bg_color command-line argument, or create default. */ static void levelmeters_init_bg(GtkWidget* widget, GdkGC *gc) { if (meter_bg == NULL) { /* if --bg_color command-line argument not provided */ meter_bg = (GdkColor *)g_malloc(sizeof(GdkColor)); /* free()'d on exit() */ if (!gdk_color_parse("#304050", meter_bg)) { /* a dark background color */ free((void*) meter_bg); /* if for some impossible reason above fails, continue on valiantly */ meter_bg = &(gtk_widget_get_style(widget)->text_aa[GTK_STATE_PRELIGHT]); /* should never happen, but this would pick up color from existing gtk style w/o needing any addl color allocs */ } else gdk_color_alloc(gdk_colormap_get_system(), meter_bg); } gdk_gc_set_foreground(gc, meter_bg); } gint level_meters_configure_event(GtkWidget *widget, GdkEventConfigure *event) { int idx = get_index(gtk_widget_get_name(widget)); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); if (pixmap[idx] != NULL) gdk_pixmap_unref(pixmap[idx]); pixmap[idx] = gdk_pixmap_new(gtk_widget_get_window(widget), allocation.width, allocation.height, -1); penWhiteLight[idx] = get_pen(idx, 0xffff, 0xffff, 0xffff); penGreenLight[idx] = get_pen(idx, 0, 0xffff, 0); /* NPM: Setup penForeground[idx] color for meters */ levelmeters_init_fg(widget, (penForeground[idx] = gdk_gc_new(pixmap[idx]))); /* NPM: Setup penBackground[idx] color for meters */ levelmeters_init_bg(widget, (penBackground[idx] = gdk_gc_new(pixmap[idx]))); penOrangeLight[idx] = get_pen(idx, 0xff11, 0x9911, 0); peak_label_color = (GdkColor *)g_malloc(sizeof(GdkColor)); /* free()'d on exit() */ gdk_color_parse("red", peak_label_color); gdk_color_alloc(gdk_colormap_get_system(), peak_label_color); penRedLight[idx] = get_pen(idx, 0xffff, 0, 0); gdk_draw_rectangle(pixmap[idx], gtk_widget_get_style(widget)->black_gc, TRUE, 0, 0, allocation.width, allocation.height); /* NPM: ensure redraw_meters() below does a full refresh, per meter */ if (idx == 0) { /* "if stereo" -- special case for L/R output pair of digital mixer */ // g_print("level_meters_configure_event() for stereo\n"); peak_levels[IDX_LMIX] = 0; peak_levels[IDX_RMIX] = 0; previous_levels[IDX_LMIX] = 0; previous_levels[IDX_RMIX] = 0; peak_changed[IDX_LMIX] = RESET; peak_changed[IDX_RMIX] = RESET; } else { // g_print("level_meters_configure_event() for %i\n", idx); peak_levels[idx-1] = 0; previous_levels[idx-1] = 0; peak_changed[idx-1] = RESET; } // g_print("configure: %i:%i\n", allocation.width, allocation.height); redraw_meters(idx, allocation.width, allocation.height, 0, 0); return TRUE; } gint level_meters_expose_event(GtkWidget *widget, GdkEventExpose *event) { int idx = get_index(gtk_widget_get_name(widget)); int l1, l2; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); get_levels(idx, &l1, &l2); redraw_meters(idx, allocation.width, allocation.height, l1, l2); gdk_draw_pixmap(gtk_widget_get_window(widget), gtk_widget_get_style(widget)->black_gc, pixmap[idx], event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } gint level_meters_timeout_callback(gpointer data) { GtkWidget *widget; int idx, l1, l2; GtkAllocation allocation; update_peak_switch(); for (idx = 0; idx <= pcm_output_channels; idx++) { get_levels(idx, &l1, &l2); widget = idx == 0 ? mixer_mix_drawing : mixer_drawing[idx-1]; if (gtk_widget_get_visible(widget) && (pixmap[idx] != NULL)) { gtk_widget_get_allocation(widget, &allocation); redraw_meters(idx, allocation.width, allocation.height, l1, l2); gdk_draw_pixmap(gtk_widget_get_window(widget), gtk_widget_get_style(widget)->black_gc, pixmap[idx], 0, 0, 0, 0, allocation.width, allocation.height); } /* NPM: both cases below are special-case hack to get "Analog Volume" PCM peak levels updating correctly, should "Analog Volume" panel be selected before "Monitor PCM outs" panel. In that situation, "(gtk_widget_get_visible(widget) && (pixmap[idx] != NULL))" fails as level_meters_configure_event() hasn't been called yet (it gets called when user selects "Monitor PCM outs" panel). */ else if ((idx != 0) && ((idx-1) < envy_dac_volumes()) && (peak_changed[idx-1] == TRUE)) { draw_peak_labels(idx-1, FALSE, l1, l2); peak_changed[idx-1] = FALSE; // g_print("NPM PCM outputs hack calling draw_peak_labels(%i): %i\n", idx-1, l1); } /* NPM: make it work, even after user does a RESET, while in "Analog Volume" panel, before "Monitor PCM outs" created/configured */ else if ((idx != 0) && ((idx-1) < envy_dac_volumes()) && (peak_changed[idx-1] == RESET)) { /* note that get_levels() never retrieved l1, l2 due to unfulfilled RESET */ GtkWidget* lbl; /* NPM: Reset colors of peak labels for DACs in "Analog Volume" panel */ if ((idx-1) < envy_dac_volumes() /* index 0-8 corresponds to one of the eight DAC's */ && ((idx-1) < MAX_OUTPUT_CHANNELS) /* make sure within bounds of dac_peak_label[] */ && (lbl = dac_peak_label[idx-1]) != NULL) gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, NULL); /* reset color */ /* NPM: Reset colors of peak labels for ADCs in "Analog Volume" panel */ else if (((idx-1) >= (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) /* ADC channels begin at 11 = MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS */ && ((idx-1) < (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS + envy_adc_volumes())) /* ADC channels end at 19 */ && (((idx-1) - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) < MAX_INPUT_CHANNELS) /* make sure within bounds of adc_peak_label[] */ && (lbl = adc_peak_label[(idx-1) - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)]) != NULL) gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, NULL); /* reset color */ peak_changed[idx-1] = FALSE; /* hack -- force get_levels() to retrieve levels despite RESET */ get_levels(idx, &l1, &l2); draw_peak_labels(idx-1, FALSE, l1, l2); // g_print("NPM PCM outputs RESET hack calling draw_peak_labels(%i): %i\n", idx-1, l1); } } if (view_spdif_playback) { for (idx = MAX_PCM_OUTPUT_CHANNELS + 1; idx <= MAX_OUTPUT_CHANNELS + spdif_channels; idx++) { get_levels(idx, &l1, &l2); widget = idx == 0 ? mixer_mix_drawing : mixer_drawing[idx-1]; if (gtk_widget_get_visible(widget) && (pixmap[idx] != NULL)) { gtk_widget_get_allocation(widget, &allocation); redraw_meters(idx, allocation.width, allocation.height, l1, l2); gdk_draw_pixmap(gtk_widget_get_window(widget), gtk_widget_get_style(widget)->black_gc, pixmap[idx], 0, 0, 0, 0, allocation.width, allocation.height); } } } for (idx = MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + 1; idx <= input_channels + MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS; idx++) { get_levels(idx, &l1, &l2); widget = idx == 0 ? mixer_mix_drawing : mixer_drawing[idx-1]; if (gtk_widget_get_visible(widget) && (pixmap[idx] != NULL)) { gtk_widget_get_allocation(widget, &allocation); redraw_meters(idx, allocation.width, allocation.height, l1, l2); gdk_draw_pixmap(gtk_widget_get_window(widget), gtk_widget_get_style(widget)->black_gc, pixmap[idx], 0, 0, 0, 0, allocation.width, allocation.height); } } for (idx = MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + 1; \ idx <= spdif_channels + MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS; idx++) { get_levels(idx, &l1, &l2); widget = idx == 0 ? mixer_mix_drawing : mixer_drawing[idx-1]; if (gtk_widget_get_visible(widget) && (pixmap[idx] != NULL)) { gtk_widget_get_allocation(widget, &allocation); redraw_meters(idx, allocation.width, allocation.height, l1, l2); gdk_draw_pixmap(gtk_widget_get_window(widget), gtk_widget_get_style(widget)->black_gc, pixmap[idx], 0, 0, 0, 0, allocation.width, allocation.height); } } return TRUE; } /* NPM fixed lack of implementation ( https://bugzilla.redhat.com/show_bug.cgi?id=602903 )*/ void level_meters_reset_peaks(GtkButton *button, gpointer data) { int i; for (i = 0; i < MULTI_TRACK_PEAK_CHANNELS ; i++) { peak_levels[i] = 0; previous_levels[i] = 0; peak_changed[i] = RESET; } level_meters_timeout_callback((gpointer) data); } void level_meters_init(void) { int err; snd_ctl_elem_value_malloc(&peaks); snd_ctl_elem_value_set_interface(peaks, SND_CTL_ELEM_IFACE_PCM); snd_ctl_elem_value_set_name(peaks, "Multi Track Peak"); if ((err = snd_ctl_elem_read(ctl, peaks)) < 0) /* older ALSA driver, using MIXER type */ snd_ctl_elem_value_set_interface(peaks, SND_CTL_ELEM_IFACE_MIXER); } void level_meters_postinit(void) { level_meters_timeout_callback(NULL); } mudita24-1.0.3+svn13/config.c0000644000175000017500000000337611434666733015431 0ustar alessioalessio#include #include "envy24control.h" #if GLIB_CHECK_VERSION(2,2,0) #if GLIB_CHECK_VERSION(2,8,0) #define MYMKDIR g_mkdir_with_parents #else #include #include #define MYMKDIR mkdir #endif GKeyFile *config_file; gboolean config_stereo[20]; gchar *config_filename; void config_open() { config_filename=g_strdup_printf("%s/%s", g_get_user_config_dir(), "envy24control"); config_file=g_key_file_new(); g_key_file_load_from_file(config_file, config_filename, G_KEY_FILE_KEEP_COMMENTS, NULL); } void config_close() { gsize len=0; gchar *s; g_key_file_set_boolean_list(config_file, "mixer", "stereo", config_stereo, sizeof(config_stereo)/sizeof(config_stereo[0])); s=g_key_file_to_data(config_file, &len, NULL); if(s && len) { MYMKDIR(g_get_user_config_dir(), 0700); FILE *f=fopen(config_filename, "wb"); if(f) { fwrite(s, len, 1, f); fclose(f); } } g_free(config_filename); config_filename=0; g_key_file_free(config_file); config_file=0; } void config_set_stereo(GtkWidget *but, gpointer data) { gint i=(gint)((long)data); /* NPM: "(gint)((long)data)" suppress "config.c:49: warning: cast from pointer to integer of different size" */ config_stereo[i]=GTK_TOGGLE_BUTTON(but)->active; } void config_restore_stereo() { gint i; gsize len=0; gboolean *s=g_key_file_get_boolean_list(config_file, "mixer", "stereo", &len, NULL); if(s) for(i=0; i!=len; ++i) { config_stereo[i]=s[i]; if(mixer_stereo_toggle[i]) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mixer_stereo_toggle[i]), s[i]); } } #else /* to be done */ void config_open() { } void config_close() { } void config_set_stereo(GtkWidget *but, gpointer data) { } void config_restore_stereo() { } #endif