FW: Sounds cut off while playing sounds through ALSA api

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

 



Hi,

 

Have written a thread to repeatedly play wav sounds with varying delays in between the sound being played.  Unfortunately the sound is being clipped (cut off) at the beginning of when the sound is being played.  It is not too clear to me what api calls need to be implemented (or what api calls have been implemented wrongly) in order to sort this problem out.

 

This particular piece of code works on some other hardware but doesn’t work on this particular hardware (Freescale MCIMX31 with ARMv6 compatible processor (v61) with Logitech V10 Notebook speakers (USB - connected to USB port on MCIMX31)).  The code does work on Cirrus EP9315 Arm 920T  processor with the same USB speakers connected to the USB port (this device is a slower device).

 

When I attempt to play wav files with the alsa-utils test application ‘aplay’ I am able to play mono wavs ok but stereo wavs are clipped or cut-off in exactly the same way as the sound being played in the thread.  The thread is playing mono wavs not stereo wavs (the mono wav sounds are also cut off).

 

Some other hardware details are as follows:-

 

/proc/asound/devices

   0: [ 0]   : control

 16: [ 0- 0]: digital audio playback

 33:        : timer

 

/proc/asound/card0/pcm0p/info

card: 0

device: 0

subdevice: 0

stream: PLAYBACK

id: USB Audio

name: USB Audio

subname: subdevice #0

class: 0

subclass: 0

subdevices_count: 1

subdevices_avail: 1

 

Devices are as follows:-

crw-rw----    1 root     audio     14,   4 audio

crw-rw----    1 root     audio     14,   3 dsp

 

Kernel configuration is as follows:-

CONFIG_SND

CONFIG_SND_PCM_OSS

CONFIG_SND_VERBOSE_PROCFS

CONFIG_SND_VERBOSE_PRINTK

CONFIG_SND_USB_AUDIO

 

Dmesg output:-

usbcore: registered new interface driver usbhid

drivers/hid/usbhid/hid-core.c: v2.6:USB HID core driver

Advanced Linux Sound Architecture Driver Version 1.0.14 (Thu May 31 09:03:25 2007 UTC).

usbcore: registered new interface driver snd-usb-audio

ALSA device list:

  #0:            Logitech              Logitech Speaker at usb-fsl-ehci.2-1, full spe

 

Sound thread code attached.

 

Cheers,

 

Geoff Crowther

 

 

 

#ifndef SOUNDTHREAD_H
#define SOUNDTHREAD_H

#include <QThread>
#include <QMutex>

#ifdef Q_OS_LINUX
#include <alsa/asoundlib.h>


typedef  struct
{       u_int32_t  dwSize ;
        u_int16_t  wFormatTag ;
        u_int16_t  wChannels ;
        u_int32_t  dwSamplesPerSec ;
        u_int32_t  dwAvgBytesPerSec ;
        u_int16_t  wBlockAlign ;
        u_int16_t  wBitsPerSample ;
} WAVEFORMAT ;
#endif

typedef enum SoundType
{
    ABOVEGRADESOUND,
    ONGRADESOUND,
    BELOWGRADESOUND,
    OUTSIDEEXTENTSSOUND,
    HITREFSOUND,
    BUCKETSENSORINPLANESOUND,
    HEIGHTHIGHALARM,
    HEIGHTLOWALARM,
    BUCKETINLASERSOUND
};

typedef enum Position
{
    ABOVE_HIGH_EXTENTS,
    BELOW_HIGH_EXTENT_ABOVE_DEADBAND,
    BELOW_HIGH_DEADBAND_ABOVE_LOW_DEADBAND,
    BELOW_LOW_DEADBAND_ABOVE_LOW_EXTENTS,
    BELOW_LOW_EXTENTS,
    SENSORS_OFFLINE,
    REF_COMPLETE_SUCCESS_POS,
    REF_COMPLETE_FAILURE_POS,
    HEIGHT_ALARM_POS,
    DEPTH_ALARM_POS,
    BUCKET_IN_LASER_POS
};


typedef enum SoundStateTransition
{
    ABOVEEXTENTS,
    ABOVEGRADE,
    ONGRADE,
    BELOWGRADE,
    BELOWEXTENTS,
    SENSORSOFFLINE,
    REFCOMPLETESUCCESS,
    REFCOMPLETEFAILURE,
    HEIGHTALARM,
    DEPTHALARM,
    BUCKETINLASER,
    ABOVEEXTENTS_TO_ABOVEGRADE,
    ABOVEEXTENTS_TO_ONGRADE,
    ABOVEEXTENTS_TO_BELOWGRADE,
    ABOVEEXTENTS_TO_BELOWEXTENTS,
    ABOVEEXTENTS_TO_SENSORSOFFLINE,
    ABOVEEXTENTS_TO_REFCOMPLETEFAILURE,
    ABOVEEXTENTS_TO_REFCOMPLETESUCCESS,
    ABOVEEXTENTS_TO_HEIGHTALARM,
    ABOVEEXTENTS_TO_DEPTHALARM,
    ABOVEEXTENTS_TO_BUCKETINLASER,
    ABOVEGRADE_TO_ABOVEEXTENTS,
    ABOVEGRADE_TO_ONGRADE,
    ABOVEGRADE_TO_BELOWGRADE,
    ABOVEGRADE_TO_BELOWEXTENTS,
    ABOVEGRADE_TO_SENSORSOFFLINE,
    ABOVEGRADE_TO_REFCOMPLETEFAILURE,
    ABOVEGRADE_TO_REFCOMPLETESUCCESS,
    ABOVEGRADE_TO_HEIGHTALARM,
    ABOVEGRADE_TO_DEPTHALARM,
    ABOVEGRADE_TO_BUCKETINLASER,
    ONGRADE_TO_ABOVEEXTENTS,
    ONGRADE_TO_ABOVEGRADE,
    ONGRADE_TO_BELOWGRADE,
    ONGRADE_TO_BELOWEXTENTS,
    ONGRADE_TO_SENSORSOFFLINE,
    ONGRADE_TO_REFCOMPLETESUCCESS,
    ONGRADE_TO_REFCOMPLETEFAILURE,
    ONGRADE_TO_HEIGHTALARM,
    ONGRADE_TO_DEPTHALARM,
    ONGRADE_TO_BUCKETINLASER,
    BELOWGRADE_TO_ABOVEEXTENTS,
    BELOWGRADE_TO_ABOVEGRADE,
    BELOWGRADE_TO_ONGRADE,
    BELOWGRADE_TO_BELOWEXTENTS,
    BELOWGRADE_TO_SENSORSOFFLINE,
    BELOWGRADE_TO_REFCOMPLETESUCCESS,
    BELOWGRADE_TO_REFCOMPLETEFAILURE,
    BELOWGRADE_TO_HEIGHTALARM,
    BELOWGRADE_TO_DEPTHALARM,
    BELOWGRADE_TO_BUCKETINLASER,
    BELOWEXTENTS_TO_ABOVEEXTENTS,
    BELOWEXTENTS_TO_ABOVEGRADE,
    BELOWEXTENTS_TO_ONGRADE,
    BELOWEXTENTS_TO_BELOWGRADE,
    BELOWEXTENTS_TO_SENSORSOFFLINE,
    BELOWEXTENTS_TO_HEIGHTALARM,
    BELOWEXTENTS_TO_DEPTHALARM,
    BELOWEXTENTS_TO_REFCOMPLETESUCCESS,
    BELOWEXTENTS_TO_REFCOMPLETEFAILURE,
    BELOWEXTENTS_TO_BUCKETINLASER,
    SENSORSOFFLINE_TO_ABOVEEXTENTS,
    SENSORSOFFLINE_TO_ABOVEGRADE,
    SENSORSOFFLINE_TO_ONGRADE,
    SENSORSOFFLINE_TO_BELOWGRADE,
    SENSORSOFFLINE_TO_BELOWEXTENTS,
    SENSORSOFFLINE_TO_REFCOMPLETESUCCESS,
    SENSORSOFFLINE_TO_REFCOMPLETEFAILURE,
    SENSORSOFFLINE_TO_HEIGHTALARM,
    SENSORSOFFLINE_TO_DEPTHALARM,
    SENSORSOFFLINE_TO_BUCKETINLASER,
    REFCOMPLETESUCCESS_TO_ABOVEEXTENTS,
    REFCOMPLETESUCCESS_TO_ABOVEGRADE,
    REFCOMPLETESUCCESS_TO_ONGRADE,
    REFCOMPLETESUCCESS_TO_BELOWGRADE,
    REFCOMPLETESUCCESS_TO_BELOWEXTENTS,
    REFCOMPLETESUCCESS_TO_SENSORSOFFLINE,
    REFCOMPLETESUCCESS_TO_REFCOMPLETEFAILURE,
    REFCOMPLETESUCCESS_TO_HEIGHTALARM,
    REFCOMPLETESUCCESS_TO_DEPTHALARM, 
    REFCOMPLETESUCCESS_TO_BUCKETINLASER,
    REFCOMPLETEFAILURE_TO_ABOVEEXTENTS,
    REFCOMPLETEFAILURE_TO_ABOVEGRADE,
    REFCOMPLETEFAILURE_TO_ONGRADE,
    REFCOMPLETEFAILURE_TO_BELOWGRADE,
    REFCOMPLETEFAILURE_TO_BELOWEXTENTS,
    REFCOMPLETEFAILURE_TO_SENSORSOFFLINE,
    REFCOMPLETEFAILURE_TO_REFCOMPLETESUCCESS,
    REFCOMPLETEFAILURE_TO_DEPTHALARM,
    REFCOMPLETEFAILURE_TO_HEIGHTALARM,
    REFCOMPLETEFAILURE_TO_BUCKETINLASER,
    DEPTHALARM_TO_SENSORSOFFLINE,
    DEPTHALARM_TO_ABOVEEXTENTS,
    DEPTHALARM_TO_ABOVEGRADE,
    DEPTHALARM_TO_ONGRADE,
    DEPTHALARM_TO_BELOWGRADE,
    DEPTHALARM_TO_BELOWEXTENTS,
    DEPTHALARM_TO_REFCOMPLETESUCCESS,
    DEPTHALARM_TO_REFCOMPLETEFAILURE,
    DEPTHALARM_TO_HEIGHTALARM,
    DEPTHALARM_TO_BUCKETINLASER,
    HEIGHTALARM_TO_SENSORSOFFLINE,
    HEIGHTALARM_TO_ABOVEEXTENTS,
    HEIGHTALARM_TO_ABOVEGRADE,
    HEIGHTALARM_TO_ONGRADE,
    HEIGHTALARM_TO_BELOWGRADE,
    HEIGHTALARM_TO_BELOWEXTENTS,
    HEIGHTALARM_TO_REFCOMPLETESUCCESS,
    HEIGHTALARM_TO_REFCOMPLETEFAILURE,
    HEIGHTALARM_TO_DEPTHALARM,
    HEIGHTALARM_TO_BUCKETINLASER,
    BUCKETINLASER_TO_SENSORSOFFLINE,
    BUCKETINLASER_TO_ABOVEEXTENTS,
    BUCKETINLASER_TO_ABOVEGRADE,
    BUCKETINLASER_TO_ONGRADE,
    BUCKETINLASER_TO_BELOWGRADE,
    BUCKETINLASER_TO_BELOWEXTENTS,
    BUCKETINLASER_TO_REFCOMPLETESUCCESS,
    BUCKETINLASER_TO_REFCOMPLETEFAILURE,
    BUCKETINLASER_TO_HEIGHTALARM,
    BUCKETINLASER_TO_DEPTHALARM
};



class QSoundThread : public QThread
{
    Q_OBJECT

public:
    QSoundThread( const QString& filename, QObject* parent=0) ;
    virtual ~QSoundThread();

    virtual void run();
    bool Initialise(const QString & soundfile);
    const QString & GetWaveFile(){ return Path; }
    void SetThreadDelay(int delay);
    bool is_available;
    bool isAvailable() { return is_available ; }
    void ExitThread();
    bool SoundAvailable() { return !SoundNotAvailable; }

private:
    QMutex mutex_soundfile;
    QMutex mutex_threaddelay;

    QString Path;

    int threaddelay;
    int dummythreaddelay;
    bool exitthread;
    bool SoundNotAvailable;

#ifdef Q_OS_LINUX
    // ALSA parameters
    snd_pcm_t               *handle;
    snd_pcm_sframes_t       frames;
    char                    *device ;                        // playback device
    snd_pcm_uframes_t chunk_size, buffer_size;
    size_t bits_per_sample, bits_per_frame, chunk_bytes;

    // File parser
    int     fd;                             // Open file descriptor or -1
    char* findchunk(char* pstart, char* fourcc, size_t n);
    WAVEFORMAT waveformat ;
    u_long samples, datastart;
#endif

};

#endif // SOUNDTHREAD_H
#include "SoundThread.h"
#include <QThread>
#include <QtDebug>
#include <QSound>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Formats.h"

#ifdef Q_OS_LINUX
#include <sys/time.h>
#include <alsa/asoundlib.h>
#include <malloc.h>
#if WORDS_BIGENDIAN
#define SwapLE16(x) ((((u_int16_t)x)<<8)|(((u_int16_t)x)>>8))
#define SwapLE32(x) ((((u_int32_t)x)<<24)|((((u_int32_t)x)<<8)&0x00FF0000) \
                     |((((u_int32_t)x)>>8)&0x0000FF00)|(((u_int32_t)x)>>24))
#else
#define SwapLE16(x) (x)
#define SwapLE32(x) (x)
#endif
#endif

#define BUFFERSIZE 1024


QSoundThread::QSoundThread( const QString& filename, QObject* parent) : QThread(parent)
{
    SoundNotAvailable = false;
    Path = filename;
    dummythreaddelay = threaddelay = 300;
    is_available= Initialise(filename);
    if(!is_available)
    {
        //Speakers not plugged in
        exitthread = true;
        SoundNotAvailable = true;
        return;
    }
    exitthread = false;
#ifdef Q_OS_LINUX
    snd_pcm_close(handle);
    close(fd);
#endif
}

QSoundThread::~QSoundThread()
{

}

bool QSoundThread::Initialise(const QString & soundfile)
{
        Path.clear();
        Path += soundfile;

        if (SoundNotAvailable) return false;

#ifdef _WIN32
        if (QSound::isAvailable())
                return true ; 
#endif

        mutex_soundfile.lock();
        exitthread = false;
        mutex_soundfile.unlock();

#ifdef Q_OS_LINUX
        char   buffer [ BUFFERSIZE ] ;

        //device = strdup("plughw:0,0");      // playback device
        device = strdup("default");           // playback device


        char*   ptr ;
        u_long  databytes ;
        snd_pcm_format_t format;
        snd_pcm_hw_params_t *params;

        int err;

        if ( (fd = open(Path.toLatin1().constData(),O_RDONLY)) < 0 ) {
                qWarning("Error Opening WAV file %s",Path.toLatin1().constData());
                return false;
        }

        if ( lseek(fd,0L,SEEK_SET) != 0L ) {
                qWarning("Error nRewinding WAV file %s",Path.toLatin1().constData());
                return false;           // Wav file must be seekable device
        }


        read (fd, buffer, BUFFERSIZE) ;

        if (findchunk (buffer, "RIFF", BUFFERSIZE) != buffer) {
                qWarning("Bad format: Cannot find RIFF file marker");
                return  false;
        }

        if (! findchunk (buffer, "WAVE", BUFFERSIZE)) {
                qWarning("Bad format: Cannot find WAVE file marker");
                return  false;
        }

        ptr = findchunk (buffer, "fmt ", BUFFERSIZE) ;

        if (! ptr) {
                qWarning("Bad format: Cannot find 'fmt' file marker");
                return  false;
        }

        ptr += 4 ;      // Move past "fmt ".
        memcpy (&waveformat, ptr, sizeof (WAVEFORMAT)) ;
        waveformat.dwSize = SwapLE32(waveformat.dwSize);
        waveformat.wFormatTag = SwapLE16(waveformat.wFormatTag) ;
        waveformat.wChannels = SwapLE16(waveformat.wChannels) ;
        waveformat.dwSamplesPerSec = SwapLE32(waveformat.dwSamplesPerSec) ;
        waveformat.dwAvgBytesPerSec = SwapLE32(waveformat.dwAvgBytesPerSec) ;
        waveformat.wBlockAlign = SwapLE16(waveformat.wBlockAlign) ;
        waveformat.wBitsPerSample = SwapLE16(waveformat.wBitsPerSample) ;

        qDebug("waveformat.dwSize %i", waveformat.dwSize);
        qDebug("waveformat.wFormatTag %i", waveformat.wFormatTag);
        qDebug("waveformat.wChannels %i", waveformat.wChannels);
        qDebug("waveformat.dwSamplesPerSec %i",waveformat.dwSamplesPerSec);
        qDebug("waveformat.dwAvgBytesPerSec %i", waveformat.dwAvgBytesPerSec);
        qDebug("waveformat.wBlockAlign %i",waveformat.wBlockAlign);
        qDebug("waveformat.wBitsPerSample %i",waveformat.wBitsPerSample);
        
        ptr = findchunk (buffer, "data", BUFFERSIZE) ;

        if (! ptr) 
	{
                qWarning("Bad format: unable to find 'data' file marker");      
                return  false;
        }

        ptr += 4 ;      // Move past "data".
        memcpy (&databytes, ptr, sizeof (u_long)) ;

        samples    = databytes / waveformat.wBlockAlign ;
        datastart  = ((u_long) (ptr + 4)) - ((u_long) (&(buffer[0]))) ;

        switch (waveformat.wBitsPerSample)
        {
                case 8:
                        format = SND_PCM_FORMAT_U8 ;
                        break;
                case 16:        
                        format = SND_PCM_FORMAT_S16_LE ;
                        break;
                case 32 :
                        format = SND_PCM_FORMAT_S32_LE;
                        break;
                default :
                        qWarning("Bad format: %i bits per seconds",waveformat.wBitsPerSample );
                        return false;
                        break;
        }

       // qDebug("%s - format :%d, %i Hz, %i channels",Path.toLatin1(),waveformat.wBitsPerSample, waveformat.dwSamplesPerSec,waveformat.wChannels);

        //ALSA pain
        snd_pcm_hw_params_alloca(&params);
        

        if ((err = snd_pcm_open (&handle, device,SND_PCM_STREAM_PLAYBACK,SND_PCM_ASYNC)) < 0)
	{
        	qWarning("cannot open audio device %s (%s)", device,snd_strerror (err));
        	return false;
        }

        if ((err = snd_pcm_nonblock(handle, 1))< 0) 
	{
        	qWarning("nonblock setting error: %s", snd_strerror(err));
        	return false;
        }
        

        // Init hwparams with full configuration space
        if (snd_pcm_hw_params_any(handle, params) < 0)
	{
        	qFatal("Can not configure this PCM device.");
        	return false;
        }


        err = snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED);

        if (err < 0) 
	{
                qFatal("Access type not available");
                return false;
        }

        err = snd_pcm_hw_params_set_format(handle, params, format);

        if (err < 0)
	{
                qFatal("Sample format not available");
                return false;
        }

        err = snd_pcm_hw_params_set_channels(handle, params, waveformat.wChannels);

        if (err < 0) {
                qFatal("Channels count not available");
                return false;
        }

        err = snd_pcm_hw_params_set_rate_near(handle, params,&waveformat.dwSamplesPerSec, 0);
        if (err < 0) {
                qFatal("Unable to set rate : %d", waveformat.dwSamplesPerSec);
                return false;
        }

        assert(err >= 0);       

        err = snd_pcm_hw_params(handle, params);

        if (err < 0) 
	{
                qFatal("Unable to install hw params:");
                return false;
        }

        chunk_size = 0;
        buffer_size=0;
        snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
        snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
        bits_per_sample = snd_pcm_format_physical_width(format);
        bits_per_frame = bits_per_sample * waveformat.wChannels;
        chunk_bytes = chunk_size * bits_per_frame / 8;
        
        qDebug("bits_per_sample %i", bits_per_sample);
        qDebug("bits_per_frame %i", bits_per_frame);
        qDebug("chunk_size %i", chunk_size);
        qDebug("chunk_bytes %i", chunk_bytes);
        return true;
#endif
}

void QSoundThread::ExitThread()
{
    mutex_soundfile.lock();
    exitthread = true;
    mutex_soundfile.unlock();
    wait(-1);
#ifdef Q_OS_LINUX
	snd_pcm_close(handle);
	close(fd);
#endif
}

void QSoundThread::run()
{
    bool flag;

#ifdef Q_OS_LINUX
    int err;

    // start playback
    err=lseek(fd,datastart,SEEK_SET);

    int count,f;
    char *buffer2;
    buffer2 = (char *)malloc (buffer_size);
#else //_WIN32
    QSound soundtoplay(Path);
#endif

    while(1) //thread
    {

#ifdef Q_OS_LINUX
        while ((count = read (fd, buffer2,buffer_size))) 
        {
            f=count*8/bits_per_frame;
            //f = count;

            qDebug("bits_per_frame is %i, count is %i", bits_per_frame, count);

            while ((frames = snd_pcm_writei(handle, buffer2, f)) < 0) 
	        {
                snd_pcm_prepare(handle);
	        }
        }
        snd_pcm_drain(handle);

#elif _WIN32
       soundtoplay.play();
#else
       qWarning("Platform has no sound configured");
#endif

        mutex_threaddelay.lock();
        threaddelay = dummythreaddelay; //don't want to block on the sleep
        mutex_threaddelay.unlock();

        msleep(threaddelay);

        mutex_soundfile.tryLock();
        flag = exitthread;
        mutex_soundfile.unlock();

#ifdef Q_OS_LINUX
        if(fd >= 0 && !flag)
	{
           err=lseek(fd,datastart,SEEK_SET);
	}
        else
        {
           free(buffer2);
           err=lseek(fd,datastart,SEEK_SET);
#else
        if(!flag)
        {

        }
        else
        {
#endif
           mutex_soundfile.tryLock();
           exitthread = false;
           mutex_soundfile.unlock();
           return;
        }
    }
}

void QSoundThread::SetThreadDelay(int delay)
{
    mutex_threaddelay.lock();
    dummythreaddelay = delay;
    mutex_threaddelay.unlock();
}

#ifdef Q_OS_LINUX
char* QSoundThread::findchunk  (char* pstart, char* fourcc, size_t n)
{
        char *pend;
        int k, test;

        pend = pstart + n;

        while (pstart < pend)
        {       if (*pstart == *fourcc)       // found match for first char
                {       test = TRUE ;
                        for (k = 1 ; fourcc [k] != 0 ; k++)
                                test = (test ? ( pstart [k] == fourcc [k] ) : FALSE) ;
                        if (test)
                                return  pstart ;
                        } ; // if
                pstart ++ ;
                } ; // while lpstart

        return  NULL ;
}  // findchuck
#endif

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Alsa-user mailing list
Alsa-user@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/alsa-user

[Index of Archives]     [ALSA Devel]     [Linux Audio Users]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]

  Powered by Linux