? wav.diff Index: wav.c =================================================================== RCS file: /cvs/xmms/Input/wav/wav.c,v retrieving revision 1.14 diff -u -r1.14 wav.c --- wav.c 6 Nov 2003 23:16:49 -0000 1.14 +++ wav.c 22 Dec 2004 21:32:31 -0000 @@ -1,5 +1,5 @@ /* XMMS - Cross-platform multimedia player - * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * Copyright (C) 1998-2005 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies * * 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 @@ -15,38 +15,58 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "wav.h" + +/* + 2004-12-21 Dirk Jagdmann + - added 32bit pcm,float support + + 2004-12-22 Dirk Jagdmann + - added 12bit pcm support + - added 24bit pcm support + - added 64bit float support + - fixed big endian + + TODO: + - dither to 16 bits +*/ + #include "libxmms/util.h" #include "libxmms/titlestring.h" #include "xmms/i18n.h" +#include "xmms/plugin.h" +#include "config.h" -InputPlugin wav_ip = +#include +#include +#include +#include + +extern InputPlugin wav_ip; + +#if 0 +#define WAVDEBUGf +#define WAVDEBUG(f, a...) printf(f, ##a) +#else +#define WAVDEBUG(f, a...) +#endif + +#define WAVE_FORMAT_PCM 0x0001 +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE + +typedef struct { - NULL, - NULL, - NULL, /* Description */ - wav_init, - NULL, - NULL, - is_our_file, - NULL, - play_file, - stop, - wav_pause, - seek, - NULL, - get_time, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - get_song_info, - NULL, /* file_info_box */ - NULL -}; + FILE *file; + unsigned short format_tag; + short channels, block_align, bits_per_sample; + long samples_per_sec, avg_bytes_per_sec; + unsigned long position, length; + int seek_to, data_offset; + pid_t pid; + volatile gboolean going, eof; + int afmt; +} +WaveFile; static WaveFile *wav_file = NULL; static pthread_t decode_thread; @@ -58,130 +78,6 @@ return &wav_ip; } -static void wav_init(void) -{ -} - -/* needed for is_our_file() */ -static int read_n_bytes(FILE * file, guint8 * buf, int n) -{ - - if (fread(buf, 1, n, file) != n) - { - return FALSE; - } - return TRUE; -} - -static guint32 convert_to_header(guint8 * buf) -{ - - return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; -} - -static guint32 convert_to_long(guint8 * buf) -{ - - return (buf[3] << 24) + (buf[2] << 16) + (buf[1] << 8) + buf[0]; -} - -static guint16 read_wav_id(char *filename) -{ - FILE *file; - guint16 wavid; - guint8 buf[4]; - guint32 head; - long seek; - - if (!(file = fopen(filename, "rb"))) - { /* Could not open file */ - return 0; - } - if (!(read_n_bytes(file, buf, 4))) - { - fclose(file); - return 0; - } - head = convert_to_header(buf); - if (head == ('R' << 24) + ('I' << 16) + ('F' << 8) + 'F') - { /* Found a riff -- maybe WAVE */ - if (fseek(file, 4, SEEK_CUR) != 0) - { /* some error occured */ - fclose(file); - return 0; - } - if (!(read_n_bytes(file, buf, 4))) - { - fclose(file); - return 0; - } - head = convert_to_header(buf); - if (head == ('W' << 24) + ('A' << 16) + ('V' << 8) + 'E') - { /* Found a WAVE */ - seek = 0; - do - { /* we'll be looking for the fmt-chunk which comes before the data-chunk */ - /* A chunk consists of an header identifier (4 bytes), the length of the chunk - (4 bytes), and the chunkdata itself, padded to be an even number of bytes. - We'll skip all chunks until we find the "data"-one which could contain - mpeg-data */ - if (seek != 0) - { - if (fseek(file, seek, SEEK_CUR) != 0) - { /* some error occured */ - fclose(file); - return 0; - } - } - if (!(read_n_bytes(file, buf, 4))) - { - fclose(file); - return 0; - } - head = convert_to_header(buf); - if (!(read_n_bytes(file, buf, 4))) - { - fclose(file); - return 0; - } - seek = convert_to_long(buf); - seek = seek + (seek % 2); /* Has to be even (padding) */ - if (seek >= 2 && head == ('f' << 24) + ('m' << 16) + ('t' << 8) + ' ') - { - if (!(read_n_bytes(file, buf, 2))) - { - fclose(file); - return 0; - } - wavid = buf[0] + 256 * buf[1]; - seek -= 2; - /* we could go on looking for other things, but all we wanted was the wavid */ - fclose(file); - return wavid; - } - } - while (head != ('d' << 24) + ('a' << 16) + ('t' << 8) + 'a'); - /* it's RIFF WAVE */ - } - /* it's RIFF */ - } - /* it's not even RIFF */ - fclose(file); - return 0; -} - -static int is_our_file(char *filename) -{ - gchar *ext; - - ext = strrchr(filename, '.'); - if (ext) - if (!strcasecmp(ext, ".wav")) - if (read_wav_id(filename) == WAVE_FORMAT_PCM) - return TRUE; - return FALSE; -} - static gchar *get_title(gchar *filename) { TitleInput *input; @@ -213,26 +109,92 @@ return title; } -static int read_le_long(FILE * file, long *ret) +static void convert(char *data, int* bytes) { - unsigned char buf[4]; - - if (fread(buf, 1, 4, file) != 4) - return 0; - - *ret = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; - return TRUE; -} - -static int read_le_short(FILE * file, short *ret) -{ - unsigned char buf[2]; + if(wav_file->format_tag == WAVE_FORMAT_PCM) + { + switch(wav_file->bits_per_sample) + { + case 8: + case 12: + case 16: + default: + return; - if (fread(buf, 1, 2, file) != 2) - return 0; + case 24: + { + char *dst=data; + char *src=data; + int src_=*bytes; + int dst_=0; + while(src_>0) + { + dst_+=2; + src_-=3; + ++src; /* skip low byte */ + *dst++=*src++; /* copy 16bit */ + *dst++=*src++; + } + *bytes=dst_; + } + break; - *ret = (buf[1] << 8) | buf[0]; - return TRUE; + case 32: + { + signed short *dst=(signed short*)data; + signed short *src=(signed short*)data; + int i= *bytes / sizeof(signed short); + *bytes=i; + while(i--) + { + ++src; /* skip low word */ + *dst++=*src++; /* copy 16 bit */ + } + } + break; + } + } + else if(wav_file->format_tag == WAVE_FORMAT_IEEE_FLOAT) + { + if(wav_file->bits_per_sample == 32) + { + signed short *dst=(signed short*)data; + unsigned long *src=(unsigned long *)data; + int src_ = *bytes / sizeof(unsigned long); + while(src_--) + { + union { + unsigned long l; + float f; + } u; + u.l=GUINT32_FROM_LE(*src); + if(u.f>1.0f) u.f=1.0f; + else if(u.f<-1.0f) u.f=-1.0f; + *dst++=u.f * 32767.0f; + ++src; + } + *bytes/=2; + } + else if(wav_file->bits_per_sample == 64) + { + signed short *dst=(signed short*)data; + guint64 *src=(guint64 *)data; + int src_ = *bytes / sizeof(guint64); + while(src_--) + { + union { + guint64 i; + double f; + } u; + u.i=GUINT64_FROM_LE(*src); + if(u.f>1.0f) u.f=1.0f; + else if(u.f<-1.0f) u.f=-1.0f; + *dst++=u.f * 32767.0f; + ++src; + } + *bytes/=4; + } + } } static void *play_loop(void *arg) @@ -258,14 +220,15 @@ if (actual_read == 0) { - wav_file->eof = 1; + wav_file->eof = TRUE; wav_ip.output->buffer_free(); wav_ip.output->buffer_free(); } else { - wav_ip.add_vis_pcm(wav_ip.output->written_time(), (wav_file->bits_per_sample == 16) ? FMT_S16_LE : FMT_U8, - wav_file->channels, bytes, data); + convert(data, &bytes); + + wav_ip.add_vis_pcm(wav_ip.output->written_time(), wav_file->afmt, wav_file->channels, bytes, data); while(wav_ip.output->buffer_free() < bytes && wav_file->going && wav_file->seek_to == -1) xmms_usleep(10000); if(wav_file->going && wav_file->seek_to == -1) @@ -297,132 +260,168 @@ pthread_exit(NULL); } -static void play_file(char *filename) +static int read_le_long(FILE * file, long *ret) +{ + long l; + if (fread(&l, sizeof(l), 1, file) != 1) + return 0; + *ret = GINT32_FROM_LE(l); + return TRUE; +} + +static int read_le_short(FILE * file, short *ret) +{ + short s; + if (fread(&s, sizeof(s), 1, file) != 1) + return 0; + *ret = GINT16_FROM_LE(s); + return TRUE; +} + +static WaveFile* construct_WaveFile(char *filename) { - char magic[4], *name; unsigned long len; - int rate; + char magic[4]; - audio_error = FALSE; + /* alloc mem for struct */ + WaveFile *wav=g_malloc0(sizeof(WaveFile)); + if(!wav) + return wav; + + /* open file */ + wav->file=fopen(filename, "rb"); + if(!wav->file) + goto exit_on_error; + + /* check for RIFF chunk */ + fread(magic, 1, 4, wav->file); + if (strncmp(magic, "RIFF", 4)) + goto exit_on_error; + read_le_long(wav->file, &len); + + fread(magic, 1, 4, wav->file); + if (strncmp(magic, "WAVE", 4)) + goto exit_on_error; - wav_file = g_malloc0(sizeof (WaveFile)); - if ((wav_file->file = fopen(filename, "rb"))) + /* wait for fmt chunk */ + for (;;) { - fread(magic, 1, 4, wav_file->file); - if (strncmp(magic, "RIFF", 4)) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - read_le_long(wav_file->file, &len); - fread(magic, 1, 4, wav_file->file); - if (strncmp(magic, "WAVE", 4)) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - for (;;) - { - fread(magic, 1, 4, wav_file->file); - if (!read_le_long(wav_file->file, &len)) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - if (!strncmp("fmt ", magic, 4)) - break; - fseek(wav_file->file, len, SEEK_CUR); - } - if (len < 16) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - read_le_short(wav_file->file, &wav_file->format_tag); - switch (wav_file->format_tag) - { - case WAVE_FORMAT_UNKNOWN: - case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - case WAVE_FORMAT_ADPCM: - case WAVE_FORMAT_OKI_ADPCM: - case WAVE_FORMAT_DIGISTD: - case WAVE_FORMAT_DIGIFIX: - case IBM_FORMAT_MULAW: - case IBM_FORMAT_ALAW: - case IBM_FORMAT_ADPCM: - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - read_le_short(wav_file->file, &wav_file->channels); - read_le_long(wav_file->file, &wav_file->samples_per_sec); - read_le_long(wav_file->file, &wav_file->avg_bytes_per_sec); - read_le_short(wav_file->file, &wav_file->block_align); - read_le_short(wav_file->file, &wav_file->bits_per_sample); - if (wav_file->bits_per_sample != 8 && wav_file->bits_per_sample != 16) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - len -= 16; - if (len) - fseek(wav_file->file, len, SEEK_CUR); + fread(magic, 1, 4, wav->file); + if (!read_le_long(wav->file, &len)) + goto exit_on_error; - for (;;) - { - fread(magic, 4, 1, wav_file->file); + if (!strncmp("fmt ", magic, 4)) + break; + fseek(wav->file, len, SEEK_CUR); + } - if (!read_le_long(wav_file->file, &len)) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - if (!strncmp("data", magic, 4)) - break; - fseek(wav_file->file, len, SEEK_CUR); - } - wav_file->data_offset = ftell(wav_file->file); - wav_file->length = len; + /* check length of fmt chunk */ + if (len < 16) + goto exit_on_error; - wav_file->position = 0; - wav_file->going = 1; + /* read format */ + read_le_short(wav->file, &wav->format_tag); + read_le_short(wav->file, &wav->channels); + read_le_long (wav->file, &wav->samples_per_sec); + read_le_long (wav->file, &wav->avg_bytes_per_sec); + read_le_short(wav->file, &wav->block_align); + read_le_short(wav->file, &wav->bits_per_sample); + + /* check for valid formats */ + if(wav->format_tag == WAVE_FORMAT_EXTENSIBLE) + wav->format_tag = WAVE_FORMAT_PCM; + if(wav->format_tag == WAVE_FORMAT_PCM) + { + switch(wav->bits_per_sample) + { + case 8: wav->afmt=FMT_U8; break; + case 12: + case 16: + case 24: + case 32: wav->afmt=FMT_S16_LE; break; + default: goto exit_on_error; + } + } + else if(wav->format_tag == WAVE_FORMAT_IEEE_FLOAT) + { + if(wav->bits_per_sample == 32) + wav->afmt=FMT_S16_LE; + else if(wav->bits_per_sample == 64) + wav->afmt=FMT_S16_LE; + else + goto exit_on_error; + } + else + goto exit_on_error; - if (wav_ip.output->open_audio((wav_file->bits_per_sample == 16) ? FMT_S16_LE : FMT_U8, wav_file->samples_per_sec, wav_file->channels) == 0) - { - audio_error = TRUE; - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - name = get_title(filename); - rate = wav_file->samples_per_sec * wav_file->channels * (wav_file->bits_per_sample / 8); - wav_ip.set_info(name, 1000 * (wav_file->length / rate), 8 * rate, wav_file->samples_per_sec, wav_file->channels); - g_free(name); - wav_file->seek_to = -1; - pthread_create(&decode_thread, NULL, play_loop, NULL); + /* skip any superflous bytes */ + len -= 16; + if (len) + { + fseek(wav->file, len, SEEK_CUR); + } + + /* wait for data chunk */ + for (;;) + { + fread(magic, 4, 1, wav->file); + if (!read_le_long(wav->file, &len)) + goto exit_on_error; + if (!strncmp("data", magic, 4)) + break; + fseek(wav->file, len, SEEK_CUR); + } + wav->data_offset = ftell(wav->file); + wav->length = len; + + wav->position = 0; + wav->going = TRUE; + + return wav; + + exit_on_error: + if(wav->file) + fclose(wav->file); + g_free(wav); + return 0; +} + +static void play_file(char *filename) +{ + char *name; + int rate; + + audio_error = FALSE; + + if((wav_file = construct_WaveFile(filename)) == 0) + return; + + /* open audio channel according to wav format */ + if (wav_ip.output->open_audio(wav_file->afmt, wav_file->samples_per_sec, wav_file->channels) == 0) + { + audio_error = TRUE; + fclose(wav_file->file); + g_free(wav_file); + wav_file = NULL; + return; } + + /* set some xmms config */ + name = get_title(filename); + rate = wav_file->samples_per_sec * wav_file->channels * (wav_file->bits_per_sample / 8); + wav_ip.set_info(name, 1000 * (wav_file->length / rate), 8 * rate, wav_file->samples_per_sec, wav_file->channels); + g_free(name); + wav_file->seek_to = -1; + + /* create play thread */ + pthread_create(&decode_thread, NULL, play_loop, NULL); } static void stop(void) { if (wav_file && wav_file->going) { - wav_file->going = 0; + wav_file->going = FALSE; pthread_join(decode_thread, NULL); wav_ip.output->close_audio(); g_free(wav_file); @@ -461,108 +460,64 @@ static void get_song_info(char *filename, char **title, int *length) { - char magic[4]; - unsigned long len; - int rate; - WaveFile *wav_file; + WaveFile *wav; - wav_file = g_malloc(sizeof (WaveFile)); - memset(wav_file, 0, sizeof (WaveFile)); - if (!(wav_file->file = fopen(filename, "rb"))) + if(! (wav=construct_WaveFile(filename))) return; - fread(magic, 1, 4, wav_file->file); - if (strncmp(magic, "RIFF", 4)) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - read_le_long(wav_file->file, &len); - fread(magic, 1, 4, wav_file->file); - if (strncmp(magic, "WAVE", 4)) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - for (;;) - { - fread(magic, 1, 4, wav_file->file); - if (!read_le_long(wav_file->file, &len)) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - if (!strncmp("fmt ", magic, 4)) - break; - fseek(wav_file->file, len, SEEK_CUR); - } - if (len < 16) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - read_le_short(wav_file->file, &wav_file->format_tag); - switch (wav_file->format_tag) - { - case WAVE_FORMAT_UNKNOWN: - case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - case WAVE_FORMAT_ADPCM: - case WAVE_FORMAT_OKI_ADPCM: - case WAVE_FORMAT_DIGISTD: - case WAVE_FORMAT_DIGIFIX: - case IBM_FORMAT_MULAW: - case IBM_FORMAT_ALAW: - case IBM_FORMAT_ADPCM: - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - read_le_short(wav_file->file, &wav_file->channels); - read_le_long(wav_file->file, &wav_file->samples_per_sec); - read_le_long(wav_file->file, &wav_file->avg_bytes_per_sec); - read_le_short(wav_file->file, &wav_file->block_align); - read_le_short(wav_file->file, &wav_file->bits_per_sample); - if (wav_file->bits_per_sample != 8 && wav_file->bits_per_sample != 16) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - len -= 16; - if (len) - fseek(wav_file->file, len, SEEK_CUR); + *length = 1000 * (wav->length / (wav->samples_per_sec * wav->channels * (wav->bits_per_sample / 8)) ); + *title = get_title(filename); - for (;;) - { - fread(magic, 4, 1, wav_file->file); + fclose(wav->file); + g_free(wav); + wav = NULL; +} - if (!read_le_long(wav_file->file, &len)) - { - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; - return; - } - if (!strncmp("data", magic, 4)) - break; - fseek(wav_file->file, len, SEEK_CUR); - } - rate = wav_file->samples_per_sec * wav_file->channels * (wav_file->bits_per_sample / 8); - (*length) = 1000 * (len / rate); - (*title) = get_title(filename); +static int is_our_file(char *filename) +{ + int ret=FALSE; + WaveFile *wav; + if(! (wav=construct_WaveFile(filename))) + return FALSE; - fclose(wav_file->file); - g_free(wav_file); - wav_file = NULL; + if(wav->format_tag == WAVE_FORMAT_PCM) + ret=TRUE; + if(wav->format_tag == WAVE_FORMAT_EXTENSIBLE) + ret=TRUE; + if(wav->format_tag == WAVE_FORMAT_IEEE_FLOAT) + ret=TRUE; + + g_free(wav); + return ret; +} + +#ifdef WAVDEBUGf +static void plugin_init(void) +{ + printf("wav.c compiled " __DATE__ " " __TIME__ " : "); +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + printf("little endian"); +#elif G_BYTE_ORDER == G_BIG_ENDIAN + printf("big endian"); +#elif G_BYTE_ORDER == G_PDP_ENDIAN + printf("pdp endian"); +#else + printf("unknown endian"); +#endif + printf("\n"); } +#endif + +InputPlugin wav_ip = +{ + .is_our_file = is_our_file, + .play_file = play_file, + .stop = stop, + .pause = wav_pause, + .seek = seek, + .get_time = get_time, + .get_song_info = get_song_info, +#ifdef WAVDEBUGf + .init = plugin_init +#endif +}; Index: wav.h =================================================================== RCS file: /cvs/xmms/Input/wav/wav.h,v retrieving revision 1.4 diff -u -r1.4 wav.h --- wav.h 27 Feb 2002 21:56:48 -0000 1.4 +++ wav.h 22 Dec 2004 21:32:31 -0000 @@ -1,5 +1,5 @@ /* XMMS - Cross-platform multimedia player - * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * Copyright (C) 1998-2005 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies * * 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 @@ -18,57 +18,6 @@ #ifndef WAV_H #define WAV_H -#include "config.h" - -#include - -#include -#include -#include - -#include - -#include "xmms/plugin.h" - -#define WAVE_FORMAT_UNKNOWN (0x0000) -#define WAVE_FORMAT_PCM (0x0001) -#define WAVE_FORMAT_ADPCM (0x0002) -#define WAVE_FORMAT_ALAW (0x0006) -#define WAVE_FORMAT_MULAW (0x0007) -#define WAVE_FORMAT_OKI_ADPCM (0x0010) -#define WAVE_FORMAT_DIGISTD (0x0015) -#define WAVE_FORMAT_DIGIFIX (0x0016) -#define IBM_FORMAT_MULAW (0x0101) -#define IBM_FORMAT_ALAW (0x0102) -#define IBM_FORMAT_ADPCM (0x0103) - -extern InputPlugin wav_ip; - -typedef struct -{ - FILE *file; - short format_tag, channels, block_align, bits_per_sample, eof, going; - long samples_per_sec, avg_bytes_per_sec; - unsigned long position, length; - int seek_to, data_offset; - pid_t pid; -} -WaveFile; - -static void wav_init(void); -static int is_our_file(char *filename); -static void play_file(char *filename); -static void stop(void); -static void seek(int time); -static void wav_pause(short p); -static int get_time(void); -static void get_song_info(char *filename, char **title, int *length); +#error this file should be removed from CVS #endif - - - - - - -