shithub: sox

Download patch

ref: 7a078e8c7e5eea3ba56c1a8e4cd738a47b4bd795
parent: b8a72eb31135e817d63325d463896e6cb7f70bc3
author: robs <robs>
date: Sun Mar 9 07:16:37 EDT 2008

read MP3 ID3 tags; simpler play display

--- a/ChangeLog
+++ b/ChangeLog
@@ -39,6 +39,7 @@
     encoding/decoding; display name of unsupported encoding.  (robs)
   o Can now write .amb (.wav variant) files [FR 1902232].  (robs)
   o Can now read 2,3(2.6),4 bit ADPCM .voc files [FR 1714991].  (robs)
+  o Can now read some MP3 ID3 tags.  (robs)
 
 Effects:
 
@@ -64,6 +65,7 @@
   o Default audio devices (sox), and allow environment variables to
     be used to override the default audio device driver (rec/play)
     and default audio device (all).  (robs)
+  o Simpler file info display for `play'.  (robs)
 
 Other bug fixes:
 
--- a/src/misc.c
+++ b/src/misc.c
@@ -56,6 +56,33 @@
   "LPC10",
 };
 
+const char * const sox_encodings_short_str[] = {
+  "n/a",
+  "Signed PCM",
+  "Unsigned PCM",
+  "F.P. PCM",
+  "F.P. PCM",
+  "FLAC",
+  "HCOM",
+  "", /* Lossless above, lossy below */
+  "u-law",
+  "A-law",
+  "G.721 ADPCM",
+  "G.723 ADPCM",
+  "CL ADPCM (8)",
+  "CL ADPCM (16)",
+  "MS ADPCM",
+  "IMA ADPCM",
+  "OKI ADPCM",
+  "GSM",
+  "MPEG audio",
+  "Vorbis",
+  "AMR-WB",
+  "AMR-NB",
+  "CVSD",
+  "LPC10",
+};
+
 assert_static(array_length(sox_encodings_str) == SOX_ENCODINGS,
     SIZE_MISMATCH_BETWEEN_sox_encodings_t_AND_sox_encodings_str);
 
--- a/src/mp3-duration.h
+++ b/src/mp3-duration.h
@@ -38,25 +38,44 @@
   return NULL;
 }
 
-static sox_size_t id3tag_duration_ms(FILE * fp)
+static void read_comments(sox_format_t * ft)
 {
+  static char const * list[][2] = {
+    {ID3_FRAME_TITLE,   "Title"},
+    {ID3_FRAME_ARTIST,  "Artist"},
+    {ID3_FRAME_ALBUM,   "Album"},
+    {ID3_FRAME_TRACK,   "Tracknumber"},
+    {ID3_FRAME_YEAR,    "Year"},
+    {ID3_FRAME_GENRE,   "Genre"},
+    {ID3_FRAME_COMMENT, "Comment"},
+    {"TPOS",            "Discnumber"},
+    {NULL, NULL}
+  };
   struct id3_file   * id3struct;
   struct id3_tag    * tag;
   id3_utf8_t        * utf8;
-  sox_size_t        duration_ms = 0;
-  int               fd = dup(fileno(fp));
+  int               i, fd = dup(fileno(ft->fp));
 
   if ((id3struct = id3_file_fdopen(fd, ID3_FILE_MODE_READONLY))) {
     if ((tag = id3_file_tag(id3struct)) && tag->frames)
+      for (i = 0; list[i][0]; ++i) 
+        if ((utf8 = utf8_id3tag_findframe(tag, list[i][0], 0))) {
+          char * comment = xmalloc(strlen(list[i][1]) + 1 + strlen((char *)utf8) + 1);
+          sprintf(comment, "%s=%s", list[i][1], utf8);
+          append_comment(&ft->comments, comment);
+          free(comment);
+          free(utf8);
+        }
       if ((utf8 = utf8_id3tag_findframe(tag, "TLEN", 0))) {
-        if (atoi((char *)utf8) > 0)
-          duration_ms = atoi((char *)utf8);
+        if (atoi((char *)utf8) > 0) {
+          ft->length = atoi((char *)utf8); /* In ms; convert to samples later */
+          sox_debug("got exact duration from ID3 TLEN");
+        }
         free(utf8);
       }
     id3_file_close(id3struct);
   }
   else close(fd);
-  return duration_ms;
 }
 
 #endif
@@ -85,14 +104,6 @@
   sox_size_t          initial_bitrate = 0; /* Initialised to prevent warning */
   sox_size_t          tagsize = 0, consumed = 0, frames = 0;
   sox_bool            vbr = sox_false, depadded = sox_false;
-
-#if HAVE_ID3TAG && HAVE_UNISTD_H
-  sox_size_t duration_ms = id3tag_duration_ms(fp);
-  if (duration_ms) {
-    sox_debug("got exact duration from ID3 TLEN");
-    return duration_ms;
-  }
-#endif
 
   mad_stream_init(&mad_stream);
   mad_header_init(&mad_header);
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -175,8 +175,13 @@
     p->Timer=(mad_timer_t *)xmalloc(sizeof(mad_timer_t));
     p->InputBuffer=(unsigned char *)xmalloc(INPUT_BUFFER_SIZE);
 
-    if (ft->seekable)
-      ft->length = mp3_duration_ms(ft->fp, p->InputBuffer);
+    if (ft->seekable) {
+#if HAVE_ID3TAG && HAVE_UNISTD_H
+      read_comments(ft);
+      if (!ft->length)
+#endif 
+        ft->length = mp3_duration_ms(ft->fp, p->InputBuffer);
+    }
 
     mad_stream_init(p->Stream);
     mad_frame_init(p->Frame);
@@ -189,14 +194,13 @@
      * format.  The decoded frame will be saved off so that it
      * can be processed later.
      */
-    ReadSize=sox_readbuf(ft, p->InputBuffer, INPUT_BUFFER_SIZE);
-    if(ReadSize<=0)
-    {
-        if(sox_error(ft))
-            sox_fail_errno(ft,SOX_EOF,"read error on bitstream");
-        if(sox_eof(ft))
-            sox_fail_errno(ft,SOX_EOF,"end of input stream");
-        return(SOX_EOF);
+    ReadSize = sox_readbuf(ft, p->InputBuffer, INPUT_BUFFER_SIZE);
+    if (ReadSize < INPUT_BUFFER_SIZE) {
+      if (sox_error(ft))
+        sox_fail_errno(ft, SOX_EOF, "error reading input file");
+      else if (sox_eof(ft))
+        sox_fail_errno(ft, SOX_EOF, "input file too short");
+      return SOX_EOF;
     }
 
     mad_stream_buffer(p->Stream, p->InputBuffer, ReadSize);
--- a/src/sf.c
+++ b/src/sf.c
@@ -107,11 +107,10 @@
                 sf->info.sf_chans = sox_swapdw(sf->info.sf_chans);
         }
         if ((sfmagic1(&sfhead) != SF_MAGIC1) ||
-            (sfmagic2(&sfhead) != SF_MAGIC2))
-                sox_fail(
-"SF %s file: can't read, it is byte-swapped or it is not an IRCAM SoundFile",
-                        ft->filename);
-
+            (sfmagic2(&sfhead) != SF_MAGIC2)) {
+          sox_fail_errno(ft, SOX_EHDR, "sf: can't find IRCAM identifier");
+          return SOX_EOF;
+        }
 
         /*
          * If your format specifies or your file header contains
--- a/src/sox.c
+++ b/src/sox.c
@@ -187,10 +187,71 @@
   return string[i];
 }
 
+static void play_file_info(sox_format_t * ft, file_t f, sox_bool full)
+{
+  FILE * const output = sox_mode == sox_soxi? stdout : stderr;
+  char const * text;
+  char buffer[30];
+  sox_size_t ws = ft->length / ft->signal.channels;
+  (void)full;
+
+  fprintf(output, "\n%s\n\n", ft->filename);
+
+  fprintf(output, "  Encoding: %-14s", sox_encodings_short_str[ft->encoding.encoding]);
+  text = find_comment(f->ft->comments, "Comment");
+  if (!text)
+    text = find_comment(f->ft->comments, "Description");
+  if (!text)
+    text = find_comment(f->ft->comments, "Year");
+  if (text)
+    fprintf(output, "Info: %s", text);
+  fprintf(output, "\n");
+
+  sprintf(buffer, "  Channels: %u @ %u-bit", ft->signal.channels, ft->signal.precision);
+  fprintf(output, "%-25s", buffer);
+  text = find_comment(f->ft->comments, "Tracknumber");
+  if (text) {
+    fprintf(output, "Track: %s", text);
+    text = find_comment(f->ft->comments, "Tracktotal");
+    if (text)
+      fprintf(output, " of %s", text);
+  }
+  fprintf(output, "\n");
+
+  sprintf(buffer, "Samplerate: %gHz", ft->signal.rate);
+  fprintf(output, "%-25s", buffer);
+  text = find_comment(f->ft->comments, "Album");
+  if (text)
+    fprintf(output, "Album: %s", text);
+  fprintf(output, "\n");
+
+  if (f && f->replay_gain != HUGE_VAL){
+    sprintf(buffer, "%s gain: %+.1fdB", find_enum_value(f->replay_gain_mode, rg_modes)->text, f->replay_gain);
+    buffer[0] += 'A' - 'a';
+    fprintf(output, "%-24s", buffer);
+  } else
+    fprintf(output, "%-24s", "Replaygain: off");
+  text = find_comment(f->ft->comments, "Artist");
+  if (text)
+    fprintf(output, "Artist: %s", text);
+  fprintf(output, "\n");
+
+  fprintf(output, "  Duration: %-13s", ft->length? str_time((double)ws / ft->signal.rate) : "unknown");
+  text = find_comment(f->ft->comments, "Title");
+  if (text)
+    fprintf(output, "Title: %s", text);
+  fprintf(output, "\n\n");
+}
+
 static void display_file_info(sox_format_t * ft, file_t f, sox_bool full)
 {
   static char const * const no_yes[] = {"no", "yes"};
   FILE * const output = sox_mode == sox_soxi? stdout : stderr;
+
+  if (sox_mode == sox_play) {
+    play_file_info(ft, f, full);
+    return;
+  }
 
   fprintf(output, "\n%s: '%s'",
     ft->mode == 'r'? "Input File     " : "Output File    ", ft->filename);
--- a/src/sox.h
+++ b/src/sox.h
@@ -360,6 +360,7 @@
 
 /* declared in misc.c */
 extern const char * const sox_encodings_str[];
+extern const char * const sox_encodings_short_str[];
 
 int sox_format_init(void);
 sox_format_t * sox_open_read(
--- a/src/sunaudio.c
+++ b/src/sunaudio.c
@@ -320,7 +320,7 @@
     names, SOX_FILE_DEVICE,
     sox_sunstartread, sox_rawread, sox_rawstopread,
     sox_sunstartwrite, sox_rawwrite, sox_rawstopwrite,
-    NULL, write_encodings
+    NULL, write_encodings, NULL
   };
   return &handler;
 }
--- a/src/wve.c
+++ b/src/wve.c
@@ -33,7 +33,7 @@
       sox_readdw(ft, &num_samples) || sox_skipbytes(ft, sizeof(ID2)))
     return SOX_EOF;
   if (memcmp(ID1, buf, sizeof(buf))) {
-    sox_fail_errno(ft,SOX_EHDR,"wve: can't find Psion identifier");
+    sox_fail_errno(ft, SOX_EHDR, "wve: can't find Psion identifier");
     return SOX_EOF;
   }
   return sox_check_read_params(ft, 1, 8000., SOX_ENCODING_ALAW, 8, (off_t)num_samples);