2

I want to implement a specific functionality to my project which includes continuously playing a sample-default.wav in the background and play a sample-specific.wav file when some specific condition matches. What I really want is that, when sample-specific.wav file is running, volume of sample-default.wav file becomes 0 (or simply, I want to mute sample-default.wav file when sample-specific.wav is running).

I am trying to implement this functionality using alsa-library for c. I have tried almost every approach to achieve this but nothing seems to works for me.


My current approach

I am trying to add two virtual sound devices using dmix plugin of alsa leaving default for system sounds. I searched google and find that I need to edit /etc/asound.conf file for creating different sound devices.

I added two sound devices, named notification and sample-sound. My current /etc/asound.conf file looks something like the one below:-

pcm.notification {
    type dmix
    ipc_key 1024
    slave {
        pcm "hw:1,0"
    }
}

ctl.notification {
     type hw
     card 2
}

pcm.sample-sound {
     type dmix
     ipc_key 1025
    slave {
         pcm "hw:1,0"
    }
}

ctl.sample-sound {
     type hw
    card 3
}

This works perfectly fine with aplay i.e. when I am playing sample-default.wav file using aplay by the following command:-

aplay -D plug:notification sample-default.wav

It works, but when i am trying to run the following code to play sample-default.wav using alsa library on my notification device:-

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <pthread.h>

void default_sound (char * str) {
    snd_pcm_t *handle;
    snd_pcm_hw_params_t *params;
    snd_pcm_uframes_t frames;

    /* Open PCM device for playback */
    int status = snd_pcm_open(&handle, "notification", SND_PCM_STREAM_PLAYBACK, 0);
    if (status < 0) {
        printf("Unable to open pcm deveice%s\n", snd_strerror(status));
        return;
    }

    /* Allocate a hardware parameter obejct */
    snd_pcm_hw_params_alloca(&params);

    /* Fill it with default values */
    snd_pcm_hw_params_any(handle, params);

    /* Set the desired hardware parameters */

    /* Interleaved mode */
    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

    /* Signed 16-bit little-endian format */
    snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

    /* Single Channel (mono) */
    snd_pcm_hw_params_set_channels(handle, params, 1);

    /* sampling rate */
    unsigned int sample_rate = 16000;
    int dir;
    snd_pcm_hw_params_set_rate_near(handle, params, &sample_rate, &dir);

    /* set period size to 32 frames */
    frames = 32;
    snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

    /* Write the parameters to the driver */
    status = snd_pcm_hw_params(handle, params);
    if(status < 0) {
        printf("Unable to set hw params: %s\n", snd_strerror(status));
        return;
    }

    /* Use a buffer large enough to hold one period */
    snd_pcm_hw_params_get_period_size(params, &frames, &dir);

    int size = frames * 2;
    char *buffer = (char *)malloc(size);

    int readfd = open("sample-call.wav", O_RDONLY);
    if(readfd < 0) {
        printf("Error in opening wav file\n");
        exit(1);
    }


    int readval;
    while(readval = read(readfd, buffer, size) > 0) {
        status = snd_pcm_writei(handle, buffer, frames);
        if(status == -EPIPE) {
            printf("underrun occured\n");
            snd_pcm_prepare(handle);
        }
        else if(status < 0) {
            printf("Error from writei: %s\n", snd_strerror(status));
        }
    }

    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
}

int main() {
    pthread_t pthread_id;
    int status = pthread_create(&pthread_id, NULL, default_sound, "running");
    if (status != 0) {
        printf("Error in creating thread\n");
    }
    pthread_join(pthread_id, NULL);

    return 0;
}

It compiles successfully but throws run-time error :-

8211 segmentation fault (core dumped)  ./play-multiple-sound

I don't know what's going wrong with the above code.

If the above code works fine then I will play two sound files on two devices (notification and sample-sound) and control the volume of these devices using the alsa-mixer library which I have already implemented.

I am trying to achieve this from the past few weeks and none of the solution seems to work for me, so please help me out with this and if you know a better approach then please suggest.


iamNitin16
  • 56
  • 4
  • It might be easier to debug with more information; A [segmentation fault](https://stackoverflow.com/questions/19641597/what-is-segmentation-fault-core-dumped) is a memory issue (which you'll come into a lot in learning C). If you're using gcc, I suggest checking out running it with [gdb](https://www.gnu.org/software/gdb/) so you can debug, to find out, exactly which line in your program the memory error is occuring. Then maybe you will come closer to identifying any other issues (if there are any). – WoodyDev Jun 28 '18 at 13:57
  • thank you, i will resolve that segmentation fault issue using gdb but what i really want to achieve is to play two multiple wav files simultaneously along with controlling their volumes. Is my current approach is correct or do you have any better solution? – iamNitin16 Jun 29 '18 at 11:41
  • Check out the answer by Jonny on this [other related SO post](https://stackoverflow.com/questions/14398573/alsa-api-how-to-play-two-wave-files-simultaneously/14398926) - I think it might help you do it using ALSA. Otherwise, in my mind it would involve mixing the two raw audio buffers together (probably using portaudio to get the data) - Just because DSP is life <3 – WoodyDev Jun 29 '18 at 12:06
  • Ok thanks for the help @WoodyDev. – iamNitin16 Jun 30 '18 at 05:33

0 Answers0