/* * ximp3 A simple mp3 player * * Copyright (C) 2001 Mats Peterson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Please send any comments/bug reports to * mats_peterson@swipnet.se (Mats Peterson) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xingmp3.h" #include "ximp3.h" #include "init.h" #include "audio.h" #include "audiothread.h" #include "info.h" #include "id3.h" VARS *v; static int bs_fill(int fd) { unsigned int nread; if (v->bs_bufbytes < 0) v->bs_bufbytes = 0; // signed var could be negative if (v->bs_bufbytes < v->bs_trigger) { memmove(v->bs_buffer, v->bs_bufptr, v->bs_bufbytes); nread = read(fd, v->bs_buffer + v->bs_bufbytes, BS_BUFBYTES - v->bs_bufbytes); if ((nread + 1) == 0) { /*-- test for -1 = error --*/ v->bs_trigger = 0; errmsg("File read error"); return 0; } v->bs_bufbytes += nread; v->bs_bufptr = v->bs_buffer; } return 1; } static void add_playbuf(char *pcm, int size, int in_bytes, int frames) { PLAYBUF new; memcpy(new.pcm, pcm, size); new.size = size; new.in_bytes = in_bytes; new.frames = frames; if (write(v->pfds[1], &new, sizeof(PLAYBUF)) == -1) { perror("write"); exit(1); } } static void play_file(char *filename) { char *pcm_buffer; unsigned int pcm_bufbytes; int fd; int framebytes; int frames; MPEG_HEAD head; MPEG m; unsigned int skip; IN_OUT x; int in_bytes; DEC_INFO decinfo; int bitrate; int r; if (! v->remote) printf("\nMPEG input file: %s\n", filename); mpeg_init(&m, 1); in_bytes = 0; pcm_buffer = NULL; pcm_bufbytes = 0; fd = -1; v->bs_buffer = NULL; v->bs_bufbytes = 0; v->bs_bufptr = v->bs_buffer; v->bs_trigger = 2500; /*--- test for valid cvt_to_wave compile ---*/ if (cvt_to_wave_test() != 0) { errmsg("Invalid cvt_to_wave compile"); goto abort; } /*----- open audio device ------*/ if (! (v->audio_fd = open_audio(v->device))) exit(1); /*------ open mpeg file --------*/ if ((fd = open(filename, O_RDONLY)) < 0) { errmsg("Couldn't open input file"); goto abort; } /*----- get id3 info -----*/ out_id3_info(fd, filename, v->remote); /*--- allocate bs buffer ----*/ v->bs_buffer = malloc(BS_BUFBYTES); if (v->bs_buffer == NULL) { errmsg("Couldn't allocate buffer"); goto abort; } /*--- fill bs buffer ----*/ if (! bs_fill(fd)) goto abort; /*---- parse mpeg header -------*/ framebytes = head_info3(v->bs_buffer, v->bs_bufbytes, &head, &bitrate, &skip); if (framebytes == 0) { errmsg("Bad or unsupported MPEG file"); goto abort; } v->bs_bufptr += skip; v->bs_bufbytes -= skip; /*--- display mpeg info --*/ out_mpeg_info(&head, bitrate); /*---- allocate temporary pcm buffer -----*/ pcm_buffer = malloc(PCM_BUFBYTES); if (pcm_buffer == NULL) { errmsg("Couldn't allocate PCM buffer"); goto abort; } /*------ init decoder -------*/ if (! audio_decode_init(&m, &head, framebytes, 0, 0, 0, 24000)) { errmsg("Decoder init failed"); goto abort; } /*------ display decode info ------*/ out_decode_info(&m, &decinfo); /*------ configure audio device ------*/ r = config_audio((int)decinfo.bits, decinfo.channels, decinfo.samprate); if (r < 0) exit(1); if (! r) goto abort; v->bytes_sec = decinfo.samprate * (decinfo.bits / 8) * decinfo.channels; /*--- init wave converter ---*/ cvt_to_wave_init(decinfo.bits); if (! v->remote) printf("\n"); /*---- Create audio output thread ----*/ if (pthread_create(&v->thr, NULL, audio_thread, NULL) < 0) { fprintf(stderr, "Couldn't create audio thread\n"); exit(1); } /*----- DECODE -----*/ for (frames = 0;;) { if (! bs_fill(fd)) break; if (v->bs_bufbytes < framebytes) break; /* end of file */ x = audio_decode(&m, v->bs_bufptr, (short *) (pcm_buffer + pcm_bufbytes)); if (x.in_bytes <= 0) { errmsg("Bad sync in MPEG file"); break; } v->bs_bufptr += x.in_bytes; v->bs_bufbytes -= x.in_bytes; pcm_bufbytes += x.out_bytes; frames++; if (pcm_bufbytes == PCM_BUFBYTES) { pcm_bufbytes = cvt_to_wave(pcm_buffer, pcm_bufbytes); add_playbuf(pcm_buffer, pcm_bufbytes, in_bytes, frames); pcm_bufbytes = 0; } in_bytes += x.in_bytes; } /*---------------------*/ if (pcm_bufbytes > 0) { pcm_bufbytes = cvt_to_wave(pcm_buffer, pcm_bufbytes); add_playbuf(pcm_buffer, pcm_bufbytes, in_bytes, frames); pcm_bufbytes = 0; } /*--- send terminating buffer and wait for thread to terminate ---*/ add_playbuf(NULL, 0, in_bytes, frames); pthread_join(v->thr, NULL); abort: mpeg_cleanup(&m); if (v->audio_fd) close(v->audio_fd); if (fd > 0) close(fd); if (v->bs_buffer) free(v->bs_buffer); if (pcm_buffer) free(pcm_buffer); } static void shuffle_files(void) { int i, randnum; srand(time(NULL)); v->shuffle_ord = (int *)malloc((v->flist_size + 1) * sizeof(int)); if (! v->shuffle_ord) { perror("malloc"); exit(1); } /* write songs in 'correct' order */ for (i = 0; i < v->flist_size; i++) { v->shuffle_ord[i] = i; } /* now shuffle them */ if (v->flist_size >= 2) { for (i = 0; i < v->flist_size; i++) { randnum = (rand() % (v->flist_size * 4 - 4)) / 4; randnum += (randnum >= i); v->shuffle_ord[i] ^= v->shuffle_ord[randnum]; v->shuffle_ord[randnum] ^= v->shuffle_ord[i]; v->shuffle_ord[i] ^= v->shuffle_ord[randnum]; } } } static void add_file(char *fname) { static int alloc_size = 0; if (v->flist_size + 2 > alloc_size) { alloc_size += 8; v->flist = (char **)realloc(v->flist, alloc_size * sizeof(char *)); if (! v->flist) { perror("realloc"); exit(1); } } if (! (v->flist[v->flist_size] = (char *)malloc(strlen(fname) + 1))) { perror("malloc"); exit(1); } strcpy(v->flist[v->flist_size], fname); v->flist_size++; } static void play(int argc, char **argv) { FILE *f; char fname[1026]; char *p; int i; int newlist = 1; if (v->flist) { newlist = 0; goto playshuf; } for (i = optind; i < argc; i++) { p = strrchr(argv[i], '.'); if (p && ((! strcmp(p, ".m3u") || (! strcmp(p, ".M3U"))))) { if (! (f = fopen(argv[i], "r"))) { errmsg("Couldn't open playlist"); continue; } while (fgets(fname, 1024, f)) { fname[strlen(fname) - 1] = '\0'; if (v->shuffle) add_file(fname); else play_file(fname); } fclose(f); } else { if (v->shuffle) add_file(argv[i]); else play_file(argv[i]); } } if (! v->shuffle) return; playshuf: if (newlist) shuffle_files(); for (i = 0; i < v->flist_size; i++) play_file(v->flist[v->shuffle_ord[i]]); } int main(int argc, char **argv) { init(argc, argv); do { play(argc, argv); } while (v->loop); return 0; } .