📄 sound_alsa.cxx
字号:
/* * sound_alsa.cxx * * Sound driver implementation. * * Portable Windows Library * * Copyright (c) 1993-1998 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original ALSA Code is * Damien Sandras <dsandras@seconix.com> * * Portions are Copyright (C) 1993 Free Software Foundation, Inc. * All Rights Reserved. * * Contributor(s): / * * $Log: sound_alsa.cxx,v $ * Revision 1.24 2004/11/07 20:23:00 dsandras * Removed erroneous update of lastReadCount in previous commit. * * Revision 1.23 2004/11/07 20:01:32 dsandras * Make sure lastWriteCount is updated. * * Revision 1.22 2004/11/06 16:31:12 dsandras * Removed dictionnary copy. * * Revision 1.21 2004/10/18 11:43:39 dsandras * Use Capture instead of Mic when changing the volume. Use the correct mixer when using the Default device. * * Revision 1.20 2004/10/14 19:30:16 dsandras * Removed DMIX and DSNOOP plugins and added support for DEFAULT as it is the correcti way to do things. * * Revision 1.19 2004/08/30 21:09:41 dsandras * Added DSNOOP plugin support. * * Revision 1.18 2004/05/14 10:15:26 dominance * Fixes direct opening of sound output devices. The list of devices does no longer return NULL in that case. Patch provided by Julien Puydt <julien.puydt@laposte.net>. * * Revision 1.17 2004/04/03 10:33:45 dsandras * Use PStringToOrdinal to store the detected devices, that fixes problems if there is a discontinuity in the succession of soundcard ID's. For example the user has card ID 1 and 3, but not 2. * * Revision 1.16 2004/03/13 12:36:14 dsandras * Added support for DMIX plugin output. * * Revision 1.15 2004/03/04 13:36:13 dsandras * Added check so that ALSA doesn't assert on broken installations. * * Revision 1.14 2004/02/12 09:07:57 csoutheren * Fixed typo in ALSA driver, thanks to Julien Puydt * * Revision 1.13 2004/01/04 20:59:30 dsandras * Use set_rate_near instead of set_rate. * * Revision 1.12 2003/12/28 15:10:35 dsandras * Updated to the new PCM API. * * Revision 1.11 2003/12/18 11:16:41 dominance * Removed the ALSA Abort completely upon Damien's request ;) * * Revision 1.10 2003/12/18 10:38:55 dominance * Removed ALSA Abort as it segfaults in various circumstances. * Fix proposed by Damien Sandras <dsandras@seconix.com>. * * Revision 1.9 2003/12/09 22:47:10 dsandras * Use less aggressive Abort. * * Revision 1.8 2003/12/03 21:48:21 dsandras * Better handling of buffer sizes. Removed unuseful code. * * Revision 1.7 2003/11/25 20:13:48 dsandras * Added #pragma. * * Revision 1.6 2003/11/25 09:58:01 dsandras * Removed Abort call from PlaySound (). * * Revision 1.5 2003/11/25 09:52:07 dsandras * Modified WaitForPlayCompletion so that it uses snd_pcm_drain instead of active waiting. * * Revision 1.4 2003/11/23 22:09:57 dsandras * Removed unuseful stuff and added implementation for functions permitting to play a file or a PSound. * * Revision 1.3 2003/11/14 05:28:47 csoutheren * Updated for new plugin code thanks to Damien and Snark * */#pragma implementation "sound_alsa.h"#include "sound_alsa.h"PCREATE_SOUND_PLUGIN(ALSA, PSoundChannelALSA)static PStringToOrdinal playback_devices;static PStringToOrdinal capture_devices;///////////////////////////////////////////////////////////////////////////////PSoundChannelALSA::PSoundChannelALSA(){ PSoundChannelALSA::Construct();}PSoundChannelALSA::PSoundChannelALSA (const PString &device, Directions dir, unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample){ Construct(); Open (device, dir, numChannels, sampleRate, bitsPerSample);}void PSoundChannelALSA::Construct(){ frame_bytes = 0; period_size = 0; periods = 0; card_nr = 0; os_handle = NULL;}PSoundChannelALSA::~PSoundChannelALSA(){ Close();}void PSoundChannelALSA::UpdateDictionary (Directions dir){ int card = -1, dev = -1; snd_ctl_t *handle = NULL; snd_ctl_card_info_t *info = NULL; snd_pcm_info_t *pcminfo = NULL; snd_pcm_stream_t stream; char *name = NULL; char card_id [32]; if (dir == Recorder) { stream = SND_PCM_STREAM_CAPTURE; capture_devices = PStringToOrdinal (); } else { stream = SND_PCM_STREAM_PLAYBACK; playback_devices = PStringToOrdinal (); } snd_ctl_card_info_alloca (&info); snd_pcm_info_alloca (&pcminfo); /* No sound card found */ if (snd_card_next (&card) < 0 || card < 0) { return; } while (card >= 0) { snprintf (card_id, 32, "hw:%d", card); if (snd_ctl_open (&handle, card_id, 0) == 0) { snd_ctl_card_info (handle, info); while (1) { snd_ctl_pcm_next_device (handle, &dev); if (dev < 0) break; snd_pcm_info_set_device (pcminfo, dev); snd_pcm_info_set_subdevice (pcminfo, 0); snd_pcm_info_set_stream (pcminfo, stream); if (snd_ctl_pcm_info (handle, pcminfo) >= 0) { snd_card_get_name (card, &name); if (dir == Recorder) capture_devices.SetAt (name, card); else playback_devices.SetAt (name, card); free (name); } } } snd_ctl_close(handle); snd_card_next (&card); }}PStringArray PSoundChannelALSA::GetDeviceNames (Directions dir){ PStringArray devices; UpdateDictionary (dir); if (dir == Recorder) { for (PINDEX j = 0 ; j < capture_devices.GetSize () ; j++) devices += capture_devices.GetKeyAt (j); } else { for (PINDEX j = 0 ; j < playback_devices.GetSize () ; j++) devices += playback_devices.GetKeyAt (j); } if (devices.GetSize () > 0) devices += "Default"; return devices;}PString PSoundChannelALSA::GetDefaultDevice(Directions dir){ PStringArray devicenames; devicenames = PSoundChannelALSA::GetDeviceNames (dir); return devicenames[0];}BOOL PSoundChannelALSA::Open (const PString & _device, Directions _dir, unsigned _numChannels, unsigned _sampleRate, unsigned _bitsPerSample){ PString real_device_name; POrdinalKey *i = NULL; snd_pcm_stream_t stream; Close(); os_handle = NULL; if (_dir == Recorder) stream = SND_PCM_STREAM_CAPTURE; else stream = SND_PCM_STREAM_PLAYBACK; /* Open in NONBLOCK mode */ if (_device == "Default") { real_device_name = "default"; card_nr = -2; } else { if ((_dir == Recorder && capture_devices.IsEmpty ()) || (_dir == Player && playback_devices.IsEmpty ())) UpdateDictionary (_dir); i = (_dir == Recorder) ? capture_devices.GetAt (_device) : playback_devices.GetAt (_device); if (i) { real_device_name = "plughw:" + PString (*i); card_nr = *i; } else { PTRACE (1, "ALSA\tDevice not found"); return FALSE; } } if (snd_pcm_open (&os_handle, real_device_name, stream, SND_PCM_NONBLOCK) < 0) { PTRACE (1, "ALSA\tOpen Failed"); return FALSE; } else snd_pcm_nonblock (os_handle, 0); /* save internal parameters */ direction = _dir; device = real_device_name; mNumChannels = _numChannels; mSampleRate = _sampleRate; mBitsPerSample = _bitsPerSample; isInitialised = FALSE; PTRACE (1, "ALSA\tDevice " << real_device_name << " Opened"); return TRUE;}BOOL PSoundChannelALSA::Setup(){ snd_pcm_hw_params_t *hw_params = NULL; snd_pcm_uframes_t buffer_size = 0; int err = 0; enum _snd_pcm_format val = SND_PCM_FORMAT_UNKNOWN; BOOL no_error = TRUE; if (os_handle == NULL) { PTRACE(6, "ALSA\tSkipping setup of " << device << " as not open"); return FALSE; } if (isInitialised) { PTRACE(6, "ALSA\tSkipping setup of " << device << " as instance already initialised"); return TRUE; }#if PBYTE_ORDER == PLITTLE_ENDIAN val = (mBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8;#else val = (mBitsPerSample == 16) ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_U8;#endif frame_bytes = (mNumChannels * (snd_pcm_format_width (val) / 8)); snd_pcm_hw_params_alloca (&hw_params); if ((err = snd_pcm_hw_params_any (os_handle, hw_params)) < 0) { PTRACE (1, "ALSA\tCannot initialize hardware parameter structure " << snd_strerror (err)); no_error = FALSE; } if ((err = snd_pcm_hw_params_set_access (os_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { PTRACE (1, "ALSA\tCannot set access type " << snd_strerror (err)); no_error = FALSE; } if ((err = snd_pcm_hw_params_set_format (os_handle, hw_params, val)) < 0) { PTRACE (1, "ALSA\tCannot set sample format " << snd_strerror (err)); no_error = FALSE; } if ((err = snd_pcm_hw_params_set_rate_near (os_handle, hw_params, &mSampleRate, NULL)) < 0) { PTRACE (1, "ALSA\tCannot set sample rate " << snd_strerror (err)); no_error = FALSE; } if ((err = snd_pcm_hw_params_set_channels (os_handle, hw_params, mNumChannels)) < 0) { PTRACE (1, "ALSA\tCannot set channel count " << snd_strerror (err)); no_error = FALSE; } // Ignore errors here if (periods && period_size) { if ((err = snd_pcm_hw_params_set_period_size_near (os_handle, hw_params, &period_size, 0)) < 0) PTRACE (1, "ALSA\tCannot set period size " << snd_strerror (err)); if ((err = snd_pcm_hw_params_set_periods_near (os_handle, hw_params, &periods, 0)) < 0) PTRACE (1, "ALSA\tCannot set number of periods " << snd_strerror (err)); buffer_size = periods*period_size/frame_bytes; if ((err = (int) snd_pcm_hw_params_set_buffer_size_near (os_handle, hw_params, &buffer_size)) < 0) PTRACE (1, "ALSA\tCannot set buffer size " << snd_strerror (err)); } if ((err = snd_pcm_hw_params (os_handle, hw_params)) < 0) { PTRACE (1, "ALSA\tCannot set parameters " << snd_strerror (err)); no_error = FALSE; } isInitialised = TRUE; return no_error;}BOOL PSoundChannelALSA::Close(){ PWaitAndSignal m(device_mutex); /* if the channel isn't open, do nothing */ if (!os_handle) return FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -