Index: tools/bsewavetool.cc
===================================================================
--- tools/bsewavetool.cc	(revision 4351)
+++ tools/bsewavetool.cc	(working copy)
@@ -1,6 +1,6 @@
 /* BseWaveTool - BSE Wave creation tool
  * Copyright (C) 2001-2004 Tim Janik
- * Copyright (C) 2005 Stefan Westerfeld
+ * Copyright (C) 2005-2007 Stefan Westerfeld
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -1094,6 +1094,206 @@ public:
   }
 } cmd_xinfo ("xinfo");
 
+class InfoCmd : public Command {
+  vector<gfloat> freq_list;
+  vector<string> args;
+  bool           all_chunks;
+public:
+  InfoCmd (const char *command_name) :
+    Command (command_name),
+    all_chunks (false)
+  {
+  }
+  void
+  blurb (bool bshort)
+  {
+    g_print ("{-m=midi-note|-f=osc-freq|--all-chunks} [options]\n");
+    if (bshort)
+      return;
+    g_print ("    Print information about the chunks of a bsewave file.\n");
+    g_print ("    Options:\n");
+    g_print ("    -f <osc-freq>       oscillator frequency to select a wave chunk\n");
+    g_print ("    -m <midi-note>      alternative way to specify oscillator frequency\n");
+    g_print ("    --all-chunks        show information for all chunks (default)\n");
+    g_print ("    --show <what>       print chunk information in a script readable way\n");
+    g_print ("    --xinfo <what>      print chunk XInfo (if present), script readable\n");
+    g_print ("    When called without --show or --xinfo, this command will show the\n");
+    g_print ("    information available in a human readable way.\n");
+    g_print ("    Show can be used to show one of the following things known about a chunk:\n");
+    g_print ("      osc-freq - the frequency of the chunk\n");
+    g_print ("      mix-freq - the mix freq (sampling rate) of the chunk\n");
+    g_print ("      length   - the length of the chunk in samples\n");
+    g_print ("      volume   - the volume at which the chunk is to be played\n");
+    g_print ("      format   - the storage format used to save the chunk data\n");
+    g_print ("    Sometimes, using --show and --xinfo can be used to figure out the same\n");
+    g_print ("    information, but their result will be slightly different. For instance,\n");
+    g_print ("    --show=volume asks for the replay volume beast will use and will return 1.0\n");
+    g_print ("    even for those chunks that do not have any XInfo for the volume adjustment,\n");
+    g_print ("    whereas --xinfo=volume will show the value of the XInfo, if present, or a\n");
+    g_print ("    blank line if that XInfo is not set.\n");
+    /*       "**********1*********2*********3*********4*********5*********6*********7*********" */
+  }
+  guint
+  parse_args (guint  argc,
+              char **argv)
+  {
+    // FIXME: argument parsing can be simplified after applying #450724
+    bool seen_selection = false;
+
+    for (guint i = 1; i < argc; i++)
+      {
+        const gchar *str = NULL;
+        if (parse_bool_option (argv, i, "--all-chunks"))
+          {
+            all_chunks = true;
+            seen_selection = true;
+          }
+        else if (parse_str_option (argv, i, "-f", &str, argc))
+          {
+            freq_list.push_back (g_ascii_strtod (str, NULL));
+            seen_selection = true;
+          }
+        else if (parse_str_option (argv, i, "-m", &str, argc))
+          {
+            SfiNum num = g_ascii_strtoull (str, NULL, 10);
+            gfloat osc_freq = 440.0 /* MIDI standard pitch */ * pow (BSE_2_POW_1_DIV_12, num - 69. /* MIDI kammer note */);
+            freq_list.push_back (osc_freq);
+            seen_selection = true;
+          }
+        else
+          {
+            args.push_back (argv[i]);
+            argv[i] = 0;
+          }
+      }
+    if (!seen_selection) /* default to all chunks */
+      all_chunks = true;
+    return 0; /* # args missing */
+  }
+  bool
+  parse_str_arg (vector<string>::const_iterator &ai,
+                 vector<string>::const_iterator  end,
+                 const string                   &option,
+                 string                         &parsed_value)
+  {
+    if (!ai->compare (0, option.size(), option))  // starts with option?
+      {
+        if ((*ai)[option.size()] == '=')          // -o=Value
+          {
+            parsed_value = ai->substr (option.size() + 1);
+            return true;
+          }
+        else if (ai->size() > option.size())      // -oValue
+          {
+            parsed_value = ai->substr (option.size());
+            return true;
+          }
+        else if (ai + 1 < end)                    // -o Value
+          {
+            ai++;
+            parsed_value = *ai;
+            return true;
+          }
+      }
+    return false;
+  }
+  string
+  dhandle_storage_format (GslDataHandle *dhandle)
+  {
+    GslDataHandle *tmp_handle = dhandle;
+    do        /* skip comment or cache handles */
+      {
+        dhandle = tmp_handle;
+        tmp_handle = gsl_data_handle_get_source (dhandle);
+      }
+    while (tmp_handle);
+    GslVorbis1Handle *vhandle = gsl_vorbis1_handle_new (dhandle, 0);
+    if (vhandle)
+      {
+        gsl_vorbis1_handle_destroy (vhandle);
+        return "ogg";
+      }
+    return "raw";
+  }
+  void
+  exec (Wave *wave)
+  {
+    bool first_chunk = true;
+    sort (freq_list.begin(), freq_list.end());
+    // if (!verify_chunk_selection (freq_list, wave)) [...] -> FIXME: validate freq_list (see #450724)
+    
+    /* get the wave into storage order */
+    wave->sort();
+    for (list<WaveChunk>::iterator it = wave->chunks.begin(); it != wave->chunks.end(); it++)
+      if (all_chunks || wave->match (*it, freq_list))
+        {
+          WaveChunk *chunk = &*it;
+          GslDataHandle *dhandle = chunk->dhandle;
+
+          if (args.size())
+            {
+              for (vector<string>::const_iterator ai = args.begin(); ai != args.end(); ai++)
+                {
+                  string str;
+                  if (parse_str_arg (ai, args.end(), "--show", str))
+                    {
+                      // FIXME: chunk key should be queryable (see #450724)
+                      if (str == "length")
+                        g_print ("%lld\n", gsl_data_handle_length (dhandle));
+                      else if (str == "osc-freq")
+                        g_print ("%.3f\n", gsl_data_handle_osc_freq (dhandle));
+                      else if (str == "mix-freq")
+                        g_print ("%.3f\n", gsl_data_handle_mix_freq (dhandle));
+                      else if (str == "volume")
+                        g_print ("%.3f\n", gsl_data_handle_volume (dhandle));
+                      else if (str == "format")
+                        g_print ("%s\n", dhandle_storage_format (dhandle).c_str());
+                      else
+                        {
+                          sfi_error ("In the info command, --show does not support %s as argument.", str.c_str());
+                          exit (1);
+                        }
+                    }
+                  else if (parse_str_arg (ai, args.end(), "--xinfo", str))
+                    {
+                      const char *info = bse_xinfos_get_value (dhandle->setup.xinfos, str.c_str());
+                      g_print ("%s\n", info ? info : "");
+                    }
+                }
+            }
+          else
+            {
+              if (first_chunk)
+                {
+                  first_chunk = false;
+                  g_print ("\n");
+                }
+              g_print ("Chunk\n");    // FIXME: chunk key should be shown here (see #450724)
+              g_print ("  Osc Freq   %8.2f Hz\n", gsl_data_handle_osc_freq (dhandle));
+              g_print ("  Mix Freq   %8.2f Hz\n", gsl_data_handle_mix_freq (dhandle));
+              if (bse_xinfos_get_value (dhandle->setup.xinfos, "midi-note"))
+                {
+                  int note = bse_xinfos_get_num (dhandle->setup.xinfos, "midi-note");
+                  gchar *note_str = bse_note_to_string (note);
+                  g_print ("  MIDI Note   %7d     (%s)\n", note, note_str);
+                  g_free (note_str);
+                }
+              g_print ("  Samples  %10lld     (%.2f s)\n",
+                  gsl_data_handle_length (dhandle),
+                  gsl_data_handle_length (dhandle) / gsl_data_handle_mix_freq (dhandle));
+              if (bse_xinfos_get_value (dhandle->setup.xinfos, "volume"))
+                {
+                  const double volume = gsl_data_handle_volume (dhandle);
+                  const double volume_db = bse_db_from_factor (volume, -200);
+                  g_print ("  Volume      %7.2f%%    (%.2f dB)\n", gsl_data_handle_volume (dhandle) * 100, volume_db);
+                }
+              g_print ("  Stored as  %s data\n", dhandle_storage_format (dhandle).c_str());
+              g_print ("\n");
+            }
+        }
+  }
+} cmd_info ("info");
+
 class ClipCmd : public Command {
   gfloat threshold;
   guint head_samples, tail_samples, fade_samples, pad_samples, tail_silence;
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 4351)
+++ ChangeLog	(working copy)
@@ -1,3 +1,9 @@
+Fri Jul  6 01:39:03 2007  Stefan Westerfeld  <stefan@space.twc.de>
+
+	* tools/bsewavetool.cc: Implemented a new bsewavetool command called
+	"info", which allows users and scripts to query information about all
+	or specific chunks of a bsewave file. See #454121.
+
 Sun Jul  1 17:13:28 2007  Tim Janik  <timj@gtk.org>
 
 	* launchers/bseshstart.c (custom_find_executable): fixed string buffer
