Sound distortion with snd_pcm_writei

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

 



Hi people,

I'm currently writing a small player application using alsa, and it's been 
going reasonably well. However, I have hit a problem I don't know how to fix.

I'm using async callbacks to write the sound data. I have the data from a 
bunch of wav files that are stored using 8 bit per sample, and I have set the 
hardware options to this. They have different sample rates, but I read this 
from the file and set it also.

The sound plays as it's supposed to but it's very distorted, and I have no 
clue why that is. I am using an existing wav player to compare my output 
with, and they are similar, but not the same. If one of you have an idea, I'd 
be very happy to hear about it.

I hope you can help me.

Bo Thorsen.

For completeness, here's the code I use to set up alsa:

    int err;
    if ((err = snd_pcm_open(&mPlaybackHandle, "default",
                            SND_PCM_STREAM_PLAYBACK, 0)) < 0)
        qFatal("Can't open audio device. Error: %s", snd_strerror(err));

    snd_pcm_hw_params_t* hwParams;
    if ((err = snd_pcm_hw_params_malloc(&hwParams)) < 0)
        qFatal("Can't allocate hardware parameters. Error: %s",
               snd_strerror(err));

    if ((err = snd_pcm_hw_params_any(mPlaybackHandle, hwParams)) < 0)
        qFatal("Can't initialize hardware parameter structure. Error: %s",
               snd_strerror(err));

    PlayWave* thread = m_AudioPlayer->GetPlayWaveTool();
    if ((err = snd_pcm_hw_params_set_access(mPlaybackHandle, hwParams,
                                            SND_PCM_ACCESS_RW_INTERLEAVED)
            ) < 0)
        qFatal("Can't set access type. Error: %s", snd_strerror(err));

    _snd_pcm_format format;
    switch(thread->WaveBitsPerSample()) {
    case 8: format = SND_PCM_FORMAT_S8; break;
    case 16: format = SND_PCM_FORMAT_U16_LE; break;
    case 24: format = SND_PCM_FORMAT_U24_LE; break;
    case 32: format = SND_PCM_FORMAT_U32_LE; break;
    default: format = SND_PCM_FORMAT_UNKNOWN; break;
    }
    if ((err = snd_pcm_hw_params_set_format(mPlaybackHandle, hwParams,
                                            format)) < 0)
        qFatal("Can't set sample format. Error: %s", snd_strerror(err));

    unsigned int rate = thread->GetWaveAvgBytesPerSec();
    if ((err = snd_pcm_hw_params_set_rate_near(mPlaybackHandle, hwParams,
                                               &rate, 0)) < 0)
        qFatal("Can't set sample rate. Error: %s",  snd_strerror(err));
    mPlaybackRate = rate;

    if ((err = snd_pcm_hw_params_set_channels(mPlaybackHandle, hwParams,
                                              thread->WaveChannels())) < 0)
        qFatal("Can't set channel count. Error: %s", snd_strerror(err));

    unsigned int bufferTime = 500000;
    int dir = 0;
    if ((err = snd_pcm_hw_params_set_buffer_time_near(mPlaybackHandle,
                                                      hwParams, &bufferTime,
                                                      &dir)) < 0)
        qFatal("Error setting buffer time. Error: %s\n", snd_strerror(err));

    mBufferSize = thread->WaveDataSize();

    unsigned int periodTime = 100000;
    if ((err = snd_pcm_hw_params_set_period_time_near(mPlaybackHandle,
                                                      hwParams, &periodTime,
                                                      &dir)) < 0)
        qFatal("Error setting period time. Error: %s\n", snd_strerror(err));

    snd_pcm_uframes_t periodSize;
    if ((err = snd_pcm_hw_params_get_period_size(hwParams, &periodSize, &dir))
        < 0)
        qFatal("Error getting the playback period size. Error: %s\n",
               snd_strerror(err));
    mPeriodSize = periodSize;

    if ((err = snd_pcm_hw_params(mPlaybackHandle, hwParams)) < 0)
        qFatal("Can't set hardware parameters. Error: %s", snd_strerror(err));

    // End of hw params
    snd_pcm_hw_params_free(hwParams);

    // Software params
    snd_pcm_sw_params_t* swParams;
    if ((err = snd_pcm_sw_params_malloc(&swParams)) < 0)
        qDebug("Error making the sw params struct. Error: %s",
               snd_strerror(err));

    if ((err = snd_pcm_sw_params_current(mPlaybackHandle, swParams)) < 0)
        qDebug("Unable to determine current swParams for playback: %s\n",
               snd_strerror(err));

    if ((err = snd_pcm_sw_params_set_start_threshold(mPlaybackHandle,
                                                     swParams,
                                                     (mBufferSize/periodSize)
                                                     * periodSize)
            ) < 0)
        qDebug("Unable to set start threshold mode for playback: %s\n",
               snd_strerror(err));

    if ((err = snd_pcm_sw_params_set_avail_min(mPlaybackHandle, swParams,
                                               periodSize)) < 0)
        qDebug("Unable to set avail min for playback: %s\n",
               snd_strerror(err));

    if ((err = snd_pcm_sw_params_set_xfer_align(mPlaybackHandle,
                                                swParams, 1)) < 0)
        qDebug("Unable to set transfer align for playback: %s\n",
               snd_strerror(err));

    if ((err = snd_pcm_sw_params(mPlaybackHandle, swParams)) < 0)
        qDebug("Unable to set sw params for playback: %s\n",
               snd_strerror(err));

    // End of software params
    snd_pcm_sw_params_free(swParams);

    if ((err = snd_pcm_prepare(mPlaybackHandle)) < 0)
        qFatal("Can't prepare audio interface for playback. Error: %s",
               snd_strerror(err));

    // Set the async callback handler
    if ((err = snd_async_add_pcm_handler(&mCallbackHandle, mPlaybackHandle,
                                         alsaCallback,
                                         static_cast<void*>(this))) < 0)
        qFatal("Unable to register Alsa callback handler. Error: %s\n",
               snd_strerror(err));

And the callback code:

    char* data = m_AudioPlayer->GetPlayWaveTool()->WaveData();
    char* frame = &data[mFramePointer];

    int bytes =
        m_AudioPlayer->GetPlayWaveTool()->WaveDataSize() - mFramePointer;
    if (bytes > mPeriodSize) bytes = mPeriodSize;
    if (bytes < 0) bytes = 0;

    // Update the pointer
    mFramePointer += bytes;

    if (mVolume != 100 || bytes < mPeriodSize) {
        // If the volume isn't 100 or the sound is done, we need to use
        // our own sound buffer
        if (mSoundBufferSize != mPeriodSize)
        {
            mSoundBufferSize = mPeriodSize;
            delete mSoundBuffer;
            mSoundBuffer = new char[mSoundBufferSize];
        }

        if (bytes > 0 )
        {
            // There is sound to play
            if (mVolume >= 100)
            {
                // No need for volume adjustment, so we can use memcpy
                memcpy(mSoundBuffer, frame, bytes);
            }
            else
            {
                // Do the slow volume setting
                float adjust = mVolume / 100.0f;

                for (int i=0; i<bytes; ++i)
                    // TODO: Handle non-char wide sounds
                    mSoundBuffer[i] = (char)(frame[i] * adjust);
            }
        }

        // Fill out the remining time with the sound of silence
        while (bytes < mPeriodSize)
            mSoundBuffer[bytes++] = 0;

        // Set to play the buffer
        frame = mSoundBuffer;
    }

    int rc = snd_pcm_writei(mPlaybackHandle, frame, bytes);

-- 

Thorsen Consulting ApS - Qt consulting services
http://www.thorsen-consulting.dk


_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/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