32bit pcm and float support in .wav Input plugin
Dirk Jagdmann <[email protected]> Tue, 21 Dec 2004 20:59:00 +0100
| Newsgroups | gmane.comp.multimedia.xmms.devel |
|---|---|
| Organization | Cubic |
| Message-ID | <[email protected]> |
Hello developers, I have added 32bit pcm and float support for the wav Input plugin. While doing so, I have refactored three different .wav detection routines into one and could afterwards remove several no longer needed helper functions. Thus this patch looks big and ugly, but it should provide a much nicer infrastructure for future work on the .wav code. In the future I'll get to this again and implement the following which this plugin is still missing: - 12 and 24 bit pcm - 64 bit float - a dither algorithm to 16 bit But I'd like to see this new code in the CVS tree and the next release of xmms. -- ---> doj / cubic ----> http://cubic.org/~doj -----> http://llg.cubic.org _______________________________________________ xmms-devel mailing list [email protected] http://lists.xmms.org/mailman/listinfo/xmms-devel
wav.patch
(text/plain, 17.7 KB)
? wav.patch 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 21 Dec 2004 19:46:52 -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,46 @@ * 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 <[email protected]> + - added 32bit support + + TODO: + - dither to 16 bits + - implement 12 and 24 bit pcm +*/ + #include "libxmms/util.h" #include "libxmms/titlestring.h" #include "xmms/i18n.h" +#include "xmms/plugin.h" +#include "config.h" -InputPlugin wav_ip = +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +extern InputPlugin wav_ip; + +#define WAVDEBUG(f, a...) printf(f, ##a) + +#define WAVE_FORMAT_PCM 0x0001 +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 + +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; + short format_tag, 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 +66,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; @@ -235,6 +119,52 @@ return TRUE; } +static void convert(char *data, int* bytes) +{ + if(wav_file->format_tag == WAVE_FORMAT_PCM) + { + switch(wav_file->bits_per_sample) + { + case 8: + case 16: + default: + return; + + case 12: /*todo*/ + case 24: /*todo*/ + + case 32: + { + signed short *dst=(signed short*)data; + signed long *src=(signed long*)data; + int dst_=0, src_ = *bytes / sizeof(signed long); + while(src_--) + *dst++=(signed short)(*src++ / 65536) , ++dst_; + *bytes=dst_ * sizeof(signed short); + } + break; + } + } + else if(wav_file->format_tag == WAVE_FORMAT_IEEE_FLOAT) + { + if(wav_file->bits_per_sample == 32) + { + signed short *dst=(signed short*)data; + float *src=(float*)data; + int dst_=0, src_ = *bytes / sizeof(float); + while(src_--) + { + float f = *src++; + if(f>1.0f) f=1.0f; + else if(f<-1.0f) f=-1.0f; + *dst++=f * 32767.0f; + ++dst_; + } + *bytes=dst_ * sizeof(signed short); + } + } +} + static void *play_loop(void *arg) { char *data; @@ -258,14 +188,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 +228,148 @@ pthread_exit(NULL); } -static void play_file(char *filename) +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); - wav_file = g_malloc0(sizeof (WaveFile)); - if ((wav_file->file = fopen(filename, "rb"))) + fread(magic, 1, 4, wav->file); + if (strncmp(magic, "WAVE", 4)) + goto exit_on_error; + + /* 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); + /* check length of fmt chunk */ + if (len < 16) + goto exit_on_error; + + /* 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_PCM) + { + switch(wav->bits_per_sample) + { + case 8: wav->afmt=FMT_U8; break; +#if 0 + case 12: + case 24: +#endif + case 16: + case 32: wav->afmt=FMT_S16_LE; break; + default: goto exit_on_error; } - wav_file->data_offset = ftell(wav_file->file); - wav_file->length = len; + } + else if(wav->format_tag == WAVE_FORMAT_IEEE_FLOAT) + { + if(wav->bits_per_sample == 32) + wav->afmt=FMT_S16_LE; + else + goto exit_on_error; + } + else + goto exit_on_error; - wav_file->position = 0; - wav_file->going = 1; + /* skip any superflous bytes */ + len -= 16; + if (len) + { + fseek(wav->file, len, SEEK_CUR); + } - 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); + /* 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 +408,42 @@ 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_IEEE_FLOAT) + ret=TRUE; + + g_free(wav); + return ret; } + +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, +}; 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 21 Dec 2004 19:46:52 -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 <pthread.h> - -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - -#include <glib.h> - -#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 - - - - - - -