Convert raw PCM to FLAC?

EDIT: I updated the code below to resemble the progress I made. I am trying to write a .wav header. At the moment, the code is not working properly, the sound is not written to the file properly. The code does not contain any attempts to convert it to a .flac file.


I use Raspberry Pi ( Debian Linux ) to record sound using ALSA Library . Recording works fine, but I need to encode the input sound in the FLAC codec.

This is where I got lost. I spent a lot of time trying to figure out how to convert this raw data to FLAC , but I keep coming up with examples of how to convert .wav files to .flac files.

Here is the current ( updated ) code that I have for recording sound using ALSA (this can be a little rude, I'm still typing C ++):

 // Use the newer ALSA API #define ALSA_PCM_NEW_HW_PARAMS_API #include <alsa/asoundlib.h> #include <stdio.h> #include <stdlib.h> #include <string.h> struct Riff { char chunkId[4]; // "RIFF" (assuming char is 8 bits) int chunkSize; // (assuming int is 32 bits) char format[4]; // "WAVE" }; struct Format { char chunkId[4]; // "fmt " int chunkSize; short format; // assuming short is 16 bits short numChannels; int sampleRate; int byteRate; short align; short bitsPerSample; }; struct Data { char chunkId[4]; // "data" int chunkSize; // length of data char* data; }; struct Wave // Actual structure of a PCM WAVE file { Riff riffHeader; Format formatHeader; Data dataHeader; }; int main(int argc, char *argv[]) { void saveWaveFile(struct Wave *waveFile); long loops; int rc; int size; snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned int sampleRate = 44100; int dir; snd_pcm_uframes_t frames; char *buffer; char *device = (char*) "plughw:1,0"; //char *device = (char*) "default"; printf("Capture device is %s\n", device); /* Open PCM device for recording (capture). */ rc = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0); if (rc < 0) { fprintf(stderr, "Unable to open PCM device: %s\n", snd_strerror(rc)); exit(1); } /* 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); /* Two channels (stereo) */ snd_pcm_hw_params_set_channels(handle, params, 2); /* 44100 bits/second sampling rate (CD quality) */ snd_pcm_hw_params_set_rate_near(handle, params, &sampleRate, &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 */ rc = snd_pcm_hw_params(handle, params); if (rc < 0) { fprintf(stderr, "Unable to set HW parameters: %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 */ snd_pcm_hw_params_get_period_time(params, &sampleRate, &dir); loops = 5000000 / sampleRate; while (loops > 0) { loops--; rc = snd_pcm_readi(handle, buffer, frames); 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)); } else if (rc != (int)frames) { fprintf(stderr, "Short read, read %d frames.\n", rc); } if (rc != size) fprintf(stderr, "Short write: wrote %d bytes.\n", rc); } Wave wave; strcpy(wave.riffHeader.chunkId, "RIFF"); wave.riffHeader.chunkSize = 36 + size; strcpy(wave.riffHeader.format, "WAVE"); strcpy(wave.formatHeader.chunkId, "fmt"); wave.formatHeader.chunkSize = 16; wave.formatHeader.format = 1; // PCM, other value indicates compression wave.formatHeader.numChannels = 2; // Stereo wave.formatHeader.sampleRate = sampleRate; wave.formatHeader.byteRate = sampleRate * 2 * 2; wave.formatHeader.align = 2 * 2; wave.formatHeader.bitsPerSample = 16; strcpy(wave.dataHeader.chunkId, "data"); wave.dataHeader.chunkSize = size; wave.dataHeader.data = buffer; saveWaveFile(&wave); snd_pcm_drain(handle); snd_pcm_close(handle); free(buffer); return 0; } void saveWaveFile(struct Wave *waveFile) { FILE *file = fopen("test.wav", "wb"); size_t written; if (file == NULL) { fprintf(stderr, "Cannot open file for writing.\n"); exit(1); } written = fwrite(waveFile, sizeof waveFile[0], 1, file); fclose(file); if (written < 1); { fprintf(stderr, "Writing to file failed, error %d.\n", written); exit(1); } } 

How do I go about converting PCM data to FLAC and save it to disk for later use? I already downloaded libflac-dev and just need an example to get away.


How am I doing this right now:

 ./capture > test.raw // or ./capture > test.flac 

How it should be (the program does everything for me):

 ./capture 
+4
source share
3 answers

Please refer to the code below:

FLAC Code Verification Code

This example uses a wav file as input, and then it is encoded in FLAC.

As I understand it, there is no major difference in the w / w file of the WAV and your RAW data, I think you can change this code to read the "buffer" directly and convert it. You already have all the relevant information (Channel / Bitrate, etc.), so it should not be a big problem to remove the code for reading WAV headers.

+2
source

If I understand the documentation of FLAC::Encoder::File , you can do something like

 #include <FLAC++/encoder.h> FLAC::Encoder::File encoder; encoder.init("outfile.flac"); encoder.process(buffer, samples); encoder.finish(); 

where buffer is an array (size of samples ) of 32-bit integer pointers.

Unfortunately, I know almost nothing about audio encoding, so I can’t talk about any other options. Good luck

+3
source

Note: this is a modified version of the Flac Encoder sample from the git repository.

It contains some comments and tips on how to change it to OP requirements, the whole source for this will be a little longer.

And note that this is a C API, which is usually a bit more complex than C ++. But it’s pretty easy to convert between them as soon as you get this idea.

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "share/compat.h" #include "FLAC/metadata.h" #include "FLAC/stream_encoder.h" /* this call back is what tells your program the progress that the encoder has made */ static void progress_callback(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data); #define READSIZE 1024 static unsigned total_samples = 0; /* can use a 32-bit number due to WAVE size limitations */ /* buffer is where we record to, in your case what ALSA writes to */ /* Note the calculation here to take the total bytes that the buffer takes */ static FLAC__byte buffer[READSIZE/*samples*/ * 2/*bytes_per_sample*/ * 2/*channels*/]; /* pcm is input to FLAC encoder */ /* the PCM data should be here, bps is 4 here...but we are allocating ints! */ static FLAC__int32 pcm[READSIZE/*samples*/ * 2/*channels*/]; int main(int argc, char *argv[]) { FLAC__bool ok = true; FLAC__StreamEncoder *encoder = 0; FLAC__StreamEncoderInitStatus init_status; FLAC__StreamMetadata *metadata[2]; FLAC__StreamMetadata_VorbisComment_Entry entry; FILE *fin; unsigned sample_rate = 0; unsigned channels = 0; unsigned bps = 0; if((fin = fopen(argv[1], "rb")) == NULL) { fprintf(stderr, "ERROR: opening %s for output\n", argv[1]); return 1; } /* set sample rate, bps, total samples to encode here, these are dummy values */ sample_rate = 44100; channels = 2; bps = 16; total_samples = 5000; /* allocate the encoder */ if((encoder = FLAC__stream_encoder_new()) == NULL) { fprintf(stderr, "ERROR: allocating encoder\n"); fclose(fin); return 1; } ok &= FLAC__stream_encoder_set_verify(encoder, true); ok &= FLAC__stream_encoder_set_compression_level(encoder, 5); ok &= FLAC__stream_encoder_set_channels(encoder, channels); ok &= FLAC__stream_encoder_set_bits_per_sample(encoder, bps); ok &= FLAC__stream_encoder_set_sample_rate(encoder, sample_rate); ok &= FLAC__stream_encoder_set_total_samples_estimate(encoder, total_samples); /* sample adds meta data here I've removed it for clarity */ /* initialize encoder */ if(ok) { /* client data is whats the progress_callback is called with, any objects you need to update on callback can be passed thru this pointer */ init_status = FLAC__stream_encoder_init_file(encoder, argv[2], progress_callback, /*client_data=*/NULL); if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) { fprintf(stderr, "ERROR: initializing encoder: %s\n", FLAC__StreamEncoderInitStatusString[init_status]); ok = false; } } /* read blocks of samples from WAVE file and feed to encoder */ if(ok) { size_t left = (size_t)total_samples; while(ok && left) { /* record using ALSA and set SAMPLES_IN_BUFFER */ /* convert the packed little-endian 16-bit PCM samples from WAVE into an interleaved FLAC__int32 buffer for libFLAC */ /* why? because bps=2 means that we are dealing with short int(16 bit) samples these are usually signed if you do not explicitly say that they are unsigned */ size_t i; for(i = 0; i < SAMPLES_IN_BUFFER*channels; i++) { /* THIS. this isn't the only way to convert between formats, I do not condone this because at first the glance the code seems like it processing two channels here, but it not it just copying 16bit data to an int array, I prefer to use proper type casting, none the less this works so... */ pcm[i] = (FLAC__int32)(((FLAC__int16)(FLAC__int8)buffer[2*i+1] << 8) | (FLAC__int16)buffer[2*i]); } /* feed samples to encoder */ ok = FLAC__stream_encoder_process_interleaved(encoder, pcm, SAMPLES_IN_BUFFER); left-=SAMPLES_IN_BUFFER; } } ok &= FLAC__stream_encoder_finish(encoder); fprintf(stderr, "encoding: %s\n", ok? "succeeded" : "FAILED"); fprintf(stderr, " state: %s\n", FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(encoder)]); FLAC__stream_encoder_delete(encoder); fclose(fin); return 0; } /* the updates from FLAC encoder system comes here */ void progress_callback(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data) { (void)encoder, (void)client_data; fprintf(stderr, "wrote %" PRIu64 " bytes, %" PRIu64 "/%u samples, %u/%u frames\n", bytes_written, samples_written, total_samples, frames_written, total_frames_estimate); } 
+1
source

Source: https://habr.com/ru/post/1486525/


All Articles