HI. I'm reading in a wave file and writing it to a sound device using ALSA. Im not a C++ guy (C#) so not very handy with its file I/O methods..

Any idea how do i change :

int rc = read(0, buffer, size);

To work with any file i.e. one whose path is specified in code as a string, instead of 0 (which is the standard input)

Recommended Answers

All 11 Replies

Member Avatar for iamthwee

Hey jbennet,

Is this a binary file?

Yeah, its a wave (.wav) audio file....

Cheers, this bit looks promising, gonna give it a go. I dont want to read it all in to memory at once though? I need to get 5 second samples at a time, or else ill flood the soundcards buffer...

ifstream file ("example.bin", ios::in|ios::binary|ios::ate);
  if (file.is_open())
  {
    size = file.tellg();
    memblock = new char [size];
    file.seekg (0, ios::beg);
    file.read (memblock, size);
// Do something with it
    file.close();
}

If your ultimately wanting to play sound then I would suggest using a library that handles reading and playing sounds for you already. A suggestion would be to use FMOD. Or if you just want to develop on windows then you can use the PlaySound function

Its got to run on a small embedded (i.e. handheld) system, though. So needs to be done pretty low level.

.wav takes a little more than shoveling it onto a sound card. A starter overview is here, for example.

So, what you need to do is to read and validate the chunk descriptor; read a subchunk 1, which gives you, among other things, a "byte rate"; multiply the byte rate by the number of seconds, and read that amount of bytes from the data subchunk. All of that assuming PCM data.

Ok, well ive written some code, but it doesnt work, any help guys?

Its supposed to take a wave file in the correct format piped to it from stdin. The soundcard gets configured right (seemingly) but it just doesnt play.

/*
This program opens an audio interface for playback, configures it for
"CD" audio - 2ch Stereo, 16 bit at 44.1 khz

It reads from stdin and writes 5secs of data.to the default PCM device

Compile with g++ (filename) -lasound

http://www.linuxjournal.com/article/6735?page=0,1
http://www.equalarea.com/paul/alsa-audio.html
http://soundprogramming.net/programming_apis/alsa_tutorial_1_initialization
*/

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
#include <sys/ioctl.h>

using namespace std;

// Use the newer ALSA API
#define ALSA_PCM_NEW_HW_PARAMS_API

// Globals are bad but oh well, keeps it simple.
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;

// Runs ALSA sound plackback tests
void testSound()
{
    long loops;
    int rc;
    int size;
    unsigned int val;
    int dir;
    snd_pcm_uframes_t frames;
    char *buffer;
    int err;

    // Try to open the default device
    err = snd_pcm_open( &handle, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0 );

    // Check for error on open.
    if( err < 0 )
        {
            cout << "ERROR: Cannot open audio device! " << snd_strerror (err)
                 << ")" << endl;
            return;
        }
    else
        {
            cout << "SUCCESS: Audio device opened successfully!" << endl;
        }

    // Allocate the hardware parameter structure.
    if ((err =   snd_pcm_hw_params_malloc (&params)) < 0) // malloc vs alloca ?
        {
            cout << "ERROR: Error encountered while allocating hardware parameter structure! (" << snd_strerror
                    (err) << ")" << endl;
            return;
        }

    //Fill it in with default values.
    if ((err = snd_pcm_hw_params_any (handle, params)) < 0)
        {
            cout << "ERROR: Error encountered while initializing hardware parameter structure! (" << snd_strerror
                    (err) << ")" << endl;
            return;
        }

    // Enable resampling.
    unsigned int resample = 1;
    err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
    if (err < 0)
        {
            cout << "ERROR: Setting up resampling for playback failed! " << snd_strerror(err) <<
                    endl;
            return;
        }

    // Set access to RW interleaved.
    if ((err = snd_pcm_hw_params_set_access (handle, params,
                                             SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
        {
            cout << "ERROR: Cannot set RW-Interleaved access type (" << snd_strerror (err) << ")" << endl;
            return;
        }

    // Signed 16-bit little-endian format
    if ((err = snd_pcm_hw_params_set_format (handle, params,
                                             SND_PCM_FORMAT_S16_LE)) < 0)
        {
            cout << "ERROR: Cannot set 16-Bit PCM Sample format (" << snd_strerror (err) << ")" << endl;
            return;
        }

    // Set to 2channel (Stereo)
    if ((err = snd_pcm_hw_params_set_channels (handle, params, 2)) < 0)
        {
            cout << "ERROR: Cannot set channel count to 2 (Stereo) (" << snd_strerror (err) << ")" << endl;
            return;
        }

    // Set sample rate to 44.1 khz (CD quality).
    unsigned int actualRate = 44100;
    if ((err = snd_pcm_hw_params_set_rate_near (handle, params, &actualRate,
                                                0)) < 0)
        {
            cout << "ERROR: Cannot set sample rate to 44100 (44.1khz)! (" << snd_strerror (err) << ")"
                 << endl;
            return;
        }
    if( actualRate < 44100 )
        {
            cout << "ERROR: Sample rate does not match that requested? (" << "44100 requested, " << actualRate << " acquired)" << endl;
        }

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

    // Write the hardware parameters that we've just set to the driver.
    if ((err = snd_pcm_hw_params (handle, params)) < 0)
        {
            cout << "ERROR: Cannot set HW parameters (" << snd_strerror (err) << ")" << endl;
            return;
        }
    else
        {
            cout << "SUCCESS: Audio device HW parameters have been set successfully!" << endl;
        }

    // Get the buffer size.
    snd_pcm_uframes_t bufferSize;
    snd_pcm_hw_params_get_buffer_size( params, &bufferSize );

    // If we were going to do more with our sound device we would want to store
    // the buffer size so we know how much data we will need to fill it with.
    cout << "Buffer size: " << bufferSize << " frames." << endl;

    // Display the bit size of samples.
    cout << "Significant bits (For linear samples): " <<
            snd_pcm_hw_params_get_sbits(params) << endl;

    // Free the hardware parameters now that we're done with them.
    snd_pcm_hw_params_free (params);

    // Prepare interface for use.
    if ((err = snd_pcm_prepare (handle)) < 0)
        {
            cout << "ERROR: Unable to prepare audio interface for use! (" << snd_strerror (err)
                 << ")" << endl;
            return;
        }
    else
        {
            cout << "SUCCESS: Audio device has been prepared for use." << endl;
        }

    // Done, now play back file

    // Open PCM device for playback.
    rc = snd_pcm_open(&handle, "default",
                      SND_PCM_STREAM_PLAYBACK, 0);
    if (rc < 0) {
        fprintf(stderr,
                "ERROR: Unable to open PCM device: %s\n",
                snd_strerror(rc));
        exit(1);
    }

    // Use a buffer large enough to hold one period
    snd_pcm_hw_params_get_period_size(params, &frames,
                                      &dir);
    size = frames * 4; // 2 bytes/sample, 2 channels
    buffer = (char *) malloc(size);

    // We want to loop for 5 seconds (microseconds / period time)
    snd_pcm_hw_params_get_period_time(params,
                                      &val, &dir);

    loops = 5000000 / val;

    while (loops > 0) {
        loops--;
        rc = read(0, buffer, size); // Read from file descriptor/handle 0 - stdin
        if (rc == 0) {
            fprintf(stderr, "SUCCESS: Reached EOF\n");
            break;
        } else if (rc != size) {
            fprintf(stderr,
                    "ERROR: Short read! - Read %d bytes\n", rc);
        }
        rc = snd_pcm_writei(handle, buffer, frames);
        if (rc == -EPIPE) {
            // EPIPE means underrun
            fprintf(stderr, "ERROR: Underrun occurred!\n");
            snd_pcm_prepare(handle);
        } else if (rc < 0) {
            fprintf(stderr,
                    "ERROR: Writei: %s\n",
                    snd_strerror(rc));
        }  else if (rc != (int)frames) {
            fprintf(stderr,
                    "ERROR: Short write, wrote %d frames!\n", rc);
        }
    }

    // Tidy up
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    cout << "SUCCESS: Audio device has been uninitialized." << endl;
}

int main(int argc, char *argv[])
{
    testSound();
    return 0;
}

Right near the bottom, you're looping for 5 seconds, but do you have 5 seconds worth of audio in your .wav file to read in? Probably, it's only 5 seconds....

Also, from your earlier question, you're still reading audio data from stdin ... how are you getting the data out of the .wav file? It's not just raw audio samples (see the previous comment), so you can't do cat sound.wav | my_sound_player.exe . Look up how to read data from a .wav file (or find and use an existing library).

nevermind was just a one line issue in the code... was trying to reinitialise the device which led to a floating point error

Cool! If/when you're all set, pease mark this thread as "Solved". :)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.