C中的音频记录和播放:音频增益问题

问题描述 投票:0回答:1

本质上是如何正确地将增益应用于音频样本?

我正在FreeBSD和OSS上进行编程,但是在音频样本中操纵音量对于其他OS和应用程序可能是相同的。

我正在研究其他人的应用程序内部,例如ecasound(在C ++中)和SoX(在C中),但我不知道在读取示例并将其应用于增益时出了什么问题:它变得失真且嘈杂。 我的意思是要理解为什么调低音量不起作用(增益小于1)。

我正在处理立体声16位LE样本。在不施加增益的情况下,它可以完美地工作(记录和播放)。

我以为我应该将整数样本转换为float;乘以增益因子并将其恢复为整数。但这是行不通的。对于函数SoX in src/vol.c中的static int flow,似乎是完全相同的方法。

下面是我的代码(不使用其他库)。功能回放是我要应用增益的地方。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "/usr/include/sys/soundcard.h"
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/stat.h> //man 2 chmod
#include <signal.h>

#define DEBUG 1
#define log(msg) if (DEBUG) printf("[LOG] %s\n",msg)
#define err(msg)  {printf("[ERR] %s\n",msg); exit(1); }


const char *device = "/dev/dsp3.1";     //Audio device
char *rawFile = "/tmp/raw-file.wav";    //Raw file to record and playback
int fragmentSize = 256;
int b_continue = 1;


void signalHandler(int sigNum){
    log("Signal captured");
    b_continue = 0;
}


void configDevice(int fdDsp){
    int ossCapabilities = 0;

    if(fdDsp == -1)
        err("can't open device");

    if( ioctl(fdDsp, SNDCTL_DSP_GETCAPS, &ossCapabilities) == -1)
        err("unsupported: SNDCTL_DSP_GETCAPS");

    /*
     * http://www.opensound.com/pguide/audio2.html
     */

    if(ossCapabilities & DSP_CAP_TRIGGER != DSP_CAP_TRIGGER){
        err("Triggering of recording/playback is not possible with this OSS device.");

    }

    if(ossCapabilities & DSP_CAP_REALTIME != DSP_CAP_REALTIME){
        err("No DSP_CAP_REALTIME.");

    }

    if(ioctl(fdDsp, SNDCTL_DSP_SETDUPLEX, &ossCapabilities) == -1)
        err("can't SNDCTL_DSP_SETDUPLEX");

    if(ossCapabilities & DSP_CAP_DUPLEX != DSP_CAP_DUPLEX)
        err("can't DSP_CAP_DUPLEX");

    int format = AFMT_S16_LE;   //set format
    if(ioctl(fdDsp, SNDCTL_DSP_SETFMT, &format ) == -1){
        err("Error setting format.");

    }

    int channels = 1; //mono=0 stereo=1
    if(ioctl(fdDsp, SNDCTL_DSP_STEREO, &channels ) == -1){
        err("Error setting channels." );

    }
    // FREQUENCY RATE
    int speed = 44100;
    if(ioctl(fdDsp, SNDCTL_DSP_SPEED, &speed ) == -1){
        err("Error setting speed.");

    }

    // FRAGMENT SIZE
    if(ioctl(fdDsp, SNDCTL_DSP_SETBLKSIZE, &fragmentSize) == -1){ //normalmente 2048 bits
        err("Cannot SNDCTL_DSP_SETBLKSIZE.");

    }


}

void record(){
    int fdDsp = open(device, O_RDONLY);
    configDevice(fdDsp);
    //create file for writing
    const int fdOutput = open(rawFile, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR);

    if(fdOutput ==-1)
        err("can't open file to write");
    log("Recording...");

    do{
         // Triggers recording
         int enableBits = PCM_ENABLE_INPUT;
         if(ioctl(fdDsp, SNDCTL_DSP_SETTRIGGER, &enableBits) == -1)
             err("Can't record: SNDCTL_DSP_SETTRIGGER");

         int *buf[fragmentSize];
         read(fdDsp, buf, fragmentSize);
         write(fdOutput, buf, fragmentSize);

     } while(b_continue == 1);

     close(fdOutput);
     close(fdDsp);
}

void playback(){
    log("Opening file:");
    log(rawFile);
    log("On device:");
    log(device);

    int fdDsp = open(device, O_WRONLY);
    configDevice(fdDsp);

    const int fdInput = open(rawFile, O_RDONLY);

    if(fdInput ==-1)
        err("can't open file");
    log("Playing...");

    int eof = 0;

    do{
        // TRIGGERs PLAYBACK
        int enableBits = PCM_ENABLE_OUTPUT;
        if(ioctl(fdDsp, SNDCTL_DSP_SETTRIGGER, &enableBits) == -1){
            err("Cannot SNDCTL_DSP_SETTRIGGER.");

        }

        int buf[fragmentSize];
        eof = read(fdInput, buf, fragmentSize); //bytes read or -1 if EOF

        // audio processing:
        for(int i=0;i<fragmentSize;i++){
            // learning how to get left and right channels from buffer
            int l = (buf)[i] & 0xffff;
            int r = ((buf)[i] >> 16)  & 0xffff ;

            // FIXME: it is causing distortion:
            float fl = l;
            float fr = r;
            fl *= 1.0;
            fr *= 0.3; //if different than 1, sounds distorted and noisy
            l = fl;
            r = fr;


            // OK: unite Left and Right channels again
            int lr = (l ) | (r << 16);
            // OK: other options to mix these two channels:
            int lleft = l;              //Just the left channel
            int rright = (r << 16);     //Just the right channel
            int lmono = (l << 16) | l;  //Left ch. on both channels
            int rmono = (r << 16) | r;  //Right ch. on both channels

            // the output:
            (buf)[i] = lr;

        }

        write(fdDsp, buf, fragmentSize);
        if(b_continue == 0) break;
    } while(eof > 0);

    close(fdInput);
    close(fdDsp);
}

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

    signal(SIGINT, signalHandler);
    log("Ctrl^C to stop recording/playback");
    record();
    b_continue = 1; playback();
    log("Stopped.");
    return 0;
}

UPDATE:

CL所指出,我使用了错误的类型,并且read()/ write()的最后一个参数大于缓冲区的大小。

因此,在FreeBSD中,我将缓冲区类型更改为#include <stdint.h>中定义的int16_t(短)。

现在,我可以根据需要正确应用增益:

        float fl = l;
        float fr = r;
        fl *= 1.0f;
        fr *= 1.5f;
        l = fl;
        r = fr;

我接受CL's答案。

现在音频处理循环每次处理一个样本(左右交错)。

更新的代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "/usr/include/sys/soundcard.h"
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/stat.h> //man 2 chmod
#include <signal.h>
#include <stdint.h> //has type int16_t (short)

#define DEBUG 1
#define log(msg) if (DEBUG) printf("[LOG] %s\n",msg)
#define err(msg)  {printf("[ERR] %s\n",msg); exit(1); }


const char *device = "/dev/dsp3.1";     //Audio device
char *rawFile = "/tmp/stereo.wav";    //Raw file to record and playback
int fragmentSize = 256;
int b_continue = 1;


void signalHandler(int sigNum){
    log("Signal captured");
    b_continue = 0;
}


void configDevice(int fdDsp){
    int ossCapabilities = 0;

    if(fdDsp == -1)
        err("can't open device");

    if( ioctl(fdDsp, SNDCTL_DSP_GETCAPS, &ossCapabilities) == -1)
        err("unsupported: SNDCTL_DSP_GETCAPS");

    /*
     * http://www.opensound.com/pguide/audio2.html
     */

    if(ossCapabilities & DSP_CAP_TRIGGER != DSP_CAP_TRIGGER){
        err("Triggering of recording/playback is not possible with this OSS device.");

    }

    if(ossCapabilities & DSP_CAP_REALTIME != DSP_CAP_REALTIME){
        err("No DSP_CAP_REALTIME.");

    }

    if(ioctl(fdDsp, SNDCTL_DSP_SETDUPLEX, &ossCapabilities) == -1)
        err("can't SNDCTL_DSP_SETDUPLEX");

    if(ossCapabilities & DSP_CAP_DUPLEX != DSP_CAP_DUPLEX)
        err("can't DSP_CAP_DUPLEX");

    int format = AFMT_S16_LE;   //set format
    if(ioctl(fdDsp, SNDCTL_DSP_SETFMT, &format ) == -1){
        err("Error setting format.");

    }

    int channels = 1; //mono=0 stereo=1
    if(ioctl(fdDsp, SNDCTL_DSP_STEREO, &channels ) == -1){
        err("Error setting channels." );

    }
    // FREQUENCY RATE
    int speed = 44100;
    if(ioctl(fdDsp, SNDCTL_DSP_SPEED, &speed ) == -1){
        err("Error setting speed.");

    }

    // FRAGMENT SIZE
    if(ioctl(fdDsp, SNDCTL_DSP_SETBLKSIZE, &fragmentSize) == -1){ //normalmente 2048 bits
        err("Cannot SNDCTL_DSP_SETBLKSIZE.");

    }


}

void record(){
    int fdDsp = open(device, O_RDONLY);
    configDevice(fdDsp);
    //create file for writing
    const int fdOutput = open(rawFile, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR);

    if(fdOutput ==-1)
        err("can't open file to write");
    log("Recording...");

    do{
         // Triggers recording
         int enableBits = PCM_ENABLE_INPUT;
         if(ioctl(fdDsp, SNDCTL_DSP_SETTRIGGER, &enableBits) == -1)
             err("Can't record: SNDCTL_DSP_SETTRIGGER");

         // Wrong:
//         int *buf[fragmentSize];
//         read(fdDsp, buf, fragmentSize);
//         write(fdOutput, buf, fragmentSize);
         int16_t *buf[fragmentSize/sizeof (int16_t)];
         read(fdDsp, buf, fragmentSize/sizeof (int16_t));
         write(fdOutput, buf, fragmentSize/sizeof (int16_t));

     } while(b_continue == 1);

     close(fdOutput);
     close(fdDsp);
}

void playback(){
    log("Opening file:");
    log(rawFile);
    log("On device:");
    log(device);

    int fdDsp = open(device, O_WRONLY);
    configDevice(fdDsp);

    const int fdInput = open(rawFile, O_RDONLY);

    if(fdInput ==-1)
        err("can't open file");
    log("Playing...");

    int eof = 0;

    do{
        // TRIGGERs PLAYBACK
        int enableBits = PCM_ENABLE_OUTPUT;
        if(ioctl(fdDsp, SNDCTL_DSP_SETTRIGGER, &enableBits) == -1){
            err("Cannot SNDCTL_DSP_SETTRIGGER.");

        }

        //Wrong buffer type (too large) and wrong last parameter for read():
//        int buf[fragmentSize];
//        eof = read(fdInput, buf, fragmentSize);
        int16_t buf[fragmentSize/sizeof (int16_t)];
        eof = read(fdInput, buf, fragmentSize/sizeof (int16_t));

        // audio processing:
        for(int i=0;i<fragmentSize/sizeof (int16_t);i++){
            int16_t l = buf[i];
            int16_t r = buf[i+1];

            // Using int16_t (short) buffer, gain works but stereo is inverted with factor >= 1.4f
            float fl = l;
            float fr = r;
            fl *= 2.0f;
            fr *= 3.0f;
            l = fl;
            r = fr;

            // the output:
            (buf)[i] = l;
            i++;
            (buf)[i] = r;

        }

//        write(fdDsp, buf, fragmentSize); //wrong
        write(fdDsp, buf, fragmentSize/sizeof (int16_t));
        if(b_continue == 0) break;
    } while(eof > 0);

    close(fdInput);
    close(fdDsp);
}

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

    signal(SIGINT, signalHandler);
    log("Ctrl^C to stop recording/playback");
    record();
    b_continue = 1; playback();
    log("Stopped.");
    return 0;
}

谢谢,

audio freebsd alsa
1个回答
0
投票

read()/ write()的最后一个参数是字节数,但是buf []中的一项具有多个字节。

在二进制数的二进制补码表示中,负值是(或必须是)符号扩展的,即,最高有效位是1。在此代码中,对于负样本,提取L / R通道或对其进行组合均无法正常工作。

处理负样本的最简单方法是每个样本使用一个数组条目,即short int

© www.soinside.com 2019 - 2024. All rights reserved.