aboutsummaryrefslogtreecommitdiff
path: root/examples/plugins/sample_pcm_plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/plugins/sample_pcm_plugin.c')
-rw-r--r--examples/plugins/sample_pcm_plugin.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/examples/plugins/sample_pcm_plugin.c b/examples/plugins/sample_pcm_plugin.c
new file mode 100644
index 0000000..40bf684
--- /dev/null
+++ b/examples/plugins/sample_pcm_plugin.c
@@ -0,0 +1,332 @@
+/* sample_mixer_plugin.c
+**
+** 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,
+};