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);