Last active
September 26, 2021 12:03
-
-
Save ChunMinChang/03741999c8f5e27dd655368b999870c0 to your computer and use it in GitHub Desktop.
Set channel map on ALSA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CXX=gcc | |
CFLAGS=-lasound | |
all: | |
$(CC) alsa_channel_map.c -o alsa_channel_map $(CFLAGS) | |
clean: | |
rm alsa_channel_map |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment