Re: Third time: Unlink PCM resource ?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Jaroslav Kysela wrote:
> The question is if it's not better to free also allocated memory 
> associated to the pcm handle in child process - so snd_pcm_close() 
> call in child is not a bad idea.
>   
Okay, while you both have been discussing some internals, which I didn't
try yet, I created a demonstration program to illustrate the mixed
semantics of the 'sharing' versus 'not sharing' behavior of the dsp
devices. In the example the parent thread will play a sound, then the
child closes the dsp, which affects the playback in the parent (it
either hangs or skips all remaining samples). From this one would
conclude that the pcm device is fully shared between the two processes
(semantics I can live with). However, when the parent then tries to
reopen the device it cannot do so because the device is still 'busy'.

As a solution, a call to an unlink function would maybe solve it but it
is indicative of a deeper unclarity of how the semantics of shared DSP
devices should be. I would propose to let alsa act as a true filehandle.
In such a scenario a close in the child should not affect the playback
in the parent since the parent still has the descriptor open. Please
have a look at the attached code. It is 256 lines :-) The problem is
documented in the main routine.

With kind regards,

Werner,-

-- 
Dr. Werner Van Belle
http://werner.yellowcouch.org/

using namespace std;
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <stdarg.h>
#include <alsa/asoundlib.h>
#include "memory.h"
#include <iostream>

typedef unsigned int unsigned4;
typedef signed long long signed8;
typedef signed short signed2;
snd_pcm_t *dsp;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
unsigned4 * buffer;
unsigned4 filled;
int        arg_latency;
char *     arg_dev;
const int err_dsp=1;
const int err_none=0;

void Info(const char* script, ...)
{
  char toexecute[1024];
  va_list ap;
  va_start(ap,script);
  vsnprintf(toexecute,1024,script,ap);
  va_end(ap);
  cout << toexecute << '\n';
  fflush(stdout);
};

void dsp_write(unsigned4 *value)
{
  int err = 0;
  while(!err)
    {
      err = snd_pcm_writei(dsp,value,1);
    }
  assert(err!=-EAGAIN);
  assert(err!=-ESTRPIPE);
  if (err==-EPIPE)
    {
      Info("underrun occured...");
      err = snd_pcm_prepare(dsp);
      if (err < 0)
	Info("cant recover from underrun: %s",snd_strerror(err));
      return;
    }
}

int dsp_open()
{
  int err;
  unsigned int buffer_time, period_time;
  snd_pcm_hw_params_t *hparams;
  snd_pcm_sw_params_t *sparams;
  snd_output_t * output = NULL;
  err = snd_output_stdio_attach(&output,stdout,0);
  if (err < 0 )
    {
      Info("attaching to stdio didn't succeed: %s",snd_strerror(err));
      return err_dsp;
    }
  err = snd_pcm_open(&dsp, arg_dev, SND_PCM_STREAM_PLAYBACK, 0);
  if (err < 0)
    {
      Info("opening alsa device failed : %s",snd_strerror(err));
      return err_dsp;
    }
  
  snd_pcm_sw_params_alloca(&sparams);
  snd_pcm_hw_params_alloca(&hparams);
  
  err = snd_pcm_hw_params_any(dsp, hparams);
  if (err<0)
    {
      Info("Broken configuration file for pcm : %s",snd_strerror(err));
      return err_dsp;
    }  
  err = snd_pcm_hw_params_set_access(dsp, hparams, SND_PCM_ACCESS_RW_INTERLEAVED);
  if (err<0)
    {
      Info("Unable to set interleaved access to pcm device: %s",snd_strerror(err));
      return err_dsp;
    }
  err = snd_pcm_hw_params_set_format(dsp, hparams, SND_PCM_FORMAT_S16);
  if (err<0)
    {
      Info("Unable to set sample format to standard 16 bit: %s",snd_strerror(err));
      return err_dsp;
    }
  err = snd_pcm_hw_params_set_channels(dsp, hparams, 2);
  if (err < 0)
    {
      Info("setting dsp to 2 channels failed : %s",snd_strerror(err));
      return err_dsp;
    }
  
  unsigned int q = 44100;
  err = snd_pcm_hw_params_set_rate_near(dsp, hparams, &q, 0);
  if (err < 0)
    {
      Info("setting dsp speed (%d) failed",q);
      return err_dsp;
    }
  if (q != 44100)
    Info("setting dsp speed (%d) failed, resulting rate = %d ",44100, q);

  period_time = arg_latency * 1000;
  buffer_time = period_time *2;
  {
    unsigned int t = buffer_time;
    int dir = 1;
    err = snd_pcm_hw_params_set_buffer_time_near(dsp,hparams,&t,&dir);
    if (err<0)
      {
	Info("dsp-alsa: Impossible to set pcm buffer time to %i (%i): %s", buffer_time,t,snd_strerror(err));
	return err_dsp;
      }
    
    err = snd_pcm_hw_params_get_buffer_size(hparams,&buffer_size);
    if (err<0)
      {
	Info("     impossible to obtain buffer size: %s",snd_strerror(err));
	return err_dsp;
      }
    
    t = period_time;
    err = snd_pcm_hw_params_set_period_time_near(dsp, hparams,&t,&dir);
    if (err<0)
      {
	Info("     impossible to set pcm period time to %i (%i): %s", period_time,t,snd_strerror(err));
	return err_dsp;
      }
    err = snd_pcm_hw_params_get_period_size(hparams,&period_size,&dir);
    if (err<0)
      {
	Info("     ompossible to obtain period data size: %s",snd_strerror(err));
	return err_dsp;
      }
    if (period_size*2 - 1>buffer_size)
      {
	Info("The alsa driver cannot allocate sufficiently large buffers due to the "
	      "large requested latency. (playbuffer size   = %li, "
	      "total buffer size = %li). Try decreasing the latency.",period_size,buffer_size);
	return err_dsp;
      }
    dir = 0;
  }
  err = snd_pcm_hw_params(dsp,hparams);
  if (err < 0)
    {
      Info("unable to set hw parameters for pcm: %s",snd_strerror(err));
      return err_dsp;
    }
  
  err = snd_pcm_sw_params_current(dsp,sparams);
  if (err < 0)
    {
      Info("unable to determine sw parameters for pcm: %s",snd_strerror(err));
      return err_dsp;
    } 
  err = snd_pcm_sw_params_set_start_threshold(dsp,sparams,buffer_size);
  if (err < 0)
    {
      Info("unable to set start to treshold mode: %s",snd_strerror(err));
      return err_dsp;
    }
  err = snd_pcm_sw_params_set_avail_min(dsp,sparams,period_size);
  if (err < 0 )
    {
      Info("unable to set minimum start size: %s",snd_strerror(err));
      return err_dsp;
    }
  err = snd_pcm_sw_params(dsp,sparams);
  if (err < 0)
    {
      Info("unable to set software parameters: %s",snd_strerror(err));
      return err_dsp;
    }
  snd_pcm_dump(dsp,output);
  buffer = (unsigned4*)malloc(period_size*sizeof(unsigned4));
  return err_none;
}

void dsp_close()
{
  int err = snd_pcm_close(dsp);
  if (err < 0)
    Info("cant close pcm device: %s",snd_strerror(err));
  dsp=0;
}

void stream_wave()
{
  for(int t=10; t>=0; t--)
    {
      cerr << "Parent plays (" << t << "s left)\n";
      for(int i = 0 ; i < 44100; i++)
	{
	  float x=i;
	  float y=16384.*sin(x*2*M_PI*220./44100.)/4;
	  struct {signed2 l; signed2 r;} lr;
	  lr.l=y;
	  lr.r=y;
	  dsp_write((unsigned4*)&lr);
	}
    }
}

/**
 * This example first shows that a close in the child will 
 * affect the playback in the parent. Sometimes the parents
 * just hangs in the write call, sometimes it just skips
 * all samples.
 * 
 * However, the device in the parent is actually not 
 * closed at all since we cannot reopen it.
 */
int main (int argc, char* argv[])
{
  arg_dev = "hw:0";
  dsp = NULL;
  filled = 0;
  buffer_size = 0;
  period_size = 0;
  arg_latency = 150;
  // first open the DSP device.
  Info("Opening DSP Device in parent thread");
  dsp_open();
  int childpid=fork(); assert(childpid>-1);
  if (childpid)
    {
      // the parent thread now waits two seconds and the closes the device
      Info("Parent thread playing");
      stream_wave();
      Info("Parent tries to reopen the device");
      dsp_open();
      stream_wave();
    }
  else
    {
      sleep(5);
      Info("Child closes the dsp device");
      dsp_close();
    }
}

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux