0

Im trying to record audio using ALSA and pass it to be processed. The audio sample is returned from this which is char* to a float*

Ive tried so many solutions I think I understand that it's not really a char buffer but a byte buffer but how I get it a float.

This returns the buffer:

const unsigned char* arBuffer(void)
{
    return buffer;
}

I need to consume the output of the microphone as a float

 int32_t O_DecodeAudioBuffer(float *audioBuffer, int size, void *oxyingObject)
{
  Core *oxying = (COxyCore*)oxyingObject;

  //Decode audioBuffer to check if begin token is found, we should keep previous buffer to check if token was started in previous
  //var mDecoding > 0 when token has been found, once decoding is finished, mDecoding = 0
  return oxying->mDecoder->DecodeAudioBuffer(audioBuffer, size);
}

Im writing a program to consume the the above as api:

void* mOxyCore; is declared

I then try and pass the arBuffer() which wouldn't work as expected.

while(arIsRunning())
    {

        int ret = DecodeAudioBuffer(arBuffer(), arBufferSize(), mCore);
    }

The Alsa:

/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API

#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <pthread.h>
#include "settings.h"
#include "audiorecorder.h"


pthread_t thr;
pthread_mutex_t mutex;
snd_pcm_t *handle;
snd_pcm_uframes_t frames;
unsigned char* buffer;
BOOL running;
size_t buffersize;


BOOL arIsRunning(void)
{
    return running;
}


void arAcquireBuffer(void)
{
    //printf("Acquired buffer\n");
    pthread_mutex_lock(&mutex);
}


void arReleaseBuffer(void)
{
    //printf("Released buffer\n");
    pthread_mutex_unlock(&mutex);
}


const unsigned char* arBuffer(void)
{
    return buffer;
}


const size_t arBufferSize(void)
{
    return buffersize;
}


void* entry_point(void *arg)
{
    int rc;

    fprintf(stderr, "Listening...\n");

  while (running)
    {
        arAcquireBuffer();
    rc = snd_pcm_readi(handle, buffer, frames);

    //stream to stdout - useful for testing/debugging
    //write(1, buffer, buffersize);
        arReleaseBuffer();

    if (rc == -EPIPE) {
      /* EPIPE means overrun */
      fprintf(stderr, "overrun occurred\n");
      snd_pcm_prepare(handle);
    }
        else if (rc < 0) {
      fprintf(stderr, "error from read: %s\n", snd_strerror(rc));
            running = FALSE;
    }
        else if (rc != (int)frames) {
      fprintf(stderr, "short read, read %d frames\n", rc);
    }
 }

    return NULL;
}



int arInitialise(void)
{
  snd_pcm_hw_params_t *params;
  unsigned int val;
  int rc, dir;

    running = FALSE;

  /* Open PCM device for recording (capture). */
  rc = snd_pcm_open(&handle, RECORDER_DEVICE, SND_PCM_STREAM_CAPTURE, 0);
  if (rc < 0) {
    fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
    return rc;
  }
    else
    {
        fprintf(stderr, "Successfully opened default capture device.\n");
    }

  /* Allocate a hardware parameters object. */
  snd_pcm_hw_params_alloca(&params);

  /* Fill it in 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);
    fprintf(stderr, "Format set to PCM Signed 16bit Little Endian.\n");


  /* Channels */
  snd_pcm_hw_params_set_channels(handle, params, NUM_CHANNELS);
    fprintf(stderr, "Channels set to %d.\n", NUM_CHANNELS);


  /* sampling rate */
  val = SAMPLE_RATE;
  snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
    fprintf(stderr, "Samplerate set to %d.\n", val);

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

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

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

  /* We want to loop forever */
  //snd_pcm_hw_params_get_period_time(params, &val, &dir);

  return 0;
}


int arStartRecording(void)
{
    if(running) return 1;

  if(pthread_mutex_init(&mutex, NULL))
  {
    printf("Unable to initialize mutex\n");
    return -1;
  }

  if(pthread_create(&thr, NULL, &entry_point, NULL))
  {
    fprintf(stderr, "Could not create recorder thread!\n");
        running = FALSE;
    return -1;
  }

    running = TRUE;
    return 0;
}


void arStopRecording(void)
{
    running = FALSE;
}


void arFree(void)
{
    running = FALSE;
    sleep(500);
  snd_pcm_drain(handle);
  snd_pcm_close(handle);
  pthread_mutex_destroy(&mutex);
  free(buffer);
}
  • https://stackoverflow.com/questions/18494218/how-to-convert-char-into-float please have a look at this – Sambhav jain May 07 '20 at 08:42
  • thanks I've seen that answer for me the stumble is the C-style cast from 'const unsigned char *' to 'float' is not allowed. The Alsa recorder is in c and the main program is in c++ –  May 07 '20 at 09:06
  • Is `Decoder::DecodeAudioBuffer` writing to `audioBuffer`? If so, is `arBuffer()` intentionally returning a `const char*`? Maybe provide a [MRE] (e.g., what is `mCore` and why do you pass it as argument to a function which does not take it?) – chtz May 07 '20 at 11:08
  • I've updated my answer thank you for you help. arBuffer() is intentionally the returning const char* is their another way? –  May 07 '20 at 11:52
  • The Decoder::DecodeAudioBuffer function is writing and then it analysed for a token –  May 07 '20 at 12:04
  • Please update the question to describe how the number to be converted are represented in the `char` data. Some examples: (a) The `char` data contains characters forming decimal numerals, such as “3.4” or “-5.6e7”. (b) The `char` data contains a 16-bit integer in two consecutive `char` objects, little-end first, using two’s complement, and the desired `float` is that integer or, perhaps, is that integer multiplied by some scale factor. – Eric Postpischil May 07 '20 at 12:12

1 Answers1

0

The problem here isn't a cast, but a representation issue.

Audio is generally represented as a series of samples. There are quite a few ways to represent each sample: on a scale from -1.0f to +1.0f, or -32767 to +32767, or many others.

Alsa supports in fact many formats, and you chose SND_PCM_FORMAT_S16_LE so that's -32767 to +32767. You could cast that to std::int16_t*, assuming your C++ environment is Little-Endian (almost certain). You can't cast it to float*, for that you'd need to ask for SND_PCM_FORMAT_FLOAT_LE

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • After changing the format and doing this //Cast to float float value = (*(float*)arBuffer()); im getting numbers like 1.46939e-39 when printed im hoping this is getting closer –  May 07 '20 at 16:45
  • 1
    @JJohn: Well, _now_ you have a cast error. You should create a `std::vector values`; you won't get a single value. Resize this to `arBufferSize()/sizeof(float)`, then `memcpy` from `arBuffer` to `&values[0]`. – MSalters May 08 '20 at 07:04
  • Thank you so much for pointing me in the right direction again Ive had to ask a another question to community again hopefully I can master this https://stackoverflow.com/questions/61676227/cast-void-to-memcpy-to-get-a-float-value –  May 08 '20 at 09:41