diff options
| -rw-r--r-- | README | 1 | ||||
| -rwxr-xr-x | bsgrep | 512 | ||||
| l--------- | bsjoin | 1 | ||||
| -rwxr-xr-x | find_key | 48 | ||||
| -rw-r--r-- | image_diff.pl | 58 | ||||
| -rwxr-xr-x | litterbox | 465 | ||||
| -rwxr-xr-x | termbin | 44 | ||||
| -rwxr-xr-x | tolatin | 8 | ||||
| -rwxr-xr-x | tunebass | 83 | ||||
| -rwxr-xr-x | uleft | 24 | ||||
| -rw-r--r-- | unifmt.pl | 3 | ||||
| -rwxr-xr-x | vol | 110 | ||||
| -rwxr-xr-x | ztunekey | 76 | ||||
| -rw-r--r-- | ztunekey.desktop | 9 | ||||
| -rw-r--r-- | ztunekey.png | bin | 0 -> 55391 bytes |
15 files changed, 1357 insertions, 85 deletions
@@ -21,6 +21,7 @@ gammatrip.sh - randomly change gamma in X, crude simulation of an acid trip imagebin - pastebin an image lddsafe - https://github.com/rg3/lddsafe/ (local copy here) lddtree - show library dependencies as a tree +litterbox - upload files/directories to litter.catbox.moe mkclick.pl - generate a click track at a given tempo netqiv - wget and display an image file noobfarm2fortune.sh - scrape noobfarm.org, create fortune(6) file (no longer works!) @@ -0,0 +1,512 @@ +#!/usr/bin/perl -w + +$VERSION = "0.0.1"; + +use Getopt::Std; +use File::Find; + +($self = $0) =~ s,.*/,,; + +%printed = (); + +$SIG{__WARN__} = sub { + my $m = shift; + + # don't include the line number in warnings. + $m =~ s/ at \S+ line \d+\.$//; + + # File::Find seems to use double newlines for its warnings.. + $m =~ s/\n\n+/\n/; + + # warnings that don't start with $self: are e.g. file access errors + # from the 'while(<>)' or File::Find. + if($m !~ /^$self:/) { + $m = "$self: $m"; + $ret = 2 unless $opt{q}; + } + + print STDERR $m unless $opt{s}; +}; + +sub grep_options { + my @nargv; + my $was_e; + + # first, grab all the -e options and remove them from @ARGV, + # because Getopt::Std doesn't support multiple occurrences of + # a flag with different args. probably it would be better to + # use Getopt::Long, but for now this works. + for(@ARGV) { + if($was_e) { + push @patterns, $_; + $was_e = 0; + } elsif($_ eq '-e') { + $was_e = 1; + } elsif($_ =~ /-e(.*)/) { + push @patterns, $1; + } else { + push @nargv, $_; + } + } + @ARGV = @nargv; + + getopts('d:FiklnNqrsvwz', \%opt) || exit 1; +} + +sub print_line { + print "$ARGV:" if $filecount > 1; + print "$start_line:" if $opt{n}; + print $_[0]; + print $opt{z} ? "\0" : "\n"; +} + +sub join_options { + getopts('d:knwz', \%opt) || exit 1; +} + +sub handle_line { + my $match = 0; + + for my $pat (@patterns) { + if($opt{v}) { + $match++ if $out !~ /$pat/; + } else { + $match++ if $out =~ /$pat/; + } + } + + return unless $match; + + if($opt{N}) { + return unless $match == @patterns; + } + + $ret = 0 if $ret == 1; + return if $opt{q}; + + if($opt{l}) { + if(!$printed{$ARGV}++) { + print "$ARGV\n"; + } + } else { + print_line($out); + } +} + +### main() +# TODO: do we need 'use locale'? +# also, why does reading iso-8859-1 text auto-convert to utf-8? +for (qw/LANG LC_CTYPE LC_ALL/) { + if(($ENV{$_} // "") =~ /utf-?8/i) { + binmode(\*STDIN, ':utf8'); + binmode(\*STDOUT, ':utf8'); + last; + } +} + +if(defined($ARGV[0])) { + if($ARGV[0] =~ /-help/) { + exec "perldoc $0"; + exit(1); + } elsif($ARGV[0] eq '--man') { + exec "pod2man --stderr -s1 -cUrchlaysStuff -r$VERSION -u $0"; + exit(1); + } elsif($ARGV[0] eq '--version') { + print "bsgrep $VERSION\n"; + exit(0); + } +} + +if($self =~ /join/) { + join_options(); + push @patterns, '^'; # every string has a beginning... +} else { + grep_options(); + + if(!@patterns) { + if(!($patterns[0] = shift)) { + grep_usage(); + die("$self: missing required pattern argument\n"); + } + } + + map { $_ = quotemeta } @patterns if $opt{F}; + map { $_ = "(?i)$_" } @patterns if $opt{i}; +} + +if($opt{r}) { + @ARGV = (".") unless @ARGV; + for(@ARGV) { + if(-d $_) { + find({ + wanted => sub { push @nargv, $_ if -f _; }, + follow => 0, + no_chdir => 1 }, + $_); + } else { + push @nargv, $_; + } + } + + @ARGV = @nargv; +} + +$ret = 1; # return value from main(), set to 0 if anything matched. + +$filecount = @ARGV; # used to decide whether to print filename prefixes. + +$cont = quotemeta($opt{d} // '\\'); + +$/ = "\0" if $opt{z}; + +while(<>) { + chomp; + if(s/\r//) { + if(!$cr_warning) { + warn "$self: $ARGV: stripping carriage returns\n" unless $opt{s}; + $cr_warning = 1; + } + } + if(/$cont\s+$/) { + warn "$self: $ARGV:$.: whitespace after continuation, malformed input?\n" unless $opt{s}; + } + s/^\s+// if $out && $opt{w}; + $start_line = $. unless defined $out; + $out .= $_; + if(/$cont$/) { + if(!$opt{k}) { + $out =~ s/$cont$//; + } + } else { + handle_line(); + undef $out; + } +} continue { + # reset $. on each new file (perldoc -f eof) + if(eof) { + if($out) { + warn "$self: $ARGV:$.: last line ends with continuation\n" unless $opt{s}; + handle_line(); + undef $out; + } + close ARGV; + $cr_warning = 0; + } +} + +exit $ret; + +### rest of file is the docs + +=pod + +=head1 NAME + +bsgrep - search for strings in files with backslash continuation + +bsjoin - join lines with backslash continuation + +=head1 SYNOPSIS + +bsgrep [B<[-FiklnNqrsvwz]> B<-d> I<char> I<...>] [B<-e> I<pattern> ... | I<pattern>] [I<file> I<...>] + +bsjoin [B<[-knwz]> B<-d> I<char> I<...>] [I<file> I<...>] + +=head1 DESCRIPTION + +B<bsgrep> (backslash grep) uses a regular expression to search for +strings in a file, much like B<grep>(1). The main difference is, +B<bsgrep> joins together lines that use the backslash for continuation +(e.g. as B<sh>(1) does). + +Other differences: B<bsgrep> doesn't support the full set of B<grep> +options, and it uses Perl regular expressions rather than POSIX. + +Input is read from one or more files, or standard input if no files +are given. Output goes to standard output. + +The search is done after lines are joined together, so the regex can +match text split across continuation lines. + +If B<bsgrep> is run as B<bsjoin> (via symbolic or hard link, or just +copying the executable), it will simply join together continued lines +without searching for anything. In this mode, only the B<-k>, B<-n>, +B<-w>, B<--version>, and B<--help> options are supported. + +=head1 OPTIONS + +These options work with both B<bsgrep> and B<bsjoin>: + +=over 4 + +=item -d I<char> + +Use I<char> as the continuation character, rather than a backslash. +Actually, there's no law that says it has to be a single character, +if you can think of a use for a string here... though it's treated as +a fixed string, not a regular expression. This option does not exist +in B<grep>. + +=item -k + +Keep the continuation characters when joining continued lines together. +This option does not exist in B<grep>. + +=item -n + +Prefix output lines with line numbers (same as B<grep>). For lines +that are split with continuation characters, the line number will be +that of the first line in the set. Same as B<grep>. + +=item -w + +For continuation lines, remove any leading whitespace. This option is +specific to B<bsgrep>. The B<grep> B<-w> option can be simulated with +the Perl B<\b> syntax in the regex. + +=item -z + +Use zero bytes (ASCII NUL) rather than newlines for line terminators, +for both input and output. Same as B<grep>. + +=item --version + +Print the version of B<bsgrep> and exit. + +=item --help + +Prints this help text, via B<perldoc>(1). + +=item --man + +Prints this help text as a man page, via B<pod2man>(1). Suggested use: + + bsgrep --man > bsgrep.1 + +=back + +These options are only supported by B<bsgrep>: + +=over 4 + +=item -e I<pattern> + +Use I<pattern> as the pattern. May be used multiple times, in which case +they are ORed together (a line that matches any I<pattern> is a match)... unless +the B<-N> option is used, q.v. Same as B<grep>. + +=item -F + +Treat pattern(s) as fixed strings, not regular expression(s). Same as B<grep>. + +=item -i + +Case-insensitive search (same as B<grep>). + +=item -l + +Instead of printing lines that match, print only the names of files +that contain matches (same as B<grep>). + +=item -N + +When multiple patterns are given with multiple B<-e> options, only +select lines that match all of the patterns; the default is to select +lines that match any of the patterns. This option doesn't exist +in B<grep>. + +=item -q + +Quiet: don't write to standard output. Exit status will be zero if +a match was found, even if there were errors. This doesn't prevent +warnings/errors being printed to standard error; use B<-s> to silence +those. Same as B<grep>. + +=item -r + +Recursively read all files under each directory, following symlinks +only if they're on the command line. If no files or directories are +given, reads the current directory. Same as B<grep>. + +=item -s + +Silence warnings (same as B<grep>). This includes error messages +about unreadable files as well as warnings about the input (see +B<DIAGNOSTICS>, below). + +=item -v + +Print only lines that do I<not> match (same as B<grep>). + +=back + +=head1 EXAMPLE + +Given the file B<trs80-roms.info> (which comes from SlackBuilds.org), containing: + + PRGNAM="trs80-roms" + VERSION="20230516" + HOMEPAGE="https://sdltrs.sourceforge.net/docs/index.html" + DOWNLOAD="https://www.filfre.net/misc/trs_roms.zip \ + http://cpmarchives.classiccmp.org/trs80/mirrors/www.discover-net.net/~dmkeil/trs80/files/trs80-62.zip \ + https://www.tim-mann.org/trs80/ld4-631.zip \ + https://archive.org/download/mame-0.250-roms-split_202212/MAME%200.250%20ROMs%20%28split%29/trs80m4p.zip \ + http://www.tim-mann.org/trs80/xtrs-4.9d.tar.gz \ + https://www.classic-computers.org.nz/system-80/disks/NEWDOS_80sssd_jv1.DSK" + MD5SUM="ecd2c47c0624885fbcfb17889241f0ed \ + 9b342f4401801bbc947e303cbeb9902f \ + f2678aa45b76d935a34a0cd2b108925d \ + 8a0f1567df8f166f4056a6a71ef7dce5 \ + 8bb7cf88a3bc1da890f1f29398120bf3 \ + 6f624bdbf4b410cfbe8603fa3bef44fa" + DOWNLOAD_x86_64="" + MD5SUM_x86_64="" + REQUIRES="" + MAINTAINER="B. Watson" + EMAIL="urchlay@slackware.uk" + +We can extract all the download URLs from the file with: + + $ bsgrep '^DOWNLOAD=' trs80-roms + + DOWNLOAD="https://www.filfre.net/misc/trs_roms.zip http://cpmarchives.classiccmp.org/trs80/mirrors/www.discover-net.net/~dmkeil/trs80/files/trs80-62.zip https://www.tim-mann.org/trs80/ld4-631.zip https://archive.org/download/mame-0.250-roms-split_202212/MAME%200.250%20ROMs%20%28split%29/trs80m4p.zip http://www.tim-mann.org/trs80/xtrs-4.9d.tar.gz https://www.classic-computers.org.nz/system-80/disks/NEWDOS_80sssd_jv1.DSK" + DOWNLOAD_x86_64="" + +All the URLs are listed as one long line (apologies for the ugly formatting). +Note that the whitespace that indents the continuation lines is +preserved. In this case, the whitespace is all spaces, but tabs would +be treated the same way. To compress the whitespace into a single space, +use the B<-w> option. + +=head1 DIAGNOSTICS + +Unless disabled with the B<-s> option, B<bsgrep> may print these messages +on standard error: + + bsgrep: <file>: stripping carriage returns + +The input file has MS-DOS/Windows CRLF line endings. B<bsgrep>'s +output will have these removed. Note that other Unix-flavored tools +that understand continuation lines will generally fail when fed CRLF +files. + + bsgrep: <file>, line <line>: whitespace after continuation, malformed input? + +In shell scripts (and most other uses of backslash continuation), a +line that ends with whitespace after the backslash is not treated as a +continuation line. This is a very easy error to create, when manually +editing files. The above warning will help you avoid this. As usual, +it can be ignored if you know exactly what you're doing. + + bsgrep: <file>: last line ends with continuation + +This warning is self-explanatory. There's nothing for the last line +to continue onto, so this is almost certainly an error. + +The above warnings don't affect the exit status. + +=head1 ENVIRONMENT + +B<bsgrep> doesn't define any environment variables of its own, but +it does pay attention to B<LANG>, B<LC_ALL>, and B<LC_CTYPE>. If any +of these contain the string I<UTF-8>, the input and output will be +read/written as Unicode, encoded as UTF-8. If the input turns out not +to be Unicode, it will be assumed ISO-8859-1, and converted to Unicode. + +=head1 EXIT STATUS + +0 if there were any matches, 1 if there were none, or 2 if there +were errors (e.g. nonexistent file). However, with B<-q>, the exit +status will be 0 or 1 even if there were errors. This is the same as +B<grep>'s exit status. + +=head1 LIMITATIONS + +B<bsgrep> doesn't detect binary files like B<grep> does. It can and +will print them to your terminal instead of "binary file matches". + +Not all B<grep> options are supported. Options that aren't implemented +but might be someday include B<--color>, B<-a>, B<-A>, B<-B>, B<-C>, B<-o>. +I don't intend to implement every single option B<grep> has, there are +too many of them. + +There are no long options other than B<--help> and B<--version>. + +B<bsgrep> does not comply with the POSIX (or any other) standard for +B<grep>, and does not intend do. + +Locale support isn't quite the same as B<grep>: in a UTF-8 locale, +if the input isn't plain ASCII or valid UTF-8, it will be treated +as ISO-8859-1, internally converted to Unicode, and output will be +UTF-8. This isn't intended; it's a side-effect of how Perl UTF-8 +filehandles work. In non-UTF-8 locales, things should work as +expected. I hope. + +=head1 AUTHOR + +B<bsgrep> was written by B. Watson <urchlay@slackware.uk> and released +under the WTFPL: Do WTF you want with this. + +=head1 SEE ALSO + +B<grep>(1), B<perl>(1) + +=cut + +__END__ + +implemented: + --help + -V, --version + -F, --fixed-strings + -e PATTERNS, --regexp=PATTERNS + -i, --ignore-case + -v, --invert-match + -q, --quiet, --silent + -s, --no-messages + -n, --line-number + -z, --null-data + -r, --recursive + -l, --files-with-matches + +todo: + -f FILE, --file=FILE + -y Obsolete synonym for -i. + -c, --count + -R, --dereference-recursive + -L, --files-without-match + -Z, --null + -A NUM, --after-context=NUM + -B NUM, --before-context=NUM + -C NUM, -NUM, --context=NUM + -H, --with-filename + -h, --no-filename + -w, --word-regexp + -x, --line-regexp + +do not implement: + -E, --extended-regexp + -G, --basic-regexp + -P, --perl-regexp + --no-ignore-case + +undecided: + --color[=WHEN], --colour[=WHEN] + -m NUM, --max-count=NUM + -o, --only-matching + -b, --byte-offset + --label=LABEL + -T, --initial-tab + --group-separator=SEP + --no-group-separator + -a, --text + --binary-files=TYPE + -D ACTION, --devices=ACTION + -d ACTION, --directories=ACTION + --exclude=GLOB + --exclude-from=FILE + --exclude-dir=GLOB + -I + --include=GLOB + --line-buffered + -U, --binary @@ -0,0 +1 @@ +bsgrep
\ No newline at end of file @@ -55,8 +55,9 @@ $0: Find key of music in audio (or maybe video) files. Usage: $0 [-m|-M] file.wav [file.wav ...] --m: Force major keys to relative minor --M: Force minor keys to relative major +-q: Quiet, only output the most likely key. +-m: Force major keys to relative minor. +-M: Force minor keys to relative major. EOF exit 0; } @@ -70,6 +71,9 @@ for(@ARGV) { $fold_major = 0; $fold_minor = 1; next; + } elsif(/^-q/) { + $quiet++; + next; } my $sec; @@ -79,22 +83,23 @@ for(@ARGV) { $sf = Audio::SndFile->open("<", $_); }; if($@) { - warn "Extracting to tmp.wav\n"; - system("mplayer -vo null -ao pcm:fast:file=tmp.wav \"$_\""); + my $rq = $quiet ? "-really-quiet" : ""; + warn "Extracting to tmp.wav\n" unless $quiet; + system("mplayer $rq -vo null -ao pcm:fast:file=tmp.wav \"$_\""); $sf = Audio::SndFile->open("<", "tmp.wav"); $_ = "tmp.wav"; } $sec = ($sf->frames) * (1 / $sf->samplerate); } - printf "file is %03.2f sec\n", $sec; + printf "file is %03.2f sec\n", $sec unless $quiet; my $oldstamp = -1; my $oldkey = -1; - my $got = `vamp-simple-host qm-vamp-plugins:qm-keydetector "$_" 2`; + my $got = `vamp-simple-host qm-vamp-plugins:qm-keydetector "$_" 2 2>/dev/null`; die "Analysis failed\n" unless defined $got; - print "\n"; + print "\n" unless $quiet; my @got = split "\n", $got; ## for my $line (@got) { @@ -122,24 +127,25 @@ for(@ARGV) { } elsif($fold_minor) { $key = get_relative_minor($key); } - if($key =~ /m$/) { - $relkey = get_relative_major($key); - } else { - $relkey = get_relative_minor($key); - } + $relkey = get_relative_key($key); if($oldstamp != -1) { $times{$oldkey} += ($stamp - $oldstamp); } $oldstamp = $stamp; $oldkey = $key; - print "$stamp: $key ($relkey)\n"; + print "$stamp: $key ($relkey)\n" unless $quiet; } $times{$oldkey} += ($sec - $oldstamp); - print "\n"; - for(sort { $times{$b} <=> $times{$a} } keys %times) { - printf "%3s: %4.2f sec, %4.2f%%\n", $_, $times{$_}, $times{$_} / $sec * 100; + print "\n" unless $quiet; + my @sorted = sort { $times{$b} <=> $times{$a} } keys %times; + if($quiet) { + print $sorted[0] . " (" . get_relative_key($sorted[0]) . ")\n"; + } else { + for(@sorted) { + printf "%3s: %4.2f sec, %4.2f%%\n", $_, $times{$_}, $times{$_} / $sec * 100; + } } } @@ -153,6 +159,16 @@ sub get_relative_minor { return $relative_minors{$key} || $key; } +sub get_relative_key { + my $key = shift || die "missing key"; + if($key =~ /m$/) { + $relkey = get_relative_major($key); + } else { + $relkey = get_relative_minor($key); + } + return $relkey; +} + __END__ Output of plugin: - Estimated key (from C major = 1 to B major = 12 and C minor = 13 to B minor = 24) diff --git a/image_diff.pl b/image_diff.pl new file mode 100644 index 0000000..9199f61 --- /dev/null +++ b/image_diff.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl -w + +# given 2 images of the same size, create a 3rd image, with the pixel +# at each (x,y) transparent if it's the same RGB color in both images, +# or set to the color in the 2nd image if they're different. + +# the 2nd image should be an edited version of the 1st image, and the +# output image can be displayed overlaid on the 1st image to show what +# the 2nd image looked like. + +use Image::Magick; + +sub die_usage { + die "usage: $0 <input-image1> <input-image2> <output-image>]\n"; +} + +sub readimage { + my $file = shift; + my $i = new Image::Magick; + my $r = $i->Read($file); + die "$r\n" if $r; + warn "read $file, OK\n"; + return $i; +} + +die_usage() if @ARGV != 3; +$a = readimage(shift); +$b = readimage(shift); + +$w = $a->Get('width'); +$h = $a->Get('height'); + +if($w != $b->Get('width') || $h != $b->Get('height')) { + die "input images are not the same pixel size\n"; +} + +$out = Image::Magick->new(size => $w . 'x' . $h); +$r = $out->ReadImage('xc:transparent'); +die "$r\n" if $r; + +# this is rather slow... +for($y = 0; $y < $h; $y++) { + for($x = 0; $x < $w; $x++) { + my @ap = $a->GetPixel(map => "RGB", x => $x, y => $y); + my @bp = $b->GetPixel(map => "RGB", x => $x, y => $y); + if($ap[0] != $bp[0] || $ap[1] != $bp[1] || $ap[2] != $bp[2]) { + push @bp, 65535; # fully opaque + } else { + @bp = (0, 0, 0, 0); # transparent (and black) + } + $r = $out->SetPixel(map => "RGBA", x => $x, y => $y, color => \@bp); + die "$r\n" if $r; + } +} + +$r = $out->Write($ARGV[0]); +die "$r\n" if $r; +warn "wrote to $ARGV[0]\n"; diff --git a/litterbox b/litterbox new file mode 100755 index 0000000..ad730ab --- /dev/null +++ b/litterbox @@ -0,0 +1,465 @@ +#!/bin/bash + +# based on the API spec: https://litterbox.catbox.moe/tools.php + +VERSION="0.0.1" +SELF="$( basename $0 )" +APIURL="https://litterbox.catbox.moe/resources/internals/api.php" +URLS="" +EXPIRE="1" +DELAY="${DELAY:-2}" +UPLOAD_COUNT=0 +TMP="$( mktemp -t -d $SELF.XXXXXXXXXX )" +trap cleanup EXIT + +die() { + echo "$SELF: fatal: $@" 1>&2 + exit 1 +} + +warn() { + echo "$SELF: warning: $@" 1>&2 +} + +info() { + [ "$VERBOSE" = "1" ] && echo "$SELF: info: $@" 1>&2 +} + +check_path() { + type -p "$1" &>/dev/null +} + +cleanup() { + [ "$TMP" != "" ] && rm -rf "$TMP" +} + +usage() { + if ! check_path perldoc; then + warn "can't find perldoc on PATH, printing raw POD." + exec sed -n '/^=pod/,/^=cut/p' $0 + fi + + exec perldoc "$0" +} + +manpage() { + exec pod2man --stderr -s1 -c"UrchlaysStuff" -r$VERSION "$0" +} + +pluralize() { + echo -n "$1 $2" + [ "$1" != 1 ] && echo -n "s" +} + +delay() { + info "sleeping $DELAY sec between $1" + sleep "$DELAY" +} + +set_expiration() { + case "$1" in + 1|12|24|72) ;; # OK + *) die "invalid expiration time, allowed values are: 1 12 24 72" ;; + esac + EXPIRE="$1" +} + +add_url() { + if [ "$URLS" = "" ]; then + URLS="$1" + else + URLS+=" $1" + fi + : $(( UPLOAD_COUNT++ )) +} + +copy_urls() { + if ! check_path xsel; then + warn "xsel not found in PATH, not copying URL(s) to clipboard" + return + fi + + if [ "$DISPLAY" = "" ]; then + warn "DISPLAY not set in env, not copying URL(s) to clipboard" + return + fi + + echo -n "$URLS" | xsel -i +} + +# compress_file() gets called on both files and directories. +compress_file() { + local ext + local basefile + local is_dir + + [ -d "$FILE" ] && is_dir=1 + + if [ "$ZIP" = "1" ]; then + ext=.zip + else + ext=.gz + fi + + basefile="$( basename "$FILE" )" + + # TODO: create a random tmp dir, don't use /tmp! + TMPFILE="$TMP/$basefile$ext" + rm -f "$TMPFILE" + if [ "$ZIP" = "1" ]; then + zip -qr "$TMPFILE" "$FILE" + info "zipped $FILE as $TMPFILE" + else + if [ "$is_dir" = 1 ]; then + tar cfz "$TMPFILE" "$FILE" + info "tarred up $FILE as $TMPFILE" + else + gzip -9 < "$FILE" > "$TMPFILE" + info "gzipped $FILE as $TMPFILE" + fi + fi + FILE="$TMPFILE" +} + +# TODO: curl options: +# --max-time --retry --retry-max-time +upload_file() { + local url + local use_compression + + info "checking $FILE" + + case "$FILE" in + *.exe|*.scr|*.cpl|*.doc*|*.jar) + warn "forbidden file extension, compression forced" + use_compression=1 ;; + *.zip|*.gz) + use_compression=0 ;; + *) + use_compression="$COMPRESS" ;; + esac + + if [ "$use_compression" = 1 ]; then + # can't easily zip stdin, write it to a file + if [ "$FILE" = "-" ]; then + FILE="$TMP/$SELF.stdin" + cat > "$FILE" + if [ "$?" != 0 ]; then + rm -f "$FILE" + return + fi + fi + compress_file # resets FILE, sets TMPFILE + fi + + # at this point, we have an actual file, even if input was stdin, + # so we can encrypt it. + if [ "$ENC" = "1" ]; then + CRYPTFILE="$FILE.aes" + openssl enc -pbkdf2 -aes-256-cbc < "$FILE" > "$CRYPTFILE" + if [ "$?" != "0" ]; then + warn "encryption failed, skipping file" + return + fi + FILE="$CRYPTFILE" + fi + + if [ "$URLS" != "" ]; then + delay "uploads" + fi + + info "uploading $FILE" + + url="$( + cat "$FILE" | \ + curl --silent \ + -F "reqtype=fileupload" \ + -F "time=${EXPIRE}h" \ + -F "fileToUpload=@-" \ + "$APIURL" + )" + # TODO: check curl's exit status! + + case "$url" in + https://litter.catbox.moe/*) add_url "$url" ;; + *) warn "upload failed" ;; + esac +} + +download_and_decrypt() { + local url="$1" + info "downloading and decrypting $url" + curl --silent "$url" | openssl enc -d -pbkdf2 -aes-256-cbc + [ "$?" != 0 ] && warn "download/decrypt failed" +} + +# main() +if [ "$1" = "--man" ]; then + manpage ; exit 0 +fi + +if [ "$1" = "--help" ]; then + usage ; exit 0 +fi + +if ! check_path curl; then + die "can't find curl on PATH (PATH is \"$PATH\")" +fi + +# option string starts with a :, meaning we're responsible for +# printing our own error messages. +while getopts ":e:gzcvdo:" OPT; do + case "$OPT" in + e) set_expiration "$OPTARG"; opt_e=1 ;; + o) DLOUT="$OPTARG" ;; + g) GZIP=1 ;; + z) ZIP=1 ;; + c) ENC=1 ;; + v) VERBOSE=1 ;; + d) DOWNLOAD=1 ;; + *) die "invalid option -$OPTARG, try --help" ;; + esac +done + +# getopts doesn't remove the args/options for us, so: +shift $(($OPTIND - 1)) + +# sanity check options and external binaries' existence. +[ "$GZIP" = 1 -a "$ZIP" = 1 ] && die "can't give both -g and -z" + +if [ "$ZIP" = 1 ] && ! check_path zip; then + warn "can't find zip on PATH, disabling -z option" + ZIP=0 +fi + +if [ "$GZIP" = 1 ] && ! check_path gzip; then + warn "can't find gzip on PATH, disabling -g option" + GZIP=0 +fi + +[ "$GZIP" = 1 -o "$ZIP" = 1 ] && COMPRESS=1 + +if [ "$DOWNLOAD" = 1 -o "$ENC" = 1 ]; then + check_path openssl || die "can't find openssl on path, -c/-d won't work" +fi + +# download mode (-d) +if [ "$DOWNLOAD" = 1 ]; then + [ "$GZIP" = 1 ] && warn "-g option ignored when using -d" + [ "$ZIP" = 1 ] && warn "-z option ignored when using -d" + [ "$ENC" = 1 ] && warn "-c option ignored when using -d" + [ "$opt_e" = 1 ] && warn "-e option ignored when using -d" + [ "$DLOUT" != "" ] && exec 1>"$DLOUT" || die "can't redirect stdout" + for arg; do + [ "$need_delay" = "1" ] && delay "downloads" + download_and_decrypt "$arg" + need_delay=1 + done + exit 0 +else + [ "$DLOUT" != "" ] && warn "-o option ignored when not using -d" +fi + +info "expire time set to $( pluralize $EXPIRE hour )" + +# upload mode +[ "$1" = "" ] && set -- - +for arg; do + TMPFILE="" + CRYPTFILE="" + + if [ "$arg" = "-" -o -f "$arg" ]; then + FILE="$arg" + elif [ -d "$arg" ]; then + FILE="$arg" + compress_file # sets FILE + else + # broken symlink, device node, FIFO, ??? + warn "$arg not a regular file, ignoring" + continue + fi + + upload_file + + # clean up if needed + if [ "$TMPFILE" != "" ]; then + info "removing temp file $TMPFILE" + rm -f "$TMPFILE" + fi + + if [ "$TMPFILE" != "" ]; then + info "removing temp encrypted file $CRYPTFILE" + rm -f "$CRYPTFILE" + fi +done + +if [ "$UPLOAD_COUNT" = "0" ]; then + die "no files uploaded" +fi + +echo "$URLS" +copy_urls + +info "$( pluralize $UPLOAD_COUNT file ) uploaded" + +exit 0 + +# Rest of the file is perldoc. +: <<EOF +=pod + +=head1 NAME + +B<litterbox> - upload files to litterbox.catbox.moe + +=head1 SYNOPSIS + +B<litterbox> [-e I<hours>] [-g] [-z] [-c] [-v] I<file> <I<file ...>> + +B<litterbox> -d [-o I<output>] I<url> <I<url ...>> + +=head1 DESCRIPTION + +B<litterbox> uploads files to (or downloads files from) the temporary +file storage site B<https://litterbox.catbox.moe>. The site is +similar to a "pastebin" site, except that it allows any type of +file (not just text), and uploads can be up to 1GB in size. + +=head2 Uploading + +Each I<file> can be a filename, a directory name, or "-" for standard +input. If no files are given, litterbox reads from standard input. +Directories are uploaded as B<.tar.gz> archives, by default. Files +can be optionally encrypted with AES-256. + +Once a file is uploaded, its URL is printed on stdout (one per +line). If B<xsel>(1) is installed and X is running, URLs are also +copied to the X paste buffer (separated by spaces). + +When uploading multiple files, a delay of 2 seconds is added between +uploads, to avoid hammering the site. + +=head2 Downloading + +Download mode is only for downloading AES-256 encrypted files that +were previously uploaded with B<litterbox>. Non-encrypted files can +simply be downloaded with B<wget>(1), B<curl>(1), a web browser, etc. +You will, of course, have to provide the correct decryption key +(password). + +=head1 OPTIONS + +=head2 Upload Options + +=over 4 + +=item B<-e> I<hours> + +Set expiration time in hours. Allowed values are B<1>, B<12>, +B<24>, or B<72>. Default is B<1>. + +=item B<-g> + +Upload gzipped files. This is automatically enabled +when uploading files whose names end in .exe, .scr, +.cpl, .doc*, .jar (unless B<-z> is given). Also, directories +are automatically tarred/gzipped (unless B<-z> is given). + +=item B<-z> + +Upload zipped files or dirs, rather than .gz or .tar.gz. + +=item B<-c> + +Upload encrypted file. Currently this means the file +is encrypted with 'openssl enc -pbkdf2 -aes-256-cbc', and +you're prompted for an encryption key. Whoever downloads +the encrypted file will have to decrypt it with the B<-d> +option, or else do it manually. + +=back + +=head2 Download Options + +=over 4 + +=item B<-d> + +Download and decrypt previously uploaded file(s) from URL(s). +Equivalent to downloading the file normally, then piping it +through: + + openssl enc -d -pbkdf2 -aes-256-cbc + +...which means the file will be printed to your terminal, unless you +use shell redirection or the B<-o> option. The upload options above (B<-e> +B<-g> B<-z> B<-t> B<-c>) are ignored in download mode. + +=item B<-o> I<file> + +Set output file for B<-d> option. Note that giving multiple URLs +will result in all the data being written to the same file. If you need +separate files, run B<litterbox> multiple times. + +The B<-o> option is ignored in upload mode (when B<-d> is not used). + +=back + +=head2 General Options + +=over 4 + +=item B<-v> + +Verbose operation. + +=item B<--help> + +Print this help to standard output and exit. Since the help is long, +it will be passed through your pager. + +=item B<--man> + +Print this help in man page (troff) format to standard output and exit. + +=back + +=head1 NOTES + +=over 2 + +=item B<-> + +B<litter.catbox.moe> is an amazingly useful service. B<Please> be +respectful of the site's owner, B<follow> the site's rules, and don't +abuse the service with excessive requests. You should also consider +supporting the site financially, by buying merch from its store, or +simply making a donation. + +=item B<-> + +With the B<-c> and B<-d> options, you will be prompted interactively +for the encryption key (password), on the process's controlling tty. +This could pose a problem if there's no controlling tty. With +multiple files, you will be prompted separately for each. + +=item B<-> + +Copying URLs to the copy/paste buffer only works under X11. It might +be possible to support gpm(8) for the console. If Wayland has an +xsel-like utility, it could be supported too (the author doesn't use +Wayland so it's not high on the priority list). + +=back + +=head1 AUTHOR + +B. Watson <urchlay@slackware.uk> + +=head1 COPYRIGHT + +B<litterbox> is licensed under the WTFPL: do WTF you want to with this. +See http://www.wtfpl.net/txt/copying/ for details. + +=cut +EOF @@ -1,16 +1,25 @@ -#!/bin/sh +#!/bin/bash + +VERSION="0.1.0" +SELF="$( basename $0 )" # 20200424 bkw: This used to be a one-line script: # cat "${1:--}" | nc termbin.com 9999 # ...but I really wanted the "copy link to X clipboard" feature. -if [ "$1" = "--help" ]; then +if [ "$1" = "--help" -o "$1" = "-h" ]; then cat <<EOF -Usage: $( basename $0 ) <filename> +termbin v$VERSION + +Usage: $SELF <filename> [<filename> ...] + +Uses nc (netcat) to paste stdin to termbin.com, or pastes one or more +files if <filename(s)> are given. Spits out paste URL on stdout. Also +copies URL to the X clipboard if X is running and either xsel or xclip +is installed. -Uses nc (NetCat) to paste stdin to termbin.com, or pastes a file if -<filename> is given. Spits out paste URL on stdout. Also copies URL to -the X clipboard if X is running and either xsel or xclip is installed. +Only one paste is created. If you give multiple filenames, they are +concatenated together. Written by B. Watson <urchlay@slackware.uk>, released under the WTFPL: do WTF you want with this. @@ -18,20 +27,21 @@ EOF exit 0 fi -if ! which nc &>/dev/null; then - echo "$( basename $0 ): can't find nc on path, install nc or netcat package" - exit 1 -fi +# 20251226 bkw: the sed is because termbin.com sends us e.g. +# https://termbin.com/XXXX\n\0 +# and we get "warning: ignoring null byte" from bash. +url="$( cat "${1:--}" | nc termbin.com 9999 | sed 's,\x00,,' )" -url="$( cat "${1:--}" | nc termbin.com 9999 )" -[ -z "$url" ] && exit "$?" +# 20251227 bkw: don't print an error message if nc fails (nc will print +# its own, or the shell will if it's "command not found"). +err="$?" +[ -z "$url" ] && exit "$err" echo "$url" if [ -n "$DISPLAY" ]; then - if which xsel &>/dev/null; then - echo -n "$url" | xsel -i - elif which xclip &>/dev/null; then - echo -n "$url" | xclip - fi + echo -n "$url" | xsel -i >/dev/null 2>&1 || \ + echo -n "$url" | xclip >/dev/null 2>&1 fi + +exit 0 @@ -0,0 +1,8 @@ +#!/bin/sh + +case "$@" in + -u) PIPE="| unaccent utf8" ;; + *) ;; +esac + +eval exec uconv -x Any-Latin $PIPE diff --git a/tunebass b/tunebass new file mode 100755 index 0000000..5394abc --- /dev/null +++ b/tunebass @@ -0,0 +1,83 @@ +#!/bin/bash + +# Ear tuner for guitar and bass. Install tunebass on your $PATH, and +# symlink it to tunegtr. + +# this used to be a one-liner: +#play -n synth 2 pluck ${1:-A2} repeat 99999 + +case "$0" in + *gtr*|*guit*) + notes="E4 B3 G3 D3 A2 E2" + note=6 + lastnote=6 + ;; + *) + notes="G2 D2 A1 E1" + note=4 + lastnote=4 + ;; +esac + +prompt="Space: next string, BkSpc: prev string, 1-$lastnote: strings, q: quit, ?: help" + +# these seem like they ought to be the arrow keys, but they're actually +# \n and \b... kcud1 and kcub1 are the 'application mode' versions, which +# doesn't do us any good here. +#down="$( tput cud1 )" +#left="$( tput cub1 )" + +# these are correct for xterm and derivatives, and for the linux console. +down="$( printf "\x1b\x5bB" )" +left="$( printf "\x1b\x5bD" )" + +up="$( tput cuu1 )" +right="$( tput cuf1 )" +pageup="$( tput kpp )" +pagedown="$( tput knp )" + +esc="$( printf "\x1b" )" +enter=' +' + +oldnote= +pid= +echo "$prompt" +while true; do + narg="$( echo $notes | cut -d' ' -f $note )" + if [ "$oldnote" != "$note" ]; then + [ "$pid" != "" ] && kill "$pid" + echo -e -n "\rString: $note Note: $narg" + tput el + play --buffer 1024 -qn synth 2 pluck $narg repeat - &>/dev/null & + pid="$!" + oldnote="$note" + fi + key= + read -rs -N5 -t 0.1 key + if [ "$key" != "" ]; then + case "$key" in + [1-9]) + [ "$key" -le "$lastnote" ] && note=$key ;; + a|A|h|H|||$left|$pageup) + note=$(( note + 1 )); [ $note -gt $lastnote ] && note=1 ;; + d|D|l|L|' '|' '|$right|$enter|$pagedown) + note=$(( note - 1 )); [ $note = 0 ] && note=$lastnote ;; + s|S|j|J|$down) + note=$lastnote ;; + w|W|k|K|$up) + note=1 ;; + \?) + echo + cat <<EOF +Use space + backspace, arrow keys, pageup/down, WASD, HJKL, or numbers +1 to $lastnote to pick a string. Tune your strings to the notes you hear. +Press Q or Escape to exit. +EOF + ;; + $esc|q|Q) break ;; + esac + fi +done +kill "$pid" +echo @@ -25,9 +25,19 @@ #GLEFT="104x51+1280+0" #GRIGHT="101x51+2222+0" #GFULL="205x51-71+0" -GLEFT="101x49+1280+0" -GRIGHT="90x49+2295+0" -GFULL="191x49+1280+0" + +# Before my Dell widescreen monitor died: +#GLEFT="101x49+1280+0" +#GRIGHT="90x49+2295+0" +#GFULL="191x49+1280+0" + +GLEFT="80x57+1920+0" +GRIGHT="80x57+2720+0" +GFULL="160x57+1920+0" + +CLEFT="-bg #000a00" +CRIGHT="-bg #0a0000" +CFULL="" murder_shell() { # kill the shell that called the shell that's running this script @@ -50,11 +60,11 @@ get_next_geom() { case "$0" in *kill*) murder_shell ;; - *left*) G=$GLEFT ;; - *full*) G=$GFULL ;; + *left*) G=$GLEFT; C="$CLEFT" ;; + *full*) G=$GFULL; C="$CFULL" ;; *next*) G=$( get_next_geom ) ;; - *) G=$GRIGHT ;; + *) G=$GRIGHT; C="$CRIGHT" ;; esac -urxvt -g "$G" "$@" -e sh -c "while true; do bash -login; done" &>/dev/null & +urxvt -g "$G" $C "$@" -e sh -c "while true; do bash -login; done" &>/dev/null & disown @@ -1,5 +1,8 @@ #!/usr/bin/perl +# TODO: +# Braille. + # to read the docs outside of irssi: perldoc /path/to/unifmt.pl # in irssi, "/script load unifmt.pl", then "/unifmt_help" @@ -5,7 +5,7 @@ # (c) 2020 B. Watson <urchlay@slackware.uk> # Released under the WTFPL, see http://www.wtfpl.net/txt/copying/ -# Requires xosd. +# Requires aosd_cat (on Slackware, libaosd from SBo). # Intended for use with xbindkeys, like so: #$ cat ~/.xbindkeysrc @@ -20,7 +20,8 @@ # "Next" and "Prior" are PageDown and PageUp. Replace with "Up" and "Down" # to use arrow keys. If you have multimedia keys, you can probably use -# them. See xbindkeys(1). +# them (try XF86AudioRaiseVolume, XF86AudioLowerVolume, and +# XF86AudioMute). See xbindkeys(1). # Can also be used from the command line. Run with no args for help. @@ -33,27 +34,23 @@ CHANNEL=Master # Each up/down adjustment is by this much. Use the dB suffix if you -# prefer decibels, or leave it off for percentage. -ADJ=3dB +# prefer decibels, % percentage, no suffix for raw units (on one of my +# sound cards, the raw unit range is 0 to 87; on another, 0-127). +ADJ=1% -# osd_cat seems a bit picky about what fonts it'll use. If you give a -# nonexistent font, it won't run at all. If you give a font it "doesn't -# like", you'll see the percentage bar, but no "Volume" text. -FONT=12x24 +# Pango font string, see: +# https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html +# The size is in points; 30px would be pixels. +FONT="Mono Normal 30" -# What color is the OSD bar and text? See /usr/share/X11/rgb.txt. +# What color is the OSD bar and text? See /usr/share/X11/rgb.txt, +# or use hex escapes (with #). # COLOR is for when we're unmuted, MUTECOLOR is for muted. -COLOR=orange -MUTECOLOR=red +COLOR='#00ff00' +MUTECOLOR='#ff0000' -# Text drop shadow, 0 to disable. -SHADOW=3 - -# Where does the OSD show up on screen? One of: top middle bottom -POSITION=bottom - -# How long does the OSD persist? In seconds, integer only. -DELAY=2 +# How long does the OSD persist? In milliseconds. +DELAY=2000 # amixer options. Could use -c, -D here. Don't use -q. # default is "-M", see amixer(1). @@ -67,23 +64,43 @@ SELF="$( basename $0 )" PIDFILE="$HOME/.$SELF.osd.pid" -# osd() will kill any previously-spawned osd_cat process. Ideally this +BLOCKS="██████████████████████████████████████████████████" +BLANKS="──────────────────────────────────────────────────" +PARTIAL="▌" + +# progress() prints a UTF-8 progress bar with 50 characters. +# takes a percentage, 0 to 100 +progress() { + local text="$1" + local count="$2" + local solidblocks + local partial + local trailing + + solidblocks="$(( $count / 2 ))" + partial="$(( $count % 2 ))" + trailing="$(( 50 - ( $solidblocks + $partial) ))" + + echo -n "$text${BLOCKS:0:$solidblocks}" + [ "$partial" = "1" ] && echo -n $PARTIAL + echo -n "${BLANKS:0:$trailing}" +} + +# osd() will kill any previously-spawned aosd_cat process. Ideally this # means rapid multiple keypresses won't "step on" each other. In practice # it seems to work OK, but if you press the key really fast on a slow # system, or hold it down with your key-repeat rate cranked up (on any # system), the OSD bar will never get a chance to display. - osd() { local got="$( amixer $AMIXER_OPTS get $CHANNEL | tail -1 )" local muted="$( echo "$got" | grep '\[off\]$' )" local volpct="$( echo "$got" | cut -d'[' -f2 | cut -d% -f1 )" local db="$( echo "$got" | cut -d'[' -f3 | cut -d']' -f1 )" - local text="Volume $db ($volpct%)" local color="$COLOR" + local text="$( printf "%8s" $db) $( printf "%-3s" $volpct% ) " local oldpid if [ -n "$muted" ]; then - text="$text [Muted]" color="$MUTECOLOR" fi @@ -91,18 +108,29 @@ osd() { # files whose PIDs have been recycled. if [ -e $PIDFILE ]; then oldpid="$( cat $PIDFILE )" - grep -q '^osd_cat' /proc/$oldpid/cmdline 2>/dev/null && \ - kill "$oldpid" 2>/dev/null + grep -q '^aosd_cat' /proc/$oldpid/cmdline 2>/dev/null && \ + kill -9 "$oldpid" 2>/dev/null + rm -f $PIDFILE fi - osd_cat -b percentage \ - -d $DELAY \ - -P $volpct \ - -p $POSITION \ - -c $color \ - -s $SHADOW \ - -f $FONT \ - -T "$text" & + # TODO: make these variables in the config section rather than + # hardcoding them. + progress "$text" "$volpct" | \ + aosd_cat \ + --fore-color $color \ + --back-color '#808080' \ + --font "$FONT" \ + --output 1 \ + --x-offset 159 \ + --y-offset 0 \ + --position 7 \ + --transparency 1 \ + --back-opacity 128 \ + --padding 7 \ + --alignment 0 \ + --fade-in 0 \ + --fade-out 0 \ + --fade-full $DELAY & echo "$!" > $PIDFILE } @@ -121,17 +149,17 @@ mute() { usage() { cat <<EOF -$SELF [up|down|mute|<nnn>] +$SELF [up|down|mute|<nn>] -<nnn> is a numeric volume, possibly followed by "dB" and/or "+" or "-". - It will be passed to 'amixer $AMIXER_OPTS set $CHANNEL' as-is. +<nn> is a numeric volume, possibly followed by "dB" or "%", and/or + "+" or "-". It will be passed to 'amixer $AMIXER_OPTS set $CHANNEL' as-is. EOF exit 1 } check_deps() { local missing="no" - for dep in osd_cat amixer; do + for dep in aosd_cat amixer; do if ! type -p "$dep" > /dev/null; then echo "$SELF: missing required executable '$dep'" 1>&2 missing="yes" @@ -157,11 +185,3 @@ esac osd exit 0 - -# For reference: -#$ amixer get Master -#Simple mixer control 'Master',0 -# Capabilities: pvolume pvolume-joined pswitch pswitch-joined -# Playback channels: Mono -# Limits: Playback 0 - 87 -# Mono: Playback 31 [36%] [-42.00dB] [on] diff --git a/ztunekey b/ztunekey new file mode 100755 index 0000000..61eb9e6 --- /dev/null +++ b/ztunekey @@ -0,0 +1,76 @@ +#!/bin/sh + +# icon from: +# https://vectorified.com/images/guitar-tuner-icon-28.jpg +# ...installed via: +# convert guitar-tuner-icon-28.jpg .local/share/icons/hicolor/256x256/apps/ztunekey.png + +if [ "$1" = "--help" ]; then + cat <<EOF +ztunekey: show tuning and key of an audio file, using a zenity-based UI. + +Usage: ztunekey [file] + +[file] is an audio file, in any format supported by mplayer (since +mplayer is used to convert it to a .wav file for processing). + +With no [file], ztunekey queries audacious (using audtool) to get the +currently playing file. This will fail if audacious isn't running. + +The tuning (in cents relative to A440) and the detected key (and +relative key, e.g. G (Em) or A (F#m)) are shown in a window, after the +file is analyzed. +EOF + exit 0 +fi + +ICON=ztunekey +ZOPT='--width 300 --height 100 --title Tuning/Key' +file="$1" + +if [ "$file" = "" ]; then + file="$( audtool --current-song-filename )" + if [ "$file" = "" ]; then + zenity --error $ZOPT --text="Audacious is not running, or no song is playing." + exit 1 + fi +fi + +if [ ! -e "$file" ]; then + zenity --error $ZOPT --text="No such file:\\n$file" + exit 1 +fi + +bn="$( basename "$file" )" +tmpfile=/tmp/ztunekey.$$.wav + +keyfile=/tmp/key.$$ +tunefile=/tmp/tuning.$$ +( mplayer -really-quiet -benchmark -vo null -ao pcm:fast:file=$tmpfile "$file" + find_tuning -q "$tmpfile" > $tunefile & + find_key -q "$tmpfile" > $keyfile & + wait ) | zenity --progress --no-cancel $ZOPT --icon-name=ztunekey --text="$bn" --pulsate --auto-close + +TUNING="$( cat $tunefile )" +KEY="$( cat $keyfile )" +rm -f $keyfile $tunefile + +zenity --question $ZOPT --icon-name=ztunekey --text="$bn\\nTuning: $TUNING cents\\nKey: $KEY )\\n\\nRetune to standard?" +[ "$?" = 1 ] && exit 1 + +zenity --scale $ZOPT --icon-name=ztunekey --text="Semitones to transpose?\\n0 = original key." --min-value=-12 --max-value=12 --value=0 > /tmp/semi.$$ + +S="$( cat /tmp/semi.$$ )" +rm -f /tmp/semi.$$ +P="$( perl -MPOSIX=round -e "printf '%+2.2f', round(100 * ($S - $TUNING / 100)) / 100" )" +newfile="$( echo "$bn" | sed 's,\.[^.]*$,'$P'.wav,' )" + +mkdir -p ~/retuned +cd ~/retuned +rubberband --pitch $P $tmpfile "$newfile" | zenity --progress --no-cancel $ZOPT --icon-name=ztunekey --text "Retuning $bn by $P semitones." --pulsate --auto-close +flacfile="$( basename "$newfile" .wav )".flac +flac -f --delete-input-file -o "$flacfile" "$newfile" | zenity --progress --no-cancel $ZOPT --icon-name=ztunekey --text "Encoding $flacfile" --pulsate --auto-close +id3cp "$file" "$flacfile" +id3 -2 -t "%|%t||%_f|? (Retuned $P)" "$flacfile" +audtool --playlist-addurl-to-new-playlist ~/retuned/"$flacfile" + diff --git a/ztunekey.desktop b/ztunekey.desktop new file mode 100644 index 0000000..184792e --- /dev/null +++ b/ztunekey.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Version=1.0 +Name=Tuning/Key +Type=Application +Exec=/home/urchlay/bin/ztunekey %f +Icon=ztunekey +Terminal=false +StartupNotify=false +Hidden=false diff --git a/ztunekey.png b/ztunekey.png Binary files differnew file mode 100644 index 0000000..d152c37 --- /dev/null +++ b/ztunekey.png |
