NetBSD-Bugs archive

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

kern/52196: ossaudio+portaudio2 fails on HEAD (with test prog)



>Number:         52196
>Category:       kern
>Synopsis:       ossaudio+portaudio2 fails on HEAD
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Apr 26 16:55:00 +0000 2017
>Originator:     Manuel Bouyer
>Release:        HEAD as of today
>Organization:
-- 
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
     NetBSD: 26 ans d'experience feront toujours la difference
--
>Environment:
System: NetBSD chartplotter 7.99.70 NetBSD 7.99.70 (CHARTPLOTTER) #7: Fri Apr 21 18:57:58 MEST 2017 evbarm
Architecture: earmv7hf
Machine: evbarm

>Description:
	OpenCPN (from pkgsrc) now wedge when playing sound.
	It worked file with older HEAD, and also works fine on netbsd-7.
	ktrace shows that it's writing the same 64k chunk of data over
	and over at a slow rate on the /dev/audio device.
	In case this matters this is on a allwinnner A20 evbarm board,
	with the awinac audio device. It supports only 48k, 16 or 24 bits.

	From the OopenCPN source I wrote a simple program which reproduces
	the problem, see below. You need audio/portaudio-devel from pkgsrc.
	Then compile with:
	gcc -o testpa -I/usr/pkg/include testpasound.c -Wl,-R/usr/pkg/lib/portaudio2 -L/usr/pkg/lib/portaudio2 -lportaudio

	and test with the 1bells.wav file from the opencpn source file
	(or download it from
	https://github.com/OpenCPN/OpenCPN/tree/master/data/sounds)
	./testpa /usr/pkg/share/opencpn/sounds/1bells.wav
	On netbsd-7 it plays fine, on HEAD it hangs (at last in my
	setup).


>How-To-Repeat:
	Here is the test program:

/******************************************************************************
 *
 * Project:  OpenCPN
 *
 ***************************************************************************
 *   Copyright (C) 2013 by David S. Register                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
 ***************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include <string.h>
#include <sys/stat.h>
#include <portaudio2/portaudio.h>

PaStream *stream;
void *sdata;
int sindex;
int smax_samples;
int splaying;

struct m_osdata {
	unsigned m_channels;
	unsigned m_samplingRate;
	unsigned m_bitsPerSample;
	unsigned m_samples;
	size_t m_dataBytes;
} m_osdata;

int LoadWAV(uint8_t *data, size_t length, int copyData);

/* This routine will be called by the PortAudio engine when audio is needed.
 * It may called at interrupt level on some machines so don't do anything
 * that could mess up the system like calling malloc() or free().
 */
static int OCPNSoundCallback( const void *inputBuffer, void *outputBuffer,
                           unsigned long framesPerBuffer,
                           const PaStreamCallbackTimeInfo* timeInfo,
                           PaStreamCallbackFlags statusFlags,
                           void *userData )
{
    /* Cast data passed through stream to our structure. */
    uint16_t *data = (uint16_t *)userData;
    uint16_t *out = (uint16_t*)outputBuffer;
    unsigned int i;
    int bdone = 0;
    (void) inputBuffer; /* Prevent unused variable warning. */
    for( i=0; i<framesPerBuffer; i++ )
    {
        *out = data[sindex];
        out++;
        sindex++;

        if( sindex >= smax_samples ) {
            bdone = 1;
            break;
        }
    }

    if(bdone)
        return paComplete;
    else
        return 0;
}

int
main(int argc, const char *argv[])
{
    int d;
    struct stat sb;
    PaError paerr = Pa_Initialize();
    if( paerr != paNoError )
        errx(1, "PortAudio CTOR error: %s\n", Pa_GetErrorText( paerr ) );

    if (argc != 2) {
	errx(1, "usage: testpa <filename>");
    }

    d = open(argv[1], O_RDONLY, 0);
    if (d < 0) {
	err(1, "open %s", argv[1]);
    }

    if (fstat(d, &sb) != 0) {
	err(1, "fstat");
    }

    size_t len = sb.st_size;
    char *data = malloc(len);
    if (data == NULL) {
	err(1, "malloc");
    }
    size_t rlen;
    size_t todo = len;
    char *buf = data;
    while (todo > 0) {
	    rlen = read(d, buf, todo);
	    if (rlen < 0) {
		err(1, "read");
	    }
	    if (rlen == 0) {
		errx(1, "short read");
	    }
	    buf += rlen;
	    todo -= rlen;
    }

    if ((rlen = LoadWAV(data, len, 0)) != 0)
    {
        errx(1, "Sound file is in unsupported format %d", rlen);
    }
    
    sindex = 0;
    smax_samples = m_osdata.m_samples;
 
    PaStreamParameters outputParameters;
    outputParameters.device = Pa_GetDefaultOutputDevice();
    outputParameters.channelCount = m_osdata.m_channels;
    outputParameters.sampleFormat = paInt16;
    outputParameters.suggestedLatency = 0;
    outputParameters.hostApiSpecificStreamInfo = NULL;
    
    PaStream *m_stream;
    /* Open an audio I/O stream. */
    paerr = Pa_OpenStream( &m_stream,
                         NULL, /* no input channels */
                         &outputParameters,
                         m_osdata.m_samplingRate,
                         256, /* frames per buffer, i.e. the number
                                 of sample frames that PortAudio will
                                 request from the callback. Many apps
                                 may want to use
                                 paFramesPerBufferUnspecified, which
                                 tells PortAudio to pick the best,
                                 possibly changing, buffer size.*/
                         paNoFlag, // flags
                         OCPNSoundCallback, /* this is your callback function */
                         sdata ); /*This is a pointer that will be passed to
                                    your callback*/
    if( paerr != paNoError )
        errx(1, "PortAudio Create() error: %s", Pa_GetErrorText( paerr ) );

    Pa_StopStream( m_stream );
    
    sindex = 0;
    smax_samples = m_osdata.m_samples;
    
    paerr = Pa_StartStream( m_stream );
    if( paerr != paNoError ) {
         errx(1,  "PortAudio Play() error: %s\n", Pa_GetErrorText( paerr ) );
    }
    sleep(10);
    exit(0);
}

typedef struct
{
    uint32_t      uiSize;
    uint16_t      uiFormatTag;
    uint16_t      uiChannels;
    uint32_t      ulSamplesPerSec;
    uint32_t      ulAvgBytesPerSec;
    uint16_t      uiBlockAlign;
    uint16_t      uiBitsPerSample;
} WAVEFORMAT;

#define WAVE_FORMAT_PCM  1
#define WAVE_INDEX       8
#define FMT_INDEX       12

int
LoadWAV(uint8_t *data, size_t length, int copyData)
{
    // the simplest wave file header consists of 44 bytes:
    //
    //      0   "RIFF"
    //      4   file size - 8
    //      8   "WAVE"
    //
    //      12  "fmt "
    //      16  chunk size                  |
    //      20  format tag                  |
    //      22  number of channels          |
    //      24  sample rate                 | WAVEFORMAT
    //      28  average bytes per second    |
    //      32  bytes per frame             |
    //      34  bits per sample             |
    //
    //      36  "data"
    //      40  number of data bytes
    //      44  (wave signal) data
    //
    // so check that we have at least as much
    if ( length < 44 )
        return 1;
    
    WAVEFORMAT waveformat;
    memcpy(&waveformat, &data[FMT_INDEX + 4], sizeof(WAVEFORMAT));
    
    //  Sanity checks
    if (memcmp(data, "RIFF", 4) != 0)
        return 2;
    if (memcmp(&data[WAVE_INDEX], "WAVE", 4) != 0)
        return 3;
    if (memcmp(&data[FMT_INDEX], "fmt ", 4) != 0)
        return 4;
 
    // get the sound data size
        size_t ul = 0;
    //  Get the "data" chunk length
    if (memcmp(&data[FMT_INDEX + waveformat.uiSize + 8], "data", 4) == 0) {
        memcpy(&ul, &data[FMT_INDEX + waveformat.uiSize + 12], 4);
    }
    
    //  There may be a "fact" chunk in the header, which will displace the first "data" chunk
    //  If so, find the "data" chunk 12 bytes further along
    else if (memcmp(&data[FMT_INDEX + waveformat.uiSize + 8], "fact", 4) == 0) {
        if (memcmp(&data[FMT_INDEX + waveformat.uiSize + 8 + 12], "data", 4) == 0) {
            memcpy(&ul, &data[FMT_INDEX + waveformat.uiSize + 12 + 12], 4);
        }
    }
    
    if ( length < ul + FMT_INDEX + waveformat.uiSize + 16 ) {
	printf("length %d < %d\n", length, ul + FMT_INDEX + waveformat.uiSize + 16);
        return 5;
    }
    
    if (waveformat.uiFormatTag != WAVE_FORMAT_PCM)
        return 6;
    
    if (waveformat.ulSamplesPerSec !=
        waveformat.ulAvgBytesPerSec / waveformat.uiBlockAlign)
        return 7;
    
    m_osdata.m_channels = waveformat.uiChannels;
    m_osdata.m_samplingRate = waveformat.ulSamplesPerSec;
    m_osdata.m_bitsPerSample = waveformat.uiBitsPerSample;
    m_osdata.m_samples = ul / (m_osdata.m_channels * m_osdata.m_bitsPerSample / 8);
    m_osdata.m_dataBytes = ul;
    
    sdata = (&data[FMT_INDEX + waveformat.uiSize + 8]);
    
    return 0;
}

>Fix:
	unknown



Home | Main Index | Thread Index | Old Index