summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2009-05-26 03:32:26 +0200
committerLennart Poettering <lennart@poettering.net>2009-05-26 03:32:26 +0200
commit65837e669bf8b02819f02db9d7d21a47babe1356 (patch)
tree9f1e30ae457fd8f20fe93f9f94d5d331733dd3df
parenta094ed99c3231baf9a9ae1d003c67b86fd7faa59 (diff)
add proper multichannel support (includes of WAVEX files)
-rw-r--r--src/pulse.c51
-rw-r--r--src/read-sound-file.c9
-rw-r--r--src/read-sound-file.h24
-rw-r--r--src/read-vorbis.c37
-rw-r--r--src/read-vorbis.h3
-rw-r--r--src/read-wav.c121
-rw-r--r--src/read-wav.h1
7 files changed, 236 insertions, 10 deletions
diff --git a/src/pulse.c b/src/pulse.c
index c959719..47dc96f 100644
--- a/src/pulse.c
+++ b/src/pulse.c
@@ -688,6 +688,45 @@ static const pa_sample_format_t sample_type_table[] = {
[CA_SAMPLE_U8] = PA_SAMPLE_U8
};
+static const pa_channel_position_t channel_table[_CA_CHANNEL_POSITION_MAX] = {
+ [CA_CHANNEL_MONO] = PA_CHANNEL_POSITION_MONO,
+ [CA_CHANNEL_FRONT_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT,
+ [CA_CHANNEL_FRONT_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT,
+ [CA_CHANNEL_FRONT_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER,
+ [CA_CHANNEL_REAR_LEFT] = PA_CHANNEL_POSITION_REAR_LEFT,
+ [CA_CHANNEL_REAR_RIGHT] = PA_CHANNEL_POSITION_REAR_RIGHT,
+ [CA_CHANNEL_REAR_CENTER] = PA_CHANNEL_POSITION_REAR_CENTER,
+ [CA_CHANNEL_LFE] = PA_CHANNEL_POSITION_LFE,
+ [CA_CHANNEL_FRONT_LEFT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+ [CA_CHANNEL_FRONT_RIGHT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+ [CA_CHANNEL_SIDE_LEFT] = PA_CHANNEL_POSITION_SIDE_LEFT,
+ [CA_CHANNEL_SIDE_RIGHT] = PA_CHANNEL_POSITION_SIDE_RIGHT,
+ [CA_CHANNEL_TOP_CENTER] = PA_CHANNEL_POSITION_TOP_CENTER,
+ [CA_CHANNEL_TOP_FRONT_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT,
+ [CA_CHANNEL_TOP_FRONT_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT,
+ [CA_CHANNEL_TOP_FRONT_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER,
+ [CA_CHANNEL_TOP_REAR_LEFT] = PA_CHANNEL_POSITION_REAR_LEFT,
+ [CA_CHANNEL_TOP_REAR_RIGHT] = PA_CHANNEL_POSITION_REAR_RIGHT,
+ [CA_CHANNEL_TOP_REAR_CENTER] = PA_CHANNEL_POSITION_TOP_REAR_CENTER
+};
+
+static ca_bool_t convert_channel_map(ca_sound_file *f, pa_channel_map *cm) {
+ const ca_channel_position_t *positions;
+ unsigned c;
+
+ ca_assert(f);
+ ca_assert(cm);
+
+ if (!(positions = ca_sound_file_get_channel_map(f)))
+ return FALSE;
+
+ cm->channels = ca_sound_file_get_nchannels(f);
+ for (c = 0; c < cm->channels; c++)
+ cm->map[c] = channel_table[positions[c]];
+
+ return TRUE;
+}
+
int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) {
struct private *p;
pa_proplist *l = NULL;
@@ -701,6 +740,8 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
ca_bool_t volume_set = FALSE;
pa_cvolume cvol;
pa_sample_spec ss;
+ pa_channel_map cm;
+ ca_bool_t cm_good;
ca_cache_control_t cache_control = CA_CACHE_CONTROL_NEVER;
struct outstanding *out = NULL;
int try = 3;
@@ -841,6 +882,8 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
ss.channels = (uint8_t) ca_sound_file_get_nchannels(out->file);
ss.rate = ca_sound_file_get_rate(out->file);
+ cm_good = convert_channel_map(out->file, &cm);
+
if (!name) {
if (!(n = pa_proplist_gets(l, CA_PROP_MEDIA_NAME)))
if (!(n = pa_proplist_gets(l, CA_PROP_MEDIA_NAME)))
@@ -851,7 +894,7 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
pa_threaded_mainloop_lock(p->mainloop);
- if (!(out->stream = pa_stream_new_with_proplist(p->context, name, &ss, NULL, l))) {
+ if (!(out->stream = pa_stream_new_with_proplist(p->context, name, &ss, cm_good ? &cm : NULL, l))) {
ret = translate_error(pa_context_errno(p->context));
pa_threaded_mainloop_unlock(p->mainloop);
goto finish;
@@ -997,6 +1040,8 @@ int driver_cache(ca_context *c, ca_proplist *proplist) {
const char *n, *ct;
char *name = NULL;
pa_sample_spec ss;
+ pa_channel_map cm;
+ ca_bool_t cm_good;
ca_cache_control_t cache_control = CA_CACHE_CONTROL_PERMANENT;
struct outstanding *out;
int ret;
@@ -1062,6 +1107,8 @@ int driver_cache(ca_context *c, ca_proplist *proplist) {
ss.channels = (uint8_t) ca_sound_file_get_nchannels(out->file);
ss.rate = ca_sound_file_get_rate(out->file);
+ cm_good = convert_channel_map(out->file, &cm);
+
pa_threaded_mainloop_lock(p->mainloop);
if (!p->context) {
@@ -1070,7 +1117,7 @@ int driver_cache(ca_context *c, ca_proplist *proplist) {
goto finish;
}
- if (!(out->stream = pa_stream_new_with_proplist(p->context, name, &ss, NULL, l))) {
+ if (!(out->stream = pa_stream_new_with_proplist(p->context, name, &ss, cm_good ? &cm : NULL, l))) {
ret = translate_error(pa_context_errno(p->context));
pa_threaded_mainloop_unlock(p->mainloop);
diff --git a/src/read-sound-file.c b/src/read-sound-file.c
index bbadb02..ef20010 100644
--- a/src/read-sound-file.c
+++ b/src/read-sound-file.c
@@ -121,6 +121,15 @@ ca_sample_type_t ca_sound_file_get_sample_type(ca_sound_file *f) {
return f->type;
}
+const ca_channel_position_t* ca_sound_file_get_channel_map(ca_sound_file *f) {
+ ca_assert(f);
+
+ if (f->wav)
+ return ca_wav_get_channel_map(f->wav);
+ else
+ return ca_vorbis_get_channel_map(f->vorbis);
+}
+
int ca_sound_file_read_int16(ca_sound_file *f, int16_t *d, size_t *n) {
ca_return_val_if_fail(f, CA_ERROR_INVALID);
ca_return_val_if_fail(d, CA_ERROR_INVALID);
diff --git a/src/read-sound-file.h b/src/read-sound-file.h
index 2107c56..d221324 100644
--- a/src/read-sound-file.h
+++ b/src/read-sound-file.h
@@ -30,6 +30,29 @@ typedef enum ca_sample_type {
CA_SAMPLE_U8
} ca_sample_type_t;
+typedef enum ca_channel_position {
+ CA_CHANNEL_MONO,
+ CA_CHANNEL_FRONT_LEFT,
+ CA_CHANNEL_FRONT_RIGHT,
+ CA_CHANNEL_FRONT_CENTER,
+ CA_CHANNEL_REAR_LEFT,
+ CA_CHANNEL_REAR_RIGHT,
+ CA_CHANNEL_REAR_CENTER,
+ CA_CHANNEL_LFE,
+ CA_CHANNEL_FRONT_LEFT_OF_CENTER,
+ CA_CHANNEL_FRONT_RIGHT_OF_CENTER,
+ CA_CHANNEL_SIDE_LEFT,
+ CA_CHANNEL_SIDE_RIGHT,
+ CA_CHANNEL_TOP_CENTER,
+ CA_CHANNEL_TOP_FRONT_LEFT,
+ CA_CHANNEL_TOP_FRONT_RIGHT,
+ CA_CHANNEL_TOP_FRONT_CENTER,
+ CA_CHANNEL_TOP_REAR_LEFT,
+ CA_CHANNEL_TOP_REAR_RIGHT,
+ CA_CHANNEL_TOP_REAR_CENTER,
+ _CA_CHANNEL_POSITION_MAX
+} ca_channel_position_t;
+
typedef struct ca_sound_file ca_sound_file;
int ca_sound_file_open(ca_sound_file **f, const char *fn);
@@ -38,6 +61,7 @@ void ca_sound_file_close(ca_sound_file *f);
unsigned ca_sound_file_get_nchannels(ca_sound_file *f);
unsigned ca_sound_file_get_rate(ca_sound_file *f);
ca_sample_type_t ca_sound_file_get_sample_type(ca_sound_file *f);
+const ca_channel_position_t* ca_sound_file_get_channel_map(ca_sound_file *f);
off_t ca_sound_file_get_size(ca_sound_file *f);
diff --git a/src/read-vorbis.c b/src/read-vorbis.c
index e450cb7..4d1fcb0 100644
--- a/src/read-vorbis.c
+++ b/src/read-vorbis.c
@@ -35,6 +35,7 @@
struct ca_vorbis {
OggVorbis_File ovf;
off_t size;
+ ca_channel_position_t channel_map[6];
};
static int convert_error(int or) {
@@ -130,6 +131,42 @@ unsigned ca_vorbis_get_rate(ca_vorbis *v) {
return (unsigned) vi->rate;
}
+const ca_channel_position_t* ca_vorbis_get_channel_map(ca_vorbis *v) {
+
+ /* See http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9 */
+
+ switch (ca_vorbis_get_nchannels(v)) {
+
+ case 6:
+ v->channel_map[5] = CA_CHANNEL_LFE;
+ /* fall through */
+
+ case 5:
+ v->channel_map[3] = CA_CHANNEL_REAR_LEFT;
+ v->channel_map[4] = CA_CHANNEL_REAR_RIGHT;
+ /* fall through */
+
+ case 3:
+ v->channel_map[0] = CA_CHANNEL_FRONT_LEFT;
+ v->channel_map[1] = CA_CHANNEL_FRONT_CENTER;
+ v->channel_map[2] = CA_CHANNEL_FRONT_RIGHT;
+ return v->channel_map;
+
+ case 4:
+ v->channel_map[2] = CA_CHANNEL_REAR_LEFT;
+ v->channel_map[3] = CA_CHANNEL_REAR_RIGHT;
+ /* fall through */
+
+ case 1:
+ v->channel_map[0] = CA_CHANNEL_FRONT_LEFT;
+ v->channel_map[1] = CA_CHANNEL_FRONT_RIGHT;
+ return v->channel_map;
+
+ }
+
+ return NULL;
+}
+
int ca_vorbis_read_s16ne(ca_vorbis *v, int16_t *d, size_t *n){
long r;
int section;
diff --git a/src/read-vorbis.h b/src/read-vorbis.h
index c98c7e5..6edbcaf 100644
--- a/src/read-vorbis.h
+++ b/src/read-vorbis.h
@@ -24,6 +24,8 @@
#include <stdio.h>
#include <inttypes.h>
+#include "read-sound-file.h"
+
typedef struct ca_vorbis ca_vorbis;
int ca_vorbis_open(ca_vorbis **v, FILE *f);
@@ -31,6 +33,7 @@ void ca_vorbis_close(ca_vorbis *v);
unsigned ca_vorbis_get_nchannels(ca_vorbis *v);
unsigned ca_vorbis_get_rate(ca_vorbis *v);
+const ca_channel_position_t* ca_vorbis_get_channel_map(ca_vorbis *v);
int ca_vorbis_read_s16ne(ca_vorbis *v, int16_t *d, size_t *n);
diff --git a/src/read-wav.c b/src/read-wav.c
index 0313a3b..95c46c8 100644
--- a/src/read-wav.c
+++ b/src/read-wav.c
@@ -29,6 +29,50 @@
#define FILE_SIZE_MAX (64U*1024U*1024U)
+/* Stores the bit indexes in dwChannelMask */
+enum {
+ BIT_FRONT_LEFT,
+ BIT_FRONT_RIGHT,
+ BIT_FRONT_CENTER,
+ BIT_LOW_FREQUENCY,
+ BIT_BACK_LEFT,
+ BIT_BACK_RIGHT,
+ BIT_FRONT_LEFT_OF_CENTER,
+ BIT_FRONT_RIGHT_OF_CENTER,
+ BIT_BACK_CENTER,
+ BIT_SIDE_LEFT,
+ BIT_SIDE_RIGHT,
+ BIT_TOP_CENTER,
+ BIT_TOP_FRONT_LEFT,
+ BIT_TOP_FRONT_CENTER,
+ BIT_TOP_FRONT_RIGHT,
+ BIT_TOP_BACK_LEFT,
+ BIT_TOP_BACK_CENTER,
+ BIT_TOP_BACK_RIGHT,
+ _BIT_MAX
+};
+
+static const ca_channel_position_t channel_table[_BIT_MAX] = {
+ [BIT_FRONT_LEFT] = CA_CHANNEL_FRONT_LEFT,
+ [BIT_FRONT_RIGHT] = CA_CHANNEL_FRONT_RIGHT,
+ [BIT_FRONT_CENTER] = CA_CHANNEL_FRONT_CENTER,
+ [BIT_LOW_FREQUENCY] = CA_CHANNEL_LFE,
+ [BIT_BACK_LEFT] = CA_CHANNEL_REAR_LEFT,
+ [BIT_BACK_RIGHT] = CA_CHANNEL_REAR_RIGHT,
+ [BIT_FRONT_LEFT_OF_CENTER] = CA_CHANNEL_FRONT_LEFT_OF_CENTER,
+ [BIT_FRONT_RIGHT_OF_CENTER] = CA_CHANNEL_FRONT_RIGHT_OF_CENTER,
+ [BIT_BACK_CENTER] = CA_CHANNEL_REAR_CENTER,
+ [BIT_SIDE_LEFT] = CA_CHANNEL_SIDE_LEFT,
+ [BIT_SIDE_RIGHT] = CA_CHANNEL_SIDE_RIGHT,
+ [BIT_TOP_CENTER] = CA_CHANNEL_TOP_CENTER,
+ [BIT_TOP_FRONT_LEFT] = CA_CHANNEL_TOP_FRONT_LEFT,
+ [BIT_TOP_FRONT_CENTER] = CA_CHANNEL_TOP_FRONT_CENTER,
+ [BIT_TOP_FRONT_RIGHT] = CA_CHANNEL_TOP_FRONT_RIGHT,
+ [BIT_TOP_BACK_LEFT] = CA_CHANNEL_TOP_REAR_LEFT,
+ [BIT_TOP_BACK_CENTER] = CA_CHANNEL_TOP_REAR_CENTER,
+ [BIT_TOP_BACK_RIGHT] = CA_CHANNEL_TOP_REAR_RIGHT
+};
+
struct ca_wav {
FILE *file;
@@ -36,6 +80,17 @@ struct ca_wav {
unsigned nchannels;
unsigned rate;
unsigned depth;
+ uint32_t channel_mask;
+
+ ca_channel_position_t channel_map[_BIT_MAX];
+};
+
+#define CHUNK_ID_DATA 0x61746164U
+#define CHUNK_ID_FMT 0x20746d66U
+
+static const uint8_t pcm_guid[16] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
};
static int skip_to_chunk(ca_wav *w, uint32_t id, uint32_t *size) {
@@ -77,10 +132,12 @@ fail_io:
}
int ca_wav_open(ca_wav **_w, FILE *f) {
- uint32_t header[3], fmt_chunk[4];
+ uint32_t header[3], fmt_chunk[10];
int ret;
ca_wav *w;
uint32_t file_size, fmt_size, data_size;
+ ca_bool_t extensible;
+ uint32_t format;
ca_return_val_if_fail(_w, CA_ERROR_INVALID);
ca_return_val_if_fail(f, CA_ERROR_INVALID);
@@ -107,22 +164,47 @@ int ca_wav_open(ca_wav **_w, FILE *f) {
}
/* Skip to the fmt chunk */
- if ((ret = skip_to_chunk(w, 0x20746d66U, &fmt_size)) < 0)
+ if ((ret = skip_to_chunk(w, CHUNK_ID_FMT, &fmt_size)) < 0)
goto fail;
- if (fmt_size != 16) {
- ret = CA_ERROR_NOTSUPPORTED;
- goto fail;
+ switch (fmt_size) {
+
+ case 14: /* WAVEFORMAT */
+ case 16:
+ case 18: /* WAVEFORMATEX */
+ extensible = FALSE;
+ break;
+
+ case 40: /* WAVEFORMATEXTENSIBLE */
+ extensible = TRUE;
+ break;
+
+ default:
+ ret = CA_ERROR_NOTSUPPORTED;
+ goto fail;
}
- if (fread(fmt_chunk, sizeof(uint32_t), CA_ELEMENTSOF(fmt_chunk), f) != CA_ELEMENTSOF(fmt_chunk))
+ if (fread(fmt_chunk, 1, fmt_size, f) != fmt_size)
goto fail_io;
- if ((CA_UINT32_FROM_LE(fmt_chunk[0]) & 0xFFFF) != 1) {
+ /* PCM? or WAVEX? */
+ format = (CA_UINT32_FROM_LE(fmt_chunk[0]) & 0xFFFF);
+ if ((!extensible && format != 0x0001) ||
+ (extensible && format != 0xFFFE)) {
ret = CA_ERROR_NOTSUPPORTED;
goto fail;
}
+ if (extensible) {
+ if (memcmp(fmt_chunk + 6, pcm_guid, 16) != 0) {
+ ret = CA_ERROR_NOTSUPPORTED;
+ goto fail;
+ }
+
+ w->channel_mask = CA_UINT32_FROM_LE(fmt_chunk[5]);
+ } else
+ w->channel_mask = 0;
+
w->nchannels = CA_UINT32_FROM_LE(fmt_chunk[0]) >> 16;
w->rate = CA_UINT32_FROM_LE(fmt_chunk[1]);
w->depth = CA_UINT32_FROM_LE(fmt_chunk[3]) >> 16;
@@ -138,7 +220,7 @@ int ca_wav_open(ca_wav **_w, FILE *f) {
}
/* Skip to the data chunk */
- if ((ret = skip_to_chunk(w, 0x61746164U, &data_size)) < 0)
+ if ((ret = skip_to_chunk(w, CHUNK_ID_DATA, &data_size)) < 0)
goto fail;
w->data_size = (off_t) data_size;
@@ -186,6 +268,29 @@ unsigned ca_wav_get_rate(ca_wav *w) {
return w->rate;
}
+const ca_channel_position_t* ca_wav_get_channel_map(ca_wav *w) {
+ unsigned c;
+ ca_channel_position_t *p;
+
+ ca_assert(w);
+
+ if (!w->channel_mask)
+ return NULL;
+
+ p = w->channel_map;
+
+ for (c = 0; c < _BIT_MAX; c++)
+ if ((w->channel_mask & (1 << c)))
+ *(p++) = channel_table[c];
+
+ ca_assert(p <= w->channel_map + _BIT_MAX);
+
+ if (p != w->channel_map + w->nchannels)
+ return NULL;
+
+ return w->channel_map;
+}
+
ca_sample_type_t ca_wav_get_sample_type(ca_wav *w) {
ca_assert(w);
diff --git a/src/read-wav.h b/src/read-wav.h
index 00708c3..e7c6178 100644
--- a/src/read-wav.h
+++ b/src/read-wav.h
@@ -33,6 +33,7 @@ void ca_wav_close(ca_wav *f);
unsigned ca_wav_get_nchannels(ca_wav *f);
unsigned ca_wav_get_rate(ca_wav *f);
ca_sample_type_t ca_wav_get_sample_type(ca_wav *f);
+const ca_channel_position_t* ca_wav_get_channel_map(ca_wav *f);
int ca_wav_read_u8(ca_wav *f, uint8_t *d, size_t *n);
int ca_wav_read_s16le(ca_wav *f, int16_t *d, size_t *n);