diff options
Diffstat (limited to 'src/pulsecore')
-rw-r--r-- | src/pulsecore/core-scache.c | 30 | ||||
-rw-r--r-- | src/pulsecore/sndfile-util.c | 462 | ||||
-rw-r--r-- | src/pulsecore/sndfile-util.h | 52 | ||||
-rw-r--r-- | src/pulsecore/sound-file-stream.c | 51 | ||||
-rw-r--r-- | src/pulsecore/sound-file.c | 102 | ||||
-rw-r--r-- | src/pulsecore/sound-file.h | 2 |
6 files changed, 586 insertions, 113 deletions
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index 34d60a8f..086f5fcb 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -219,11 +219,14 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3 pa_assert(name); pa_assert(filename); - if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0) - return -1; - p = pa_proplist_new(); pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename); + + if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk, p) < 0) { + pa_proplist_free(p); + return -1; + } + r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx); pa_memblock_unref(chunk.memblock); pa_proplist_free(p); @@ -311,11 +314,14 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) return -1; + merged = pa_proplist_new(); + pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name); + if (e->lazy && !e->memchunk.memblock) { pa_channel_map old_channel_map = e->channel_map; - if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk) < 0) - return -1; + if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, merged) < 0) + goto fail; pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); @@ -328,7 +334,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t } if (!e->memchunk.memblock) - return -1; + goto fail; pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name); @@ -344,17 +350,13 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t else pass_volume = FALSE; - merged = pa_proplist_new(); - pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name); pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist); if (p) pa_proplist_update(merged, PA_UPDATE_REPLACE, p); - if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0) { - pa_proplist_free(merged); - return -1; - } + if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0) + goto fail; pa_proplist_free(merged); @@ -362,6 +364,10 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t time(&e->last_used_time); return 0; + +fail: + pa_proplist_free(merged); + return -1; } int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) { diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c new file mode 100644 index 00000000..032aefca --- /dev/null +++ b/src/pulsecore/sndfile-util.c @@ -0,0 +1,462 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +/* Shared between pacat/parec/paplay and the server */ + +#include <pulse/xmalloc.h> +#include <pulse/utf8.h> + +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> + +#include "sndfile-util.h" + +int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) { + SF_INFO sfi; + + pa_assert(sf); + pa_assert(ss); + + pa_zero(sfi); + pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0); + + switch (sfi.format & SF_FORMAT_SUBMASK) { + + case SF_FORMAT_PCM_16: + case SF_FORMAT_PCM_U8: + case SF_FORMAT_PCM_S8: + ss->format = PA_SAMPLE_S16NE; + break; + + case SF_FORMAT_PCM_24: + case SF_FORMAT_PCM_32: + ss->format = PA_SAMPLE_S32NE; + break; + + case SF_FORMAT_ULAW: + ss->format = PA_SAMPLE_ULAW; + break; + + case SF_FORMAT_ALAW: + ss->format = PA_SAMPLE_ALAW; + break; + + case SF_FORMAT_FLOAT: + case SF_FORMAT_DOUBLE: + default: + ss->format = PA_SAMPLE_FLOAT32NE; + break; + } + + ss->rate = (uint32_t) sfi.samplerate; + ss->channels = (uint8_t) sfi.channels; + + if (!pa_sample_spec_valid(ss)) + return -1; + + return 0; +} + +int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) { + pa_assert(sfi); + pa_assert(ss); + + sfi->samplerate = (int) ss->rate; + sfi->channels = (int) ss->channels; + + if (pa_sample_format_is_le(ss->format) > 0) + sfi->format = SF_ENDIAN_LITTLE; + else if (pa_sample_format_is_be(ss->format) > 0) + sfi->format = SF_ENDIAN_BIG; + + switch (ss->format) { + + case PA_SAMPLE_U8: + ss->format = PA_SAMPLE_S16NE; + sfi->format = SF_FORMAT_PCM_U8; + break; + + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S16BE: + ss->format = PA_SAMPLE_S16NE; + sfi->format |= SF_FORMAT_PCM_16; + break; + + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_S24_32BE: + ss->format = PA_SAMPLE_S32NE; + sfi->format |= SF_FORMAT_PCM_24; + break; + + case PA_SAMPLE_S32LE: + case PA_SAMPLE_S32RE: + ss->format = PA_SAMPLE_S32NE; + sfi->format |= SF_FORMAT_PCM_32; + break; + + case PA_SAMPLE_ULAW: + sfi->format = SF_FORMAT_ULAW; + break; + + case PA_SAMPLE_ALAW: + sfi->format = SF_FORMAT_ALAW; + break; + + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + default: + ss->format = PA_SAMPLE_FLOAT32NE; + sfi->format |= SF_FORMAT_FLOAT; + break; + } + + + if (!pa_sample_spec_valid(ss)) + return -1; + + return 0; +} + +int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm) { + + static const pa_channel_position_t table[] = { + [SF_CHANNEL_MAP_MONO] = PA_CHANNEL_POSITION_MONO, + [SF_CHANNEL_MAP_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT, /* libsndfile distuingishes left und front-left, which we don't */ + [SF_CHANNEL_MAP_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT, + [SF_CHANNEL_MAP_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER, + [SF_CHANNEL_MAP_FRONT_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT, + [SF_CHANNEL_MAP_FRONT_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT, + [SF_CHANNEL_MAP_FRONT_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER, + [SF_CHANNEL_MAP_REAR_CENTER] = PA_CHANNEL_POSITION_REAR_CENTER, + [SF_CHANNEL_MAP_REAR_LEFT] = PA_CHANNEL_POSITION_REAR_LEFT, + [SF_CHANNEL_MAP_REAR_RIGHT] = PA_CHANNEL_POSITION_REAR_RIGHT, + [SF_CHANNEL_MAP_LFE] = PA_CHANNEL_POSITION_LFE, + [SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + [SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + [SF_CHANNEL_MAP_SIDE_LEFT] = PA_CHANNEL_POSITION_SIDE_LEFT, + [SF_CHANNEL_MAP_SIDE_RIGHT] = PA_CHANNEL_POSITION_SIDE_RIGHT, + [SF_CHANNEL_MAP_TOP_CENTER] = PA_CHANNEL_POSITION_TOP_CENTER, + [SF_CHANNEL_MAP_TOP_FRONT_LEFT] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT, + [SF_CHANNEL_MAP_TOP_FRONT_RIGHT] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, + [SF_CHANNEL_MAP_TOP_FRONT_CENTER] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER, + [SF_CHANNEL_MAP_TOP_REAR_LEFT] = PA_CHANNEL_POSITION_TOP_REAR_LEFT, + [SF_CHANNEL_MAP_TOP_REAR_RIGHT] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT, + [SF_CHANNEL_MAP_TOP_REAR_CENTER] = PA_CHANNEL_POSITION_TOP_REAR_CENTER + }; + + SF_INFO sfi; + int *channels; + unsigned c; + + pa_assert(sf); + pa_assert(cm); + + pa_zero(sfi); + pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0); + + channels = pa_xnew(int, sfi.channels); + if (!sf_command(sf, SFC_GET_CHANNEL_MAP_INFO, + channels, sizeof(channels[0]) * sfi.channels)) { + + pa_xfree(channels); + return -1; + } + + cm->channels = (uint8_t) sfi.channels; + for (c = 0; c < cm->channels; c++) { + if (channels[c] <= SF_CHANNEL_MAP_INVALID || + (unsigned) channels[c] >= PA_ELEMENTSOF(table)) { + pa_xfree(channels); + return -1; + } + + cm->map[c] = table[channels[c]]; + } + + pa_xfree(channels); + + if (!pa_channel_map_valid(cm)) + return -1; + + return 0; +} + +int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm) { + static const int table[PA_CHANNEL_POSITION_MAX] = { + [PA_CHANNEL_POSITION_MONO] = SF_CHANNEL_MAP_MONO, + + [PA_CHANNEL_POSITION_FRONT_LEFT] = SF_CHANNEL_MAP_FRONT_LEFT, + [PA_CHANNEL_POSITION_FRONT_RIGHT] = SF_CHANNEL_MAP_FRONT_RIGHT, + [PA_CHANNEL_POSITION_FRONT_CENTER] = SF_CHANNEL_MAP_FRONT_CENTER, + + [PA_CHANNEL_POSITION_REAR_CENTER] = SF_CHANNEL_MAP_REAR_CENTER, + [PA_CHANNEL_POSITION_REAR_LEFT] = SF_CHANNEL_MAP_REAR_LEFT, + [PA_CHANNEL_POSITION_REAR_RIGHT] = SF_CHANNEL_MAP_REAR_RIGHT, + + [PA_CHANNEL_POSITION_LFE] = SF_CHANNEL_MAP_LFE, + + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, + + [PA_CHANNEL_POSITION_SIDE_LEFT] = SF_CHANNEL_MAP_SIDE_LEFT, + [PA_CHANNEL_POSITION_SIDE_RIGHT] = SF_CHANNEL_MAP_SIDE_RIGHT, + + [PA_CHANNEL_POSITION_AUX0] = -1, + [PA_CHANNEL_POSITION_AUX1] = -1, + [PA_CHANNEL_POSITION_AUX2] = -1, + [PA_CHANNEL_POSITION_AUX3] = -1, + [PA_CHANNEL_POSITION_AUX4] = -1, + [PA_CHANNEL_POSITION_AUX5] = -1, + [PA_CHANNEL_POSITION_AUX6] = -1, + [PA_CHANNEL_POSITION_AUX7] = -1, + [PA_CHANNEL_POSITION_AUX8] = -1, + [PA_CHANNEL_POSITION_AUX9] = -1, + [PA_CHANNEL_POSITION_AUX10] = -1, + [PA_CHANNEL_POSITION_AUX11] = -1, + [PA_CHANNEL_POSITION_AUX12] = -1, + [PA_CHANNEL_POSITION_AUX13] = -1, + [PA_CHANNEL_POSITION_AUX14] = -1, + [PA_CHANNEL_POSITION_AUX15] = -1, + [PA_CHANNEL_POSITION_AUX16] = -1, + [PA_CHANNEL_POSITION_AUX17] = -1, + [PA_CHANNEL_POSITION_AUX18] = -1, + [PA_CHANNEL_POSITION_AUX19] = -1, + [PA_CHANNEL_POSITION_AUX20] = -1, + [PA_CHANNEL_POSITION_AUX21] = -1, + [PA_CHANNEL_POSITION_AUX22] = -1, + [PA_CHANNEL_POSITION_AUX23] = -1, + [PA_CHANNEL_POSITION_AUX24] = -1, + [PA_CHANNEL_POSITION_AUX25] = -1, + [PA_CHANNEL_POSITION_AUX26] = -1, + [PA_CHANNEL_POSITION_AUX27] = -1, + [PA_CHANNEL_POSITION_AUX28] = -1, + [PA_CHANNEL_POSITION_AUX29] = -1, + [PA_CHANNEL_POSITION_AUX30] = -1, + [PA_CHANNEL_POSITION_AUX31] = -1, + + [PA_CHANNEL_POSITION_TOP_CENTER] = SF_CHANNEL_MAP_TOP_CENTER, + + [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SF_CHANNEL_MAP_TOP_FRONT_LEFT, + [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SF_CHANNEL_MAP_TOP_FRONT_RIGHT, + [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SF_CHANNEL_MAP_TOP_FRONT_CENTER , + + [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SF_CHANNEL_MAP_TOP_REAR_LEFT, + [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SF_CHANNEL_MAP_TOP_REAR_RIGHT, + [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SF_CHANNEL_MAP_TOP_REAR_CENTER, + }; + + int *channels; + unsigned c; + + pa_assert(sf); + pa_assert(cm); + + /* Suppress channel mapping for the obvious cases */ + if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_MONO) + return 0; + + if (cm->channels == 2 && + cm->map[0] == PA_CHANNEL_POSITION_FRONT_LEFT && + cm->map[1] == PA_CHANNEL_POSITION_FRONT_RIGHT) + return 0; + + channels = pa_xnew(int, cm->channels); + for (c = 0; c < cm->channels; c++) { + + if (cm->map[c] < 0 || + cm->map[c] >= PA_CHANNEL_POSITION_MAX || + table[cm->map[c]] < 0) { + pa_xfree(channels); + return -1; + } + + channels[c] = table[cm->map[c]]; + } + + if (!sf_command(sf, SFC_SET_CHANNEL_MAP_INFO, + channels, sizeof(channels[0]) * cm->channels)) { + pa_xfree(channels); + return -1; + } + + pa_xfree(channels); + return 0; +} + +void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p) { + + static const char* table[] = { + [SF_STR_TITLE] = PA_PROP_MEDIA_TITLE, + [SF_STR_COPYRIGHT] = PA_PROP_MEDIA_COPYRIGHT, + [SF_STR_SOFTWARE] = PA_PROP_MEDIA_SOFTWARE, + [SF_STR_ARTIST] = PA_PROP_MEDIA_ARTIST, + [SF_STR_COMMENT] = "media.comment", + [SF_STR_DATE] = "media.date" + }; + + SF_INFO sfi; + SF_FORMAT_INFO fi; + unsigned c; + + pa_assert(sf); + pa_assert(p); + + for (c = 0; c < PA_ELEMENTSOF(table); c++) { + const char *s; + char *t; + + if (!table[c]) + continue; + + if (!(s = sf_get_string(sf, c))) + continue; + + t = pa_utf8_filter(s); + pa_proplist_sets(p, table[c], t); + pa_xfree(t); + } + + pa_zero(sfi); + pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0); + + pa_zero(fi); + fi.format = sfi.format; + if (sf_command(sf, SFC_GET_FORMAT_INFO, &fi, sizeof(fi)) == 0 && fi.name) { + char *t; + + t = pa_utf8_filter(fi.name); + pa_proplist_sets(p, "media.format", t); + pa_xfree(t); + } +} + +pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss) { + pa_assert(ss); + + switch (ss->format) { + case PA_SAMPLE_S16NE: + return (pa_sndfile_readf_t) sf_readf_short; + + case PA_SAMPLE_S32NE: + return (pa_sndfile_readf_t) sf_readf_int; + + case PA_SAMPLE_FLOAT32NE: + return (pa_sndfile_readf_t) sf_readf_float; + + case PA_SAMPLE_ULAW: + case PA_SAMPLE_ALAW: + return NULL; + + default: + pa_assert_not_reached(); + } +} + +pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss) { + pa_assert(ss); + + switch (ss->format) { + case PA_SAMPLE_S16NE: + return (pa_sndfile_writef_t) sf_writef_short; + + case PA_SAMPLE_S32NE: + return (pa_sndfile_writef_t) sf_writef_int; + + case PA_SAMPLE_FLOAT32NE: + return (pa_sndfile_writef_t) sf_writef_float; + + case PA_SAMPLE_ULAW: + case PA_SAMPLE_ALAW: + return NULL; + + default: + pa_assert_not_reached(); + } +} + +int pa_sndfile_format_from_string(const char *name) { + int i, count = 0; + + + if (!name[0]) + return -1; + + pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0); + + /* First try to match via full type string */ + for (i = 0; i < count; i++) { + SF_FORMAT_INFO fi; + pa_zero(fi); + fi.format = i; + + pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0); + + if (strcasecmp(name, fi.name) == 0) + return i; + } + + /* Then, try to match via the full extension */ + for (i = 0; i < count; i++) { + SF_FORMAT_INFO fi; + pa_zero(fi); + fi.format = i; + + pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0); + + if (strcasecmp(name, fi.extension) == 0) + return i; + } + + /* Then, try to match via the start of the type string */ + for (i = 0; i < count; i++) { + SF_FORMAT_INFO fi; + pa_zero(fi); + fi.format = i; + + pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0); + + if (strncasecmp(name, fi.extension, strlen(name)) == 0) + return i; + } + + return -1; +} + +void pa_sndfile_dump_formats(void) { + int i, count = 0; + + pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0); + + for (i = 0; i < count; i++) { + SF_FORMAT_INFO fi; + pa_zero(fi); + fi.format = i; + + pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0); + printf("%s\t%s\n", fi.extension, fi.name); + } +} diff --git a/src/pulsecore/sndfile-util.h b/src/pulsecore/sndfile-util.h new file mode 100644 index 00000000..74021da1 --- /dev/null +++ b/src/pulsecore/sndfile-util.h @@ -0,0 +1,52 @@ +#ifndef foopulsecoresndfileutilhfoo +#define foopulsecoresndfileutilhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <sndfile.h> + +#include <pulse/sample.h> +#include <pulse/channelmap.h> +#include <pulse/proplist.h> + +int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss); +int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm); + +int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss); +int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm); + +void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p); + +typedef sf_count_t (*pa_sndfile_readf_t)(SNDFILE *sndfile, void *ptr, sf_count_t frames); +typedef sf_count_t (*pa_sndfile_writef_t)(SNDFILE *sndfile, const void *ptr, sf_count_t frames); + +/* Returns NULL if sf_read_raw() shall be used */ +pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss); + +/* Returns NULL if sf_write_raw() shall be used */ +pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss); + +int pa_sndfile_format_from_string(const char *extension); + +void pa_sndfile_dump_formats(void); + +#endif diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 3453637f..502e5c69 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -41,6 +41,7 @@ #include <pulsecore/thread-mq.h> #include <pulsecore/core-util.h> #include <pulsecore/sample-util.h> +#include <pulsecore/sndfile-util.h> #include "sound-file-stream.h" @@ -234,10 +235,11 @@ int pa_play_file( const pa_cvolume *volume) { file_stream *u = NULL; - SF_INFO sfinfo; pa_sample_spec ss; + pa_channel_map cm; pa_sink_input_new_data data; int fd; + SF_INFO sfi; pa_assert(sink); pa_assert(fname); @@ -251,8 +253,6 @@ int pa_play_file( u->readf_function = NULL; u->memblockq = NULL; - memset(&sfinfo, 0, sizeof(sfinfo)); - if ((fd = open(fname, O_RDONLY #ifdef O_NOCTTY |O_NOCTTY @@ -281,50 +281,36 @@ int pa_play_file( pa_log_debug("POSIX_FADV_WILLNEED succeeded."); #endif - if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) { + pa_zero(sfi); + if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfi, 1))) { pa_log("Failed to open file %s", fname); - pa_close(fd); goto fail; } - switch (sfinfo.format & 0xFF) { - case SF_FORMAT_PCM_16: - case SF_FORMAT_PCM_U8: - case SF_FORMAT_PCM_S8: - ss.format = PA_SAMPLE_S16NE; - u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short; - break; - - case SF_FORMAT_ULAW: - ss.format = PA_SAMPLE_ULAW; - break; - - case SF_FORMAT_ALAW: - ss.format = PA_SAMPLE_ALAW; - break; + fd = -1; - case SF_FORMAT_FLOAT: - default: - ss.format = PA_SAMPLE_FLOAT32NE; - u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float; - break; + if (pa_sndfile_read_sample_spec(u->sndfile, &ss) < 0) { + pa_log("Failed to determine file sample format."); + goto fail; } - ss.rate = (uint32_t) sfinfo.samplerate; - ss.channels = (uint8_t) sfinfo.channels; - - if (!pa_sample_spec_valid(&ss)) { - pa_log("Unsupported sample format in file %s", fname); - goto fail; + if (pa_sndfile_read_channel_map(u->sndfile, &cm) < 0) { + if (ss.channels > 2) + pa_log_info("Failed to determine file channel map, synthesizing one."); + pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); } + u->readf_function = pa_sndfile_readf_function(&ss); + pa_sink_input_new_data_init(&data); data.sink = sink; data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, &ss); + pa_sink_input_new_data_set_channel_map(&data, &cm); pa_sink_input_new_data_set_volume(&data, volume); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname)); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname); + pa_sndfile_init_proplist(u->sndfile, data.proplist); pa_sink_input_new(&u->sink_input, sink->core, &data, 0); pa_sink_input_new_data_done(&data); @@ -352,5 +338,8 @@ fail: if (u) file_stream_unref(u); + if (fd >= 0) + pa_close(fd); + return -1; } diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c index db75ae08..2d9b76ad 100644 --- a/src/pulsecore/sound-file.c +++ b/src/pulsecore/sound-file.c @@ -35,19 +35,21 @@ #include <pulsecore/macro.h> #include <pulsecore/core-error.h> #include <pulsecore/core-util.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/sndfile-util.h> #include "sound-file.h" -#include "core-scache.h" int pa_sound_file_load( pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, - pa_memchunk *chunk) { + pa_memchunk *chunk, + pa_proplist *p) { SNDFILE *sf = NULL; - SF_INFO sfinfo; + SF_INFO sfi; int ret = -1; size_t l; sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL; @@ -59,7 +61,6 @@ int pa_sound_file_load( pa_assert(chunk); pa_memchunk_reset(chunk); - memset(&sfinfo, 0, sizeof(sfinfo)); if ((fd = open(fname, O_RDONLY #ifdef O_NOCTTY @@ -78,48 +79,29 @@ int pa_sound_file_load( pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded."); #endif - if (!(sf = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) { + pa_zero(sfi); + if (!(sf = sf_open_fd(fd, SFM_READ, &sfi, 1))) { pa_log("Failed to open file %s", fname); - pa_close(fd); goto finish; } - switch (sfinfo.format & SF_FORMAT_SUBMASK) { - case SF_FORMAT_PCM_16: - case SF_FORMAT_PCM_U8: - case SF_FORMAT_PCM_S8: - ss->format = PA_SAMPLE_S16NE; - readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_short; - break; - - case SF_FORMAT_ULAW: - ss->format = PA_SAMPLE_ULAW; - break; - - case SF_FORMAT_ALAW: - ss->format = PA_SAMPLE_ALAW; - break; - - case SF_FORMAT_FLOAT: - case SF_FORMAT_DOUBLE: - default: - ss->format = PA_SAMPLE_FLOAT32NE; - readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_float; - break; - } - - ss->rate = (uint32_t) sfinfo.samplerate; - ss->channels = (uint8_t) sfinfo.channels; + fd = -1; - if (!pa_sample_spec_valid(ss)) { - pa_log("Unsupported sample format in file %s", fname); + if (pa_sndfile_read_sample_spec(sf, ss) < 0) { + pa_log("Failed to determine file sample format."); goto finish; } - if (map) + if ((map && pa_sndfile_read_channel_map(sf, map) < 0)) { + if (ss->channels > 2) + pa_log("Failed to determine file channel map, synthesizing one."); pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT); + } - if ((l = pa_frame_size(ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { + if (p) + pa_sndfile_init_proplist(sf, p); + + if ((l = pa_frame_size(ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { pa_log("File too large"); goto finish; } @@ -128,9 +110,11 @@ int pa_sound_file_load( chunk->index = 0; chunk->length = l; + readf_function = pa_sndfile_readf_function(ss); + ptr = pa_memblock_acquire(chunk->memblock); - if ((readf_function && readf_function(sf, ptr, sfinfo.frames) != sfinfo.frames) || + if ((readf_function && readf_function(sf, ptr, sfi.frames) != sfi.frames) || (!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) { pa_log("Premature file end"); goto finish; @@ -149,55 +133,35 @@ finish: if (ret != 0 && chunk->memblock) pa_memblock_unref(chunk->memblock); + if (fd >= 0) + pa_close(fd); + return ret; } int pa_sound_file_too_big_to_cache(const char *fname) { SNDFILE*sf = NULL; - SF_INFO sfinfo; + SF_INFO sfi; pa_sample_spec ss; pa_assert(fname); - if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) { + pa_zero(sfi); + if (!(sf = sf_open(fname, SFM_READ, &sfi))) { pa_log("Failed to open file %s", fname); return -1; } - sf_close(sf); - - switch (sfinfo.format & SF_FORMAT_SUBMASK) { - case SF_FORMAT_PCM_16: - case SF_FORMAT_PCM_U8: - case SF_FORMAT_PCM_S8: - ss.format = PA_SAMPLE_S16NE; - break; - - case SF_FORMAT_ULAW: - ss.format = PA_SAMPLE_ULAW; - break; - - case SF_FORMAT_ALAW: - ss.format = PA_SAMPLE_ALAW; - break; - - case SF_FORMAT_DOUBLE: - case SF_FORMAT_FLOAT: - default: - ss.format = PA_SAMPLE_FLOAT32NE; - break; - } - - ss.rate = (uint32_t) sfinfo.samplerate; - ss.channels = (uint8_t) sfinfo.channels; - - if (!pa_sample_spec_valid(&ss)) { - pa_log("Unsupported sample format in file %s", fname); + if (pa_sndfile_read_sample_spec(sf, &ss) < 0) { + pa_log("Failed to determine file sample format."); + sf_close(sf); return -1; } - if ((pa_frame_size(&ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { + sf_close(sf); + + if ((pa_frame_size(&ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { pa_log("File too large: %s", fname); return 1; } diff --git a/src/pulsecore/sound-file.h b/src/pulsecore/sound-file.h index 34e02616..4dc0c7e1 100644 --- a/src/pulsecore/sound-file.h +++ b/src/pulsecore/sound-file.h @@ -26,7 +26,7 @@ #include <pulse/channelmap.h> #include <pulsecore/memchunk.h> -int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk); +int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk, pa_proplist *p); int pa_sound_file_too_big_to_cache(const char *fname); |