c_cpp 在ALSA上设置频道映射

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c_cpp 在ALSA上设置频道映射相关的知识,希望对你有一定的参考价值。

CXX=gcc
CFLAGS=-lasound

all:
	$(CC) alsa_channel_map.c -o alsa_channel_map $(CFLAGS)

clean:
	rm alsa_channel_map
#include <alsa/asoundlib.h> // for ALSA APIs
#include <stddef.h>         // for offsetof
#include <stdio.h>          // for fprintf
#include <stdlib.h>         // for calloc
#include <string.h>         // for strcmp

#define DEBUG 1 // Set 1 to log the debugging messages.
#define LOG(...) DEBUG && fprintf(stdout, __VA_ARGS__)

#define ALSA_DEFAULT_DEVICE_NAME "default"
#define ALSA_ALL_CARDS -1
#define ALSA_MAX_CHANNELS 8

typedef struct {
  // For opening a PCM
  snd_pcm_t* pcm;
  const char* name;
  int mode;
  snd_pcm_stream_t type;
  // channel layout setting
  unsigned int channels;
  snd_pcm_chmap_t* map;
} alsa_stream;

static void
alsa_enumerate_devices()
{
  const char *interfaces[] = {"card", "pcm", "rawmidi", "timer", "seq", "hwdep", 0};
  unsigned int i = 0;
  char** hints;
  char** hint;

  char* name;
  char* io;
  char* desc;

  char* desc_tmp;
  int r;

  snd_config_update();

  for (i = 0 ; interfaces[i] ; ++i) {
    LOG("\n\t=== Query interface: %s === \n", interfaces[i]);

    r = snd_device_name_hint(ALSA_ALL_CARDS, interfaces[i], (void***)&hints);
    if (r) {
      LOG("[%d] Querying devices failed for %s.\n", r, interfaces[i]);
      continue;
    }

    for (hint = hints ; *hint ; ++hint) {
      name = snd_device_name_get_hint(*hint, "NAME");
      io = snd_device_name_get_hint(*hint, "IOID");
      desc = snd_device_name_get_hint(*hint, "DESC");

      LOG("%s\n", name);
      LOG("\tIO: %s\n", io);
      desc_tmp = strtok(desc, "\n");
      while (desc_tmp != NULL) {
        LOG("\t%s\n", desc_tmp);
        desc_tmp = strtok(NULL, "\n");
      }

      free(name);
      free(desc);
      free(io);
    }

    snd_device_name_free_hint((void**)hints);
  }
}

static void
alsa_print_channel_map(snd_pcm_chmap_t* map)
{
  assert(map);

  unsigned int i;
  LOG("\t%d channels\n", map->channels);
  for (i = 0 ; i < map->channels ; ++i) {
    LOG("\tmap->pos[%d] = %d\n", i, map->pos[i]);
  }
}

static void
alsa_get_supported_channel_map(snd_pcm_t *pcm)
{
  assert(pcm);

  LOG("\n\t=== Query channel maps === \n");
  snd_pcm_chmap_query_t** maps = snd_pcm_query_chmaps(pcm);
  if (!maps) {
    LOG("Couldn't query channel maps.\n");
    return;
  }

  unsigned int i = 0;
  snd_pcm_chmap_query_t* map;
  for (map = maps[0]; map; map = maps[i++]) {
    LOG("channel map %d\n", i);
    printf("\ttype: %d\n", map->type);
    alsa_print_channel_map(&map->map);
  }

  snd_pcm_free_chmaps(maps);
}

// static snd_pcm_chmap_t*
static void
alsa_get_current_channel_map(snd_pcm_t *pcm)
{
  assert(pcm);

  LOG("\n\t=== Get channel map === \n");
  snd_pcm_chmap_t* map = snd_pcm_get_chmap(pcm);
  if (!map) {
    LOG("Couldn't get channel map.\n");
    return;
  }
  alsa_print_channel_map(map);
  free(map);
}

static snd_pcm_chmap_t*
alsa_create_channel_map(unsigned int channels)
{
  assert(channels);

  size_t size = offsetof(snd_pcm_chmap_t, pos[channels]);
  assert(size >= sizeof(snd_pcm_chmap_t));

  snd_pcm_chmap_t* map = (snd_pcm_chmap_t*) calloc(1, size);
  assert(map);

  // Using SMPTE layout:
  enum snd_pcm_chmap_position layout[ALSA_MAX_CHANNELS][ALSA_MAX_CHANNELS] = {
    { SND_CHMAP_MONO },
    { SND_CHMAP_FL, SND_CHMAP_FR },
    { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_LFE },
    { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_SL, SND_CHMAP_SR },
    { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC, SND_CHMAP_SL, SND_CHMAP_SR },
    { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_SL, SND_CHMAP_SR },
    { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_RC, SND_CHMAP_SL, SND_CHMAP_SR },
    { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_SL, SND_CHMAP_SR },
  };

  map->channels = channels;
  unsigned int i;
  for (i = 0 ; i < map->channels ; ++i) {
    map->pos[i] = layout[map->channels][i];
  }

  return map;
}

static int
alsa_set_layout(alsa_stream* stream)
{
  assert(stream && stream->pcm && stream->channels && !stream->map);

  LOG("\n\t=== Set channel map === \n");
  stream->map = alsa_create_channel_map(stream->channels);
  assert(stream->map);
  alsa_print_channel_map(stream->map);

  int r;
  r = snd_pcm_set_chmap(stream->pcm, stream->map);

  if (r) {
    LOG("[%d] Set channel map failed.\n", r);
  }

  free(stream->map);
  return r;
}

// static XXX
// alsa_select_device(alsa_stream* stream)
// {
// }

// static XXX
// alsa_set_something(alsa_stream* stream)
// {
// }

static int
alsa_open_stream(alsa_stream* stream) {
  return snd_pcm_open(&stream->pcm, stream->name, stream->type, stream->mode);
}

static int
alsa_close_stream(alsa_stream* stream)
{
  return snd_pcm_close(stream->pcm);
}

int main()
{
  alsa_enumerate_devices();

  alsa_stream as = {
    // PCM settings
    NULL,
    ALSA_DEFAULT_DEVICE_NAME,
    SND_PCM_STREAM_PLAYBACK, // SND_PCM_STREAM_CAPTURE
    SND_PCM_NONBLOCK,
    // Channel layout settings
    6,
    NULL
  };

  // Should we open a PCM by channel map ?
  // alsa_select_device(&as);

  alsa_open_stream(&as);
  assert(!strcmp(as.name, snd_pcm_name(as.pcm)));

  // Or we need to set something before assigning the channel map ?
  // alsa_set_something(&as);

  alsa_get_supported_channel_map(as.pcm);

  alsa_set_layout(&as);

  alsa_get_current_channel_map(as.pcm);

  alsa_close_stream(&as);
}

以上是关于c_cpp 在ALSA上设置频道映射的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 这个脚本以编程方式在Linux中找到ALSA设备,这可能有点棘手。 #alsa#device-driver #linux #c

BeagleBoard:设置 ALSA 主音量

“频道关闭:连接错误”

嵌入式Bluetooth应用开发笔记第三篇:初探BLUE ALSA应用开发

使用 ALSA 从 Line IN 录制声音

从 C 代码中以 dB 为单位设置 ALSA 主音量