Hello community, here is the log from the commit of package alsa-utils for openSUSE:Factory checked in at 2012-10-18 15:19:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/alsa-utils (Old) and /work/SRC/openSUSE:Factory/.alsa-utils.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "alsa-utils", Maintainer is "[email protected]" Changes: -------- --- /work/SRC/openSUSE:Factory/alsa-utils/alsa-utils.changes 2012-09-11 09:03:54.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.alsa-utils.new/alsa-utils.changes 2012-10-18 15:19:10.000000000 +0200 @@ -1,0 +2,6 @@ +Wed Oct 17 10:10:38 CEST 2012 - [email protected] + +- backport from upstream tree: + * add support for the new chmap API + +------------------------------------------------------------------- New: ---- 0001-speaker-test-Add-support-for-channel-mapping-API.patch 0002-aplay-Add-support-for-channel-mapping.patch 0003-aplay-More-support-for-channel-map-option.patch 0004-speaker-test-Update-man-page-for-chmap-option.patch 0005-aplay-fix-typo-silence-warning.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ alsa-utils.spec ++++++ --- /var/tmp/diff_new_pack.gN59uT/_old 2012-10-18 15:19:11.000000000 +0200 +++ /var/tmp/diff_new_pack.gN59uT/_new 2012-10-18 15:19:11.000000000 +0200 @@ -44,6 +44,12 @@ Source: ftp://ftp.alsa-project.org/pub/utils/alsa-utils-%{package_version}.tar.bz2 Source1: 01beep.conf # Patch: alsa-utils-git-fixes.diff +Patch1: 0001-speaker-test-Add-support-for-channel-mapping-API.patch +Patch2: 0002-aplay-Add-support-for-channel-mapping.patch +Patch3: 0003-aplay-More-support-for-channel-map-option.patch +Patch4: 0004-speaker-test-Update-man-page-for-chmap-option.patch +Patch5: 0005-aplay-fix-typo-silence-warning.patch +# Patch99: alsa-utils-gettext-version-removal.diff Url: http://www.alsa-project.org/ BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -58,6 +64,11 @@ sed -i -e's/EXTRA_DIST= config.rpath /EXTRA_DIST=/' Makefile.am # rm -f po/Makefile* po/*.gmo po/*.pot po/*.header po/stamp-* # patch -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 %if %suse_version < 1020 %patch99 -p1 %endif ++++++ 0001-speaker-test-Add-support-for-channel-mapping-API.patch ++++++ >From 951cb2c2974293db6e12ef067ae7001074887932 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <[email protected]> Date: Tue, 11 Sep 2012 11:36:45 +0200 Subject: [PATCH 1/5] speaker-test: Add support for channel mapping API The surround channel map follows the given channel map from the driver if available. Also, the channels can be specified manually via -m option. Pass the channel map like "FL,FR,FC,LFE". Signed-off-by: Takashi Iwai <[email protected]> --- speaker-test/speaker-test.c | 139 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 15 deletions(-) --- a/speaker-test/speaker-test.c +++ b/speaker-test/speaker-test.c @@ -60,6 +60,10 @@ #include <locale.h> #endif +#ifdef SND_CHMAP_API_VERSION +#define CONFIG_SUPPORT_CHMAP 1 +#endif + enum { TEST_PINK_NOISE = 1, TEST_SINE, @@ -100,6 +104,11 @@ static const char *given_test_wav_file = static char *wav_file_dir = SOUNDSDIR; static int debug = 0; +#ifdef CONFIG_SUPPORT_CHMAP +static snd_pcm_chmap_t *channel_map; +static int channel_map_set; +#endif + static const char *const channel_name[MAX_CHANNELS] = { /* 0 */ N_("Front Left"), /* 1 */ N_("Front Right"), @@ -143,6 +152,65 @@ static const int channels8[] = { 6, /* Side Left */ 5, /* LFE */ }; + +static int get_mapped_channel(int chn) +{ +#ifdef CONFIG_SUPPORT_CHMAP + static const int maps[MAX_CHANNELS] = { + SND_CHMAP_FL, + SND_CHMAP_FR, + SND_CHMAP_RL, + SND_CHMAP_RR, + SND_CHMAP_FC, + SND_CHMAP_LFE, + SND_CHMAP_SL, + SND_CHMAP_SR, + }; + + if (channel_map && maps[chn]) { + int i; + for (i = 0; i < channel_map->channels; i++) { + if (channel_map->pos[i] == maps[chn]) + return i; + } + } +#endif + return chn; +} + +static int get_speaker_channel(int chn) +{ +#ifdef CONFIG_SUPPORT_CHMAP + if (channel_map_set) + return chn; +#endif + + switch (channels) { + case 4: + chn = channels4[chn]; + break; + case 6: + chn = channels6[chn]; + break; + case 8: + chn = channels8[chn]; + break; + } + + return get_mapped_channel(chn); +} + +static const char *get_channel_name(int chn) +{ +#ifdef CONFIG_SUPPORT_CHMAP + if (channel_map_set && chn < channel_map->channels) { + const char *name = snd_pcm_chmap_long_name(channel_map->pos[chn]); + return name ? name : "Unknown"; + } +#endif + return gettext(channel_name[chn]); +} + static const int supported_formats[] = { SND_PCM_FORMAT_S8, SND_PCM_FORMAT_S16_LE, @@ -519,6 +587,31 @@ static int set_swparams(snd_pcm_t *handl return 0; } +#ifdef CONFIG_SUPPORT_CHMAP +static int config_chmap(snd_pcm_t *handle, const char *mapstr) +{ + int err; + + if (mapstr) { + channel_map = snd_pcm_chmap_parse_string(mapstr); + if (!channel_map) { + fprintf(stderr, _("Unable to parse channel map string: %s\n"), mapstr); + return -EINVAL; + } + err = snd_pcm_set_chmap(handle, channel_map); + if (err < 0) { + fprintf(stderr, _("Unable to set channel map: %s\n"), mapstr); + return err; + } + channel_map_set = 1; + return 0; + } + + channel_map = snd_pcm_get_chmap(handle); + return 0; +} +#endif + /* * Underrun and suspend recovery */ @@ -815,6 +908,7 @@ static void help(void) "-s,--speaker single speaker test. Values 1=Left, 2=right, etc\n" "-w,--wavfile Use the given WAV file as a test sound\n" "-W,--wavdir Specify the directory containing WAV files\n" + "-m,--chmap Specify the channel map to override\n" "\n")); printf(_("Recognized sample formats are:")); for (fmt = supported_formats; *fmt >= 0; fmt++) { @@ -837,6 +931,9 @@ int main(int argc, char *argv[]) { double time1,time2,time3; unsigned int n, nloops; struct timeval tv1,tv2; +#ifdef CONFIG_SUPPORT_CHMAP + const char *chmap = NULL; +#endif static const struct option long_option[] = { {"help", 0, NULL, 'h'}, @@ -854,6 +951,9 @@ int main(int argc, char *argv[]) { {"wavfile", 1, NULL, 'w'}, {"wavdir", 1, NULL, 'W'}, {"debug", 0, NULL, 'd'}, +#ifdef CONFIG_SUPPORT_CHMAP + {"chmap", 1, NULL, 'm'}, +#endif {NULL, 0, NULL, 0 }, }; @@ -872,7 +972,11 @@ int main(int argc, char *argv[]) { while (1) { int c; - if ((c = getopt_long(argc, argv, "hD:r:c:f:F:b:p:P:t:l:s:w:W:d", long_option, NULL)) < 0) + if ((c = getopt_long(argc, argv, "hD:r:c:f:F:b:p:P:t:l:s:w:W:d" +#ifdef CONFIG_SUPPORT_CHMAP + "m:" +#endif + , long_option, NULL)) < 0) break; switch (c) { @@ -963,6 +1067,11 @@ int main(int argc, char *argv[]) { case 'd': debug = 1; break; +#ifdef CONFIG_SUPPORT_CHMAP + case 'm': + chmap = optarg; + break; +#endif default: fprintf(stderr, _("Unknown option '%c'\n"), c); exit(EXIT_FAILURE); @@ -1008,6 +1117,13 @@ int main(int argc, char *argv[]) { snd_pcm_close(handle); exit(EXIT_FAILURE); } + +#ifdef CONFIG_SUPPORT_CHMAP + err = config_chmap(handle, chmap); + if (err < 0) + exit(EXIT_FAILURE); +#endif + if (debug) { snd_output_t *log; err = snd_output_stdio_attach(&log, stderr, 0); @@ -1038,17 +1154,8 @@ int main(int argc, char *argv[]) { gettimeofday(&tv1, NULL); for(chn = 0; chn < channels; chn++) { - int channel=chn; - if (channels == 4) { - channel=channels4[chn]; - } - if (channels == 6) { - channel=channels6[chn]; - } - if (channels == 8) { - channel=channels8[chn]; - } - printf(" %d - %s\n", channel, gettext(channel_name[channel])); + int channel = get_speaker_channel(chn); + printf(" %d - %s\n", channel, get_channel_name(channel)); err = write_loop(handle, channel, ((rate*3)/period_size), frames); @@ -1066,13 +1173,15 @@ int main(int argc, char *argv[]) { printf(_("Time per period = %lf\n"), time3 ); } } else { + chn = get_speaker_channel(speaker - 1); + if (test_type == TEST_WAV) { - if (setup_wav_file(speaker - 1) < 0) + if (setup_wav_file(chn) < 0) exit(EXIT_FAILURE); } - printf(" - %s\n", gettext(channel_name[speaker-1])); - err = write_loop(handle, speaker-1, ((rate*5)/period_size), frames); + printf(" - %s\n", get_channel_name(chn)); + err = write_loop(handle, chn, ((rate*5)/period_size), frames); if (err < 0) { fprintf(stderr, _("Transfer failed: %s\n"), snd_strerror(err)); ++++++ 0002-aplay-Add-support-for-channel-mapping.patch ++++++ >From a9add2252f44fae3eb0ce5470cb842f220fb0454 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <[email protected]> Date: Tue, 11 Sep 2012 12:20:55 +0200 Subject: [PATCH 2/5] aplay: Add support for channel mapping With -m option, user can specify the order of channel map. As of this commit, it just tries to override the channel map, thus it works only on devices that support the channel map override like HDMI. Adjusting the channel order in aplay itself will be added later. Signed-off-by: Takashi Iwai <[email protected]> --- aplay/aplay.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) --- a/aplay/aplay.c +++ b/aplay/aplay.c @@ -54,6 +54,10 @@ #include "formats.h" #include "version.h" +#ifdef SND_CHMAP_API_VERSION +#define CONFIG_SUPPORT_CHMAP 1 +#endif + #ifndef LLONG_MAX #define LLONG_MAX 9223372036854775807LL #endif @@ -140,6 +144,10 @@ static char *pidfile_name = NULL; FILE *pidf = NULL; static int pidfile_written = 0; +#ifdef CONFIG_SUPPORT_CHMAP +static snd_pcm_chmap_t *channel_map = NULL; +#endif + /* needed prototypes */ static void done_stdin(void); @@ -227,7 +235,9 @@ _("Usage: %s [OPTION]... [FILE]...\n" " --process-id-file write the process ID here\n" " --use-strftime apply the strftime facility to the output file name\n" " --dump-hw-params dump hw_params of the device\n" -" --fatal-errors treat all errors as fatal\n") +" --fatal-errors treat all errors as fatal\n" +"-m, --chmap=ch1,ch2,.. Give the channel map to override\n" + ) , command); printf(_("Recognized sample formats are:")); for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { @@ -428,7 +438,11 @@ enum { int main(int argc, char *argv[]) { int option_index; - static const char short_options[] = "hnlLD:qt:c:f:r:d:MNF:A:R:T:B:vV:IPCi"; + static const char short_options[] = "hnlLD:qt:c:f:r:d:MNF:A:R:T:B:vV:IPCi" +#ifdef CONFIG_SUPPORT_CHMAP + "m:" +#endif + ; static const struct option long_options[] = { {"help", 0, 0, 'h'}, {"version", 0, 0, OPT_VERSION}, @@ -469,6 +483,9 @@ int main(int argc, char *argv[]) {"interactive", 0, 0, 'i'}, {"dump-hw-params", 0, 0, OPT_DUMP_HWPARAMS}, {"fatal-errors", 0, 0, OPT_FATAL_ERRORS}, +#ifdef CONFIG_SUPPORT_CHMAP + {"chmap", 1, 0, 'm'}, +#endif {0, 0, 0, 0} }; char *pcm_name = "default"; @@ -676,6 +693,15 @@ int main(int argc, char *argv[]) case OPT_FATAL_ERRORS: fatal_errors = 1; break; +#ifdef CONFIG_SUPPORT_CHMAP + case 'm': + channel_map = snd_pcm_chmap_parse_string(optarg); + if (!channel_map) { + fprintf(stderr, _("Unable to parse channel map string: %s\n"), optarg); + return 1; + } + break; +#endif default: fprintf(stderr, _("Try `%s --help' for more information.\n"), command); return 1; @@ -1206,6 +1232,16 @@ static void set_params(void) prg_exit(EXIT_FAILURE); } +#ifdef CONFIG_SUPPORT_CHMAP + if (channel_map) { + err = snd_pcm_set_chmap(handle, channel_map); + if (err < 0) { + error(_("Unable to set channel map")); + prg_exit(EXIT_FAILURE); + } + } +#endif + if (verbose) snd_pcm_dump(handle, log); ++++++ 0003-aplay-More-support-for-channel-map-option.patch ++++++ >From 000bf230cfb3b0abe88d27701dd77982675126ec Mon Sep 17 00:00:00 2001 From: Takashi Iwai <[email protected]> Date: Thu, 20 Sep 2012 13:53:46 +0200 Subject: [PATCH 3/5] aplay: More support for channel map option Now aplay tries to follow the given channel map by rearranging the channels even when the channel map override isn't allowed but if the device is still capable to return a channel map. Also update the man page appropriately. Signed-off-by: Takashi Iwai <[email protected]> --- aplay/aplay.1 | 11 ++++ aplay/aplay.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 149 insertions(+), 11 deletions(-) --- a/aplay/aplay.1 +++ b/aplay/aplay.1 @@ -141,6 +141,17 @@ by typing arecord. Allow interactive operation via stdin. Currently only pause/resume via space or enter key is implemented. .TP +\fI-m, \-\-chmap=ch1,ch2,...\fP +Give the channel map to override or follow. Pass channel position +strings like \fIFL\fP, \fIFR\fP, etc. + +If a device supports the override of the channel map, \fBaplay\fP +tries to pass the given channel map. +If it doesn't support the channel map override but still it provides +the channel map information, \fBaplay\fP tries to rearrange the +channel order in the buffer to match with the returned channel map +from the device. +.TP \fI\-\-disable\-resample\fP Disable automatic rate resample. .TP --- a/aplay/aplay.c +++ b/aplay/aplay.c @@ -145,7 +145,8 @@ FILE *pidf = NULL; static int pidfile_written = 0; #ifdef CONFIG_SUPPORT_CHMAP -static snd_pcm_chmap_t *channel_map = NULL; +static snd_pcm_chmap_t *channel_map = NULL; /* chmap to override */ +static unsigned int *hw_map = NULL; /* chmap to follow */ #endif /* needed prototypes */ @@ -222,6 +223,7 @@ _("Usage: %s [OPTION]... [FILE]...\n" "-V, --vumeter=TYPE enable VU meter (TYPE: mono or stereo)\n" "-I, --separate-channels one file for each channel\n" "-i, --interactive allow interactive operation from stdin\n" +"-m, --chmap=ch1,ch2,.. Give the channel map to override or follow\n" " --disable-resample disable automatic rate resample\n" " --disable-channels disable automatic channel conversions\n" " --disable-format disable automatic format conversions\n" @@ -236,7 +238,6 @@ _("Usage: %s [OPTION]... [FILE]...\n" " --use-strftime apply the strftime facility to the output file name\n" " --dump-hw-params dump hw_params of the device\n" " --fatal-errors treat all errors as fatal\n" -"-m, --chmap=ch1,ch2,.. Give the channel map to override\n" ) , command); printf(_("Recognized sample formats are:")); @@ -1083,6 +1084,74 @@ static void show_available_sample_format } } +#ifdef CONFIG_SUPPORT_CHMAP +static int setup_chmap(void) +{ + snd_pcm_chmap_t *chmap = channel_map; + char mapped[hwparams.channels]; + snd_pcm_chmap_t *hw_chmap; + unsigned int ch, i; + int err; + + if (!chmap) + return 0; + + if (chmap->channels != hwparams.channels) { + error(_("Channel numbers don't match between hw_params and channel map")); + return -1; + } + err = snd_pcm_set_chmap(handle, chmap); + if (!err) + return 0; + + hw_chmap = snd_pcm_get_chmap(handle); + if (!hw_chmap) { + fprintf(stderr, _("Warning: unable to get channel map\n")); + return 0; + } + + if (hw_chmap->channels == chmap->channels && + !memcmp(hw_chmap, chmap, 4 * (chmap->channels + 1))) { + /* maps are identical, so no need to convert */ + free(hw_chmap); + return 0; + } + + hw_map = calloc(hwparams.channels, sizeof(int)); + if (!hw_map) { + error(_("not enough memory")); + return -1; + } + + memset(mapped, 0, sizeof(mapped)); + for (ch = 0; ch < hw_chmap->channels; ch++) { + if (chmap->pos[ch] == hw_chmap->pos[ch]) { + mapped[ch] = 1; + hw_map[ch] = ch; + continue; + } + for (i = 0; i < hw_chmap->channels; i++) { + if (!mapped[i] && chmap->pos[ch] == hw_chmap->pos[i]) { + mapped[i] = 1; + hw_map[ch] = i; + break; + } + } + if (i >= hw_chmap->channels) { + char buf[256]; + error(_("Channel %d doesn't match with hw_parmas"), ch); + snd_pcm_chmap_print(hw_chmap, sizeof(buf), buf); + fprintf(stderr, "hardware chmap = %s\n", buf); + return -1; + } + } + free(hw_chmap); + return 0; +} +#else +#define setup_chmap() 0 +#endif + static void set_params(void) { snd_pcm_hw_params_t *params; @@ -1232,15 +1301,8 @@ static void set_params(void) prg_exit(EXIT_FAILURE); } -#ifdef CONFIG_SUPPORT_CHMAP - if (channel_map) { - err = snd_pcm_set_chmap(handle, channel_map); - if (err < 0) { - error(_("Unable to set channel map")); - prg_exit(EXIT_FAILURE); - } - } -#endif + if (setup_chmap()) + prg_exit(EXIT_FAILURE); if (verbose) snd_pcm_dump(handle, log); @@ -1743,6 +1805,69 @@ static void do_test_position(void) } /* + */ +#ifdef CONFIG_SUPPORT_CHMAP +static u_char *remap_data(u_char *data, size_t count) +{ + static u_char *tmp, *src, *dst; + static size_t tmp_size; + size_t sample_bytes = bits_per_sample / 8; + size_t step = bits_per_frame / 8; + size_t chunk_bytes; + unsigned int ch, i; + + if (!hw_map) + return data; + + chunk_bytes = count * bits_per_frame / 8; + if (tmp_size < chunk_bytes) { + free(tmp); + tmp = malloc(chunk_bytes); + if (!tmp) { + error(_("not enough memory")); + exit(1); + } + tmp_size = count; + } + + src = data; + dst = tmp; + for (i = 0; i < count; i++) { + for (ch = 0; ch < hwparams.channels; ch++) { + memcpy(dst, src + sample_bytes * hw_map[ch], + sample_bytes); + dst += sample_bytes; + } + src += step; + } + return tmp; +} + +static u_char **remap_datav(u_char **data, size_t count) +{ + static u_char **tmp; + unsigned int ch; + + if (!hw_map) + return data; + + if (!tmp) { + tmp = malloc(sizeof(*tmp) * hwparams.channels); + if (!tmp) { + error(_("not enough memory")); + exit(1); + } + for (ch = 0; ch < hwparams.channels; ch++) + tmp[ch] = data[hw_map[ch]]; + } + return tmp; +} +#else +#define remap_data(data, count) (data) +#define remapv_data(data, count) (data) +#endif + +/* * write function */ @@ -1755,6 +1880,7 @@ static ssize_t pcm_write(u_char *data, s snd_pcm_format_set_silence(hwparams.format, data + count * bits_per_frame / 8, (chunk_size - count) * hwparams.channels); count = chunk_size; } + data = remap_data(data, count); while (count > 0) { if (test_position) do_test_position(); @@ -1797,6 +1923,7 @@ static ssize_t pcm_writev(u_char **data, snd_pcm_format_set_silence(hwparams.format, data[channel] + offset * bits_per_sample / 8, remaining); count = chunk_size; } + data = remap_datav(data, count); while (count > 0) { unsigned int channel; void *bufs[channels]; ++++++ 0004-speaker-test-Update-man-page-for-chmap-option.patch ++++++ >From 7b14c00b56784f69ecc954a2de36e0a058fc8849 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <[email protected]> Date: Thu, 20 Sep 2012 14:00:02 +0200 Subject: [PATCH 4/5] speaker-test: Update man page for chmap option Signed-off-by: Takashi Iwai <[email protected]> --- speaker-test/speaker-test.1 | 9 +++++++++ 1 file changed, 9 insertions(+) --- a/speaker-test/speaker-test.1 +++ b/speaker-test/speaker-test.1 @@ -131,6 +131,11 @@ Use the given WAV file for the playback Specify the directory containing WAV files for playback. The default path is \fI/usr/share/sounds/alsa\fP. +.TP +\fB\-m\fP | \fB\-\-chmap\fP +Pass the channel map to override. +If the playback in a specific channel order or channel positions is +required, pass the channel position strings to this option. .SH USAGE EXAMPLES @@ -159,6 +164,10 @@ To do a 2-speaker test using the spdif ( speaker-test -Dplug:spdif -c2 .EE +Play in the order of front-right and front-left from the front PCM +.EX + speaker-test -Dplug:front -c2 -mFR,FL +.EE .SH SEE ALSO .BR aplay(1) ++++++ 0005-aplay-fix-typo-silence-warning.patch ++++++ >From 6017849f1b9d406ac2c882ddbde408ada38c3d61 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela <[email protected]> Date: Mon, 24 Sep 2012 16:18:54 +0200 Subject: [PATCH 5/5] aplay: fix typo & silence warning.. Signed-off-by: Jaroslav Kysela <[email protected]> --- aplay/aplay.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) --- a/aplay/aplay.c +++ b/aplay/aplay.c @@ -1864,7 +1864,7 @@ static u_char **remap_datav(u_char **dat } #else #define remap_data(data, count) (data) -#define remapv_data(data, count) (data) +#define remap_datav(data, count) (data) #endif /* @@ -2122,7 +2122,6 @@ static void voc_play(int fd, int ofs, ch u_char *data, *buf; char was_extended = 0, output = 0; u_short *sp, repeat = 0; - size_t silence; off64_t filepos = 0; #define COUNT(x) nextblock -= x; in_buffer -= x; data += x @@ -2226,9 +2225,12 @@ static void voc_play(int fd, int ofs, ch COUNT1(1); hwparams.rate = 1000000 / (256 - hwparams.rate); set_params(); - silence = (((size_t) * sp) * 1000) / hwparams.rate; #if 0 - d_printf("Silence for %d ms\n", (int) silence); + { + size_t silence; + silence = (((size_t) * sp) * 1000) / hwparams.rate; + d_printf("Silence for %d ms\n", (int) silence); + } #endif voc_write_silence(*sp); break; -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
