将原始PCM转换为FLAC?

编辑:我已经更新了下面的代码,类似于我所取得的进展。 我正在尝试自己编写.wav标题。 截至目前,代码无法正常工作,音频未正确写入文件。 该代码不包含任何将其转换为.flac文件的尝试。


我正在使用Raspberry Pi( Debian Linux )来记录ALSA库的音频。 录音工作正常,但我需要将输入音频编码到FLAC编解码器中。

这是我迷路的地方。 我花了相当多的时间试图弄清楚如何将这些原始数据转换为FLAC ,但我不断提出如何将.wav文件转换为.flac文件的示例。

这是我用ALSA录制音频的当前( 更新 )代码(可能有点粗糙,我还在拿起C ++):

 // Use the newer ALSA API #define ALSA_PCM_NEW_HW_PARAMS_API #include  #include  #include  #include  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) { 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); } } 

我如何将PCM数据转换为FLAC并将其保存到磁盘供以后使用? 我已经下载了libflac-dev ,只需要一个例子即可。


我现在这样做的方式:

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

它的方式应该是(程序为我做的一切):

 ./capture 

请参考以下代码:

FLAC编码器测试代码

此示例使用wav文件作为输入,然后将其编码为FLAC。

据我所知,没有重大差异黑白WAV文件和您的RAW数据,我认为您可以修改此代码直接读取“缓冲区”并进行转换。 您已经拥有所有相关信息(通道/比特率等),因此删除WAV标头读取代码应该不会有太大问题。

如果我理解FLAC::Encoder::File文档,你可以做类似的事情

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

其中buffer是32位整数指针的数组(大小samples )。

不幸的是,我对音频编码几乎一无所知,所以我不能说任何其他选择。 祝好运!

请注意:这是来自git repo的Flac Encoder样本的修改版本。

它包含一些关于如何将其更改为OP要求的注释和提示,整个来源将有点长。

请注意,这是C API,它往往比C ++复杂一点。 但是一旦你明白了,就很容易在两者之间进行转换。

 #include  #include  #include  #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's processing two channels here, but it's not it's 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's 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); }