Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice: Where to resample PCM and which tool?

I have developed a kernel module (Android) which provides me:

PCM
16-bit
48000 Hz
2 channel

and I want to stream it to an Apple's Airport Express (AEX) in java.

The AEX needs 44.1 kHz PCM so I have to resample the PCM-stream.

I have following possibilities, but which is the best?

1. Use the C-program "raop_play" (part of raop-play)

advantages: 
            high-performant due to native C
            already uses libsamplerate to resample wav, mp3, ogg, flac, aac, pls
            openssl as static library
            usable via command-line from my java-program via Runtime.exec()

disadvantages:
            I am relative new to C
            overloaded: I don't need wav, mp3.. only PCM
            many dependencies with GPL-libraries which I have to compile for Android
            only supports PCM already with 44.1 kHz, no resampling for PCM implemented yet 
            -> have to implement resampling for PCM

2. Resample & stream in java (with libresample JNI-bridge)

advantages: 
            I CAN java :)
            middle-performant due to resamling in C , but streaming in java
            just one dependency to LGPL-library
            no Runtime.exec() needed

disadvantages:
            needs [bouncycastle][3] for AES which is a bit larger than openssl
            less performant than solution #1 (but maybe fast enough)

3. Resample already in kernel module

advantages: 
            most performant
            no resampling at higher level

disadvantages:
            I am relative new to C
            Is it possible to use libsamplerate or libresample in kernel-space?!
like image 995
Martin L. Avatar asked Jun 27 '12 07:06

Martin L.


1 Answers

I'm a java-guy in heart, but this task (especially on a cpu constrained device, like a handheld) is crying for C. I would suggest simply using the libsamplerate. It has a simple API, and even if you're new to C you'll find plenty of examples by just googling around.

of course the java based solution could and would work, it just doesn't seem polite to the users to eat up their batteries just because your new to C :)

EDIT: I might contradict myself a bit, but even though performance is a serious issue, I would avoid doing anything in kernel-space, unless I know the kernel and the hardware really well. In light of this I would go with a user-space program linked to libsamplerate. After a bit of googling around I've found this example (note that the output is the jack interface, obviously it has to be different for you)

#include <jack/jack.h>
#include <samplerate.h>

int channels;
float data_samplerate;


/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
void getDasData(float **dst,int num_frames){
/* Provide sound data here, and only here. */
}
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////



long getDasResampledData_callback(void *cb_data, float **data){
  static float ret[1024];
  static float ret3[1024];
  static float *ret2[2]={&ret[0],&ret[512]};
  getDasData(ret2,512);
  for(int i=0;i<512;i++){
    ret3[i*2]=ret2[0][i];
    ret3[i*2+1]=ret2[1][i];
  }
  *data=&ret3[0];
  return 512;
}

void getDasResampledData(float **dst,int num_frames){
  double ratio=samplerate/getSourceRate();
  float outsound[num_frames*2];
  long read=src_callback_read(dassrc_state,ratio,num_frames,outsound);
  //fprintf(stderr,"read: %d, num_frames: %d\n",read,num_frames);
  for(int i=0;i<read;i++){
      dst[0][i]=outsound[i*2];
      dst[1][i]=outsound[i*2+1];
  }
  if(read<num_frames){
    float *newdst[2]={dst[0]+read,dst[1]+read};
    getDasResampledData(newdst,num_frames-read);
  }
}


static int process (jack_nframes_t nframes, void *arg){
  int ch;
  sample_t *out[channels];

  for(ch=0;ch<channels;ch++){
    out[ch]=(sample_t*)jack_port_get_buffer(ports[ch],nframes);
  }

  if( (fabs(data_samplerate - jack_samplerate)) > 0.1)
    getDasResampledData(out,numSamples);
  else
    getDasData(outputChannelData,numSamples);
  return;

  audioCallback(NULL,0,out,channels,nframes);
}

int main(){
  dassrc_state=src_callback_new(getDasResampledData_callback,SRC_QUALITY,2,NULL,NULL);
  jack_set_process_callback(client, process,NULL);
}

from http://old.nabble.com/Example-of-using-libresample-with-jack-td8795847.html

This example seems pretty straightforward, I hope you can use it.

like image 81
Gergely Szilagyi Avatar answered Nov 12 '22 09:11

Gergely Szilagyi