Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Play incremental tone for audio progress bar #143

Open
jooyoungseo opened this issue Sep 11, 2022 · 6 comments
Open

Play incremental tone for audio progress bar #143

jooyoungseo opened this issue Sep 11, 2022 · 6 comments
Labels
Custom progress handler enhancement New feature or request help wanted Extra attention is needed

Comments

@jooyoungseo
Copy link

It would be even nicer if {progressr} could support incremental tones for audio progress bar. This is a technique being used by some screen readers, such as NVDA.

JGauge Progress Bar Monitoring Scripts can give you a better idea of this concept:

This is a set of scripts and a set of 105 wav files comprising an audio gauge. The idea is to have a tone sweep from 220Hz to 880Hz (i.e., one octave below
A 440 to one octave above it) as a progress bar advances.

@HenrikBengtsson HenrikBengtsson added the enhancement New feature or request label Sep 12, 2022
@HenrikBengtsson
Copy link
Collaborator

HenrikBengtsson commented Sep 12, 2022

This is a great example of the type of ideas and features that progressr was designed to support.

The gist of supporting custom progress reporters is to provide the basic functions for communicating the progress (e.g. audio, visual, ...). Then it's a matter of providing a thin progression_handler wrapper on top of those functions. After that, progress can be reported via this new handler.

So, a first step would be to write functions that can generate the different sounds (e.g. synthetically directly via some third-party R package, or by playing sound files). This part does not involve the progressr package, which makes it easier to create. It might become similar to how the 'beepr' progression handler wrapper uses the beepr package to generate sounds. Is this something you could start working on? When that is in place, I can write the wrapper for progressr.

@jooyoungseo
Copy link
Author

@HenrikBengtsson -- The lightweight wav files are available at Gauge Sounds - Sound Files. could we just use these files and cite the author?

@HenrikBengtsson HenrikBengtsson added the help wanted Extra attention is needed label Sep 22, 2022
@llrs
Copy link

llrs commented Jan 11, 2023

Related (duplicate?) to #20

@HenrikBengtsson
Copy link
Collaborator

@HenrikBengtsson -- The lightweight wav files are available at Gauge Sounds - Sound Files. could we just use these files and cite the author?

There's no explicit license, so probably better to re-generate. The first step is to identify exactly which frequencies are involved in those 101 pre-generated WAV files: gauge0.wav, gauge1.wav, and gauge100.wav, which are part of https://www.dlee.org/jgauge/old/gauge_sounds.zip. That ZIP also has the genwavs.pl script (full script below) that was used to generate those sound files. Looking at that script, I think the 101 frequencies are:

> freqs <- 2^seq(from = log2(220), to = log2(880), length.out = 101)

That is, they are uniformly distributed on the logaritmic scale from 220 Hz to 880 Hz.

The duration of those files a 5 ms.

genwavs.pl

#! /usr/local/bin/perl
#
# GenWavs - Generate gauge sound files
#
# This program uses augen (from the autools port) to generate wave files.
# The wave files go with the JGauge scripts and are used as an audible gauge.
#
# Author:  Doug Lee
# See the bottom of this file for licensing information.
#
# Revision History:
#     08/09/03:  Initial version.
#     08/15/03:  Added New/Exp/Obs/Unr wavs and improved the original sounds.

# See genTone() for wave type and start phase.
# See genFile for duration, amplitude, and which tones go in the files.

# Start and end frequencies for the whole file set, in Hz
$startFreq = 220;
$endFreq = 880;
# Number of evenly-spaced sounds to generate, from startFreq to endFreq
# This is inclusive, hence the odd number of sounds.
$nsounds=101;
# Output file name prefix (full name = ${fprefix}<n>.wav)
$fprefix="gauge";

# Number of octaves in the frequency range (possibly non-integral)
my $noctaves = ($endFreq / $startFreq) /2;
# Number of notes per octave (possibly non-integral)
my $npo = ($nsounds-1) / $noctaves;

for(my $i=0; $i<$nsounds; $i++) {
  $fc = $startFreq * (2 ** ($i / $npo));
  genFile( sprintf("$fprefix%-3d", $i), $fc);
}
genFile($fprefix . "New", "new");
genFile($fprefix . "Exp", "exp");
genFile($fprefix . "Obs", "obs");
genFile($fprefix . "Unr", "unr");
exit 0;

sub genFile {
# Generate one file--or rather make augen do it.
  my($fname, $fc) = @_;
  $fname =~ s/\s+$//;
  print "Generating $fname ...\n";
  open(FH, "|augen -s 11025 -ea wav -g - -o $fname")
      ||die "Can't generate $fname; $!\n";
  select(FH);
  if ($fc =~ /^\d/) {
    genTone($fc, 0.05, 0.3);
    genBorders();
  } elsif ($fc =~ /new/i) {
    my $sf = ($startFreq+$endFreq) /4;
    my $ef = ($startFreq+$endFreq) *3 /4;
    printf("t 0 0.05 %9.4f %9.4f 90 0.3 0.3\n",
        $sf, $ef);
    genBorders();
  } elsif ($fc =~ /exp/i) {
    my $sf = ($startFreq+$endFreq) *3 /4;
    my $ef = ($startFreq+$endFreq) /4;
    printf("t 0 0P.05 %9.4f %9.4f 90 0.3 0.3\n",
        $sf, $ef);
    genBorders();
  } elsif ($fc =~ /obs/i) {
    printf("n 0 0.05 0.1 0.1\n");
    genBorders();
  } elsif ($fc =~ /unr/i) {
    printf("n 0 0.05 0.1 0.1\n");
  }
  close(FH);  # waits for generation to complete
  select(STDOUT);
  die "Generation of $fname failed.\n" if !-e "$fname.wav";
}

sub genBorders {
  genTone($startFreq, 0.05, 0.08);
  genTone($endFreq, 0.05, 0.08);
}

sub genTone {
# Produce the directions needed for one tone.
  my($fc, $nsec, $amp) = @_;
  # waveType T0 T1 fc0 fc1 startPhaseDeg amp0 amp1
  printf("t 0 %4.2f %9.4f %9.4f 90 %4.2f %4.2f\n",
      $nsec, $fc, $fc, $amp, $amp);
}

__END__
/*
Copyright (c) 2006-2009, Doug Lee and SSB BART Group

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* The names of the copyright holders and contributors may not be used to
  endorse or promote products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

@HenrikBengtsson
Copy link
Collaborator

The generic progress-to-frequency mapping function for this is:

progress_freq <- function(ratio, begin_freq = 220, end_freq = 880) {
  stopifnot(length(ratio) == 1, is.numeric(ratio), is.finite(ratio),
            ratio >= 0, ratio <= 1)
  range <- (log2(end_freq) - log2(start_freq))
  freq <- 2^(log2(start_freq) + ratio * range)
  freq
}

For example, at 0% progress, we get:

> progress_freq(ratio = 0.0)
[1] 220

at 100% progress we get:

> progress_freq(ratio = 1.0)
[1] 880

At 10% progress, we get:

> progress_freq(ratio = 0.10)
[1] 252.7136

and at 50% progress, we get:

> progress_freq(ratio = 0.5)
[1] 440

@HenrikBengtsson
Copy link
Collaborator

Oh, there's more to it; https://www.dlee.org/jgauge/ says that each of these single-tone sounds also have a start and a stop sound;

Each tone file is only 0.05 seconds long, so it amounts to a very short blip. Actually, each file contains three simultaneous tones: the one indicating the current progress percentage, plus a quieter tone for the starting position and one for the ending position. The upshot of all this is that, as a progress bar moves, you hear not only that it's moving, but also a very fast and accurate description of how far it is relative to its endpoints. Easier to hear than to explain, no doubt.

I'm not sure what they are, and how they are generated, but looking at the getBorders() function in the original genwavs.pl script, I think they are simple the two boundary frequences, i.e. 220 Hz and 880 Hz.

Also, from the Perl script, I believe that these two boundary tones have an amplitude of 8% (0.08), whereas the invidual "progress" tones have an amplitude of 30% (0.30). That is, the relative amplitude of the boundary tones is 0.08/0.30 = 0.267 = 26.7%.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Custom progress handler enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants