Re: [GnomeMeeting-devel-list] ALSA module



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]