void print_card_list(void) { int status; int card = -1; // use -1 to prime the pump of iterating through card list char* longname = NULL; char* shortname = NULL; if ((status = snd_card_next(&card)) < 0) { error("cannot determine card number: %s", snd_strerror(status)); return; } if (card < 0) { error("no sound cards found"); return; } while (card >= 0) { printf("Card %d:", card); if ((status = snd_card_get_name(card, &shortname)) < 0) { error("cannot determine card shortname: %s", snd_strerror(status)); break; } if ((status = snd_card_get_longname(card, &longname)) < 0) { error("cannot determine card longname: %s", snd_strerror(status)); break; } printf("\tLONG NAME: %s\n", longname); printf("\tSHORT NAME: %s\n", shortname); if ((status = snd_card_next(&card)) < 0) { error("cannot determine card number: %s", snd_strerror(status)); break; } } }
static audio_device_name * alsa_enumerate_capture_devices(void) { int rcard; // count cards size_t cnt = 0; rcard = -1; while (1) { int err = snd_card_next(&rcard); if (err != 0) break; if (rcard == -1) break; cnt ++; } if (cnt == 0) return NULL; audio_device_name *list = calloc(sizeof(audio_device_name), cnt + 1); if (!list) return NULL; // fill the list uintptr_t idx = 0; rcard = -1; while (1) { int err = snd_card_next(&rcard); if (err != 0) break; if (rcard == -1) break; char *name; if (snd_card_get_name(rcard, &name) == 0 && name != NULL) { list[idx].name = name; char *longname; if (snd_card_get_longname(rcard, &longname) == 0 && longname != NULL) list[idx].longname = longname; idx ++; if (idx >= cnt) break; } } // terminate list list[idx].name = NULL; list[idx].longname = NULL; return list; }
static char * find_pcm_name(const char *device_longname) { char *pcm_name = NULL; int card = -1; int done = 0; if (!device_longname) return strdup("default"); while (!done) { int err = snd_card_next(&card); if (err != 0) break; if (card == -1) break; char *longname = NULL; if (snd_card_get_longname(card, &longname) != 0) goto next_0; if (!longname) goto next_0; if (strcmp(device_longname, longname) != 0) goto next_2; void **hints; if (snd_device_name_hint(card, "pcm", &hints) != 0) goto next_2; for (int k = 0; hints[k] != NULL; k ++) { pcm_name = snd_device_name_get_hint(hints[k], "NAME"); if (strncmp(pcm_name, "default:", strlen("default:")) == 0) { done = 1; goto next_3; } free(pcm_name); pcm_name = NULL; } next_3: snd_device_name_free_hint(hints); next_2: free(longname); next_0: continue; } if (!pcm_name) pcm_name = strdup("default"); return pcm_name; }
OSStatus AudioComponentCopyName(AudioComponent inComponent, CFStringRef *outName) { int index = GetComponentIndex(inComponent); char* name; if (!outName) return paramErr; if (snd_card_get_longname(index, &name)) return paramErr; *outName = CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8); free(name); return noErr; }
static void QSA_DetectDevices(int iscapture, SDL_AddAudioDevice addfn) { uint32_t it; uint32_t cards; uint32_t devices; int32_t status; /* Detect amount of available devices */ /* this value can be changed in the runtime */ cards = snd_cards(); /* If io-audio manager is not running we will get 0 as number */ /* of available audio devices */ if (cards == 0) { /* We have no any available audio devices */ return; } /* Find requested devices by type */ if (!iscapture) { /* Playback devices enumeration requested */ for (it = 0; it < cards; it++) { devices = 0; do { status = snd_card_get_longname(it, qsa_playback_device [qsa_playback_devices].name, QSA_MAX_NAME_LENGTH); if (status == EOK) { snd_pcm_t *handle; /* Add device number to device name */ sprintf(qsa_playback_device[qsa_playback_devices].name + SDL_strlen(qsa_playback_device [qsa_playback_devices].name), " d%d", devices); /* Store associated card number id */ qsa_playback_device[qsa_playback_devices].cardno = it; /* Check if this device id could play anything */ status = snd_pcm_open(&handle, it, devices, SND_PCM_OPEN_PLAYBACK); if (status == EOK) { qsa_playback_device[qsa_playback_devices].deviceno = devices; status = snd_pcm_close(handle); if (status == EOK) { addfn(qsa_playback_device[qsa_playback_devices].name); qsa_playback_devices++; } } else { /* Check if we got end of devices list */ if (status == -ENOENT) { break; } } } else { break; } /* Check if we reached maximum devices count */ if (qsa_playback_devices >= QSA_MAX_DEVICES) { break; } devices++; } while (1); /* Check if we reached maximum devices count */ if (qsa_playback_devices >= QSA_MAX_DEVICES) { break; } } } else { /* Capture devices enumeration requested */ for (it = 0; it < cards; it++) { devices = 0; do { status = snd_card_get_longname(it, qsa_capture_device [qsa_capture_devices].name, QSA_MAX_NAME_LENGTH); if (status == EOK) { snd_pcm_t *handle; /* Add device number to device name */ sprintf(qsa_capture_device[qsa_capture_devices].name + SDL_strlen(qsa_capture_device [qsa_capture_devices].name), " d%d", devices); /* Store associated card number id */ qsa_capture_device[qsa_capture_devices].cardno = it; /* Check if this device id could play anything */ status = snd_pcm_open(&handle, it, devices, SND_PCM_OPEN_CAPTURE); if (status == EOK) { qsa_capture_device[qsa_capture_devices].deviceno = devices; status = snd_pcm_close(handle); if (status == EOK) { addfn(qsa_capture_device[qsa_capture_devices].name); qsa_capture_devices++; } } else { /* Check if we got end of devices list */ if (status == -ENOENT) { break; } } /* Check if we reached maximum devices count */ if (qsa_capture_devices >= QSA_MAX_DEVICES) { break; } } else { break; } devices++; } while (1); /* Check if we reached maximum devices count */ if (qsa_capture_devices >= QSA_MAX_DEVICES) { break; } } } }
/* Plays a sound with the Advanced Linux Sound Architecture, ALSA. Returns 0 on successful playback, nonzero on error. */ int PlayerALSA(u_int8_t *samples, unsigned int bits, unsigned int channels, unsigned int rate, unsigned int bytes) { int i; /* ALSA is a bit verbose, and it tends to require lots of structures. */ unsigned int alsa_device = 0, alsa_card = 0; char *alsa_card_name; snd_ctl_t *alsa_ctl; snd_ctl_hw_info_t alsa_hw_info; snd_pcm_t *alsa_pcm; snd_pcm_channel_params_t alsa_params; /* Playback status variables. */ unsigned int position; /* Scan for ALSA cards and devices. Each card has an integer ID less than snd_cards(). We scan for the first available card in order to demonstrate ALSA's organization, but we could find the default card and PCM device numbers immediately with the snd_defaults_pcm_card(), snd_defaults_pcm_device() functions. */ alsa_pcm = NULL; for (alsa_card = 0; alsa_card < (unsigned)snd_cards(); alsa_card++) { /* Try to open this card. */ if (snd_ctl_open(&alsa_ctl,alsa_card) < 0) continue; /* Retrieve card info. */ if (snd_ctl_hw_info(alsa_ctl,&alsa_hw_info) < 0) { snd_ctl_close(alsa_ctl); continue; } snd_ctl_close(alsa_ctl); /* Find a suitable device on this card. */ alsa_pcm = NULL; for (alsa_device = 0; alsa_device < alsa_hw_info.pcmdevs; alsa_device++) { if (snd_pcm_open(&alsa_pcm,alsa_card, alsa_device,SND_PCM_OPEN_PLAYBACK) < 0) continue; /* Device successfully opened. */ break; } if (alsa_pcm != NULL) break; } /* Were we able to open a device? */ if (alsa_card == (unsigned)snd_cards()) { printf("ALSA player: unable to find a configured device.\n"); return -1; } /* Print info about the device. */ if (snd_card_get_longname(alsa_card,&alsa_card_name) < 0) alsa_card_name = "(unknown)"; printf("ALSA player: using device %i:%i (%s)\n", alsa_card, alsa_device, alsa_card_name); /* Configure the device for the loaded sound data. */ memset(&alsa_params,0,sizeof (alsa_params)); alsa_params.channel = SND_PCM_CHANNEL_PLAYBACK; /* Use stream mode. In this mode, we don't have to give ALSA complete blocks; we can send it data as we get it. Block mode is needed for mmap() functionality. Unlike OSS, ALSA's mmap() functionality is quite reliable, and easily accessible through library functions. We won't use it here, though; there's no need. */ alsa_params.mode = SND_PCM_MODE_STREAM; alsa_params.format.interleave = 1; /* We'll assume little endian samples. You may wish to use the data in the GNU C Library's endian.h to support other endiannesses. We're ignoring that case for simplicity. */ if (bits == 8) alsa_params.format.format = SND_PCM_SFMT_U8; else if (bits == 16) alsa_params.format.format = SND_PCM_SFMT_S16_LE; else { printf("ALSA player: invalid sample size.\n"); return -1; } alsa_params.format.rate = rate; alsa_params.format.voices = channels; alsa_params.start_mode = SND_PCM_START_DATA; alsa_params.stop_mode = SND_PCM_STOP_ROLLOVER; alsa_params.buf.block.frag_size = 4096; alsa_params.buf.block.frags_min = 1; alsa_params.buf.block.frags_max = 2; if ((i = snd_pcm_plugin_params(alsa_pcm,&alsa_params)) < 0) { printf("ALSA player: unable to set parameters.\n"); snd_pcm_close(alsa_pcm); return -1; } if (snd_pcm_plugin_prepare(alsa_pcm, SND_PCM_CHANNEL_PLAYBACK) < 0) { printf("ALSA player: unable to prepare playback.\n"); snd_pcm_close(alsa_pcm); return -1; } /* Feed the sound data to ALSA. */ position = 0; while (position < bytes) { int written, blocksize; if (bytes-position < 4096) blocksize = bytes-position; else blocksize = 4096; /* Write to the sound device. */ written = snd_pcm_plugin_write(alsa_pcm, &samples[position], blocksize); /* If ALSA can't take any more data right now, it'll return -EAGAIN. If this were sound code for a game, we'd probably just contine the game loop and try to write data the next time around. In a game, you'd probably also want to put the device in nonblocking mode (see the snd_pcm_nonblock_mode() function). */ if (written == -EAGAIN) { /* Waste some time. This keeps us from using 100% CPU. */ usleep(1000); written = 0; } else { if (written < 0) { perror("\nALSA player: error writing to sound device"); snd_pcm_close(alsa_pcm); return -1; } } /* Update the position. */ position += written; /* Print some information. */ WritePlaybackStatus(position, bytes, channels, bits, rate); printf("\r"); fflush(stdout); } printf("\n"); /* Wait until ALSA's internal buffers are empty, then stop playback. This will make sure that the entire sound clip has played. */ snd_pcm_channel_flush(alsa_pcm, SND_PCM_CHANNEL_PLAYBACK); snd_pcm_close(alsa_pcm); return 0; }