📄 alsa_a.c
字号:
/* -*- c-file-style: "gnu" -*- TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp> Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi> ALSA 0.[56] support by Katsuhiro Ueno <katsu@blue.sky.or.jp> rewritten by Takashi Iwai <tiwai@suse.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA alsa_a.c Functions to play sound on the ALSA audio driver*/#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#ifndef NO_STRING_H#include <string.h>#else#include <strings.h>#endif/*ALSA header file*/#if HAVE_ALSA_ASOUNDLIB_H#define ALSA_PCM_OLD_HW_PARAMS_API#define ALSA_PCM_OLD_SW_PARAMS_API#include <alsa/asoundlib.h>#else#include <sys/asoundlib.h>#endif#if SND_LIB_MAJOR > 0#define ALSA_LIB 9#elif defined(SND_LIB_MINOR)#define ALSA_LIB SND_LIB_MINOR#else#define ALSA_LIB 3#endif#if ALSA_LIB < 4typedef void snd_pcm_t;#endif#include "timidity.h"#include "common.h"#include "output.h"#include "controls.h"#include "timer.h"#include "instrum.h"#include "playmidi.h"#include "miditrace.h"static int open_output(void); /* 0=success, 1=warning, -1=fatal error */static void close_output(void);static int output_data(char *buf, int32 nbytes);static int acntl(int request, void *arg);#if ALSA_LIB >= 5static int detect(void);#endif/* export the playback mode */#define dpm alsa_play_modePlayMode dpm = { DEFAULT_RATE, PE_16BIT|PE_SIGNED, PF_PCM_STREAM|PF_CAN_TRACE|PF_BUFF_FRAGM_OPT, -1, {0}, /* default: get all the buffer fragments you can */ "ALSA pcm device", 's', "", /* here leave it empty so that the pcm device name can be given * via command line option. */ open_output, close_output, output_data, acntl,#if ALSA_LIB >= 5 detect#endif};/*************************************************************************//* We currently only honor the PE_MONO bit, the sample rate, and the number of buffer fragments. We try 16-bit signed data first, and then 8-bit unsigned if it fails. If you have a sound device that can't handle either, let me know. *//*ALSA PCM handler*/static snd_pcm_t* handle = NULL;#if ALSA_LIB <= 5static int card = 0;static int device = 0;#endifstatic int total_bytes = -1;static int frag_size = 0;static int sample_shift = 0;static int output_counter;#if ALSA_LIB > 5static char *alsa_device_name(void){ if (dpm.name && *dpm.name) return dpm.name; else return "alsa pcm";}#elsestatic char *alsa_device_name(void){ static char name[32]; sprintf(name, "card%d/device%d", card, device); return name;}#endifstatic void error_report (int snd_error){ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", alsa_device_name(), snd_strerror (snd_error));}#if ALSA_LIB < 6/*return value == 0 sucess == -1 fails */static int check_sound_cards (int* card__, int* device__, const int32 extra_param[5]){ /*Search sound cards*/ struct snd_ctl_hw_info ctl_hw_info; snd_pcm_info_t pcm_info; snd_ctl_t* ctl_handle; const char* env_sound_card = getenv ("TIMIDITY_SOUND_CARD"); const char* env_pcm_device = getenv ("TIMIDITY_PCM_DEVICE"); int tmp; /*specify card*/ *card__ = 0; if (env_sound_card != NULL) *card__ = atoi (env_sound_card); /*specify device*/ *device__ = 0; if (env_pcm_device != NULL) *device__ = atoi (env_pcm_device); tmp = snd_cards (); if (tmp == 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "No sound card found."); return -1; } if (tmp < 0) { error_report (tmp); return -1; } if (*card__ < 0 || *card__ >= tmp) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "There is %d sound cards." " %d is invalid sound card. assuming 0.", tmp, *card__); *card__ = 0; } tmp = snd_ctl_open (&ctl_handle, *card__); if (tmp < 0) { error_report (tmp); return -1; } /*check whether sound card has pcm device(s)*/ tmp = snd_ctl_hw_info (ctl_handle, & ctl_hw_info); if (ctl_hw_info.pcmdevs == 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%d-th sound card(%s) has no pcm device", ctl_hw_info.longname, *card__); snd_ctl_close (ctl_handle); return -1; } if (*device__ < 0 || *device__ >= ctl_hw_info.pcmdevs) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%d-th sound cards(%s) has %d pcm device(s)." " %d is invalid pcm device. assuming 0.", *card__, ctl_hw_info.longname, ctl_hw_info.pcmdevs, *device__); *device__ = 0; if (ctl_hw_info.pcmdevs == 0) {/*sound card has no pcm devices*/ snd_ctl_close (ctl_handle); return -1; } } /*check whether pcm device is able to playback*/ tmp = snd_ctl_pcm_info(ctl_handle, *device__, &pcm_info); if (tmp < 0) { error_report (tmp); snd_ctl_close (ctl_handle); return -1; } if ((pcm_info.flags & SND_PCM_INFO_PLAYBACK) == 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%d-th sound cards(%s), device=%d, " "type=%d, flags=%d, id=%s, name=%s," " does not support playback", *card__, ctl_hw_info.longname, ctl_hw_info.pcmdevs, pcm_info.type, pcm_info.flags, pcm_info.id, pcm_info.name); snd_ctl_close (ctl_handle); return -1; } tmp = snd_ctl_close (ctl_handle); if (tmp < 0) { error_report (tmp); return -1; } return 0;}#endif#if ALSA_LIB > 5/*================================================================ * ALSA API version 0.9.x *================================================================*/static char *get_pcm_name(void){ char *name; if (dpm.name && *dpm.name) return dpm.name; name = getenv("TIMIDITY_PCM_NAME"); if (! name || ! *name) name = "default"; return name;}static void error_handle(const char *file, int line, const char *func, int err, const char *fmt, ...){}static int detect(void){ snd_pcm_t *pcm; snd_lib_error_set_handler(error_handle); if (snd_pcm_open(&pcm, get_pcm_name(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return 0; snd_pcm_close(pcm); return 1; /* found */}/*return value == 0 sucess == 1 warning == -1 fails */static int open_output(void){ int orig_rate = dpm.rate; int ret_val = 0; int tmp, frags, r, pfds; int rate; snd_pcm_hw_params_t *pinfo; snd_pcm_sw_params_t *swpinfo; dpm.name = get_pcm_name(); snd_lib_error_set_handler(NULL); tmp = snd_pcm_open(&handle, dpm.name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); /* avoid blocking by open */ if (tmp < 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Can't open pcm device '%s'.", dpm.name); return -1; } snd_pcm_nonblock(handle, 0); /* set back to blocking mode */ snd_pcm_hw_params_alloca(&pinfo); snd_pcm_sw_params_alloca(&swpinfo); if (snd_pcm_hw_params_any(handle, pinfo) < 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "ALSA pcm '%s' can't initialize hw_params", alsa_device_name()); snd_pcm_close(handle); return -1; }#ifdef LITTLE_ENDIAN#define S16_FORMAT SND_PCM_FORMAT_S16_LE#define U16_FORMAT SND_PCM_FORMAT_U16_LE#else#define S16_FORMAT SND_PCM_FORMAT_S16_BE#define U16_FORMAT SND_PCM_FORMAT_U16_LE#endif dpm.encoding &= ~(PE_ULAW|PE_ALAW|PE_BYTESWAP); /*check sample bit*/ if (snd_pcm_hw_params_test_format(handle, pinfo, S16_FORMAT) < 0 && snd_pcm_hw_params_test_format(handle, pinfo, U16_FORMAT) < 0) dpm.encoding &= ~PE_16BIT; /*force 8bit samples*/ if (snd_pcm_hw_params_test_format(handle, pinfo, SND_PCM_FORMAT_U8) < 0 && snd_pcm_hw_params_test_format(handle, pinfo, SND_PCM_FORMAT_S8) < 0) dpm.encoding |= PE_16BIT; /*force 16bit samples*/ /*check format*/ if (dpm.encoding & PE_16BIT) { /*16bit*/ if (snd_pcm_hw_params_set_format(handle, pinfo, S16_FORMAT) == 0) dpm.encoding |= PE_SIGNED; else if (snd_pcm_hw_params_set_format(handle, pinfo, U16_FORMAT) == 0) dpm.encoding &= ~PE_SIGNED; else { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "ALSA pcm '%s' doesn't support 16 bit sample width", alsa_device_name()); snd_pcm_close(handle); return -1; } } else { /*8bit*/ if (snd_pcm_hw_params_set_format(handle, pinfo, SND_PCM_FORMAT_U8) == 0) dpm.encoding &= ~PE_SIGNED; else if (snd_pcm_hw_params_set_format(handle, pinfo, SND_PCM_FORMAT_S8) == 0) dpm.encoding |= PE_SIGNED; else { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "ALSA pcm '%s' doesn't support 8 bit sample width", alsa_device_name()); snd_pcm_close(handle); return -1; } } if (snd_pcm_hw_params_set_access(handle, pinfo, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "ALSA pcm '%s' doesn't support interleaved data", alsa_device_name()); snd_pcm_close(handle); return -1; } /*check rate*/ r = snd_pcm_hw_params_get_rate_min(pinfo, NULL); if (r >= 0 && r > dpm.rate) { dpm.rate = r; ret_val = 1; } r = snd_pcm_hw_params_get_rate_max(pinfo, NULL); if (r >= 0 && r < dpm.rate) { dpm.rate = r; ret_val = 1; } if ((rate = snd_pcm_hw_params_set_rate_near(handle, pinfo, dpm.rate, 0)) < 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "ALSA pcm '%s' can't set rate %d", alsa_device_name(), dpm.rate); snd_pcm_close(handle); return -1; } /*check channels*/ if (dpm.encoding & PE_MONO) { if (snd_pcm_hw_params_test_channels(handle, pinfo, 1) < 0) dpm.encoding &= ~PE_MONO; } else { if (snd_pcm_hw_params_test_channels(handle, pinfo, 2) < 0) dpm.encoding |= PE_MONO; } if (dpm.encoding & PE_MONO) { if (snd_pcm_hw_params_set_channels(handle, pinfo, 1) < 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "ALSA pcm '%s' can't set mono channel", alsa_device_name()); snd_pcm_close(handle); return -1; } } else { if (snd_pcm_hw_params_set_channels(handle, pinfo, 2) < 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "ALSA pcm '%s' can't set stereo channels", alsa_device_name()); snd_pcm_close(handle); return -1; } } sample_shift = 0; if (!(dpm.encoding & PE_MONO)) sample_shift++; if (dpm.encoding & PE_16BIT) sample_shift++; /* Set buffer fragment size (in extra_param[1]) */ if (dpm.extra_param[1] != 0) frag_size = dpm.extra_param[1]; else frag_size = audio_buffer_size << sample_shift; /* Set buffer fragments (in extra_param[0]) */ if (dpm.extra_param[0] == 0) frags = 4; else frags = dpm.extra_param[0];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -