2013-06-17 5 views
4

EDIT: Я обновил код, приведенный ниже, чтобы соответствовать прогрессу, который я сделал. Я сам пытаюсь написать заголовок .wav. На данный момент код не работает должным образом, звук не записывается в файл должным образом. Код не содержит попыток конвертировать его в файл .flac.Преобразование raw PCM в FLAC?


Я использую Raspberry Pi (Debian Linux) для записи аудио с ALSA library. Запись работает отлично, но мне нужно кодировать входной звук в кодек FLAC.

Здесь я теряюсь. Я потратил немало времени, пытаясь понять, как преобразовать эти необработанные данные в FLAC, но я продолжаю приводить примеры того, как конвертировать файлы .wav в файлы .flac.

Вот текущий (обновленный) код у меня есть для записи звука с ALSA (это может быть немного грубо, я все еще собирание 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); 
     } 
} 

Как бы идти о преобразование данных PCM в FLAC и сохранение их на диск для последующего использования? Я уже загрузил libflac-dev и просто нужен пример, чтобы уйти.


Как я делаю это прямо сейчас:

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

, как это должно быть (программа делает все для меня):

./capture 
+2

Возможно, для этого вам потребуется использовать C++ и потоковые буферы. Я немного осмотрел интерфейс libflac и нашел это: http://flac.sourceforge.net/api/group__flac__stream__encoder.html#ga56 Это запустило бы кодировку из источника потока, как вы хотите это сделать, но для этого требуется обеспечить поиск() Перезвони. Это может быть сложно в вашем случае, если вы сначала не забудете свой поток. Возможно, C++ API для вас: http://flac.sourceforge.net/api/group__flacpp__encoder.html –

+0

Я бы порекомендовал вам протестировать абсолютно все обращения к alsa для ошибок. Это общая PITA, но она абсолютно необходима. – marko

ответ

2

Пожалуйста, обратитесь к ниже код:

FLAC Encoder Test Code

Этот пример использует WAV-файл в качестве входных данных и затем кодирует его в FLAC.

Как я понимаю, нет существенного различия в черно-белом WAV-файле и ваших RAW-данных, я думаю, вы можете изменить этот код, чтобы напрямую прочитать «буфер» и преобразовать его. У вас уже есть вся соответствующая информация (Channel/Bitrate и т. Д.), Поэтому не следует сильно удалять код чтения заголовка WAV.

+0

Просто, чтобы быть ясным ... Я не пробовал реализовать его :): P –

+0

Я закончил использовать код из вашей ссылки, чтобы заставить кодер работать, поэтому я отмечу это как принятый ответ. – syb0rg

3

Если я понимаю документацию FLAC::Encoder::File, вы можете сделать что-то вроде

#include <FLAC++/encoder.h> 

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

где buffer - массив (размер samples) 32-разрядных целых указателей.

К сожалению, я почти ничего не знаю об аудиокодировании, поэтому я не могу говорить ни о каких других вариантах. Удачи!

1

Обратите внимание: это модифицированная версия образца Flac Encoder из их git-репо.

В него включены некоторые комментарии и подсказки о том, как изменить его на требования OP, весь источник для этого будет немного длиннее.

И обратите внимание, что это C API, который имеет тенденцию быть немного более сложным, чем C++. Но довольно легко конвертировать между этими двумя, как только вы получите идею.

#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'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); 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^