c++linuxalsalibalsa

C++ alsa api bad recorded quality


I'm trying to record microphone with alsa api on linux, but result is strange sounds like freezed glitchy robot. Recorded pcm data sended by UDP protocol to pcm player endpoint.

        char* device = "default";
        unsigned int rate = 44100;
        unsigned int channels = 2;
        snd_pcm_uframes_t frames{};
        snd_pcm_t* capture_handle{};
        snd_pcm_hw_params_t* hw_params{};

        if (snd_pcm_open(&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0) < 0)
            throw new std::runtime_error{ "Can't open device for capture" };

        if (snd_pcm_hw_params_malloc(&hw_params) < 0)
            throw new std::runtime_error{ "Can't allocate hw parameters structure" };

        if (snd_pcm_hw_params_any(capture_handle, hw_params) < 0)
            throw new std::runtime_error{ "Can't initialize parameters structure" };

        if (snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
            throw new std::runtime_error{ "Can't set access parameter" };

        if (snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16_LE) < 0)
            throw new std::runtime_error{ "Can't set access parameter" };

        if (snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rate, 0) < 0)
            throw new std::runtime_error{ "Can't set rate" };

        if (snd_pcm_hw_params_set_channels(capture_handle, hw_params, channels) < 0)
            throw new std::runtime_error{ "Can't set channels count" };

        frames = 32;
        if (snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, &frames, 0))
            throw new std::runtime_error{ "Can't set period size" };

        if (snd_pcm_hw_params(capture_handle, hw_params) < 0)
            throw new std::runtime_error{ "Can't set parameters" };

        snd_pcm_hw_params_free(hw_params);

        if (snd_pcm_prepare(capture_handle) < 0)
            throw new std::runtime_error{ "Can't prepare capture device" };

        if (snd_pcm_hw_params_get_period_size(hw_params, &frames, 0) < 0)
            throw new std::runtime_error{ "Can't get frames count" };

        const unsigned int bufSize = frames * channels * 2;
        unsigned int buf[bufSize];
        while (true) {
            if (snd_pcm_readi(capture_handle, &buf[0], bufSize) != bufSize)
                throw new std::runtime_error{ "Can't read from buffer" };
            
            if (connectable != nullptr)
                connectable->sendData(buf, bufSize);
        }

        snd_pcm_close(capture_handle);

Sample result: https://voca.ro/1m3zDAmdW5cc


Solution

  • So the problem is because i'm not specifying period time, period size. And after period manipulation i need retrieve actual values. I have mistake in buffer size formula. Fixed code:

    ...
     frames = periodSize;
            if (snd_pcm_hw_params_set_period_size_near(captureHandle, hwParams, &frames, 0) < 0)
                throw new std::runtime_error{ "Can't set period" };
    
            if (snd_pcm_hw_params_set_period_time_near(captureHandle, hwParams, const_cast<unsigned int*>(&framesTime), 0) < 0)
                throw new std::runtime_error{ "Can't set period time" };
    ...
    snd_pcm_hw_params_get_rate(hwParams, &actualSampleRate, 0);
    
    snd_pcm_hw_params_get_period_size(hwParams, &frames, 0);
    
    snd_pcm_hw_params_get_period_time(hwParams, &actualPeriodTime, 0);
    
    snd_pcm_hw_params_get_channels(hwParams, &actualChannels);
    ...
    bufferSize = (frames * channels * snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE)) / 8;
    ...
    snd_pcm_readi(captureHandle, buffer, frames);
    ...
    connectable->sendData(buffer, bufferSize);