diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/pulse.c | 51 | ||||
| -rw-r--r-- | src/read-sound-file.c | 9 | ||||
| -rw-r--r-- | src/read-sound-file.h | 24 | ||||
| -rw-r--r-- | src/read-vorbis.c | 37 | ||||
| -rw-r--r-- | src/read-vorbis.h | 3 | ||||
| -rw-r--r-- | src/read-wav.c | 121 | ||||
| -rw-r--r-- | src/read-wav.h | 1 | 
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);  | 
