Skip to content

Instantly share code, notes, and snippets.

@eruffaldi
Last active October 19, 2019 16:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eruffaldi/86755065f4c777f01f972abf51890a6e to your computer and use it in GitHub Desktop.
Save eruffaldi/86755065f4c777f01f972abf51890a6e to your computer and use it in GitHub Desktop.
PortAudio to MP3
// https://github.com/eruffaldi/cmakego
find_package(cmakego COMPONENTS portaudio lame)
add_executable(testpa testpa.cpp)
target_link_libraries(testpa p::portaudio p::lame)
// NOTES:
// https://github.com/EddieRingle/portaudio/blob/master/src/common/pa_converters.c
// http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html#a6fea69f3d81b628288325c06310b2fcf
//
#include "portaudio.h"
#include <iostream>
#include <vector>
#include <fstream>
#include "lame/lame.h"
struct Mp3encoder
{
Mp3encoder(int samplerate, int channels, int bitrate, std::string name);
~Mp3encoder();
void encode_inter(const short * s, int samples);
void encode_sep(const int32_t * L, const int32_t *R, int samples);
void encode_sep(const int16_t * L, const int16_t *R, int samples);
void flush();
std::vector<char> buf;
std::ofstream onf;
lame_global_flags *p;
};
Mp3encoder::Mp3encoder(int samplerate, int channels, int bitrate, std::string name): onf(name.c_str(),std::ios::binary)
{
buf.resize(1024*64);
p = lame_init();
if(!p)
{
std::cout << "bad p " << std::endl;
exit(0);
}
lame_set_in_samplerate(p,samplerate); // default is 44100
lame_set_num_channels(p,channels);
lame_set_out_samplerate(p,0); // automatic
/*
internal algorithm selection. True quality is determined by the bitrate
but this variable will effect quality by selecting expensive or cheap algorithms.
quality=0..9. 0=best (very slow). 9=worst.
recommended: 2 near-best quality, not too slow
5 good quality, fast
7 ok quality, really fast
*/
//lame_set_quality(p,...)
lame_set_brate(p,bitrate);
lame_init_params(p);
}
void Mp3encoder::encode_inter(const short * s, int samples)
{
int n = lame_encode_buffer_interleaved(p,(short int*)s,samples,(unsigned char*)&buf[0],buf.size());
onf.write(&buf[0],n);
}
void Mp3encoder::encode_sep(const int32_t * L,const int32_t * R, int samples)
{
int n = lame_encode_buffer_int(p,(int32_t*)L,(int32_t*)R,samples,(unsigned char*)&buf[0],buf.size());
onf.write(&buf[0],n);
}
void Mp3encoder::encode_sep(const int16_t * L,const int16_t * R, int samples)
{
int n = lame_encode_buffer(p,(int16_t*)L,(int16_t*)R,samples,(unsigned char*)&buf[0],buf.size());
onf.write(&buf[0],n);
}
void Mp3encoder::flush()
{
int n = lame_encode_flush(p,(unsigned char*)&buf[0],buf.size());
onf.write(&buf[0],n);
}
Mp3encoder::~Mp3encoder()
{
lame_close(p);
}
int portAudioCallback_int(const void * input, void * output,
unsigned long frameCount, const PaStreamCallbackTimeInfo * timeInfo,
PaStreamCallbackFlags statusFlags, void * userData){
Mp3encoder * enc = (Mp3encoder*)userData;
enc->encode_inter((short*)input,frameCount);
return 0;
}
int portAudioCallback_i32(const void * input, void * output,
unsigned long frameCount, const PaStreamCallbackTimeInfo * timeInfo,
PaStreamCallbackFlags statusFlags, void * userData){
Mp3encoder * enc = (Mp3encoder*)userData;
const int32_t ** pc = (const int32_t**)input;
enc->encode_sep(pc[0],pc[1],frameCount);
return 0;
}
int portAudioCallback_i16(const void * input, void * output,
unsigned long frameCount, const PaStreamCallbackTimeInfo * timeInfo,
PaStreamCallbackFlags statusFlags, void * userData){
Mp3encoder * enc = (Mp3encoder*)userData;
const int16_t ** pc = (const int16_t**)input;
enc->encode_sep(pc[0],pc[1],frameCount);
return 0;
}
int main(int argc, char const *argv[])
{
static_assert(sizeof(int) == 4,"lame support 32");
const char * whichname = argc == 1 ? "Built-in Microph": argv[1];
Pa_Initialize();
int used_device = -1;
for(int i = 0; i < Pa_GetDeviceCount(); ++i){
//std::cout << Pa_GetDeviceInfo(i)->name << std::endl;
if(std::string(Pa_GetDeviceInfo(i)->name).find(whichname) != std::string::npos)
{
used_device = i;
break;
}
std::cout << "skipped " << Pa_GetDeviceInfo(i)->name << std::endl;
}
if(used_device == -1){
std::cout << "device " << whichname << std::endl;
return 0;
}
std::cout << "Using device : " << Pa_GetDeviceInfo(used_device)->name << std::endl;
const double srate = Pa_GetDeviceInfo(used_device)->defaultSampleRate;
PaStream * stream;
unsigned long framesPerBuffer = paFramesPerBufferUnspecified;
//PaStreamParameters outputParameters;
PaStreamParameters inputParameters;
inputParameters.channelCount = Pa_GetDeviceInfo(used_device)->maxInputChannels;
inputParameters.device = used_device;
inputParameters.hostApiSpecificStreamInfo = NULL;
inputParameters.sampleFormat = paInt32|paNonInterleaved; // paFloat32;
inputParameters.suggestedLatency = Pa_GetDeviceInfo(used_device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
std::cout << "initing encoder with channels " << inputParameters.channelCount << " srate " << srate << std::endl;
Mp3encoder enc(srate,inputParameters.channelCount,128000,"ciao.mp3");
std::cout << "done encoder " << std::endl;
if(Pa_OpenStream(&stream, &inputParameters, NULL, srate, framesPerBuffer, paNoFlag, portAudioCallback_i32, &enc) || Pa_StartStream(stream)){
std::cout << "error opening stream. Audio won't be available" << std::endl;
} else{
int q = 10;
while(q--)
{
std::cout << " q " << q << std::endl;
Pa_Sleep(1000);
}
}
Pa_CloseStream(stream);
Pa_Terminate();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment