2013-10-01 24 views
6

私は現在が、運と、ここでは、このアルゴリズムにC#のピッチシフト

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

を使用して上記の実装を使用する私のコードをWAVEファイルのピッチシフトをやろうとしています。出力された波形ファイルが破損しているか、有効ではないようです。

コードがピッチシフトアルゴリズムを除いて、非常に簡単である:)

  1. これはWAVEファイルをロードし、それが波のファイルデータを読み出し、 バイト[]配列に入れ。
  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(); 
    } 
+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を使用するという事実に依存すると思う – John

0

が、私はそれが動作しながら、(0.5F)を押しピッチングながら、私はオーディオにパチパチ音を取得し、そのpitchshifterクラスを試してみました。あなたはそれを回避する方法を考えていますか?