簡體   English   中英

在libsndfile中使用libsamplerate

[英]Using libsamplerate with libsndfile

這段代碼是我嘗試更好地理解音頻編碼的一部分。 在這里,使用libsndfile打開文件,使用libsamplerate轉換為新的采樣率,並使用libao播放結果。

播放位,通道和速率的各種組合時,結果如下:

測試編號,位數,通道,速率,結果

  1. 8、1、11025,好的
  2. 8,2,11025,音頻不穩。 音高和速度都可以,否則。
  3. 16,1,11025可以
  4. 16,2,11025,聲音不穩。 音高和速度都可以,否則。
  5. 8、1、44100,可以
  6. 8、2、44100,好的
  7. 16,1,44100還可以
  8. 16,2,44100還可以

為什么測試2和4失敗?

 /*
 * Objective: sample rate conversion
 * compile with
 * "gcc -o glurp glurp.c -lao -lsndfile -lsamplerate"
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ao/ao.h>
#include <sndfile.h>
#include <samplerate.h>

#define DEFAULT_CONVERTER SRC_SINC_MEDIUM_QUALITY
#define NEW_RATE 44100

#define BUFFSIZE 4096
#define MAX(x,y) ((x)>(y)) ? (x) : (y)
#define MIN(x,y) ((x)<(y)) ? (x) : (y)

int playfile(FILE *, int);
void floattopcm16(short *, float *, int);
void pcm16tofloat(float *, short *, int);

int main(int argc, char *argv[])
{
    FILE *fp;
    int newrate;

    if (argc < 2) {
        printf("usage: %s <filename> <rate>\n", argv[0]);
    exit(1);
    }

    fp = fopen(argv[1], "rb");
    if (fp == NULL) {
        printf("Cannot open %s.\n", argv[1]);
    exit(1);
    }

    if (argv[2])
        newrate = atoi(argv[2]);
    else
        newrate = NEW_RATE;

    playfile(fp, newrate);

    return 0;
}

int playfile(FILE *fp, int newrate)
{
    int default_driver;
    int frames_read;
    int count;
    int toread;
    int readnow;
    float *floatbuffer;
    float *floatbuffer2;
    short *shortbuffer;
    long filestart;

    int volcount;

    ao_device *device;
    ao_sample_format format;
    SNDFILE     *sndfile;
    SF_INFO sf_info;

    SRC_STATE   *src_state;
    SRC_DATA    src_data;
    int     error;
    double  max = 0.0;
    sf_count_t  output_count = 0;

    ao_initialize();
    default_driver = ao_default_driver_id();

    sf_info.format = 0;

    filestart = ftell(fp);

    sndfile = sf_open_fd(fileno(fp), SFM_READ, &sf_info, 0);

    memset(&format, 0, sizeof(ao_sample_format));

    format.byte_format = AO_FMT_NATIVE;
    format.bits = 16;
    format.channels = sf_info.channels;
    format.rate = newrate;

    printf("Start sample rate:  %d\n", sf_info.samplerate);
    printf("Ending sample rate: %d\n", newrate);

    device = ao_open_live(default_driver, &format, NULL /* no options */);
    if (device == NULL) {
        printf("Error opening sound device.\n");
        return 1;
    }

    floatbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
    floatbuffer2 = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
    shortbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(short));
    frames_read = 0;
    toread = sf_info.frames * sf_info.channels;

    /* Set up for conversion */
    if ((src_state = src_new(DEFAULT_CONVERTER, sf_info.channels, &error)) == NULL) {
        printf("Error: src_new() failed: %s.\n", src_strerror(error));
        exit(1);
    }
    src_data.end_of_input = 0;
    src_data.input_frames = 0;
    src_data.data_in = floatbuffer;
    src_data.src_ratio = (1.0 * newrate) / sf_info.samplerate;
    src_data.data_out = floatbuffer2;
    src_data.output_frames = BUFFSIZE / sf_info.channels;

    while (1) {
         /* if floatbuffer is empty, refill it */
         if (src_data.input_frames == 0) {
             src_data.input_frames = sf_read_float(sndfile, floatbuffer, BUFFSIZE / sf_info.channels);
             src_data.data_in = floatbuffer;

             /* mark end of input */
             if (src_data.input_frames < BUFFSIZE / sf_info.channels)
             src_data.end_of_input = SF_TRUE;
         }

         if ((error = src_process(src_state, &src_data))) {
             printf("Error: %s\n", src_strerror(error));
             exit(1);
         }

         /* terminate if done */
         if (src_data.end_of_input && src_data.output_frames_gen == 0)
             break;

         /* write output */
         output_count += src_data.output_frames_gen;
         src_data.data_in += src_data.input_frames_used * sf_info.channels;
         src_data.input_frames -= src_data.input_frames_used;

         floattopcm16(shortbuffer, floatbuffer2, src_data.output_frames_gen);
         ao_play(device, (char *)shortbuffer, src_data.output_frames_gen * sizeof(short));

    }

    src_state = src_delete(src_state);

    free(shortbuffer);
    free(floatbuffer);
    free(floatbuffer2);
    fseek(fp, filestart, SEEK_SET);
    ao_close(device);
    sf_close(sndfile);
    ao_shutdown();
    printf("Finished\n");

    return 0;
}


/* Convert back to shorts */
void floattopcm16(short *outbuf, float *inbuf, int length)
{
    int   count;

    const float mul = (32768.0f);
    for (count = 0; count <= length; count++) {
        int32_t tmp = (int32_t)(mul * inbuf[count]);
        tmp = MAX( tmp, -32768 ); // CLIP < 32768
        tmp = MIN( tmp, 32767 );  // CLIP > 32767
        outbuf[count] = tmp;
    }
}

在Erik的大力幫助下,我終於使此測試代碼生效。 我的問題是音頻幀與音頻樣本的誤解。 一幀由每個通道一個樣本組成。 樣本就是那個,一個數字表示瞬間的音頻信號。 我以為我知道區別,但是在應用在主循環中看到的Erik的示例代碼時卻忘記了。 該代碼來自libsamplerate發行版壓縮包中示例目錄中的sndfile sndfile-resample.c 這種誤解的結果是,在立體聲采樣中,最后幾個采樣(大約23至60,具體取決於緩沖區大小)將為零。 導致播放不流暢。 如果我將緩沖區大小減小到512,我會聽到失真,聽起來像是模擬合成器上的環形調制器。 注意從sf_read_float()sf_readf_float()的更改。 floattopcm16()的循環錯誤地測試了count <= length 我已將其更正為count < length

對於那些也有麻煩的人,下面的代碼可以正常工作並通過-Wall。

/*
 * Objective: sample rate conversion
 * compile with
 * "gcc -o glurp glurp.c -lao -lsndfile -lsamplerate"
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ao/ao.h>
#include <sndfile.h>
#include <samplerate.h>

#define DEFAULT_CONVERTER SRC_SINC_MEDIUM_QUALITY
#define NEW_RATE 44100

#define BUFFSIZE 4096
#define MAX(x,y) ((x)>(y)) ? (x) : (y)
#define MIN(x,y) ((x)<(y)) ? (x) : (y)

int playfile(FILE *);
void floattopcm16(short *, float *, int);
void pcm16tofloat(float *, short *, int);

int main(int argc, char *argv[])
{
    FILE *fp;

    if (argc != 2) {
        printf("usage: %s <input>\n", argv[0]);
    exit(1);
    }

    fp = fopen(argv[1], "rb");
    if (fp == NULL) {
        printf("Cannot open %s.\n", argv[1]);
    exit(1);
    }

    playfile(fp);
    fclose(fp);

    return 0;
}

int playfile(FILE *fp)
{
    int default_driver;
    float *floatbuffer;
    float *floatbuffer2;
    short *shortbuffer;
    long filestart;

    int newrate = NEW_RATE;

    ao_device *device;
    ao_sample_format format;
    SNDFILE     *sndfile;
    SF_INFO sf_info;

    SRC_STATE   *src_state;
    SRC_DATA    src_data;
    int     error;
    sf_count_t  output_count = 0;

    ao_initialize();
    default_driver = ao_default_driver_id();

    sf_info.format = 0;

    filestart = ftell(fp);

    sndfile = sf_open_fd(fileno(fp), SFM_READ, &sf_info, 0);

    memset(&format, 0, sizeof(ao_sample_format));

    format.byte_format = AO_FMT_NATIVE;
    format.bits = 16;
    format.channels = sf_info.channels;
    format.rate = newrate;

    printf("Channels:           %d\n", sf_info.channels);
    printf("Start sample rate:  %d\n", sf_info.samplerate);
    printf("Ending sample rate: %d\n", newrate);

    device = ao_open_live(default_driver, &format, NULL /* no options */);
    if (device == NULL) {
        printf("Error opening sound device.\n");
        return 1;
    }

    floatbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
    floatbuffer2 = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
    shortbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(short));

    /* Set up for conversion */
    if ((src_state = src_new(DEFAULT_CONVERTER, sf_info.channels, &error)) == NULL) {
        printf("Error: src_new() failed: %s.\n", src_strerror(error));
        exit(1);
    }
    src_data.end_of_input = 0;
    src_data.input_frames = 0;
    src_data.data_in = floatbuffer;
    src_data.src_ratio = (1.0 * newrate) / sf_info.samplerate;
    src_data.data_out = floatbuffer2;
    src_data.output_frames = BUFFSIZE / sf_info.channels;

    while (1) {
        /* if floatbuffer is empty, refill it */
        if (src_data.input_frames == 0) {
            src_data.input_frames = sf_readf_float(sndfile, floatbuffer, BUFFSIZE / sf_info.channels);
            src_data.data_in = floatbuffer;

            /* mark end of input */
            if (src_data.input_frames < BUFFSIZE / sf_info.channels)
                src_data.end_of_input = SF_TRUE;
        }

        if ((error = src_process(src_state, &src_data))) {
            printf("Error: %s\n", src_strerror(error));
            exit(1);
        }

        /* terminate if done */
        if (src_data.end_of_input && src_data.output_frames_gen == 0)
            break;

        /* write output */
        floattopcm16(shortbuffer, floatbuffer2, src_data.output_frames_gen * sf_info.channels);
        ao_play(device, (char *)shortbuffer, src_data.output_frames_gen * sizeof(short) * sf_info.channels);

        output_count += src_data.output_frames_gen;
        src_data.data_in += src_data.input_frames_used * sf_info.channels;
        src_data.input_frames -= src_data.input_frames_used;
    }

    src_state = src_delete(src_state);

    free(shortbuffer);
    free(floatbuffer);
    free(floatbuffer2);
    fseek(fp, filestart, SEEK_SET);
    ao_close(device);
    sf_close(sndfile);
    ao_shutdown();
    printf("Finished\n");

    return 0;
}


/* Convert back to shorts */
void floattopcm16(short *outbuf, float *inbuf, int length)
{
    int   count;

    const float mul = (32768.0f);
    for (count = 0; count < length; count++) {
        int32_t tmp = (int32_t)(mul * inbuf[count]);
        tmp = MAX( tmp, -32768 ); // CLIP < 32768
        tmp = MIN( tmp, 32767 );  // CLIP > 32767
        outbuf[count] = tmp;
    }
}

Libsndfile不會對立體聲音頻進行去隔行,您必須手動進行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM