Jaroslav Kysela wrote:

>On Wed, 12 Dec 2001, Tim Goetze wrote:
>
>> Abramo Bagnara wrote:
>>
>> >Tim Goetze wrote:
>> >>
>> >> >>but sometimes i also get inexplicable corruption within ordinary
>> >> >>dynamically allocated memory of the process before it exits.
>> >> >>
>> >> >>i have spent considerable time on verifying that these are indeed
>> >> >>caused by changing the period_size, and not by my own code.
>> >> >
>> >> >i would be too certain of this conclusion. i spent many a long night
>> >> >and day and night tracking down places in the audioengine code that
>> >> >had overrun the mmapped areas. i was only ever to track things down by
>> >> >generating huge log files that detailed every byte i wrote, and then
>> >> >use perl and awk to digest them.
>> >> >
>> >> >i'm not saying its not a problem in ALSA, only that its unlikely at
>> >> >this point. what have you done to ensure that its not your code? do
>> >> >you completely avoid writing data to the mapped areas?
>> >> >
>> >>
>> >> yes, exactly that. i just did
>> >>   n * (open, configure, m * (poll, mmap_begin, mmap_commit), close)
>> >> and nothing more.
>> >
>> >You're missing avail_update: it's *mandatory*.
>> >
>> >> i ran this twice, one time with the usual configure, and one time
>> >> with f/c hardcoded to 64 in the hw setup so my code in all places believes
>> >> it's going 2048, 1024, 512, ... -- not that it matters because i don't
>> >> process any data. the one that's doing the 'real configure' crashes.
>>
>> yes, i did avail_update. sorry for not quoting all the 12111 lines of code
>> involved.
>>
>> and before i receive another message like this: i also did snd_pcm_link,
>> _unlink, _start and _drop.
>
>I think that it would be best for us to see some C code invoking the bad
>behaviour to debug the alsa-lib's code.

so thinks me too. i've isolated the audio stream code and attached it.

compile with 'gcc -o a.out main.cc -lasound -L/usr/lib', and run with
for example
$ ./a.out hw:0,0 2048

it will try to configure both streams on hw:0,0 for period_size of
2048, 1024 ... 64. on the awe it crashes when reaching 512.

this code does not even link, start, drop or unlink - just configure
the streams. it segfaults here before exit() even if i only configured
them one time.

tim
#include <unistd.h>
#include <stdio.h>

#include <alsa/asoundlib.h>
#include <exception>

#define D_SAMPLE_TO_INT(in,out)  \
                __asm__ __volatile__ ("fistpl %0" : "=m" (out) : "t" (in) : "st");

typedef float                                                           d_sample; 
typedef char                                                            int8;
typedef short                                                           int16;
typedef int                                                                     int32;
typedef long long                                               int64;
        
class blewit : public std::exception
{
        public:
                char excuse [200];

                blewit (const char * s = ""); 

                blewit & operator << (const char * s);
                blewit & operator << (int i);
                blewit & operator << (double d);
                blewit & operator << (void * v);

                virtual const char * what() const {
                        return excuse;
                }
};

#define msg() _msg (__FILE__, __LINE__)

class _msg
{
        public:
                static FILE * file;
                FILE * f;

                static void init (const char * path = 0);
                
                _msg (FILE * _f); 
                _msg (char * __file__ = 0, int __line__ = -1); 
         
                ~_msg() { 
                        if (f == file) fputc ('\n', f); fflush (f); 
                }

                operator FILE * () { 
                        return f; 
                }

                _msg & operator << (const char * s) 
                        {
                                if (!s) s = "(char *) 0";
                                fputs (s, f);  return *this;
                        }
                
                _msg & operator << (unsigned long long ll) 
                        {
                                fprintf (f, "%llu ", ll); return *this;
                        }
                
                _msg & operator << (long l) 
                        {
                                fprintf (f, "%ld ", l); return *this;
                        }
                
                _msg & operator << (unsigned long l) 
                        {
                                fprintf (f, "%lu ", l); return *this;
                        }
                
                _msg & operator << (int i) 
                        {
                                fprintf (f, "%d ", i); return *this;
                        }
                
                _msg & operator << (unsigned int i) 
                        {
                                fprintf (f, "%u ", i); return *this;
                        }
                
                _msg & operator << (const void * p) 
                        {
                                fprintf (f, "<%p> ", p); return *this;
                        }
                
                _msg & operator << (double d) 
                        {
                                fprintf (f, "%.5f ", d); 
                                return *this;
                        }
};

FILE * _msg::file = stderr;

_msg::_msg (FILE * _f)
{
        f = _f;
}

_msg::_msg (char * __file__ = 0, int __line__ = -1)
{ 
        f = file;       
        
        if (__file__ && __line__ >= 0)
                fprintf (f, "%s:%d:", __file__, __line__);
}

void 
_msg::init (const char * path)
{
        file = path ? fopen (path, "w") : 0;

        if (!file)
                file = stderr;
        else if (0)
                fcntl (
                        fileno (file), F_SETFL, 
                        fcntl (fileno (file), F_GETFL) | O_NONBLOCK
                );
}

blewit::blewit (const char * s)
{
        strcpy (excuse, s);
}

blewit & 
blewit::operator << (const char * s)
{
        strcpy (excuse + strlen (excuse), s);
        return *this;
}

blewit & 
blewit::operator << (int i)
{
        sprintf (excuse + strlen (excuse), "%d", i);
        return *this;
}

blewit & 
blewit::operator << (double d)
{
        sprintf (excuse + strlen (excuse), "%.4f", d);
        return *this;
}

blewit & 
blewit::operator << (void * v)
{
        sprintf (excuse + strlen (excuse), "<%p>", v);
        return *this;
}

enum {
        Input = 0,
        Output = 1
};

class AudioStream 
{
        public:
                snd_pcm_t       *                                       handle;
                snd_pcm_hw_params_t * hw_params;
                snd_pcm_sw_params_t * sw_params;
                
                const snd_pcm_channel_area_t *
                                                                                       
                 areas;

        public:
                snd_pcm_sframes_t                       available, 
                                                                                       
                 delay;
                snd_pcm_uframes_t                       offset,
                                                                                       
                 frames;

                int frames_per_cycle, sample_rate;

                int             type; /* Port::Input || Port::Output */
                
                bool    interleaved,
                                        configured;
                                        
                uint    bytes_per_sample;

                int             channels;
                                        
                AudioStream();
                ~AudioStream();

                void open (char * _name, int _type);

                /* Plugin::* */
                virtual void    configure (uint sample_rate, uint frames_per_cycle);
                virtual void    process (uint _samples);

        private:
                void get_pollfd (struct pollfd * pfd) {
                        snd_pcm_poll_descriptors (handle, pfd, 1);
                }

                bool    link (AudioStream *);
                bool    unlink();
                
                bool    prepare();
                bool    start();
                bool    stop();
                
                void    complete_silence (uint frames);

                /* returns negative on xrun. */
                int             frames_available();
                
                /* returns number of frames accessible */
                uint    begin_cycle (uint _frames);
                
                /* commits */
                bool    end_cycle (uint _frames);
                
        protected:
                void    silence (int channel);

                void    stream_in (int channel, d_sample *);
                void    stream_out (int channel, d_sample *);

                /* called by configure to initialize the ports. */
                void    setup_ports();
};
                
AudioStream::AudioStream()
{
        type = -1;
        frames = 0;
        handle = 0;

        int error = 
                snd_pcm_hw_params_malloc (&hw_params) |
                snd_pcm_sw_params_malloc (&sw_params);
        
        if (error)
                throw blewit ("cannot create a pcm_device_stream since we're out of 
memory.");
}

AudioStream::~AudioStream()
{
        if (handle)
                snd_pcm_close (handle);

        snd_pcm_hw_params_free (hw_params);
        snd_pcm_sw_params_free (sw_params);
}

void
AudioStream::open (char * _name, int _type)
{
        type = _type;

        int error;

        if (type == Input)
                error = snd_pcm_open (&handle, _name, SND_PCM_STREAM_CAPTURE, 0); 
        else
                error = snd_pcm_open (&handle, _name, SND_PCM_STREAM_PLAYBACK, 0); 
        
        if (error)
        {
                msg() << "cannot open " << _name
                                        << " " << -error << ":" << snd_strerror 
(error) << ".";
                throw blewit() << "snd_pcm_open failed.";
        }
}

bool
AudioStream::link (AudioStream * other)
{
        int error = snd_pcm_link (handle, other->handle);

        if (error == 0)
                return true;
        
        msg()   << " cannot link because " 
                                << -error << ":" << snd_strerror (error) << ".";
        
        /* already means that we haven't failed in a way. */
        return error == -EALREADY || false;
}

bool
AudioStream::unlink()
{
        int error = snd_pcm_unlink (handle);

        if (error == 0)
                return true;
        
        msg()   << " cannot unlink because " 
                                << -error << ":" << snd_strerror (error) << ".";
        
        /* already means that we haven't failed in a way. */
        return error == -EALREADY || false;
}

bool
AudioStream::prepare()
{
        if (!configured)
        {
                msg() << " is not configured yet.";
                return false;
        }       

        int error = snd_pcm_prepare (handle);
        if (error == 0)
                return true;
        
        msg() << " cannot prepare because " 
                                << -error << ":" << snd_strerror (error) << ".";

        return false;
}

bool
AudioStream::start()
{
        int error = snd_pcm_start (handle);
        if (error == 0)
                return true;
        
        msg() << " could not be started because "
                                << -error << ":" << snd_strerror (error) << ".";

        return false;
}

bool
AudioStream::stop()
{
        int error = snd_pcm_drop (handle);
        if (error == 0)
                return true;
        
        msg() << "could not stop "
                                << "because " 
                                << -error << ":" << snd_strerror (error) << ".";

        return false;
}

int
AudioStream::frames_available()
{
        snd_pcm_sframes_t a = snd_pcm_avail_update (handle);
        snd_pcm_delay (handle, (snd_pcm_sframes_t *) &delay);

        if (a >= 0)
                return (int) (available = a);
        
        int xrun = abs (delay);
        msg() << xrun << "samples " 
                                << (char *) (type == Input ? "over" : "under") 
                                << "run.";
        return -xrun;
}

uint
AudioStream::begin_cycle (uint _frames)
{
        frames = _frames;

        areas = 0;

        int error = 
                snd_pcm_mmap_begin (
                        handle, &areas, 
                        (snd_pcm_uframes_t *) &offset, 
                        (snd_pcm_uframes_t *) &frames
                );

        // msg() << "::begin_cycle = " << frames;

        if (error == 0)
                return frames;
                
        msg() << "mmap_begin failed"
                                << " because " << snd_strerror (error);
        return 0;
}

bool
AudioStream::end_cycle (uint _frames)
{
        int error = snd_pcm_mmap_commit (handle, offset, _frames);

        // msg() << "::end_cycle = " << _frames;

        /* remember that the cycle has ended. */
        frames = 0;
        areas = 0;

        /* the ALSA doc says it returns 0 on success, but >= 0 is more like it. */
        if (error >= 0)
                return true;
        
        msg() << "cannot mmap_commit"
                                << " because " << snd_strerror (error);
        return false;
}

/* Plugin::process for an AudioStream. Note that we inherently assume
 * that uint samples is the same as our available frames count. */
void
AudioStream::process (uint samples)
{
        if (samples != frames)
                throw blewit() << "number of frames to process mismatch.";

        end_cycle (frames);
}

/* silences *all* channels. could be optimized by checking for adjacency
 * of channel buffers. later. */
void
AudioStream::complete_silence (uint frames)
{
        for (int i = 0; i < channels; i++)
                silence (i);
}

/* silences this channel. */
void
AudioStream::silence (int channel)
{
        if (!frames) 
                throw blewit() << "ringbuffer not open.";

        const snd_pcm_channel_area_t & area = areas[channel];
        
        int8 * ptr = (int8 *) area.addr + ((area.first + area.step * offset) >> 3);

        uint to_process = frames;
        int step = area.step >> 3;

        if (bytes_per_sample == 4)
        {
                while (to_process--)
                {
                        *((int32 *) ptr) = 0;
                        ptr += step;
                }
        }
        else if (bytes_per_sample == 2)
        {
                while (to_process--)
                {
                        *((int16 *) ptr) = 0;
                        ptr += step;
                }
        }
        else if (bytes_per_sample == 1)
        {
                while (to_process--)
                {
                        *ptr = 0;
                        ptr += step;
                }
        }
}


/* streams audio data into the mmap buffer */
void
AudioStream::stream_in (int channel, d_sample * buffer)
{
        if (!frames) 
                throw blewit() << "ringbuffer not open.";

        const snd_pcm_channel_area_t & area = areas[channel];
        
        int8 * ptr = (int8 *) area.addr + ((area.first + area.step * offset) >> 3);

        int step = area.step >> 3;

        uint to_process = frames;

        if (bytes_per_sample == 4)
        {
                while (to_process--)
                {
                        D_SAMPLE_TO_INT (0x7FFFFFFF * (*buffer), *((int32 *) ptr));
                                        
                        buffer++;
                        ptr += step;
                }
        }
        else if (bytes_per_sample == 2)
        {
                while (to_process--)
                {
                        *((int16 *) ptr) = (int16) (0x7FFF * (*buffer));
                                        
                        buffer++;
                        ptr += step;
                }
        }
        else if (bytes_per_sample == 1)
        {
                while (to_process--)
                {
                        *ptr = (int8) (0x7F * (*buffer));
                                        
                        buffer++;
                        ptr += step;
                }
        }
}

/* streams audio data from the mmap buffer */
void
AudioStream::stream_out (int channel, d_sample * buffer)
{
        if (!frames) 
                throw blewit() << "ringbuffer not open.";

        const snd_pcm_channel_area_t & area = areas[channel];
        
        int8 * ptr = (int8 *) area.addr + ((area.first + area.step * offset) >> 3);

        uint step = area.step >> 3;

        uint to_process = frames;

        if (bytes_per_sample == 4)
        {
                while (to_process--)
                {
                        *buffer = 
                                (d_sample) *((int32 *) ptr) / (d_sample) 0x7FFFFFFF;
                        
                        buffer++;
                        ptr += step;
                }
        }
        else if (bytes_per_sample == 2)
        {
                while (to_process--)
                {
                        *buffer = 
                                (d_sample) *((int16 *) ptr) / (d_sample) 32767.;

                        buffer++;
                        ptr += step;
                }
        }
        else if (bytes_per_sample == 1)
        {
                while (to_process--)
                {
                        *buffer = 
                                (d_sample) *ptr / 127.;

                        buffer++;
                        ptr += step;
                }
        }
}

/* after configuring the stream, this creates the ports that the stream
 * presents as a Plugin. */
void
AudioStream::setup_ports()
{
}












/* the _big_ configure. */

#define CONFIGURE(what,ifnot) \
        { \
                int error = what; \
                if (error) \
                { \
                        msg() << ifnot << " in AudioStream" \
                                                << "::configure (line " << __LINE__ << 
"): " \
                                                << snd_strerror (error); \
                        throw blewit() << ifnot; \
                } \
        }

void
AudioStream::configure (uint _sample_rate, uint _frames_per_cycle)
{
        if (configured)
                if (sample_rate == _sample_rate)
                        if (frames_per_cycle == _frames_per_cycle)
                                return;

        configured = false;

        sample_rate = _sample_rate;
        frames_per_cycle = _frames_per_cycle;
        
        CONFIGURE (snd_pcm_hw_params_any (handle, hw_params), 
                "no 'any' configuration to start with.");

        CONFIGURE (snd_pcm_hw_params_set_periods_integer (handle, hw_params),
                "no integer period size.");
        
        int error = snd_pcm_hw_params_set_access 
                        (handle, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
                        
        if (error == 0)
                interleaved = false;
        else
        {
                CONFIGURE (snd_pcm_hw_params_set_access 
                                (handle, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED),
                                "memory-mapped access method refused.");
                interleaved = true;
        }

        /* determine sample format */
        snd_pcm_format_t formats [3] = {
                SND_PCM_FORMAT_S32,
                SND_PCM_FORMAT_S16,
                SND_PCM_FORMAT_S8
        };

        bytes_per_sample = ~0;
        for (int i = 0; i < 3; i++)
        {
                error = snd_pcm_hw_params_set_format (handle, hw_params, formats[i]);
                if (error == 0)
                {
                        bytes_per_sample = 4 >> i;
                        break;
                }
        }
                        
        if (bytes_per_sample > 4)
                throw blewit() << "none of (32, 16, 8) bit sample sizes available.";

        CONFIGURE (snd_pcm_hw_params_set_rate (handle, hw_params, sample_rate, 0),
                "unsupported sample rate.");

        channels = snd_pcm_hw_params_get_channels_max (hw_params);

                error = snd_pcm_hw_params_set_channels (handle, hw_params, channels);
                if (error)
                {
                        msg() << " does not want to give us all its "
                                                << channels << " channels.";
                        throw blewit() << "no channels available.";
                }

        CONFIGURE (snd_pcm_hw_params_set_period_size 
                (handle, hw_params, (uint) frames_per_cycle, 0), /* XXX */
                "hardware refuses this frames_per_cycle setting.");

        CONFIGURE (snd_pcm_hw_params_set_periods
                (handle, hw_params, 2, 0),
                "hardware refuses 2 cycles per buffer.");

        CONFIGURE (snd_pcm_hw_params_set_buffer_size 
                (handle, hw_params, 2 * frames_per_cycle), /* XXX */
                "hardare refuses a buffer size of 2 * frames_per_cycle.");

        CONFIGURE (snd_pcm_hw_params (handle, hw_params),
                "hardware setup failed.");

        /* sw params */
        snd_pcm_sw_params_current (handle, sw_params);
        
        CONFIGURE (snd_pcm_sw_params_set_start_threshold (handle, sw_params, ~0u),
                "cannot turn off start threshold.");
                
        CONFIGURE (snd_pcm_sw_params_set_stop_threshold (handle, sw_params, ~0u),
                "cannot turn off stop threshold.");

        CONFIGURE (snd_pcm_sw_params_set_silence_threshold (handle, sw_params, 0),
                "cannot set 0 silence threshold.");

        CONFIGURE (snd_pcm_sw_params_set_silence_size (handle, sw_params, 0),
                "cannot set 0 silence size.");

        uint avail_min = frames_per_cycle;
        CONFIGURE (snd_pcm_sw_params_set_avail_min (handle, sw_params, avail_min),
                "software setup for minimum available frames failed.");

        CONFIGURE (snd_pcm_sw_params (handle, sw_params),
                "software setup failed.");

        setup_ports();

        configured = true;

        msg() 
                                << " configured for " 
                                << sample_rate 
                                << frames_per_cycle 
                                << (8 * bytes_per_sample) << "bit "
                                << (char *) (interleaved ? "interleaved" : 
"non-interleaved");
}


int main (int argc, char ** argv)
{
        if (argc != 3) {
                _msg() << argv[0] << " <device> <initial period_size>";
                return 1;
        }
        
        int fpc;

        if (sscanf (argv[2], "%d", &fpc) != 1) {
                _msg() << argv[2] << " is not a valid period size.";
                return 2;
        }

        try {
                while (fpc >= 64)
                {
                        AudioStream playback, capture;

                        capture.open (argv[1], Input);
                        playback.open (argv[1], Output);

                        capture.configure (44100, fpc);
                        playback.configure (44100, fpc);

                        sleep (1);

                        fpc >>= 1;
                }
        } catch (exception & e) {
                _msg() << e.what();
                return 3;
        }

        return 0;
}

Reply via email to