音频处理--PCM基础

  • 采样率:一秒内对信号的采样次数,单位HZ,采样次数越高,音频质量越好,相同时间的PCM数据量也就越大,一般音频的采样率为16000HZ,44100HZ,48000HZ。
  • 音频数据量化时有个基准参考线0,因此一遍音频数据都是有符号的
  • 采样位宽:一个采样点用多大的数据来表示,有一个字节(8位),和两个字节(16位)之分,是用来衡量声音的波动变化的一个参数。
  • 字节序:音频一般是小端存储
  • 声道数:有单声道,双声道等

PCM数据排列格式

pcm数据存储布局
下图是一个16位,小端存储的pcm,从图中可以看出,

  • 两个声道的pcm数值并不一定是相同的
  • 小端存储,其实际的值是0xE64A,0xF72C,从文件读取时需要注意处理。下面给出了处理函数
    在这里插入图片描述
    下面是从一个位宽为16的PCM文件中读取PCM数据的代码
/**
  buffer:用于存放读取采样点的缓存
  maxSamples:一个通道最多读取多少个采样点
  channels:通道数
  return:返回实际读取的采样点数
*/
#define READ_BUFFER_SIZE 4096
int readSamplesFromPCMFile(FILE *pcmFile, short *buffer, int maxSamples, int channels) {
    
    
  unsigned char buf[READ_BUFFER_SIZE];
  if (maxSamples * channels * 2 > READ_BUFFER_SIZE) {
    
    
    maxSamples = READ_BUFFER_SIZE / (channels << 1);
  }
  int read_bytes = fread(buf, sizeof(char), maxSamples * channels * 2, pcmFile);
  int samplesRead = read_bytes / (channels << 1);

  int bytePos = 0;
  for (int i = 0; i < samplesRead * channels; i++) {
    
    
    short sample = buf[bytePos++];
    sample |= (unsigned int)buf[bytePos++] << 8;
    *buffer++ = sample;
  }
  return samplesRead;
}

下面是使用sonic库读取PCM做倍速处理后,保存WAV的代码,地址:https://gitcode.net/xiwenhec/soundspeed

#include "sonic.h"
#include "wave.h"
#include <corecrt_wstdio.h>
#include <cstdio>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 2048
#define READ_BUFFER_SIZE 4096

struct PCMFile {
    
    
  std::string name;
  int sampleRate;
  int numChannels;
};

/**
  buffer:用于存放读取采样点的缓存
  maxSamples:一个通道最多读取多少个采样点
  channels:通道数
  return:返回实际读取的采样点
*/
int readSamplesFromPCMFile(FILE *pcmFile, short *buffer, int maxSamples, int channels) {
    
    
  unsigned char buf[READ_BUFFER_SIZE];
  if (maxSamples * channels * 2 > READ_BUFFER_SIZE) {
    
    
    maxSamples = READ_BUFFER_SIZE / (channels << 1);
  }
  int read_bytes = fread(buf, sizeof(char), maxSamples * channels * 2, pcmFile);
  int samplesRead = read_bytes / (channels << 1);

  int bytePos = 0;
  for (int i = 0; i < samplesRead * channels; i++) {
    
    
    short sample = buf[bytePos++];
    sample |= (unsigned int)buf[bytePos++] << 8;
    *buffer++ = sample;
  }
  return samplesRead;
}

/**
  test.pcm:48000,2声道,16位
*/
void runSonicPCM(const PCMFile &inputPCM, const char *outFileName, float speed, float pitch, float rate, float volume,
                 int outputSampleRate, int emulateChordPitch, int quality) {
    
    
  FILE *inFile = nullptr;

  // 打开输入文件
  errno_t error = fopen_s(&inFile, inputPCM.name, "rb+");
  if (error != 0) {
    
    
    fprintf(stderr, "Unable to open pcn file %s\n", inputPCM.name);
    return;
  }
  printf("open input file:%s success\n", inputPCM.name);

  int sampleRate = outputSampleRate == 0 ? inputPCM.sampleRate : outputSampleRate;
  // 打开输出文件
  waveFile outFile = openOutputWaveFile(outFileName, sampleRate, inputPCM.numChannels);
  if (error != 0) {
    
    
    fclose(inFile);
    fprintf(stderr, "Unable to open wave file %s for writing\n", outFileName);
    exit(1);
  }
  printf("open output file:%s success.\n", outFileName);

  sonicStream stream = sonicCreateStream(sampleRate, inputPCM.numChannels);
  sonicSetSpeed(stream, speed);
  sonicSetPitch(stream, pitch);
  sonicSetRate(stream, rate);
  sonicSetVolume(stream, volume);
  sonicSetChordPitch(stream, emulateChordPitch);
  sonicSetQuality(stream, quality);

  int samplesRead = 0, samplesWritten = 0;
  short inBuffer[BUFFER_SIZE], outBuffer[BUFFER_SIZE];

  do {
    
    
    samplesRead = readSamplesFromPCMFile(inFile, inBuffer, BUFFER_SIZE / inputPCM.numChannels, inputPCM.numChannels);
    if (samplesRead == 0) {
    
    
      sonicFlushStream(stream);
    } else {
    
    
      sonicWriteShortToStream(stream, inBuffer, samplesRead);
    }

    do {
    
    
      samplesWritten = sonicReadShortFromStream(stream, outBuffer, BUFFER_SIZE / inputPCM.numChannels);
      if (samplesWritten > 0) {
    
    
        printf("samplesWrite:%d\n", samplesWritten);
        writeToWaveFile(outFile, outBuffer, samplesWritten);
      }
    } while (samplesWritten > 0);

  } while (samplesRead > 0);

  sonicDestroyStream(stream);
  fclose(inFile);
  closeWaveFile(outFile);
}

int main() {
    
    
  PCMFile pcmInputFile{
    
    
    .name = "res/test.pcm", 
    .sampleRate = 48000, 
    .numChannels = 2
    };
  const char *outputFileName = "res/test_2x.wav";
  runSonicPCM(pcmInputFile, outputFileName, 1.5f, 1.0, 1.0, 1.0, 0, 0, 0);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/xiwenhec/article/details/129701091