diff options
author | dvdli <70133153+dvdli@users.noreply.github.com> | 2021-07-12 22:21:58 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-12 22:21:58 +0800 |
commit | cb2790bca6c88adad5cbe1e02789f066b5953f6f (patch) | |
tree | 47bc67ac90225d9628b79681eb9c0bd73b24c3b3 /examples/plugins | |
parent | 496e653335d27557bbd1f795b1eacd2948ab192a (diff) | |
parent | a36069e2b551db4f3be3a0bc4f0f38bc3f0d1899 (diff) |
Merge pull request #210 from rohkkumar/plugin_example
tinyalsa: Add sample pcm/mixer plugin and sample sndcardparser
Diffstat (limited to 'examples/plugins')
-rw-r--r-- | examples/plugins/Android.bp | 15 | ||||
-rw-r--r-- | examples/plugins/sample_mixer_plugin.c | 319 | ||||
-rw-r--r-- | examples/plugins/sample_pcm_plugin.c | 331 |
3 files changed, 665 insertions, 0 deletions
diff --git a/examples/plugins/Android.bp b/examples/plugins/Android.bp new file mode 100644 index 0000000..840480e --- /dev/null +++ b/examples/plugins/Android.bp @@ -0,0 +1,15 @@ +cc_library { + name: "libtinyalsav2_example_plugin_pcm", + vendor: true, + srcs: ["sample_pcm_plugin.c"], + cflags: ["-Werror", "-Wno-unused-parameter"], + header_libs: ["libtinyalsav2_headers"], +} + +cc_library { + name: "libtinyalsav2_example_plugin_mixer", + vendor: true, + srcs: ["sample_mixer_plugin.c"], + cflags: ["-Werror", "-Wno-unused-parameter"], + header_libs: ["libtinyalsav2_headers"], +} diff --git a/examples/plugins/sample_mixer_plugin.c b/examples/plugins/sample_mixer_plugin.c new file mode 100644 index 0000000..e1a7567 --- /dev/null +++ b/examples/plugins/sample_mixer_plugin.c @@ -0,0 +1,319 @@ +/* +** Copyright (c) 2021, The Linux Foundation. All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** * Neither the name of The Linux Foundation nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include <errno.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <sound/asound.h> +#include <tinyalsa/plugin.h> +#include <tinyalsa/asoundlib.h> + +#define SAMPLE_MIXER_PRIV_GET_CTL_PTR(p, idx) (p->ctls + idx) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +static const char *const sample_enum_text[] = {"One", "Two", "Three"}; + +struct sample_mixer_priv { + struct snd_control *ctls; + int ctl_count; + + struct snd_value_enum sample_enum; + + mixer_event_callback event_cb; +}; + +static int sample_mixer_int_ctl_get(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ + return 0; +} + +static int sample_mixer_int_ctl_put(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ +/* + * Integer values can be retrieved using: + * uint32_t val1 = (uint32_t)ev->value.integer.value[0]; + * uint32_t val2 = (uint32_t)ev->value.integer.value[1]; + * uint32_t val3 = (uint32_t)ev->value.integer.value[2]; + */ + return 0; +} + +static int sample_mixer_byte_array_ctl_get(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ + return 0; +} + +static int sample_mixer_byte_array_ctl_put(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ +/* + * Byte array payload can be retrieved using: + * void *payload = ev->value.bytes.data; + */ + + return 0; +} + +static int sample_mixer_tlv_ctl_get(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_tlv *ev) +{ + return 0; +} + +static int sample_mixer_tlv_ctl_put(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_tlv *tlv) +{ +/* + * TLV payload and len can be retrieved using: + * void *payload = &tlv->tlv[0]; + * size_t tlv_size = tlv->length; + */ + + return 0; +} + +static int sample_mixer_enum_ctl_get(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ + return 0; +} + +static int sample_mixer_enum_ctl_put(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ +/* + * Enum value can be retrieved using: + * unsigned int val = ev->value.enumerated.item[0]; + */ + return 0; +} + +static struct snd_value_int sample_mixer_ctl_value_int = + SND_VALUE_INTEGER(3, 0, 1000, 100); + +/* 512 max bytes for non-tlv byte controls */ +static struct snd_value_bytes byte_array_ctl_bytes = + SND_VALUE_BYTES(512); + +static struct snd_value_tlv_bytes sample_mixer_tlv_ctl_bytes = + SND_VALUE_TLV_BYTES(1024, sample_mixer_tlv_ctl_get, sample_mixer_tlv_ctl_put); + +static void create_integer_ctl(struct sample_mixer_priv *priv, + int ctl_idx, int pval, void *pdata) +{ + struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx); + char *ctl_name = strdup("Sample integer control"); + + /* pval and pdata can be retrieved using snd_control during get()/put() */ + INIT_SND_CONTROL_INTEGER(ctl, ctl_name, sample_mixer_int_ctl_get, + sample_mixer_int_ctl_put, sample_mixer_ctl_value_int, pval, pdata); +} + +static void create_byte_array_ctl(struct sample_mixer_priv *priv, + int ctl_idx, int pval, void *pdata) +{ + struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx); + char *ctl_name = strdup("Sample byte array control"); + + INIT_SND_CONTROL_BYTES(ctl, ctl_name, sample_mixer_byte_array_ctl_get, + sample_mixer_byte_array_ctl_put, byte_array_ctl_bytes, + pval, pdata); +} + +static void create_tlv_ctl(struct sample_mixer_priv *priv, + int ctl_idx, int pval, void *pdata) +{ + struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx); + char *ctl_name = strdup("Sample tlv control"); + + INIT_SND_CONTROL_TLV_BYTES(ctl, ctl_name, sample_mixer_tlv_ctl_bytes, + pval, pdata); +} + +static void create_enum_ctl(struct sample_mixer_priv *priv, + int ctl_idx, struct snd_value_enum *e, + int pval, void *pdata) +{ + struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx); + char *ctl_name = strdup("Sample enum control"); + + INIT_SND_CONTROL_ENUM(ctl, ctl_name, sample_mixer_enum_ctl_get, + sample_mixer_enum_ctl_put, e, pval, pdata); +} + +static int sample_mixer_form_ctls(struct sample_mixer_priv *priv, int ctl_idx) +{ + create_integer_ctl(priv, ctl_idx, 0, NULL); + ctl_idx++; + create_byte_array_ctl(priv, ctl_idx, 0, NULL); + ctl_idx++; + create_tlv_ctl(priv, ctl_idx, 0, NULL); + ctl_idx++; + create_enum_ctl(priv, ctl_idx, &priv->sample_enum, 0, NULL); + ctl_idx++; + + return 0; +} + +static ssize_t sample_mixer_read_event(struct mixer_plugin *plugin, + struct snd_ctl_event *ev, size_t size) +{ + /* Fill snd_ctl_event *ev before sending. + * Return : sizeof(struct snd_ctl_event), + * 0 in case no event present. + */ + + return 0; +} + +static int sample_mixer_subscribe_events(struct mixer_plugin *plugin, + mixer_event_callback event_cb) +{ + struct sample_mixer_priv *priv = plugin->priv; + + priv->event_cb = event_cb; + /* event_cb is the callback function which needs to be called + * when an event occurs. This will unblock poll() on mixer fd + * which is called from mixer_wait_event(). + * Once poll is unblocked, clients can call mixer_read_event() + * During unsubscribe(), event_cb is NULL. + */ + return 0; +} + +static int sample_mixer_alloc_ctls(struct sample_mixer_priv *priv) +{ + int ret = 0, i; + + priv->ctls = calloc(priv->ctl_count, sizeof(*priv->ctls)); + if (!priv->ctls) { + return -ENOMEM; + } + + priv->sample_enum.items = ARRAY_SIZE(sample_enum_text); + priv->sample_enum.texts = calloc(priv->sample_enum.items, sizeof(*priv->sample_enum.texts)); + + for (i = 0; i < ARRAY_SIZE(sample_enum_text); i++) + priv->sample_enum.texts[i] = strdup(sample_enum_text[i]); + + return sample_mixer_form_ctls(priv, 0); +} + +static void sample_mixer_free_ctls(struct sample_mixer_priv *priv) +{ + int num_enums, i; + struct snd_control *ctl = NULL; + + for (i = 0; i < priv->ctl_count; i++) { + ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, i); + if (ctl->name) + free((void *)ctl->name); + } + + num_enums = priv->sample_enum.items; + + for (i = 0; i < num_enums; i++) + free(priv->sample_enum.texts[i]); + + free(priv->sample_enum.texts); + priv->ctl_count = 0; + + if (priv->ctls) { + free(priv->ctls); + priv->ctls = NULL; + } +} + +static void sample_mixer_close(struct mixer_plugin **plugin) +{ + struct mixer_plugin *mp = *plugin; + struct sample_mixer_priv *priv = mp->priv; + + /* unblock mixer event during close */ + if (priv->event_cb) + priv->event_cb(mp); + sample_mixer_subscribe_events(mp, NULL); + sample_mixer_free_ctls(priv); + free(priv); + free(*plugin); + *plugin = NULL; +} + +int sample_mixer_open(struct mixer_plugin **plugin, unsigned int card) +{ + struct mixer_plugin *mp; + struct sample_mixer_priv *priv; + int i, ret = 0; + int ctl_cnt = 4; + + mp = calloc(1, sizeof(*mp)); + if (!mp) { + return -ENOMEM; + } + + priv = calloc(1, sizeof(*priv)); + if (!priv) { + ret = -ENOMEM; + goto err_priv_alloc; + } + + priv->ctl_count = ctl_cnt; + ret = sample_mixer_alloc_ctls(priv); + if (ret) + goto err_ctls_alloc; + + /* Register the controls */ + mp->controls = priv->ctls; + mp->num_controls = priv->ctl_count; + mp->priv = priv; + *plugin = mp; + + return 0; + +err_ctls_alloc: + sample_mixer_free_ctls(priv); + free(priv); + +err_priv_alloc: + free(mp); + return ret; +} + +struct mixer_plugin_ops mixer_plugin_ops = { + .open = sample_mixer_open, + .close = sample_mixer_close, + .subscribe_events = sample_mixer_subscribe_events, + .read_event = sample_mixer_read_event, +}; diff --git a/examples/plugins/sample_pcm_plugin.c b/examples/plugins/sample_pcm_plugin.c new file mode 100644 index 0000000..3b02435 --- /dev/null +++ b/examples/plugins/sample_pcm_plugin.c @@ -0,0 +1,331 @@ +/* +** Copyright (c) 2021, The Linux Foundation. All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** * Neither the name of The Linux Foundation nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include <errno.h> +#include <limits.h> +#include <sys/time.h> +#include <sys/mman.h> +#include <sound/asound.h> +#include <stdint.h> +#include <stdlib.h> +#include <strings.h> +#include <stdio.h> +#include <unistd.h> +#include <tinyalsa/plugin.h> +#include <tinyalsa/asoundlib.h> + +/* 2 words of uint32_t = 64 bits of mask */ +#define PCM_MASK_SIZE (2) +#define PCM_FORMAT_BIT(x) (1ULL << x) + +struct sample_pcm_priv { + FILE *fptr; + int session_id; + int channels; + int bitwidth; + int sample_rate; + unsigned int period_size; + snd_pcm_uframes_t total_size_frames; +}; + +struct pcm_plugin_hw_constraints sample_pcm_constrs = { + .access = 0, + .format = 0, + .bit_width = { + .min = 16, + .max = 32, + }, + .channels = { + .min = 1, + .max = 8, + }, + .rate = { + .min = 8000, + .max = 384000, + }, + .periods = { + .min = 1, + .max = 8, + }, + .period_bytes = { + .min = 96, + .max = 122880, + }, +}; + +static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); +} + +static inline int param_is_interval(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && + (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); +} + +static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n) +{ + if (param_is_interval(n)) { + struct snd_interval *i = param_to_interval(p, n); + if (i->integer) + return i->max; + } + return 0; +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline int snd_mask_val(const struct snd_mask *mask) +{ + int i; + for (i = 0; i < PCM_MASK_SIZE; i++) { + if (mask->bits[i]) + return ffs(mask->bits[i]) + (i << 5) - 1; + } + return 0; +} + +static int alsaformat_to_bitwidth(int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + return 32; + case SNDRV_PCM_FORMAT_S8: + return 8; + case SNDRV_PCM_FORMAT_S24_3LE: + return 24; + default: + case SNDRV_PCM_FORMAT_S16_LE: + return 16; + }; +} + +static int param_get_mask_val(struct snd_pcm_hw_params *p, + int n) +{ + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + int val = snd_mask_val(m); + + return alsaformat_to_bitwidth(val); + } + return 0; +} + +static int sample_session_open(int sess_id, unsigned int mode, struct sample_pcm_priv *priv) +{ + char fname[128]; + + snprintf(fname, 128, "sample_pcm_data_%d.raw", sess_id); + priv->fptr = fopen(fname,"rwb+"); + if (priv->fptr == NULL) { + return -EIO; + } + rewind(priv->fptr); + return 0; +} + + +static int sample_session_write(struct sample_pcm_priv *priv, void *buff, size_t count) +{ + uint8_t *data = (uint8_t *)buff; + size_t len; + + len = fwrite(buff, 1, count, priv->fptr); + + if (len != count) + return -EIO; + + return 0; +} + +static int sample_pcm_hw_params(struct pcm_plugin *plugin, + struct snd_pcm_hw_params *params) +{ + struct sample_pcm_priv *priv = plugin->priv; + + priv->sample_rate = param_get_int(params, SNDRV_PCM_HW_PARAM_RATE); + priv->channels = param_get_int(params, SNDRV_PCM_HW_PARAM_CHANNELS); + priv->bitwidth = param_get_mask_val(params, SNDRV_PCM_HW_PARAM_FORMAT); + + return 0; +} + +static int sample_pcm_sw_params(struct pcm_plugin *plugin, + struct snd_pcm_sw_params *sparams) +{ + return 0; +} + +static int sample_pcm_sync_ptr(struct pcm_plugin *plugin, + struct snd_pcm_sync_ptr *sync_ptr) +{ + return 0; +} + +static int sample_pcm_writei_frames(struct pcm_plugin *plugin, struct snd_xferi *x) +{ + struct sample_pcm_priv *priv = plugin->priv; + void *buff; + size_t count; + + buff = x->buf; + count = x->frames * (priv->channels * (priv->bitwidth) / 8); + + return sample_session_write(priv, buff, count); +} + +static int sample_pcm_readi_frames(struct pcm_plugin *plugin, struct snd_xferi *x) +{ + return 0; +} + +static int sample_pcm_ttstamp(struct pcm_plugin *plugin, int *tstamp) +{ + return 0; +} + +static int sample_pcm_prepare(struct pcm_plugin *plugin) +{ + return 0; +} + +static int sample_pcm_start(struct pcm_plugin *plugin) +{ + return 0; +} + +static int sample_pcm_drop(struct pcm_plugin *plugin) +{ + return 0; +} + +static int sample_pcm_close(struct pcm_plugin *plugin) +{ + struct sample_pcm_priv *priv = plugin->priv; + int ret = 0; + + fclose(priv->fptr); + free(plugin->priv); + free(plugin); + + return ret; +} + +static int sample_pcm_poll(struct pcm_plugin *plugin, struct pollfd *pfd, + nfds_t nfds, int timeout) +{ + return 0; +} + +static void* sample_pcm_mmap(struct pcm_plugin *plugin, void *addr, size_t length, int prot, + int flags, off_t offset) +{ + return MAP_FAILED; +} + +static int sample_pcm_munmap(struct pcm_plugin *plugin, void *addr, size_t length) +{ + return 0; +} + +int sample_pcm_open(struct pcm_plugin **plugin, unsigned int card, + unsigned int device, unsigned int mode) +{ + struct pcm_plugin *sample_pcm_plugin; + struct sample_pcm_priv *priv; + int ret = 0, session_id = device; + + sample_pcm_plugin = calloc(1, sizeof(struct pcm_plugin)); + if (!sample_pcm_plugin) + return -ENOMEM; + + priv = calloc(1, sizeof(struct sample_pcm_priv)); + if (!priv) { + ret = -ENOMEM; + goto err_plugin_free; + } + + sample_pcm_constrs.access = (PCM_FORMAT_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED) | + PCM_FORMAT_BIT(SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)); + sample_pcm_constrs.format = (PCM_FORMAT_BIT(SNDRV_PCM_FORMAT_S16_LE) | + PCM_FORMAT_BIT(SNDRV_PCM_FORMAT_S24_LE) | + PCM_FORMAT_BIT(SNDRV_PCM_FORMAT_S24_3LE) | + PCM_FORMAT_BIT(SNDRV_PCM_FORMAT_S32_LE)); + + sample_pcm_plugin->card = card; + sample_pcm_plugin->mode = mode; + sample_pcm_plugin->constraints = &sample_pcm_constrs; + sample_pcm_plugin->priv = priv; + + priv->session_id = session_id; + + ret = sample_session_open(session_id, mode, priv); + if (ret) { + errno = -ret; + goto err_priv_free; + } + *plugin = sample_pcm_plugin; + return 0; + +err_priv_free: + free(priv); +err_plugin_free: + free(sample_pcm_plugin); + return ret; +} + +struct pcm_plugin_ops pcm_plugin_ops = { + .open = sample_pcm_open, + .close = sample_pcm_close, + .hw_params = sample_pcm_hw_params, + .sw_params = sample_pcm_sw_params, + .sync_ptr = sample_pcm_sync_ptr, + .writei_frames = sample_pcm_writei_frames, + .readi_frames = sample_pcm_readi_frames, + .ttstamp = sample_pcm_ttstamp, + .prepare = sample_pcm_prepare, + .start = sample_pcm_start, + .drop = sample_pcm_drop, + .mmap = sample_pcm_mmap, + .munmap = sample_pcm_munmap, + .poll = sample_pcm_poll, +}; |