1. 程式人生 > >如何轉換音訊資料格式1

如何轉換音訊資料格式1

前面的音訊處理背景知識就先跳過,需要的請自行腦補。

直接上乾貨。

一、聲道轉換

1、單聲道轉立體聲

原理,雙聲道的16位取樣,每16位是一個聲道,也就是兩位元組;下一個16位是另外一個聲道,交錯進行。

private byte[] MonoToStereo(byte[] input)
{
    byte[] output = new byte[input.Length * 2];
    int outputIndex = 0;
    for (int n = 0; n < input.Length; n+=2)
    {
        // copy in the first 16 bit sample
        output[outputIndex++] = input[n];
        output[outputIndex++] = input[n+1];
        // now copy it in again
        output[outputIndex++] = input[n];
        output[outputIndex++] = input[n+1];        
    }
    return output;
}

2、立體聲轉單聲道

原理,去掉一半的資料即可。

private byte[] StereoToMono(byte[] input)
{
    byte[] output = new byte[input.Length / 2];
    int outputIndex = 0;
    for (int n = 0; n < input.Length; n+=4)
    {
        // copy in the first 16 bit sample
        output[outputIndex++] = input[n];
        output[outputIndex++] = input[n+1];
    }
    return output;
}

3、混合立體聲轉單聲道

如果是混合立體聲,則可以把左右聲道的資料求平均,得到單聲道的值

private byte[] MixStereoToMono(byte[] input)
{
    byte[] output = new byte[input.Length / 2];
    int outputIndex = 0;
    for (int n = 0; n < input.Length; n+=4)
    {
        int leftChannel = BitConverter.ToInt16(input,n);
        int rightChannel = BitConverter.ToInt16(input,n+2);
        int mixed = (leftChannel + rightChannel) / 2;
        byte[] outSample = BitConverter.GetBytes((short)mixed);
        
        // copy in the first 16 bit sample
        output[outputIndex++] = outSample[0];
        output[outputIndex++] = outSample[1];
    }
    return output;
}

二、位寬轉換

4、16位轉32位float

相對簡單,把每個16bit(兩個byte,合成一個short)除以16位的最大值,得到一個相對的float值(介於0-1之間)。

public float[] Convert16BitToFloat(byte[] input)
{
    int inputSamples = input.Length / 2; // 16 bit input, so 2 bytes per sample
    float[] output = new float[inputSamples];
    int outputIndex = 0;
    for(int n = 0; n < inputSamples; n++)
    {
        short sample = BitConverter.ToInt16(input,n*2);
        output[outputIndex++] = sample / 32768f;
    }
    return output;
}

5、24位轉32位float

這個就稍微麻煩了,從原資料中每次取24位,即3個byte,補上一個0,摺合成一個int,然後除以3個byte組成的資料最大值,得到一個相對float值(介於0-1之間)。

public float[] Convert24BitToFloat(byte[] input)
{
    int inputSamples = input.Length / 3; // 24 bit input
    float[] output = new float[inputSamples];
    int outputIndex = 0;
    var temp = new byte[4];
    for(int n = 0; n < inputSamples; n++)
    {
        // copy 3 bytes in
        Array.Copy(input,n*3,temp,0,3);
        int sample = BitConverter.ToInt32(temp,0);
        output[outputIndex++] = sample / 16777216f;
    }
    return output;
}
這種方式其實也相當於把3個取樣點,線性擬合變成了2個了。

6、還原資料

兩種方式還原的程式碼一樣(後一種多的一個點資訊已經丟失,還原也只有2個byte了):

for (int sample = 0; sample < sourceSamples; sample++)
{
    // adjust volume
    float sample32 = sourceBuffer[sample] * volume;
    // clip
    if (sample32 > 1.0f)
        sample32 = 1.0f;
    if (sample32 < -1.0f)
        sample32 = -1.0f;
    destBuffer[destOffset++] = (short)(sample32 * 32767);
}

三、重取樣

取樣是這個文章中比較複雜的部分。

=================== 佔坑,以後講原理====================

7、一個簡單的重取樣演算法

原理就是,拉大或縮小取樣點的間距。當然,明顯的是,如果如果新取樣率大於舊的,其實沒有意義,造成很多點只會簡單重複。

新取樣率小於舊的,就會在現有的點上,等比例往後拉。

// Just about worst resampling algorithm possible:
private float[] ResampleNaive(float[] inBuffer, int inputSampleRate, int outputSampleRate)
{
    var outBuffer = new List<float>();
    double ratio = (double) inputSampleRate / outputSampleRate;
    int outSample = 0;
    while (true)
    {
        int inBufferIndex = (int)(outSample++ * ratio);
        if (inBufferIndex < read)
            writer.WriteSample(inBuffer[inBufferIndex]);
        else
            break;    
    } 
    return outBuffer.ToArray();    
}

========== 留坑,講重取樣的測試==========

下一部分,音訊檔案格式的轉換