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
@@ -310,6 +310,155 @@ parse_bool_option (char        **argv,
   return false;
 }
 
+/* wave chunk keys for shell iteration */
+class WaveChunkKey {
+  BseFloatIEEE754 m_osc_freq;
+public:
+  WaveChunkKey (const String& key_string)
+  {
+    // mark key as invalid
+    m_osc_freq.v_float = -1;     
+    if (key_string.size() != 7)
+      return;  // invalid key
+
+    guint64 key_uint = 0;
+    for (string::const_reverse_iterator si = key_string.rbegin(); si != key_string.rend(); si++)
+      {
+        key_uint *= 62;
+        if (*si >= '0' && *si <= '9')
+          key_uint += *si - '0';
+        else if (*si >= 'A' && *si <= 'Z')
+          key_uint += *si - 'A' + 10;
+        else if (*si >= 'a' && *si <= 'z')
+          key_uint += *si - 'a' + 10 + 26;
+        else
+          return; // invalid key
+      }
+
+    const guint64 key_checksum = key_uint % 512;
+    key_uint ^= key_checksum << 32LL;  // deobfuscate high bits with checksum
+    const guint64 checksum = g_str_hash (string_printf ("%lld", key_uint - key_checksum).c_str()) % 512;
+    if (key_checksum != checksum)
+      return; // invalid key
+    key_uint >>= 9;
+
+    // decode float components in byte order independent way
+    m_osc_freq.mpn.mantissa = key_uint;
+    key_uint >>= 23;
+    m_osc_freq.mpn.biased_exponent = key_uint;
+    key_uint >>= 8;
+    m_osc_freq.mpn.sign = key_uint;
+  }
+  WaveChunkKey (float osc_freq)
+  {
+    m_osc_freq.v_float = osc_freq;
+  }
+  String
+  as_string() const
+  {
+    guint64 key_uint = 0;
+
+    // put float components to key in byte order independent way
+    key_uint |= m_osc_freq.mpn.sign;            //  1 bit
+    key_uint <<= 8;
+    key_uint |= m_osc_freq.mpn.biased_exponent; //  8 bit
+    key_uint <<= 23;
+    key_uint |= m_osc_freq.mpn.mantissa;        // 23 bit
+    key_uint <<= 9;                             // +9 bit checksum  
+    const guint64 checksum = g_str_hash (string_printf ("%lld", key_uint).c_str()) % 512;
+    key_uint |= checksum;
+    key_uint ^= (key_uint % 512) << 32LL;       // obfuscate high bits with checksum
+
+    string key_string;
+    for (int i = 0; i < 7; i++) /* encode in custom base-62 format; 7 digits  <=>  2^41 < 62^7 */
+      {
+        guint64 digit = key_uint % 62;
+        if (digit < 10)
+          key_string += '0' + digit;
+        else if (digit < (10 + 26))
+          key_string += 'A' + digit - 10;
+        else if (digit < (10 + 26 + 26))
+          key_string += 'a' + digit - 10 - 26;
+        else
+          g_assert_not_reached();
+        key_uint /= 62;
+      }
+    g_assert (key_uint == 0);
+      
+    return key_string;
+  }
+  bool
+  is_valid() const
+  {
+    return m_osc_freq.v_float >= 0;
+  }
+  float
+  osc_freq() const
+  {
+    return m_osc_freq.v_float;
+  }
+};
+
+static bool
+parse_chunk_selection (char          **argv,
+                       uint           &i,
+                       uint            argc,
+                       bool           &all_chunks,
+                       vector<float>  &freq_list)
+
+{
+  const gchar *str = NULL;
+
+  if (parse_bool_option (argv, i, "--all-chunks"))
+    {
+      all_chunks = true;
+      return true;
+    }
+  else if (parse_str_option (argv, i, "-f", &str, argc))
+    {
+      freq_list.push_back (g_ascii_strtod (str, NULL));
+      return true;
+    }
+  else if (parse_str_option (argv, i, "--chunk-key", &str, argc))
+    {
+      WaveChunkKey key (str);
+      if (key.is_valid())
+        {
+          freq_list.push_back (key.osc_freq());
+          return true;
+        }
+      else
+        {
+          sfi_error ("%s is not a valid bsewavetool key (keys need to be generated by list-chunks)", str);
+          exit (1);
+        }
+    }
+  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);
+      return true;
+    }
+  return false;
+}
+
+static bool
+verify_chunk_selection (const vector<float> &freq_list,
+                        Wave                *wave)
+{
+  for (vector<float>::const_iterator fi = freq_list.begin(); fi != freq_list.end(); fi++)
+    {
+      if (!wave->lookup (*fi))
+        {
+          sfi_error ("wave chunk with oscillator frequency %.2f does not exist", *fi);
+          if (!continue_on_error)
+            return false;
+        }
+    }
+  return true;
+}
+
 static void
 wavetool_parse_args (int    *argc_p,
                      char ***argv_p)
@@ -955,26 +1104,34 @@ public:
 } cmd_add_chunk ("add-chunk"), cmd_add_raw_chunk ("add-raw-chunk", AddChunk::RAW);
 
 class XInfoCmd : public Command {
-  vector<char*> args;
+  vector<char*>  args;
+  vector<gfloat> freq_list;
+  bool           all_chunks;
+  bool           modify_wave_xinfos; /* as opposed to modifying chunk xinfos */
 public:
   XInfoCmd (const char *command_name) :
-    Command (command_name)
+    Command (command_name),
+    all_chunks (false),
+    modify_wave_xinfos (false)
   {
   }
   void
   blurb (bool bshort)
   {
-    g_print ("{-m=midi-note|-f=osc-freq|--all-chunks|--wave} key=[value] ...\n");
+    g_print ("{-m=midi-note|-f=osc-freq|--chunk-key=key|--all-chunks|--wave} key=[value] ...\n");
     if (bshort)
       return;
     g_print ("    Add, change or remove an XInfo string of a bsewave file.\n");
+    g_print ("    Generally, options like -m or -f need to be used to specify which chunks\n");
+    g_print ("    (one or many) should be modified. Alternatively, it is possible to specify\n");
+    g_print ("    --wave to indicate that the wave itself should be modified. It is not\n");
+    g_print ("    possible to modify chunks and wave xinfos in one run.\n");
     g_print ("    Omission of [value] deletes the XInfo associated with the key.\n");
-    g_print ("    Key and value pairs may be specified multiple times, optionally preceeded\n");
-    g_print ("    by location options to control what portion of a bsewave file (the wave,\n");
-    g_print ("    individual wave chunks or all wave chunks) should be affected.\n");
+    g_print ("    Key and value pairs may be specified multiple times.\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 ("    --chunk-key <key>   select wave chunk using chunk key from list-chunks\n");
     g_print ("    --all-chunks        apply XInfo modification to all chunks\n");
     g_print ("    --wave              apply XInfo modifications to the wave itself\n");
     /*       "**********1*********2*********3*********4*********5*********6*********7*********" */
@@ -983,113 +1140,71 @@ public:
   parse_args (guint  argc,
               char **argv)
   {
+    uint missing_args = 1; /* need wave or chunks */
     args.clear();
-    for (guint i = 1; i < argc; i++)
+    for (uint i = 1; i < argc; i++)
       {
-        args.push_back (argv[i]);
-        argv[i] = NULL;
+	if (parse_chunk_selection (argv, i, argc, all_chunks, freq_list))
+          missing_args = 0;
+        else if (parse_bool_option (argv, i, "--wave"))
+          {
+            missing_args = 0;
+            modify_wave_xinfos = true;
+          }
+        else
+	  {
+	    args.push_back (argv[i]);
+	    argv[i] = NULL;
+	  }
       }
-    return 0; /* no args missing */
+    if (modify_wave_xinfos && (freq_list.size() || all_chunks))
+      {
+        sfi_error ("xinfos can only be added to one or many chunks OR to the wave, but not to both");
+        exit (1);
+      }
+    return missing_args;
   }
-  typedef enum {
-    NONE,
-    OSC_FREQ,
-    ALL_CHUNKS,
-    WAVE
-  } Location;
   void
   exec (Wave *wave)
   {
-    gfloat osc_freq = 0;
-    Location location = NONE;
+    sort (freq_list.begin(), freq_list.end());
+    if (!verify_chunk_selection (freq_list, wave))
+      exit (1);
+
+    /* get the wave into storage order */
+    wave->sort();
     for (vector<char*>::iterator it = args.begin(); it != args.end(); it++)
       {
         const char *arg = *it;
-        if (strcmp ("--all-chunks", arg) == 0)
-          location = ALL_CHUNKS;
-        else if (strcmp ("--wave", arg) == 0)
-          location = WAVE;
-        else if (strcmp ("-m", arg) == 0 ||
-                 strncmp ("-m", arg, 2) == 0)
-          {
-            const gchar *equal = arg + 2;
-            const gchar *note_str = NULL;
-            if (*equal == '=')            /* -m=Arg */
-              note_str = equal + 1;
-            else if (*equal)              /* -mArg */
-              note_str = equal;
-            else if (it + 1 != args.end())/* -m Arg */
-              {
-                it++;
-                note_str = *it;
-              }
-            if (note_str)
-              {
-                SfiNum num = g_ascii_strtoull (note_str, NULL, 10);
-                osc_freq = 440.0 /* MIDI standard pitch */ * pow (BSE_2_POW_1_DIV_12, num - 69. /* MIDI kammer note */);
-                location = OSC_FREQ;
-              }
-          }
-        else if (strcmp ("-f", arg) == 0 ||
-                 strncmp ("-f", arg, 2) == 0)
-          {
-            const gchar *equal = arg + 2;
-            const gchar *freq_str = NULL;
-            if (*equal == '=')            /* -f=Arg */
-              freq_str = equal + 1;
-            else if (*equal)              /* -fArg */
-              freq_str = equal;
-            else if (it + 1 != args.end())/* -f Arg */
-              {
-                it++;
-                freq_str = *it;
-              }
-            if (freq_str)
-              {
-                osc_freq = g_ascii_strtod (freq_str, NULL);
-                location = OSC_FREQ;
-              }
-          }
-        else /* XInfo string */
-          {
-            const gchar *equal = strchr (arg, '=');
-            if (!equal)
-              {
-                sfi_error ("missing \"=\" in XInfo assignment: %s\n", arg);
-                exit (1);
-              }
-            gchar *key = g_strndup (arg, equal - arg);
-            const gchar *value = equal + 1;
-            g_strcanon (key, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS, '-');
-            if (!key[0] || strncmp (key, arg, equal - arg) != 0)
-              {
-                sfi_error ("invalid key specified in XInfo assignment: %s\n", arg);
-                exit (1);
-              }
-            switch (location)
-              {
-              case NONE:
-                sfi_error ("missing location option for XInfo assignment: %s\n", arg);
-                exit (1);
-              case WAVE:
-                wave->set_xinfo (key, value);
-                break;
-              case OSC_FREQ:
-                if (wave->lookup (osc_freq))
-                  wave->set_chunk_xinfo (osc_freq, key, value);
-                else
-                  {
-                    sfi_error ("failed to find wave chunk with oscillator frequency: %.2f", osc_freq);
-                    if (!continue_on_error)
-                      exit (1);
-                  }
-                break;
-              case ALL_CHUNKS:
-                wave->set_all_xinfo (key, value);
-                break;
-              }
-            g_free (key);
-          }
+	const gchar *equal = strchr (arg, '=');
+	if (!equal)
+	  {
+	    sfi_error ("missing \"=\" in XInfo assignment: %s\n", arg);
+	    exit (1);
+	  }
+	gchar *key = g_strndup (arg, equal - arg);
+	const gchar *value = equal + 1;
+	g_strcanon (key, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS, '-');
+	if (!key[0] || strncmp (key, arg, equal - arg) != 0)
+	  {
+	    sfi_error ("invalid key specified in XInfo assignment: %s\n", arg);
+	    exit (1);
+	  }
+	if (modify_wave_xinfos)
+          wave->set_xinfo (key, value);
+	else
+	  {
+	    for (list<WaveChunk>::iterator ci = wave->chunks.begin(); ci != wave->chunks.end(); ci++)
+	      if (all_chunks || wave->match (*ci, freq_list))
+		{
+		  WaveChunk *chunk = &*ci;
+		  GslDataHandle *dhandle = chunk->dhandle;
+		  float osc_freq = gsl_data_handle_osc_freq (dhandle);
+
+		  wave->set_chunk_xinfo (osc_freq, key, value);
+		}
+	  }
+	g_free (key);
       }
   }
 } cmd_xinfo ("xinfo");
@@ -1114,7 +1229,7 @@ public:
   void
   blurb (bool bshort)
   {
-    g_print ("{-m=midi-note|-f=osc-freq|--all-chunks} [options]\n");
+    g_print ("{-m=midi-note|-f=osc-freq|--chunk-key=key|--all-chunks} [options]\n");
     if (bshort)
       return;
     g_print ("    Clip head and or tail of a wave chunk and produce fade-in ramps at the\n");
@@ -1123,6 +1238,7 @@ public:
     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 ("    --chunk-key <key>   select wave chunk using chunk key from list-chunks\n");
     g_print ("    --all-chunks        try to clip all chunks\n");
 #if 0
     g_print ("    --config=\"s h t f p r\"\n");
@@ -1152,23 +1268,8 @@ public:
     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;
-          }
+	if (parse_chunk_selection (argv, i, argc, all_chunks, freq_list))
+          seen_selection = true;
         else if (parse_str_option (argv, i, "-s", &str, argc))
           threshold = g_ascii_strtod (str, NULL);
         else if (parse_str_option (argv, i, "-h", &str, argc))
@@ -1188,6 +1289,9 @@ public:
   exec (Wave *wave)
   {
     sort (freq_list.begin(), freq_list.end());
+    if (!verify_chunk_selection (freq_list, wave))
+      exit (1);
+
     vector<list<WaveChunk>::iterator> deleted;
     /* level clipping */
     for (list<WaveChunk>::iterator it = wave->chunks.begin(); it != wave->chunks.end(); it++)
@@ -1258,7 +1362,7 @@ public:
   void
   blurb (bool bshort)
   {
-    g_print ("{-m=midi-note|-f=osc-freq|--all-chunks} [options]\n");
+    g_print ("{-m=midi-note|-f=osc-freq|--chunk-key=key|--all-chunks} [options]\n");
     if (bshort)
       return;
     g_print ("    Normalize wave chunk. This is used to extend (or compress) the signal\n");
@@ -1266,6 +1370,7 @@ public:
     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 ("    --chunk-key <key>   select wave chunk using chunk key from list-chunks\n");
     g_print ("    --all-chunks        try to normalize all chunks\n");
     /*       "**********1*********2*********3*********4*********5*********6*********7*********" */
   }
@@ -1276,24 +1381,8 @@ public:
     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;
-          }
+	if (parse_chunk_selection (argv, i, argc, all_chunks, freq_list))
+	  seen_selection = true;
       }
     return !seen_selection ? 1 : 0; /* # args missing */
   }
@@ -1301,6 +1390,9 @@ public:
   exec (Wave *wave)
   {
     sort (freq_list.begin(), freq_list.end());
+    if (!verify_chunk_selection (freq_list, wave))
+      exit (1);
+
     /* normalization */
     for (list<WaveChunk>::iterator it = wave->chunks.begin(); it != wave->chunks.end(); it++)
       if (all_chunks || wave->match (*it, freq_list))
@@ -1349,6 +1441,7 @@ public:
     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 ("    --chunk-key <key>   select wave chunk using chunk key from list-chunks\n");
     g_print ("    --all-chunks        try to loop all chunks\n");
     /*       "**********1*********2*********3*********4*********5*********6*********7*********" */
   }
@@ -1359,24 +1452,8 @@ public:
     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;
-          }
+	if (parse_chunk_selection (argv, i, argc, all_chunks, freq_list))
+	  seen_selection = true;
       }
     return !seen_selection ? 1 : 0; /* # args missing */
   }
@@ -1384,6 +1461,9 @@ public:
   exec (Wave *wave)
   {
     sort (freq_list.begin(), freq_list.end());
+    if (!verify_chunk_selection (freq_list, wave))
+      exit (1);
+
     vector<list<WaveChunk>::iterator> deleted;
     /* level clipping */
     for (list<WaveChunk>::reverse_iterator it = wave->chunks.rbegin(); it != wave->chunks.rend(); it++)
@@ -2060,7 +2140,6 @@ public:
   }
 } cmd_downsample2 ("downsample2");
 
-
 class Export : public Command {
 public:
   vector<gfloat> freq_list;
@@ -2075,7 +2154,7 @@ public:
   void
   blurb (bool bshort)
   {
-    g_print ("{-m=midi-note|-f=osc-freq|--all-chunks|-x=filename} [options]\n");
+    g_print ("{-m=midi-note|-f=osc-freq|--chunk-key=key|--all-chunks|-x=filename} [options]\n");
     if (bshort)
       return;
     g_print ("    Export chunks from bsewave as WAV file.\n");
@@ -2083,6 +2162,7 @@ public:
     g_print ("    -x <filename>       set export filename (supports %%N %%F and %%C, see below)\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 ("    --chunk-key <key>   select wave chunk using chunk key from list-chunks\n");
     g_print ("    --all-chunks        try to export all chunks\n");
     g_print ("    The export filename can contain the following extra information:\n");
     g_print ("      %%F  -  the frequency of the chunk\n");
@@ -2100,23 +2180,8 @@ public:
     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;
-          }
+	if (parse_chunk_selection (argv, i, argc, all_chunks, freq_list))
+          seen_selection = true;
         else if (parse_str_option (argv, i, "-x", &str, argc))
 	  {
 	    export_filename = str;
@@ -2195,6 +2260,12 @@ public:
 	    exit (1);
 	  }
       }
+
+    /* validate freq list */
+    sort (freq_list.begin(), freq_list.end());
+    if (!verify_chunk_selection (freq_list, wave))
+      exit (1);
+
     /* get the wave into storage order */
     wave->sort();
     for (list<WaveChunk>::iterator it = wave->chunks.begin(); it != wave->chunks.end(); it++)
@@ -2259,6 +2330,50 @@ public:
   }
 } cmd_export ("export");
 
+class ListChunks : public Command {
+public:
+  ListChunks (const char *command_name) :
+    Command (command_name)
+  {
+  }
+  void
+  blurb (bool bshort)
+  {
+    g_print ("[options]\n");
+    if (bshort)
+      return;
+    g_print ("    Prints a list of chunk keys of the chunks contained in the bsewave file.\n");
+    g_print ("    A chunk key for a given chunk identifies the chunk uniquely and stays valid\n");
+    g_print ("    if other chunks are inserted and deleted.\n");
+    g_print ("    Here is a bash script to export all chunks (like export with --all-chunks):\n");
+    g_print ("    for key in `bsewavetool list-chunks foo.bsewave`\n");
+    g_print ("    do\n");
+    g_print ("      bsewavetool export foo.bsewave --chunk-key $key -x /tmp/foo-note-%%N.wav\n");
+    g_print ("    done\n");
+    /*       "**********1*********2*********3*********4*********5*********6*********7*********" */
+  }
+  guint
+  parse_args (guint  argc,
+              char **argv)
+  {
+    return 0; // no missing args
+  }
+  void
+  exec (Wave *wave)
+  {
+    /* get the wave into storage order */
+    wave->sort();
+    for (list<WaveChunk>::iterator it = wave->chunks.begin(); it != wave->chunks.end(); it++)
+      {
+        WaveChunk     *chunk = &*it;
+	WaveChunkKey   chunk_key (gsl_data_handle_osc_freq (chunk->dhandle));
+
+	g_print ("%s\n", chunk_key.as_string().c_str());
+      }
+  }
+} cmd_list_chunks ("list-chunks");
+
+
 /* TODO commands:
  * bsewavetool.1 # need manual page
  * bsewavetool merge <file.bsewave> <second.bsewave>
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 4351)
+++ ChangeLog	(working copy)
@@ -1,3 +1,10 @@
+Wed Jul  4 01:06:52 2007  Stefan Westerfeld  <stefan@space.twc.de>
+
+	* tools/bsewavetool.cc: Implemented the necessary code to allow shell
+	scripts to iterate over the chunks in a bsewave file. This includes
+	support for the --chunk-key option for many commands, as well as a new
+	list-chunks command which generates chunk keys suitable for iteration.
+
 Sun Jul  1 17:13:28 2007  Tim Janik  <timj@gtk.org>
 
 	* launchers/bseshstart.c (custom_find_executable): fixed string buffer
