Re: [GnomeMeeting-devel-list] ALSA module
- From: Damien Sandras <dsandras seconix com>
- To: gnomemeeting-devel-list gnome org
- Subject: Re: [GnomeMeeting-devel-list] ALSA module
- Date: 14 Aug 2003 18:51:01 +0200
Hello,
Jonita was not ready, so I did a new version before leaving.
/me is crazy
> - In some cases, the audio will drop and the codec will be closed. I
> know why, Ill fix it.
Still present
> - There is still a weird behavior when the open of the device fails.
> That will disappear once people will be using either OSS or ALSA but not
> both at the same time. Julien is working on that.
Not present anymore
> - Too many devices are detected. For example, the microphone of the
> webcam can not be used as a player (only recorder) but it is in the list
> of possible players. That will be fixed.
>
Not present anymore: a webcam microphone can not be used to play sound,
only to record, so it won't be listed as player, only as recorder. Great
no? ;)
PS: The code is still uncleaned, don't look at it, but test it!
--
Damien Sandras <dsandras seconix com>
/*
* sound.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 Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): Loopback feature: Philip Edelbrock <phil netroedge com>.
*
* $Log: sound_alsa.cxx,v $
*/
#include "sound_alsa.h"
#include <ptlib.h>
DECLARE_PLUGIN("ALSA", PDeviceManager::SNDIN | PDeviceManager::SNDOUT);
DECLARE_PLUGIN_SOUNDINPUT(PSoundChannelALSA);
DECLARE_PLUGIN_SOUNDOUTPUT(PSoundChannelALSA);
///////////////////////////////////////////////////////////////////////////////
PAudioDelay::PAudioDelay()
{
firstTime = TRUE;
error = 0;
}
void PAudioDelay::Restart()
{
firstTime = TRUE;
}
BOOL PAudioDelay::Delay(int frameTime)
{
if (firstTime) {
firstTime = FALSE;
previousTime = PTime();
return TRUE;
}
error += frameTime;
PTime now;
PTimeInterval delay = now - previousTime;
error -= (int)delay.GetMilliSeconds();
previousTime = now;
if (error > 0)
#ifdef P_LINUX
usleep(error * 1000);
#else
PThread::Current()->Sleep(error);
#endif
return error <= -frameTime;
//if (headRoom > MAX_HEADROOM)
// PThread::Current()->Sleep(headRoom - MIN_HEADROOM);
}
///////////////////////////////////////////////////////////////////////////////
// declare type for sound handle dictionary
PSound::PSound(unsigned channels,
unsigned samplesPerSecond,
unsigned bitsPerSample,
PINDEX bufferSize,
const BYTE * buffer)
{
encoding = 0;
numChannels = channels;
sampleRate = samplesPerSecond;
sampleSize = bitsPerSample;
SetSize(bufferSize);
if (buffer != NULL)
memcpy(GetPointer(), buffer, bufferSize);
}
PSound::PSound(const PFilePath & filename)
{
encoding = 0;
numChannels = 1;
sampleRate = 8000;
sampleSize = 16;
Load(filename);
}
PSound & PSound::operator=(const PBYTEArray & data)
{
PBYTEArray::operator=(data);
return *this;
}
void PSound::SetFormat(unsigned channels,
unsigned samplesPerSecond,
unsigned bitsPerSample)
{
encoding = 0;
numChannels = channels;
sampleRate = samplesPerSecond;
sampleSize = bitsPerSample;
formatInfo.SetSize(0);
}
BOOL PSound::Load(const PFilePath & /*filename*/)
{
return FALSE;
}
BOOL PSound::Save(const PFilePath & /*filename*/)
{
return FALSE;
}
BOOL PSound::Play()
{
PSoundChannel channel(PSoundChannelALSA::GetDefaultDevice(PSoundChannelALSA::Player),
PSoundChannelALSA::Player);
if (!channel.IsOpen())
return FALSE;
return channel.PlaySound(*this, TRUE);
}
BOOL PSound::PlayFile(const PFilePath & file, BOOL wait)
{
PSoundChannel channel(PSoundChannelALSA::GetDefaultDevice(PSoundChannelALSA::Player),
PSoundChannelALSA::Player);
if (!channel.IsOpen())
return FALSE;
return channel.PlayFile(file, wait);
}
///////////////////////////////////////////////////////////////////////////////
SoundHandleEntry::SoundHandleEntry()
{
handle = -1;
direction = 0;
}
static PStringArray playback_devices;
static PStringArray 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;
buffer_size = 0;
os_handle = NULL;
}
PSoundChannelALSA::~PSoundChannelALSA()
{
Close();
}
PStringArray PSoundChannelALSA::GetDeviceNames (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 = PStringArray ();
}
else {
stream = SND_PCM_STREAM_PLAYBACK;
playback_devices = PStringArray ();
}
snd_ctl_card_info_alloca (&info);
snd_pcm_info_alloca (&pcminfo);
/* No sound card found */
if (snd_card_next (&card) < 0 || card < 0) {
return PStringArray ();
}
while (card >= 0) {
snprintf (card_id, 32, "hw:%d", card);
snd_ctl_open (&handle, card_id, 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) {
if (capture_devices.GetStringsIndex (name) == P_MAX_INDEX)
capture_devices.AppendString (name);
}
else {
if (playback_devices.GetStringsIndex (name) == P_MAX_INDEX)
playback_devices.AppendString (name);
}
free (name);
}
}
snd_ctl_close(handle);
snd_card_next (&card);
}
if (dir == Recorder)
return capture_devices;
else
return playback_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;
PINDEX i = 0;
snd_pcm_stream_t stream;
PTRACE (1, "Will open");
Close();
os_handle = NULL;
if (_dir == Recorder)
stream = SND_PCM_STREAM_CAPTURE;
else
stream = SND_PCM_STREAM_PLAYBACK;
/* Open in NONBLOCK mode */
if ((i = (_dir == Recorder) ? capture_devices.GetStringsIndex (_device) : playback_devices.GetStringsIndex (_device)) != P_MAX_INDEX)
real_device_name = "plughw:" + PString (i);
else {
PTRACE (1, "Device unavailable");
return FALSE;
}
if (snd_pcm_open (&os_handle, real_device_name, stream, 0) < 0) {
PTRACE (1, "Open 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;
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 (os_handle, hw_params,
mSampleRate, 0)) < 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 (buffer_size)
err = snd_pcm_hw_params_set_buffer_size_near (os_handle, hw_params,
buffer_size / frame_bytes);
PTRACE (1, "LALLALA " << err);
if ((err = snd_pcm_hw_params (os_handle, hw_params)) < 0) {
PTRACE (1, "ALSA\tCannot set parameters " <<
snd_strerror (err));
no_error = FALSE;
}
snd_pcm_hw_params_free (hw_params);
if ((err = snd_pcm_prepare (os_handle)) < 0) {
PTRACE (1, "ALSA\tCannot prepare audio interface for use" <<
snd_strerror (err));
no_error = FALSE;
}
snd_pcm_prepare (os_handle);
isInitialised = TRUE;
return no_error;
}
BOOL PSoundChannelALSA::Close()
{
/* if the channel isn't open, do nothing */
if (!os_handle)
return FALSE;
PTRACE (1, "Trying to close");
if (isInitialised)
Abort ();
snd_pcm_close (os_handle);
os_handle = NULL;
return TRUE;
}
BOOL PSoundChannelALSA::Write (const void *buf, PINDEX len)
{
long r = 0;
if (!isInitialised && !Setup())
return FALSE;
/* the number of frames to write is the buffer length
divided by the size of one frame */
while ((r = snd_pcm_writei (os_handle, buf, len / frame_bytes)) < 0) {
PTRACE (1, "Could not write");
if (r == -EPIPE) { /* under-run */
snd_pcm_prepare (os_handle);
} else if (r == -ESTRPIPE) {
while ((r = snd_pcm_resume (os_handle)) == -EAGAIN)
sleep(1); /* wait until the suspend flag is released */
if (r < 0)
snd_pcm_prepare (os_handle);
}
}
PTRACE (1, "Written " << r);
return TRUE;
}
BOOL PSoundChannelALSA::Read (void * buf, PINDEX len)
{
long r = 0;
char *buf2 = (char *) buf;
int pos = 0;
PTRACE (1, "Read with len " << len << " frame_bytes "
<< frame_bytes);
lastReadCount = 0;
if (!isInitialised && !Setup())
return FALSE;
do {
/* the number of frames to read is the buffer length
divided by the size of one frame */
r = snd_pcm_readi (os_handle, (char *) &buf2 [pos], len / frame_bytes);
PTRACE (1, "Read " << r << " at " << pos);
if (r > 0) {
pos += r * frame_bytes;
len -= r * frame_bytes;
lastReadCount += r * frame_bytes;
PTRACE (1, "Still len to read " << len << " at " << pos);
}
else if (r == -EPIPE) { /* under-run */
snd_pcm_prepare (os_handle);
} else if (r == -ESTRPIPE) {
while ((r = snd_pcm_resume (os_handle)) == -EAGAIN)
sleep(1); /* wait until the suspend flag is released */
if (r < 0)
snd_pcm_prepare (os_handle);
}
} while (r >= 1 && len > 0);
PTRACE (1, "Read Done " << r);
return TRUE;
}
BOOL PSoundChannelALSA::SetFormat (unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
if (!os_handle)
return SetErrorValues(NotOpen, EBADF);
/* check parameters */
PAssert((bitsPerSample == 8) || (bitsPerSample == 16), PInvalidParameter);
PAssert(numChannels >= 1 && numChannels <= 2, PInvalidParameter);
mNumChannels = numChannels;
mSampleRate = sampleRate;
mBitsPerSample = bitsPerSample;
/* mark this channel as uninitialised */
isInitialised = FALSE;
return TRUE;
}
// Get the number of channels (mono/stereo) in the sound.
unsigned PSoundChannelALSA::GetChannels() const
{
return mNumChannels;
}
// Get the sample rate in samples per second.
unsigned PSoundChannelALSA::GetSampleRate() const
{
return mSampleRate;
}
// Get the sample size in bits per sample.
unsigned PSoundChannelALSA::GetSampleSize() const
{
return mBitsPerSample;
}
BOOL PSoundChannelALSA::SetBuffers (PINDEX size, PINDEX count)
{
PTRACE (1, "Asked size " << size << " and count " << count);
buffer_size = size * count;
return TRUE;
}
BOOL PSoundChannelALSA::GetBuffers(PINDEX & size, PINDEX & count)
{
return TRUE;
}
BOOL PSoundChannelALSA::PlaySound(const PSound & sound, BOOL wait)
{
return TRUE;
}
BOOL PSoundChannelALSA::PlayFile(const PFilePath & filename, BOOL wait)
{
return TRUE;
}
BOOL PSoundChannelALSA::HasPlayCompleted()
{
return TRUE;
}
BOOL PSoundChannelALSA::WaitForPlayCompletion()
{
return TRUE;
}
BOOL PSoundChannelALSA::RecordSound(PSound & sound)
{
return FALSE;
}
BOOL PSoundChannelALSA::RecordFile(const PFilePath & filename)
{
return FALSE;
}
BOOL PSoundChannelALSA::StartRecording()
{
return TRUE;
}
BOOL PSoundChannelALSA::IsRecordBufferFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::AreAllRecordBuffersFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::WaitForRecordBufferFull()
{
return TRUE;
}
BOOL PSoundChannelALSA::WaitForAllRecordBuffersFull()
{
return FALSE;
}
BOOL PSoundChannelALSA::Abort()
{
PTRACE (1, "ABORT");
if (os_handle)
snd_pcm_drop (os_handle);
return TRUE;
}
BOOL PSoundChannelALSA::SetVolume(unsigned newVal)
{
return TRUE;
}
BOOL PSoundChannelALSA::GetVolume(unsigned &devVol)
{
return TRUE;
}
BOOL PSoundChannelALSA::IsOpen () const
{
PTRACE (1, "IsOpen ");
return (os_handle != NULL);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]