diff --git a/Makefile b/Makefile index a70d298..a9643df 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ HEADERS = \ analysis.h \ playlist.h \ ipc.h \ - constants.h + constants.h OBJECTS = \ gjay.o \ ipc.o \ @@ -30,7 +30,8 @@ OBJECTS = \ ui_colorwheel.o \ gjay_xmms.o \ analysis.o \ - playlist.o + playlist.o + INSTALL = /usr/bin/install -o root -g root -m diff --git a/README b/README index 5315544..201ce4b 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -GJay v0.2 -(c) Chuck Groom, January 2003 +GJay v0.3 +(c) Chuck Groom, 2003 gjay.sourceforge.net GJay (Gtk+ DJ) generates playlists across a collection of music (mp3, @@ -27,7 +27,6 @@ BUILD ----- GJay uses the following programs: - mp3info mpg321 ogg123 xmms @@ -44,7 +43,7 @@ To build GJay, you'll need the header files for libvorbis, libgsl, xmms, and Gtk2 The relevant Debain packages are: -mp3info mpg321 vorbis-tools xmms +mpg321 vorbis-tools xmms libgtk2.0 libgsl0 libvorbis libvorbis-dev libgtk1.2-dev xmms-dev libgsl0-dev diff --git a/TODO b/TODO index e484cf5..6e61eeb 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,9 @@ This is a list of the features I know GJay needs, and am focusing on. Input is always appreciated! +- Add freq. and BPM information to the comment portion of ogg files so + users sharing oggs won't need to analyze them again + - Expand range of GJay command-line options to support: - Add files to analysis queue diff --git a/analysis.c b/analysis.c index b2022c1..af47aa6 100644 --- a/analysis.c +++ b/analysis.c @@ -423,7 +423,7 @@ void analyze(char * fname) { while ((result = fread(buffer, 1, BUFFER_SIZE, f))) ; fclose(f); - + send_ipc(daemon_pipe_fd, ANIMATE_STOP); send_ipc_text(daemon_pipe_fd, STATUS_TEXT, "Idle"); diff --git a/changelog b/changelog index be92e42..95523c5 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,20 @@ -0.2.5 - January 07, 2002 +0.3.0 - June, 2003 +- Architecture: + - Removed dependancy on mp3info to read ID3 tags. This gives a significant + speedup. + +0.2.6 - January 19, 2003 +- Features: + - Display highlight icon next to directories containing new songs (those + which haven't been had rank or color set) +- Bugs: + - Empty directories do not cause crashes when clicked upon (doh!) + - Avoid localization alltogether w/r/t reading data ("." vs "," decimal) + by writing local strtof function which is decimal locale agnostic + - Found recursive directory scan bug + - Fixed very serious bug in which new repeat songs would not be shown + +0.2.5 - January 07, 2003 - Features: - Tweaked UI: removed "select directory contents non-recursively" option for the sake of simplicity. All selection is recursive. diff --git a/constants.h b/constants.h index 0941eac..cc7a3b7 100644 --- a/constants.h +++ b/constants.h @@ -2,7 +2,7 @@ #define __CONSTANTS_H__ /* Default directory for storing app info */ -#define GJAY_VERSION "0.2.6" +#define GJAY_VERSION "0.3.0" #define GJAY_DIR ".gjay" #define GJAY_PREFS "prefs.xml" #define GJAY_FILE_DATA "data.xml" @@ -41,4 +41,16 @@ /* For prefs */ #define DEFAULT_PLAYLIST_TIME 72 +#define HELP_TEXT "USAGE: gjay [--help] [-hdvpux] [-l length] [-c color]\n" \ + "\t--help, -h : Display this help message\n" \ + "\t-d : Run as daemon\n" \ + "\t-v : Run in verbose mode. -vv for lots more info\n" \ + "\t-p : Generate a playlist\n" \ + "\nPlaylist options:\n" \ + "\t-u : Display list in m3u format\n" \ + "\t-x : Use XMMS to play generated playlist\n" \ + "\t-l length : Length of playlist, in minutes\n" \ + "\t-f filename : Start playlist at a particular file\n" \ + "\t-c color : Start playlist at color, either a hex value or by name.\n" \ + "\t To see all options, just call -c\n" #endif /* __CONSTANTS_H__ */ diff --git a/gjay.c b/gjay.c index b3dd31e..42ab593 100644 --- a/gjay.c +++ b/gjay.c @@ -1,5 +1,5 @@ /** - * GJay, copyright (c) 2002 Chuck Groom + * GJay, copyright (c) 2002, 2003 Chuck Groom * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -47,11 +47,10 @@ #include "ipc.h" #include "playlist.h" -#define NUM_APPS 3 +#define NUM_APPS 2 static gchar * apps[NUM_APPS] = { "mpg321", - "ogg123", - "mp3info" + "ogg123" }; @@ -68,8 +67,7 @@ static int open_pipe(const char* filepath); -int main( int argc, char *argv[] ) -{ +int main( int argc, char *argv[] ) { GList * list; char buffer[BUFFER_SIZE]; GtkWidget * widget; @@ -89,18 +87,7 @@ int main( int argc, char *argv[] ) for (i = 0; i < argc; i++) { if ((strncmp(argv[i], "-h", 2) == 0) || (strncmp(argv[i], "--help", 6) == 0)) { - printf("USAGE: gjay [--help] [-hdvpux] [-l length] [-c color]\n" \ - "\t--help, -h : Display this help message\n" \ - "\t-d : Run as daemon\n" \ - "\t-v : Run in verbose mode. -vv for lots more info\n" \ - "\t-p : Generate a playlist\n" \ - "\nPlaylist options:\n" \ - "\t-u : Display list in m3u format\n" \ - "\t-x : Use XMMS to play generated playlist\n" \ - "\t-l length : Length of playlist, in minutes\n" \ - "\t-f filename : Start playlist at a particular file\n" \ - "\t-c color : Start playlist at color, either a hex value or by name.\n" \ - "\t To see all options, just call -c\n"); + printf(HELP_TEXT); return 0; } else if (strncmp(argv[i], "-l", 2) == 0) { if (i + 1 < argc) { @@ -175,7 +162,6 @@ int main( int argc, char *argv[] ) } } - if (mode != PLAYLIST) { /* Both daemon and UI app open an end of a pipe */ daemon_pipe_fd = open_pipe(DAEMON_PIPE); diff --git a/gjay.h b/gjay.h index f0d4115..42bc09d 100644 --- a/gjay.h +++ b/gjay.h @@ -49,8 +49,10 @@ extern gint xmms_session; extern gint verbosity; /* utilities */ -void read_line ( FILE * f, char * buffer, int buffer_len); -#define strdup_to_utf8(str) (strdup_convert(str, "UTF8", "LATIN1")) +void read_line ( FILE * f, + char * buffer, + int buffer_len); +#define strdup_to_utf8(str) (strdup_convert(str, "UTF8", "LATIN1")) #define strdup_to_latin1(str) (strdup_convert(str, "LATIN1", "UTF8")) gchar * strdup_convert ( const gchar * str, const gchar * enc_to, diff --git a/songs.c b/songs.c index 0d2991e..8bbe450 100644 --- a/songs.c +++ b/songs.c @@ -1,5 +1,5 @@ /** - * GJay, copyright (c) 2002 Chuck Groom + * GJay, copyright (c) 2002, 2003 Chuck Groom * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,13 +26,11 @@ #include #include #include -#include +#include +#include #include "gjay.h" #include "analysis.h" -#define CRC_TABLE_SIZE 256 -#define CRC_BYTES 50*1024 - typedef enum { E_GJAY_DATA = 0, @@ -95,6 +93,22 @@ GHashTable * song_inode_hash; GHashTable * not_song_hash; +#define NUM_TAGS 16 +#define TITLE_CHAR "t" +#define ARTIST_CHAR "c" +#define ALBUM_CHAR "a" +static char * id3_tag[NUM_TAGS] = { + "TT2", TITLE_CHAR, + "TIT2", TITLE_CHAR, + "TP1", ARTIST_CHAR, + "TPE1", ARTIST_CHAR, + "TCM", ARTIST_CHAR, + "TCOM", ARTIST_CHAR, + "TAL", ALBUM_CHAR, + "TALB", ALBUM_CHAR +}; + + static void write_song_data ( FILE * f, song * s ); static void write_not_song_data ( FILE * f, gchar * path ); static gboolean read_song_file_type ( char * path, @@ -122,7 +136,10 @@ static void data_text ( GMarkupParseContext *context, static int get_element ( gchar * element_name ); static void song_copy_attrs ( song * dest, song * original ); - +static gboolean get_mp3_info ( char * path, + char ** artist, + char ** album, + char ** title ); /* Create a new song with the given filename */ song * create_song ( void ) { @@ -778,8 +795,7 @@ static gboolean read_song_file_type ( char * path, vorbis_comment * vc; struct stat buf; waveheaderstruct header; - char buffer[BUFFER_SIZE]; - char quoted_fname[BUFFER_SIZE]; + char * t_album, * t_artist, * t_title; switch (type) { case OGG: @@ -810,36 +826,16 @@ static gboolean read_song_file_type ( char * path, fclose(f); break; case MP3: - quote_path(quoted_fname, BUFFER_SIZE, path); - snprintf(buffer, BUFFER_SIZE, - "mp3info -p \"frames:%%u\\n%%a\\n%%t\\n%%l\\n%%S\" \'%s\' %s", - quoted_fname, - verbosity ? "" : "2> /dev/null"); - if (!(f = popen(buffer, "r"))) { - fprintf(stderr, "Unable to run %s\n", buffer); - return FALSE; - } - read_line(f, buffer, BUFFER_SIZE); - if (strlen(buffer) <= strlen("frames:")) - return FALSE; - read_line(f, buffer, BUFFER_SIZE); - if (strlen(buffer)) { - *artist = strdup_to_utf8(buffer); - } - read_line(f, buffer, BUFFER_SIZE); - if (strlen(buffer)) { - *title = strdup_to_utf8(buffer); - } - read_line(f, buffer, BUFFER_SIZE); - if (strlen(buffer)) { - *album = strdup_to_utf8(buffer); - } - read_line(f, buffer, BUFFER_SIZE); - if (strlen(buffer)) { - *length = atoi(buffer); - } - pclose(f); - return TRUE; + if(get_mp3_info(path, &t_artist, &t_album, &t_title)) { + if(t_artist) + *artist = strdup_to_utf8(t_artist); + if(t_album) + *album = strdup_to_utf8(t_album); + if(t_title) + *title = strdup_to_utf8(t_title); + return TRUE; + } + break; case WAV: f = fopen(path, "r"); if (!f) @@ -877,3 +873,90 @@ int write_dirty_song_timeout ( gpointer data ) { return TRUE; } + +static gboolean get_mp3_info ( char * path, + char ** artist, + char ** album, + char ** title ) { + FILE * fp; + unsigned char buffer[BUFFER_SIZE]; + gboolean is_mp3 = FALSE; + int k, n, off; + char * str; + + assert(artist && album && title); + *artist = NULL; + *album = NULL; + *title = NULL; + + fp = fopen(path, "r"); + if (!fp) + return FALSE; + fread(buffer, 1, BUFFER_SIZE, fp); + if (strncmp(buffer, "ID3", 3) == 0) { + is_mp3 = TRUE; + for (off = 10; off < 1024; ) { + for (k = 0; k < NUM_TAGS; k+=2) { + if (strcasecmp(buffer + off, id3_tag[k]) == 0) { + n = buffer[off + 5] - 2; + str = malloc(n + 1); + str[n] = '\0'; + memcpy(str, buffer + off + 7, n); + if (id3_tag[k+1][0] == TITLE_CHAR[0]) { + free(*title); + *title = str; + } else if (id3_tag[k+1][0] == ARTIST_CHAR[0]) { + free(*artist); + *artist = str; + } else if (id3_tag[k+1][0] == ALBUM_CHAR[0]) { + free(*album); + *album = str; + } else { + free(str); + } + off += 8 + n; + break; + } + } + if (k < NUM_TAGS) + continue; + if (isalpha(buffer[off]) && + isalpha(buffer[off + 1]) && + isalpha(buffer[off + 2])) { + off += 6 + buffer[off + 5]; + } else { + break; + } + } + } else { + fseek(fp, 128, SEEK_END); + fread(buffer, 1, 128, fp); + if (strncmp(buffer, "TAG", 3) == 0) { + is_mp3 = TRUE; + if (title) { + *title = malloc(30); + memcpy(*title, buffer + 3, 30); + } + if (artist) { + *artist = malloc(30); + memcpy(*artist, buffer + 3 + 30, 30); + } + if (album) { + *album = malloc(30); + memcpy(*album, buffer + 3 + 60, 30); + } + } + } + if (!is_mp3) { + rewind(fp); + fread(buffer, 1, 3, fp); + /* A crass way to tell the difference between mp3s, oggs, and wavs, + at least, is to just look at the first three bytes. */ + if ((buffer[0] == 255) && + (buffer[1] >= 250) && + (buffer[2] >= 140)) + is_mp3 = TRUE; + } + fclose(fp); + return is_mp3; +}