153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2008 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
953a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
1053a5a1b3Sopenharmony_ci  or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1853a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci#include <stdlib.h>
2653a5a1b3Sopenharmony_ci#include <stdio.h>
2753a5a1b3Sopenharmony_ci#include <sys/types.h>
2853a5a1b3Sopenharmony_ci#include <dirent.h>
2953a5a1b3Sopenharmony_ci#include <sys/stat.h>
3053a5a1b3Sopenharmony_ci#include <errno.h>
3153a5a1b3Sopenharmony_ci#include <limits.h>
3253a5a1b3Sopenharmony_ci#include <time.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#ifdef HAVE_GLOB_H
3553a5a1b3Sopenharmony_ci#include <glob.h>
3653a5a1b3Sopenharmony_ci#endif
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci#ifdef HAVE_WINDOWS_H
3953a5a1b3Sopenharmony_ci#include <windows.h>
4053a5a1b3Sopenharmony_ci#endif
4153a5a1b3Sopenharmony_ci
4253a5a1b3Sopenharmony_ci#include <pulse/mainloop.h>
4353a5a1b3Sopenharmony_ci#include <pulse/channelmap.h>
4453a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
4553a5a1b3Sopenharmony_ci#include <pulse/util.h>
4653a5a1b3Sopenharmony_ci#include <pulse/volume.h>
4753a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
4853a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
4953a5a1b3Sopenharmony_ci
5053a5a1b3Sopenharmony_ci#include <pulsecore/sink-input.h>
5153a5a1b3Sopenharmony_ci#include <pulsecore/play-memchunk.h>
5253a5a1b3Sopenharmony_ci#include <pulsecore/core-subscribe.h>
5353a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h>
5453a5a1b3Sopenharmony_ci#include <pulsecore/sound-file.h>
5553a5a1b3Sopenharmony_ci#include <pulsecore/core-rtclock.h>
5653a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
5753a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
5853a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
5953a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
6053a5a1b3Sopenharmony_ci
6153a5a1b3Sopenharmony_ci#include "core-scache.h"
6253a5a1b3Sopenharmony_ci
6353a5a1b3Sopenharmony_ci#define UNLOAD_POLL_TIME (60 * PA_USEC_PER_SEC)
6453a5a1b3Sopenharmony_ci
6553a5a1b3Sopenharmony_cistatic void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
6653a5a1b3Sopenharmony_ci    pa_core *c = userdata;
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_ci    pa_assert(c);
6953a5a1b3Sopenharmony_ci    pa_assert(c->mainloop == m);
7053a5a1b3Sopenharmony_ci    pa_assert(c->scache_auto_unload_event == e);
7153a5a1b3Sopenharmony_ci
7253a5a1b3Sopenharmony_ci    pa_scache_unload_unused(c);
7353a5a1b3Sopenharmony_ci
7453a5a1b3Sopenharmony_ci    pa_core_rttime_restart(c, e, pa_rtclock_now() + UNLOAD_POLL_TIME);
7553a5a1b3Sopenharmony_ci}
7653a5a1b3Sopenharmony_ci
7753a5a1b3Sopenharmony_cistatic void free_entry(pa_scache_entry *e) {
7853a5a1b3Sopenharmony_ci    pa_assert(e);
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci    pa_namereg_unregister(e->core, e->name);
8153a5a1b3Sopenharmony_ci    pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
8253a5a1b3Sopenharmony_ci    pa_hook_fire(&e->core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_UNLINK], e);
8353a5a1b3Sopenharmony_ci    pa_xfree(e->name);
8453a5a1b3Sopenharmony_ci    pa_xfree(e->filename);
8553a5a1b3Sopenharmony_ci    if (e->memchunk.memblock)
8653a5a1b3Sopenharmony_ci        pa_memblock_unref(e->memchunk.memblock);
8753a5a1b3Sopenharmony_ci    if (e->proplist)
8853a5a1b3Sopenharmony_ci        pa_proplist_free(e->proplist);
8953a5a1b3Sopenharmony_ci    pa_xfree(e);
9053a5a1b3Sopenharmony_ci}
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_cistatic pa_scache_entry* scache_add_item(pa_core *c, const char *name, bool *new_sample) {
9353a5a1b3Sopenharmony_ci    pa_scache_entry *e;
9453a5a1b3Sopenharmony_ci
9553a5a1b3Sopenharmony_ci    pa_assert(c);
9653a5a1b3Sopenharmony_ci    pa_assert(name);
9753a5a1b3Sopenharmony_ci    pa_assert(new_sample);
9853a5a1b3Sopenharmony_ci
9953a5a1b3Sopenharmony_ci    if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) {
10053a5a1b3Sopenharmony_ci        if (e->memchunk.memblock)
10153a5a1b3Sopenharmony_ci            pa_memblock_unref(e->memchunk.memblock);
10253a5a1b3Sopenharmony_ci
10353a5a1b3Sopenharmony_ci        pa_xfree(e->filename);
10453a5a1b3Sopenharmony_ci        pa_proplist_clear(e->proplist);
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_ci        pa_assert(e->core == c);
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
10953a5a1b3Sopenharmony_ci        *new_sample = false;
11053a5a1b3Sopenharmony_ci    } else {
11153a5a1b3Sopenharmony_ci        e = pa_xnew(pa_scache_entry, 1);
11253a5a1b3Sopenharmony_ci
11353a5a1b3Sopenharmony_ci        if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, true)) {
11453a5a1b3Sopenharmony_ci            pa_xfree(e);
11553a5a1b3Sopenharmony_ci            return NULL;
11653a5a1b3Sopenharmony_ci        }
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci        e->name = pa_xstrdup(name);
11953a5a1b3Sopenharmony_ci        e->core = c;
12053a5a1b3Sopenharmony_ci        e->proplist = pa_proplist_new();
12153a5a1b3Sopenharmony_ci
12253a5a1b3Sopenharmony_ci        pa_idxset_put(c->scache, e, &e->index);
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index);
12553a5a1b3Sopenharmony_ci        *new_sample = true;
12653a5a1b3Sopenharmony_ci    }
12753a5a1b3Sopenharmony_ci
12853a5a1b3Sopenharmony_ci    e->last_used_time = 0;
12953a5a1b3Sopenharmony_ci    pa_memchunk_reset(&e->memchunk);
13053a5a1b3Sopenharmony_ci    e->filename = NULL;
13153a5a1b3Sopenharmony_ci    e->lazy = false;
13253a5a1b3Sopenharmony_ci    e->last_used_time = 0;
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    pa_sample_spec_init(&e->sample_spec);
13553a5a1b3Sopenharmony_ci    pa_channel_map_init(&e->channel_map);
13653a5a1b3Sopenharmony_ci    pa_cvolume_init(&e->volume);
13753a5a1b3Sopenharmony_ci    e->volume_is_set = false;
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci    pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ci    return e;
14253a5a1b3Sopenharmony_ci}
14353a5a1b3Sopenharmony_ci
14453a5a1b3Sopenharmony_ciint pa_scache_add_item(
14553a5a1b3Sopenharmony_ci        pa_core *c,
14653a5a1b3Sopenharmony_ci        const char *name,
14753a5a1b3Sopenharmony_ci        const pa_sample_spec *ss,
14853a5a1b3Sopenharmony_ci        const pa_channel_map *map,
14953a5a1b3Sopenharmony_ci        const pa_memchunk *chunk,
15053a5a1b3Sopenharmony_ci        pa_proplist *p,
15153a5a1b3Sopenharmony_ci        uint32_t *idx) {
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci    pa_scache_entry *e;
15453a5a1b3Sopenharmony_ci    char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
15553a5a1b3Sopenharmony_ci    pa_channel_map tmap;
15653a5a1b3Sopenharmony_ci    bool new_sample;
15753a5a1b3Sopenharmony_ci
15853a5a1b3Sopenharmony_ci    pa_assert(c);
15953a5a1b3Sopenharmony_ci    pa_assert(name);
16053a5a1b3Sopenharmony_ci    pa_assert(!ss || pa_sample_spec_valid(ss));
16153a5a1b3Sopenharmony_ci    pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss)));
16253a5a1b3Sopenharmony_ci
16353a5a1b3Sopenharmony_ci    if (ss && !map) {
16453a5a1b3Sopenharmony_ci        pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
16553a5a1b3Sopenharmony_ci        map = &tmap;
16653a5a1b3Sopenharmony_ci    }
16753a5a1b3Sopenharmony_ci
16853a5a1b3Sopenharmony_ci    if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
16953a5a1b3Sopenharmony_ci        return -1;
17053a5a1b3Sopenharmony_ci
17153a5a1b3Sopenharmony_ci    if (!(e = scache_add_item(c, name, &new_sample)))
17253a5a1b3Sopenharmony_ci        return -1;
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_ci    pa_sample_spec_init(&e->sample_spec);
17553a5a1b3Sopenharmony_ci    pa_channel_map_init(&e->channel_map);
17653a5a1b3Sopenharmony_ci    pa_cvolume_init(&e->volume);
17753a5a1b3Sopenharmony_ci    e->volume_is_set = false;
17853a5a1b3Sopenharmony_ci
17953a5a1b3Sopenharmony_ci    if (ss) {
18053a5a1b3Sopenharmony_ci        e->sample_spec = *ss;
18153a5a1b3Sopenharmony_ci        pa_cvolume_reset(&e->volume, ss->channels);
18253a5a1b3Sopenharmony_ci    }
18353a5a1b3Sopenharmony_ci
18453a5a1b3Sopenharmony_ci    if (map)
18553a5a1b3Sopenharmony_ci        e->channel_map = *map;
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci    if (chunk) {
18853a5a1b3Sopenharmony_ci        e->memchunk = *chunk;
18953a5a1b3Sopenharmony_ci        pa_memblock_ref(e->memchunk.memblock);
19053a5a1b3Sopenharmony_ci    }
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci    if (p)
19353a5a1b3Sopenharmony_ci        pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);
19453a5a1b3Sopenharmony_ci
19553a5a1b3Sopenharmony_ci    if (idx)
19653a5a1b3Sopenharmony_ci        *idx = e->index;
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci    pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s",
19953a5a1b3Sopenharmony_ci                 name, e->index, (unsigned long) e->memchunk.length,
20053a5a1b3Sopenharmony_ci                 pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));
20153a5a1b3Sopenharmony_ci
20253a5a1b3Sopenharmony_ci    pa_hook_fire(&e->core->hooks[new_sample ? PA_CORE_HOOK_SAMPLE_CACHE_NEW : PA_CORE_HOOK_SAMPLE_CACHE_CHANGED], e);
20353a5a1b3Sopenharmony_ci
20453a5a1b3Sopenharmony_ci    return 0;
20553a5a1b3Sopenharmony_ci}
20653a5a1b3Sopenharmony_ci
20753a5a1b3Sopenharmony_ciint pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
20853a5a1b3Sopenharmony_ci    pa_sample_spec ss;
20953a5a1b3Sopenharmony_ci    pa_channel_map map;
21053a5a1b3Sopenharmony_ci    pa_memchunk chunk;
21153a5a1b3Sopenharmony_ci    int r;
21253a5a1b3Sopenharmony_ci    pa_proplist *p;
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci#ifdef OS_IS_WIN32
21553a5a1b3Sopenharmony_ci    char buf[MAX_PATH];
21653a5a1b3Sopenharmony_ci
21753a5a1b3Sopenharmony_ci    if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
21853a5a1b3Sopenharmony_ci        filename = buf;
21953a5a1b3Sopenharmony_ci#endif
22053a5a1b3Sopenharmony_ci
22153a5a1b3Sopenharmony_ci    pa_assert(c);
22253a5a1b3Sopenharmony_ci    pa_assert(name);
22353a5a1b3Sopenharmony_ci    pa_assert(filename);
22453a5a1b3Sopenharmony_ci
22553a5a1b3Sopenharmony_ci    p = pa_proplist_new();
22653a5a1b3Sopenharmony_ci    pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
22753a5a1b3Sopenharmony_ci#ifdef SNDFILE_ENABLE
22853a5a1b3Sopenharmony_ci    if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk, p) < 0) {
22953a5a1b3Sopenharmony_ci        pa_proplist_free(p);
23053a5a1b3Sopenharmony_ci        return -1;
23153a5a1b3Sopenharmony_ci    }
23253a5a1b3Sopenharmony_ci#else
23353a5a1b3Sopenharmony_ci    return -1;
23453a5a1b3Sopenharmony_ci#endif
23553a5a1b3Sopenharmony_ci    r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
23653a5a1b3Sopenharmony_ci    pa_memblock_unref(chunk.memblock);
23753a5a1b3Sopenharmony_ci    pa_proplist_free(p);
23853a5a1b3Sopenharmony_ci
23953a5a1b3Sopenharmony_ci    return r;
24053a5a1b3Sopenharmony_ci}
24153a5a1b3Sopenharmony_ci
24253a5a1b3Sopenharmony_ciint pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
24353a5a1b3Sopenharmony_ci    pa_scache_entry *e;
24453a5a1b3Sopenharmony_ci    bool new_sample;
24553a5a1b3Sopenharmony_ci
24653a5a1b3Sopenharmony_ci#ifdef OS_IS_WIN32
24753a5a1b3Sopenharmony_ci    char buf[MAX_PATH];
24853a5a1b3Sopenharmony_ci
24953a5a1b3Sopenharmony_ci    if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
25053a5a1b3Sopenharmony_ci        filename = buf;
25153a5a1b3Sopenharmony_ci#endif
25253a5a1b3Sopenharmony_ci
25353a5a1b3Sopenharmony_ci    pa_assert(c);
25453a5a1b3Sopenharmony_ci    pa_assert(name);
25553a5a1b3Sopenharmony_ci    pa_assert(filename);
25653a5a1b3Sopenharmony_ci
25753a5a1b3Sopenharmony_ci    if (!(e = scache_add_item(c, name, &new_sample)))
25853a5a1b3Sopenharmony_ci        return -1;
25953a5a1b3Sopenharmony_ci
26053a5a1b3Sopenharmony_ci    e->lazy = true;
26153a5a1b3Sopenharmony_ci    e->filename = pa_xstrdup(filename);
26253a5a1b3Sopenharmony_ci
26353a5a1b3Sopenharmony_ci    pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
26453a5a1b3Sopenharmony_ci
26553a5a1b3Sopenharmony_ci    if (!c->scache_auto_unload_event)
26653a5a1b3Sopenharmony_ci        c->scache_auto_unload_event = pa_core_rttime_new(c, pa_rtclock_now() + UNLOAD_POLL_TIME, timeout_callback, c);
26753a5a1b3Sopenharmony_ci
26853a5a1b3Sopenharmony_ci    if (idx)
26953a5a1b3Sopenharmony_ci        *idx = e->index;
27053a5a1b3Sopenharmony_ci
27153a5a1b3Sopenharmony_ci    pa_hook_fire(&e->core->hooks[new_sample ? PA_CORE_HOOK_SAMPLE_CACHE_NEW : PA_CORE_HOOK_SAMPLE_CACHE_CHANGED], e);
27253a5a1b3Sopenharmony_ci
27353a5a1b3Sopenharmony_ci    return 0;
27453a5a1b3Sopenharmony_ci}
27553a5a1b3Sopenharmony_ci
27653a5a1b3Sopenharmony_ciint pa_scache_remove_item(pa_core *c, const char *name) {
27753a5a1b3Sopenharmony_ci    pa_scache_entry *e;
27853a5a1b3Sopenharmony_ci
27953a5a1b3Sopenharmony_ci    pa_assert(c);
28053a5a1b3Sopenharmony_ci    pa_assert(name);
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_ci    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
28353a5a1b3Sopenharmony_ci        return -1;
28453a5a1b3Sopenharmony_ci
28553a5a1b3Sopenharmony_ci    pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
28653a5a1b3Sopenharmony_ci
28753a5a1b3Sopenharmony_ci    pa_log_debug("Removed sample \"%s\"", name);
28853a5a1b3Sopenharmony_ci
28953a5a1b3Sopenharmony_ci    free_entry(e);
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci    return 0;
29253a5a1b3Sopenharmony_ci}
29353a5a1b3Sopenharmony_ci
29453a5a1b3Sopenharmony_civoid pa_scache_free_all(pa_core *c) {
29553a5a1b3Sopenharmony_ci    pa_assert(c);
29653a5a1b3Sopenharmony_ci
29753a5a1b3Sopenharmony_ci    pa_idxset_remove_all(c->scache, (pa_free_cb_t) free_entry);
29853a5a1b3Sopenharmony_ci
29953a5a1b3Sopenharmony_ci    if (c->scache_auto_unload_event) {
30053a5a1b3Sopenharmony_ci        c->mainloop->time_free(c->scache_auto_unload_event);
30153a5a1b3Sopenharmony_ci        c->scache_auto_unload_event = NULL;
30253a5a1b3Sopenharmony_ci    }
30353a5a1b3Sopenharmony_ci}
30453a5a1b3Sopenharmony_ci
30553a5a1b3Sopenharmony_ciint pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
30653a5a1b3Sopenharmony_ci    pa_scache_entry *e;
30753a5a1b3Sopenharmony_ci    pa_cvolume r;
30853a5a1b3Sopenharmony_ci    pa_proplist *merged;
30953a5a1b3Sopenharmony_ci    bool pass_volume;
31053a5a1b3Sopenharmony_ci
31153a5a1b3Sopenharmony_ci    pa_assert(c);
31253a5a1b3Sopenharmony_ci    pa_assert(name);
31353a5a1b3Sopenharmony_ci    pa_assert(sink);
31453a5a1b3Sopenharmony_ci
31553a5a1b3Sopenharmony_ci    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
31653a5a1b3Sopenharmony_ci        return -1;
31753a5a1b3Sopenharmony_ci
31853a5a1b3Sopenharmony_ci    merged = pa_proplist_new();
31953a5a1b3Sopenharmony_ci    pa_proplist_sets(merged, PA_PROP_MEDIA_NAME, name);
32053a5a1b3Sopenharmony_ci    pa_proplist_sets(merged, PA_PROP_EVENT_ID, name);
32153a5a1b3Sopenharmony_ci
32253a5a1b3Sopenharmony_ci    if (e->lazy && !e->memchunk.memblock) {
32353a5a1b3Sopenharmony_ci        pa_channel_map old_channel_map = e->channel_map;
32453a5a1b3Sopenharmony_ci#ifdef SNDFILE_ENABLE
32553a5a1b3Sopenharmony_ci        if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, merged) < 0)
32653a5a1b3Sopenharmony_ci            goto fail;
32753a5a1b3Sopenharmony_ci#else
32853a5a1b3Sopenharmony_ci            goto fail;
32953a5a1b3Sopenharmony_ci#endif
33053a5a1b3Sopenharmony_ci        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
33153a5a1b3Sopenharmony_ci
33253a5a1b3Sopenharmony_ci        if (e->volume_is_set) {
33353a5a1b3Sopenharmony_ci            if (pa_cvolume_valid(&e->volume))
33453a5a1b3Sopenharmony_ci                pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map);
33553a5a1b3Sopenharmony_ci            else
33653a5a1b3Sopenharmony_ci                pa_cvolume_reset(&e->volume, e->sample_spec.channels);
33753a5a1b3Sopenharmony_ci        }
33853a5a1b3Sopenharmony_ci    }
33953a5a1b3Sopenharmony_ci
34053a5a1b3Sopenharmony_ci    if (!e->memchunk.memblock)
34153a5a1b3Sopenharmony_ci        goto fail;
34253a5a1b3Sopenharmony_ci
34353a5a1b3Sopenharmony_ci    pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
34453a5a1b3Sopenharmony_ci
34553a5a1b3Sopenharmony_ci    pass_volume = true;
34653a5a1b3Sopenharmony_ci
34753a5a1b3Sopenharmony_ci    if (e->volume_is_set && PA_VOLUME_IS_VALID(volume)) {
34853a5a1b3Sopenharmony_ci        pa_cvolume_set(&r, e->sample_spec.channels, volume);
34953a5a1b3Sopenharmony_ci        pa_sw_cvolume_multiply(&r, &r, &e->volume);
35053a5a1b3Sopenharmony_ci    } else if (e->volume_is_set)
35153a5a1b3Sopenharmony_ci        r = e->volume;
35253a5a1b3Sopenharmony_ci    else if (PA_VOLUME_IS_VALID(volume))
35353a5a1b3Sopenharmony_ci        pa_cvolume_set(&r, e->sample_spec.channels, volume);
35453a5a1b3Sopenharmony_ci    else
35553a5a1b3Sopenharmony_ci        pass_volume = false;
35653a5a1b3Sopenharmony_ci
35753a5a1b3Sopenharmony_ci    pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
35853a5a1b3Sopenharmony_ci
35953a5a1b3Sopenharmony_ci    if (p)
36053a5a1b3Sopenharmony_ci        pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
36153a5a1b3Sopenharmony_ci
36253a5a1b3Sopenharmony_ci    if (pa_play_memchunk(sink,
36353a5a1b3Sopenharmony_ci                         &e->sample_spec, &e->channel_map,
36453a5a1b3Sopenharmony_ci                         &e->memchunk,
36553a5a1b3Sopenharmony_ci                         pass_volume ? &r : NULL,
36653a5a1b3Sopenharmony_ci                         merged,
36753a5a1b3Sopenharmony_ci                         PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND, sink_input_idx) < 0)
36853a5a1b3Sopenharmony_ci        goto fail;
36953a5a1b3Sopenharmony_ci
37053a5a1b3Sopenharmony_ci    pa_proplist_free(merged);
37153a5a1b3Sopenharmony_ci
37253a5a1b3Sopenharmony_ci    if (e->lazy)
37353a5a1b3Sopenharmony_ci        time(&e->last_used_time);
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci    return 0;
37653a5a1b3Sopenharmony_ci
37753a5a1b3Sopenharmony_cifail:
37853a5a1b3Sopenharmony_ci    pa_proplist_free(merged);
37953a5a1b3Sopenharmony_ci    return -1;
38053a5a1b3Sopenharmony_ci}
38153a5a1b3Sopenharmony_ci
38253a5a1b3Sopenharmony_ciint 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) {
38353a5a1b3Sopenharmony_ci    pa_sink *sink;
38453a5a1b3Sopenharmony_ci
38553a5a1b3Sopenharmony_ci    pa_assert(c);
38653a5a1b3Sopenharmony_ci    pa_assert(name);
38753a5a1b3Sopenharmony_ci
38853a5a1b3Sopenharmony_ci    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK)))
38953a5a1b3Sopenharmony_ci        return -1;
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci    return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
39253a5a1b3Sopenharmony_ci}
39353a5a1b3Sopenharmony_ci
39453a5a1b3Sopenharmony_ciconst char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
39553a5a1b3Sopenharmony_ci    pa_scache_entry *e;
39653a5a1b3Sopenharmony_ci
39753a5a1b3Sopenharmony_ci    pa_assert(c);
39853a5a1b3Sopenharmony_ci    pa_assert(id != PA_IDXSET_INVALID);
39953a5a1b3Sopenharmony_ci
40053a5a1b3Sopenharmony_ci    if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
40153a5a1b3Sopenharmony_ci        return NULL;
40253a5a1b3Sopenharmony_ci
40353a5a1b3Sopenharmony_ci    return e->name;
40453a5a1b3Sopenharmony_ci}
40553a5a1b3Sopenharmony_ci
40653a5a1b3Sopenharmony_ciuint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
40753a5a1b3Sopenharmony_ci    pa_scache_entry *e;
40853a5a1b3Sopenharmony_ci
40953a5a1b3Sopenharmony_ci    pa_assert(c);
41053a5a1b3Sopenharmony_ci    pa_assert(name);
41153a5a1b3Sopenharmony_ci
41253a5a1b3Sopenharmony_ci    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
41353a5a1b3Sopenharmony_ci        return PA_IDXSET_INVALID;
41453a5a1b3Sopenharmony_ci
41553a5a1b3Sopenharmony_ci    return e->index;
41653a5a1b3Sopenharmony_ci}
41753a5a1b3Sopenharmony_ci
41853a5a1b3Sopenharmony_cisize_t pa_scache_total_size(pa_core *c) {
41953a5a1b3Sopenharmony_ci    pa_scache_entry *e;
42053a5a1b3Sopenharmony_ci    uint32_t idx;
42153a5a1b3Sopenharmony_ci    size_t sum = 0;
42253a5a1b3Sopenharmony_ci
42353a5a1b3Sopenharmony_ci    pa_assert(c);
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci    if (!c->scache || !pa_idxset_size(c->scache))
42653a5a1b3Sopenharmony_ci        return 0;
42753a5a1b3Sopenharmony_ci
42853a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(e, c->scache, idx)
42953a5a1b3Sopenharmony_ci        if (e->memchunk.memblock)
43053a5a1b3Sopenharmony_ci            sum += e->memchunk.length;
43153a5a1b3Sopenharmony_ci
43253a5a1b3Sopenharmony_ci    return sum;
43353a5a1b3Sopenharmony_ci}
43453a5a1b3Sopenharmony_ci
43553a5a1b3Sopenharmony_civoid pa_scache_unload_unused(pa_core *c) {
43653a5a1b3Sopenharmony_ci    pa_scache_entry *e;
43753a5a1b3Sopenharmony_ci    time_t now;
43853a5a1b3Sopenharmony_ci    uint32_t idx;
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_ci    pa_assert(c);
44153a5a1b3Sopenharmony_ci
44253a5a1b3Sopenharmony_ci    if (!c->scache || !pa_idxset_size(c->scache))
44353a5a1b3Sopenharmony_ci        return;
44453a5a1b3Sopenharmony_ci
44553a5a1b3Sopenharmony_ci    time(&now);
44653a5a1b3Sopenharmony_ci
44753a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(e, c->scache, idx) {
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci        if (!e->lazy || !e->memchunk.memblock)
45053a5a1b3Sopenharmony_ci            continue;
45153a5a1b3Sopenharmony_ci
45253a5a1b3Sopenharmony_ci        if (e->last_used_time + c->scache_idle_time > now)
45353a5a1b3Sopenharmony_ci            continue;
45453a5a1b3Sopenharmony_ci
45553a5a1b3Sopenharmony_ci        pa_memblock_unref(e->memchunk.memblock);
45653a5a1b3Sopenharmony_ci        pa_memchunk_reset(&e->memchunk);
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
45953a5a1b3Sopenharmony_ci    }
46053a5a1b3Sopenharmony_ci}
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_cistatic void add_file(pa_core *c, const char *pathname) {
46353a5a1b3Sopenharmony_ci    struct stat st;
46453a5a1b3Sopenharmony_ci    const char *e;
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci    pa_core_assert_ref(c);
46753a5a1b3Sopenharmony_ci    pa_assert(pathname);
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci    e = pa_path_get_filename(pathname);
47053a5a1b3Sopenharmony_ci
47153a5a1b3Sopenharmony_ci    if (stat(pathname, &st) < 0) {
47253a5a1b3Sopenharmony_ci        pa_log("stat('%s'): %s", pathname, pa_cstrerror(errno));
47353a5a1b3Sopenharmony_ci        return;
47453a5a1b3Sopenharmony_ci    }
47553a5a1b3Sopenharmony_ci
47653a5a1b3Sopenharmony_ci#if defined(S_ISREG) && defined(S_ISLNK)
47753a5a1b3Sopenharmony_ci    if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
47853a5a1b3Sopenharmony_ci#endif
47953a5a1b3Sopenharmony_ci        pa_scache_add_file_lazy(c, e, pathname, NULL);
48053a5a1b3Sopenharmony_ci}
48153a5a1b3Sopenharmony_ci
48253a5a1b3Sopenharmony_ciint pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
48353a5a1b3Sopenharmony_ci    DIR *dir;
48453a5a1b3Sopenharmony_ci
48553a5a1b3Sopenharmony_ci    pa_core_assert_ref(c);
48653a5a1b3Sopenharmony_ci    pa_assert(pathname);
48753a5a1b3Sopenharmony_ci
48853a5a1b3Sopenharmony_ci    /* First try to open this as directory */
48953a5a1b3Sopenharmony_ci    if (!(dir = opendir(pathname))) {
49053a5a1b3Sopenharmony_ci#ifdef HAVE_GLOB_H
49153a5a1b3Sopenharmony_ci        glob_t p;
49253a5a1b3Sopenharmony_ci        unsigned int i;
49353a5a1b3Sopenharmony_ci        /* If that fails, try to open it as shell glob */
49453a5a1b3Sopenharmony_ci
49553a5a1b3Sopenharmony_ci        if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) {
49653a5a1b3Sopenharmony_ci            pa_log("failed to open directory '%s': %s", pathname, pa_cstrerror(errno));
49753a5a1b3Sopenharmony_ci            return -1;
49853a5a1b3Sopenharmony_ci        }
49953a5a1b3Sopenharmony_ci
50053a5a1b3Sopenharmony_ci        for (i = 0; i < p.gl_pathc; i++)
50153a5a1b3Sopenharmony_ci            add_file(c, p.gl_pathv[i]);
50253a5a1b3Sopenharmony_ci
50353a5a1b3Sopenharmony_ci        globfree(&p);
50453a5a1b3Sopenharmony_ci#else
50553a5a1b3Sopenharmony_ci        return -1;
50653a5a1b3Sopenharmony_ci#endif
50753a5a1b3Sopenharmony_ci    } else {
50853a5a1b3Sopenharmony_ci        struct dirent *e;
50953a5a1b3Sopenharmony_ci
51053a5a1b3Sopenharmony_ci        while ((e = readdir(dir))) {
51153a5a1b3Sopenharmony_ci            char *p;
51253a5a1b3Sopenharmony_ci
51353a5a1b3Sopenharmony_ci            if (e->d_name[0] == '.')
51453a5a1b3Sopenharmony_ci                continue;
51553a5a1b3Sopenharmony_ci
51653a5a1b3Sopenharmony_ci            p = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", pathname, e->d_name);
51753a5a1b3Sopenharmony_ci            add_file(c, p);
51853a5a1b3Sopenharmony_ci            pa_xfree(p);
51953a5a1b3Sopenharmony_ci        }
52053a5a1b3Sopenharmony_ci
52153a5a1b3Sopenharmony_ci        closedir(dir);
52253a5a1b3Sopenharmony_ci    }
52353a5a1b3Sopenharmony_ci
52453a5a1b3Sopenharmony_ci    return 0;
52553a5a1b3Sopenharmony_ci}
526