2013-10-01 69 views
6

目前,我試圖做一個wave文件的音高改變使用該算法C浪的#移調文件

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

這裏我的代碼,它們使用上面的實現,但沒有運氣。輸出的波形文件似乎已損壞或無效。

的代碼是相當簡單的,除了音高移位算法:)

  1. 它加載一個波形文件中,讀出波形數據文件,並把它放在一個 byte []數組。
  2. 然後它將字節數據「標準化」爲-1.0f到1.0f格式(如由音高改變算法的創建者請求的 )。
  3. 它應用音高移位算法,然後將標準化數據轉換回byte []數組。
  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(); 
    } 
+0

你確定你的音頻文件頭是44字節嗎?根據這個網頁http://www.sonicspot.com/guide/wavefiles.html它取決於很多事情,需要正確解析。 – Neil

+0

你是對的!我將自動回答我的問題以發佈正確的用法。 – John

回答

4

算法工作正常

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

我想知道是否有可能加快音高轉換算法。我在iPhone上使用它(感謝xamarin.ios),它真的很慢。我猜這取決於它使用float和double vars(這反過來壓力fpu)的事實? – John

0

遺憾重振這一點,但我想,pitchshifter類,而它的工作原理,我得到裂紋在音頻當投下來(0.5F)。你有辦法解決這個問題嗎?