c# 波形文件的音高偏移

本文关键字:高偏移 文件 波形 | 更新日期: 2023-09-27 18:33:22

我目前正在尝试使用此算法对波形文件进行音高转换

https://sites.google.com/site/mikescoderama/pitch-shifting

在这里,我的代码使用了上述实现,但没有运气。输出的波形文件似乎已损坏或无效。

代码非常简单,除了音调转换算法:)

  1. 它加载一个波文件,它读取波文件数据并将其放入字节[] 数组。
  2. 然后它将字节数据"规范化"为 -1.0f 到 1.0f 格式(如由变调算法的创建者请求)。
  3. 它应用音高转换算法,然后转换回将数据规范化为 bytes[] 数组。
  4. 最后保存一个与原始波相同的标题的波文件。文件和音高偏移数据。

我错过了什么吗?

        static void Main(string[] args)
    {
        // Read the wave file data bytes
        byte[] waveheader = null;
        byte[] wavedata = null;
        using (BinaryReader reader = new BinaryReader(File.OpenRead("sound.wav")))
        {
            // Read first 44 bytes (header);
            waveheader= reader.ReadBytes(44);
            // Read data
            wavedata = reader.ReadBytes((int)reader.BaseStream.Length - 44);
        }
        short nChannels = BitConverter.ToInt16(waveheader, 22);
        int sampleRate = BitConverter.ToInt32(waveheader, 24);
        short bitRate = BitConverter.ToInt16(waveheader, 34);
        // Normalized data store. Store values in the format -1.0 to 1.0
        float[] in_data = new float[wavedata.Length / 2];
        // Normalize wave data into -1.0 to 1.0 values
        using(BinaryReader reader = new BinaryReader(new MemoryStream(wavedata)))
        {
            for (int i = 0; i < in_data.Length; i++)
            {
                if(bitRate == 16)
                    in_data[i] = reader.ReadInt16() / 32768f;
                if (bitRate == 8)                
                    in_data[i] = (reader.ReadByte() - 128) / 128f;
            }
        }
        //PitchShifter.PitchShift(1f, in_data.Length, (long)1024, (long)32, sampleRate, in_data);
        // Backup wave data
        byte[] copydata = new byte[wavedata.Length];
        Array.Copy(wavedata, copydata, wavedata.Length);
        // Revert data to byte format
        Array.Clear(wavedata, 0, wavedata.Length);
        using (BinaryWriter writer = new BinaryWriter(new MemoryStream(wavedata)))
        {
            for (int i = 0; i < in_data.Length; i++)
            {
                if(bitRate == 16)
                    writer.Write((short)(in_data[i] * 32768f));
                if (bitRate == 8)
                    writer.Write((byte)((in_data[i] * 128f) + 128));
            }
        }
        // Compare new wavedata with copydata
        if (wavedata.SequenceEqual(copydata))
        {
            Console.WriteLine("Data has no changes");
        }
        else
        {
            Console.WriteLine("Data has changed!");
        }
        // Save modified wavedata
        string targetFilePath = "sound_low.wav";
        if (File.Exists(targetFilePath))
            File.Delete(targetFilePath);
        using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(targetFilePath)))
        {
            writer.Write(waveheader);
            writer.Write(wavedata);
        }
        Console.ReadLine();
    }

c# 波形文件的音高偏移

这里的算法工作正常

https://sites.google.com/site/mikescoderama/pitch-shifting

我的错误在于我如何读取波头和波数据。我在这里发布完全有效的代码

警告:此代码仅适用于 PCM 16 位(立体声/单声道)波。可轻松适应 PCM 8 位工作。

    static void Main(string[] args)
    {
        // Read header, data and channels as separated data
        // Normalized data stores. Store values in the format -1.0 to 1.0
        byte[] waveheader = null;
        byte[] wavedata = null;
        int sampleRate = 0;
        float[] in_data_l = null;
        float[] in_data_r = null;
        GetWaveData("sound.wav", out waveheader, out wavedata, out sampleRate, out in_data_l, out in_data_r); 
        //
        // Apply Pitch Shifting
        //
        if(in_data_l != null)
            PitchShifter.PitchShift(2f, in_data_l.Length, (long)1024, (long)10, sampleRate, in_data_l);
        if(in_data_r != null)
            PitchShifter.PitchShift(2f, in_data_r.Length, (long)1024, (long)10, sampleRate, in_data_r);
        //
        // Time to save the processed data
        //
        // Backup wave data
        byte[] copydata = new byte[wavedata.Length];
        Array.Copy(wavedata, copydata, wavedata.Length);
        GetWaveData(in_data_l, in_data_r, ref wavedata);
        //
        // Check if data actually changed
        //
        bool noChanges = true;
        for (int i = 0; i < wavedata.Length; i++)
        {
            if (wavedata[i] != copydata[i])
            {
                noChanges = false;
                Console.WriteLine("Data has changed!");
                break;
            }
        }
        if(noChanges)
            Console.WriteLine("Data has no changes");
        // Save modified wavedata
        string targetFilePath = "sound_low.wav";
        if (File.Exists(targetFilePath))
            File.Delete(targetFilePath);
        using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(targetFilePath)))
        {
            writer.Write(waveheader);
            writer.Write(wavedata);
        }
        Console.ReadLine();
    }
    // Returns left and right float arrays. 'right' will be null if sound is mono.
    public static void GetWaveData(string filename, out byte[] header, out byte[] data, out int sampleRate, out float[] left, out float[] right)
    {
        byte[] wav = File.ReadAllBytes(filename);
        // Determine if mono or stereo
        int channels = wav[22];     // Forget byte 23 as 99.999% of WAVs are 1 or 2 channels
        // Get sample rate
        sampleRate = BitConverter.ToInt32(wav, 24);
        int pos = 12;
        // Keep iterating until we find the data chunk (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal))
        while(!(wav[pos]==100 && wav[pos+1]==97 && wav[pos+2]==116 && wav[pos+3]==97)) {
            pos += 4;
            int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216;
            pos += 4 + chunkSize;
        }
        pos += 4;
        int subchunk2Size = BitConverter.ToInt32(wav, pos);
        pos += 4;
        // Pos is now positioned to start of actual sound data.
        int samples = subchunk2Size / 2;     // 2 bytes per sample (16 bit sound mono)
        if (channels == 2) 
            samples /= 2;        // 4 bytes per sample (16 bit stereo)
        // Allocate memory (right will be null if only mono sound)
        left = new float[samples];
        if (channels == 2) 
            right = new float[samples];
        else 
            right = null;
        header = new byte[pos];
        Array.Copy(wav, header, pos);
        data = new byte[subchunk2Size];
        Array.Copy(wav, pos, data, 0, subchunk2Size);
        // Write to float array/s:
        int i=0;            
        while (pos < subchunk2Size) 
        {
            left[i] = BytesToNormalized_16(wav[pos], wav[pos + 1]);
            pos += 2;
            if (channels == 2) 
            {
                right[i] = BytesToNormalized_16(wav[pos], wav[pos + 1]);
                pos += 2;
            }
            i++;
        }
    }
    // Return byte data from left and right float data. Ignore right when sound is mono
    public static void GetWaveData(float[] left, float[] right, ref byte[] data)
    {
        // Calculate k
        // This value will be used to convert float to Int16
        // We are not using Int16.Max to avoid peaks due to overflow conversions            
        float k = (float)Int16.MaxValue / left.Select(x => Math.Abs(x)).Max();          
        // Revert data to byte format
        Array.Clear(data, 0, data.Length);
        int dataLenght = left.Length;
        int byteId = -1;
        using (BinaryWriter writer = new BinaryWriter(new MemoryStream(data)))
        {
            for (int i = 0; i < dataLenght; i++)
            {
                byte byte1 = 0;
                byte byte2 = 0;
                byteId++;
                NormalizedToBytes_16(left[i], k, out byte1, out byte2);
                writer.Write(byte1);
                writer.Write(byte2);
                if (right != null)
                {
                    byteId++;
                    NormalizedToBytes_16(right[i], k, out byte1, out byte2);
                    writer.Write(byte1);
                    writer.Write(byte2);                        
                }
            }
        }        
    }
    // Convert two bytes to one double in the range -1 to 1
    static float BytesToNormalized_16(byte firstByte, byte secondByte) 
    {
        // convert two bytes to one short (little endian)
        short s = (short)((secondByte << 8) | firstByte);
        // convert to range from -1 to (just below) 1
        return s / 32678f;
    }
    // Convert a float value into two bytes (use k as conversion value and not Int16.MaxValue to avoid peaks)
    static void NormalizedToBytes_16(float value, float k, out byte firstByte, out byte secondByte)
    {
        short s = (short)(value * k);
        firstByte  = (byte)(s & 0x00FF);
        secondByte = (byte)(s >> 8);
    }

很抱歉恢复它,但我尝试了那个变调器类,虽然它有效,但在俯仰时音频中出现噼啪声(0.5f)。你想办法解决这个问题吗?