2013-11-15 20 views
12

Tüm PCM wav ses dosyalarının, örnekler başlamadan önce 44 bayt başlık verisi olduğunu varsaymanın tehlikeli olduğunu keşfettim. Bu yaygın olsa da, birçok uygulama (örneğin, örneğin, ffmpeg), 46 baytlık bir başlık ile wav üretir ve bu durumu görmezden gelirken, işlem bozuk ve okunamayan bir dosya ile sonuçlanır. Ancak, başlığın gerçekte ne kadar olduğunu nasıl anlayabilirsiniz?Bir WAV dosyasının 44 veya 46 baytlık bir üstbilgiye sahip olup olmadığını nasıl anlarım?

Açıkçası, bunu yapmanın bir yolu var, ama aradım ve bunun hakkında çok az tartışma buldum. Orada bir sürü ses projesi yazarların kendi içeriğine bağlı olarak 44 (ya da tersine, 46) olduğunu varsayar.

+3

Verilerin başka bir yerde başladığı birçok WAV dosyası var: belki dosyanın başlangıcından yüzlerce bayt, kim bilir? WAV yığın başlıklarının ayrıştırılması gerçekten kolaydır, bunları ayrıştırmadığınız için herhangi bir mazeretiniz yoktur. –

+2

Başlığın ayrıştırılması için bir mazeret olmadığı doğru, ancak bu konuda bir sürü yanlış bilgi var. Google'da "wav parser" kelimesini arayın ve en çok sayıda hit, tartışma olmadan 44 bayt uzunluğa sahip olan kod içeriyor. SO, daha büyük bir şeye yönelik ipuçlarını içeriyordu. Bakmaya giden bir sonraki sinirli insan için bu konuya dikkat çekmeye çalışıyorum. –

+0

Her zaman [WAVE PCM ses dosyası formatı] (https://ccrma.stanford.edu/courses/422/projects/WaveFormat/) sayfasında Müzik ve Akustik (Standford) Web sitesinde Bilgisayar Araştırmaları Merkezi sayfasında yer aldım. Bu tür bir şey için yararlı bir kaynak ol. – Sheridan

cevap

8

Hile, başlığın bayt 16'sından başlayan 4 baytlık bir tamsayı olan "Subchunk1Size" ye bakmaktır. Normal bir 44 baytlık wavda, bu tamsayı 16 olur [10, 0, 0, 0]. Eğer 46 baytlık bir başlığa sahipse, bu tam sayı 18 (12, 0, 0, 0) veya daha fazla uzatılabilir meta veri (nadir?) Ise daha yüksek olacaktır.

static void Main(string[] args) 
{ 
    byte[] bytes = new byte[4]; 
    FileStream fileStream = new FileStream(args[0], FileMode.Open, FileAccess.Read); 
    fileStream.Seek(16, 0); 
    fileStream.Read(bytes, 0, 4); 
    fileStream.Close(); 
    int Subchunk1Size = BitConverter.ToInt32(bytes, 0); 

    if (Subchunk1Size < 16) 
     Console.WriteLine("This is not a valid wav file"); 
    else 
     switch (Subchunk1Size) 
     { 
      case 16: 
       Console.WriteLine("44-byte header"); 
       break; 
      case 18: 
       Console.WriteLine("46-byte header"); 
       break; 
      default: 
       Console.WriteLine("Header contains extra data and is larger than 46 bytes"); 
       break; 
     } 
} 
+0

Bu beni şaşırttı .... teşekkür u! – kaitian521

22

Sen tüm kontrol edilmelidir:

ekstra veri kendisi (eğer varsa), şu şekilde görünecektir byte başlık uzunluğunu tespit etmek için 36.

Yani basit bir C# programı başlıyor gerçek boyutların ne olduğunu görmek için başlık verileri. Yayın Dalga Biçimi dosyaları daha büyük bir uzantı alt yığını içerecektir. Pro Tools'un WAV ve AIFF dosyalarının, sesin yanı sıra belgelenmemiş olan daha fazla uzantı parçası vardır. Örnek verilerinin nerede başladığını ve bittiğini bildiğinizden emin olmak istiyorsanız, gerçekte veri yığınını (WAV dosyaları için 'veri' ve AIFF için 'SSND') aramanız gerekir.

gözden gibi, tüm WAV subchunks şu biçimde:

 
Subchunk Descriptor (4 bytes) 
    Subchunk Size (4 byte integer, little endian) 
    Subchunk Data (size is Subchunk Size) 

Bu işlemek için çok kolaydır. Tek yapmanız gereken tanımlayıcıyı okumaktır, eğer aradığınız kişi değilse, veri boyutunu okuyun ve ileriye atlayın. Bunun için basit bir Java rutin şu şekilde görünecektir: Burada Özellikle

 
found 'RIFF' descriptor 
found 'bext' descriptor 
found 'fmt ' descriptor 
found 'minf' descriptor 
found 'elm1' descriptor 
found 'data' descriptor 
found 'regn' descriptor 
found 'ovwf' descriptor 
found 'umid' descriptor 
end of file 

, hem 'fmt' ve 'veri' meşru görünür: Burada Örneğin

// 
// Quick note for people who don't know Java well: 
// 'in.read(...)' returns -1 when the stream reaches 
// the end of the file, so 'if (in.read(...) < 0)' 
// is checking for the end of file. 
// 
public static void printWaveDescriptors(File file) 
     throws IOException { 
    try (FileInputStream in = new FileInputStream(file)) { 
     byte[] bytes = new byte[4]; 

     // read first 4 bytes 
     // should be RIFF descriptor 
     if (in.read(bytes) < 0) { 
      return; 
     } 

     printDescriptor(bytes); 

     // first subchunk will always be at byte 12 
     // there is no other dependable constant 
     in.skip(8); 

     for (;;) { 
      // read each chunk descriptor 
      if (in.read(bytes) < 0) { 
       break; 
      } 

      printDescriptor(bytes); 

      // read chunk length 
      if (in.read(bytes) < 0) { 
       break; 
      } 

      // skip the length of this chunk 
      // next bytes should be another descriptor or EOF 
      in.skip(
        (bytes[0] & 0xFF) 
       | (bytes[1] & 0xFF) << 8 
       | (bytes[2] & 0xFF) << 16 
       | (bytes[3] & 0xFF) << 24 
      ); 
     } 

     System.out.println("end of file"); 
    } 
} 

private static void printDescriptor(byte[] bytes) 
     throws IOException { 
    String desc = new String(bytes, "US-ASCII"); 
    System.out.println("found '" + desc + "' descriptor"); 
} 

Ben rastgele WAV dosyası Diğer öğeler arasında, Microsoft's RIFF specification, alt bölümlerin herhangi bir sırada görünebileceğini söylüyor. Bildiğim bazı büyük ses sistemleri bile yanlış anlıyorlar ve bunu hesaba katmıyorlar.

Belirli bir yığın bulmak istiyorsanız, aradığınız dosyayı bulana kadar her tanıtıcıyı kontrol ederek dosyaya geçin.

+0

Radiodef, yorumunuz için teşekkürler! Daha önce hiç kaydırma yapmamıştım ve web'deki pratik kullanım örneklerini bulamıyorum. Bu ifadenin ne yaptığını açıklayabilir misiniz, burada bit değişimi kullanılıyor? (bytes[0] & 0xFF) | (bytes[1] & 0xFF) << 8 | (bytes[2] & 0xFF) << 16 | (bytes[3] & 0xFF) << 24 Önceden teşekkürler! –

+0

@RomanM 4 bayt bir 32 bit tam sayıya dönüştürür. Örneğin, 3200 bitlik bir '00000000000000001000000010000001' (ondalık 32897) tamsayı, dört bayta, 00000000, 00000000, 10000000 ve 10000001'e bölünebilir. Bit kaydırmalı kod, bayt alır ve her baytı yer değiştirerek ve sonra bitwise OR ile birleştirerek, bunlardan 32 bit bir tam sayı oluşturur. '& 0xFF 'kısmı Java'ya özgüdür ve [burada açıklanmıştır] (https://stackoverflow.com/q/11380062/2891664). – Radiodef

+0

Çok teşekkür ederim! –

2

Radiodef'in mükemmel yanıtına ek olarak, belirgin olmayan 3 şey eklemek istiyorum.

  1. WAV dosyaları için tek kural, DATA yığınından önce FMT yığınının gelmesidir. Bunun dışında, DATA parçasından önce ve sonrasında, bilmediğiniz parçaları göreceksiniz. Bir sonraki parçayı bulmak için ileri atlamak için her bir yığın için üstbilgiyi okumalısınız.

  2. FMT yığın genellikle 16 bayt ve 18 bayt varyasyonlarda bulunur, ancak bu özellik aslında 18 bayttan daha fazlasına izin verir. FMT yığın üstbilgi alanı alanı 16'dan büyük yazıyorsa, Bayt 17 ve 18 ayrıca fazladan baytların kaç tane olduğunu belirtir. Bu nedenle, her ikisi de sıfırsa, 16 baytlık bire aynı olan bir 18 baytlık FMT öbeği ile sonuçlanırsınız. Sadece FMT parçasının ilk 16 baytında okumak ve daha fazlasını görmezden gelmek güvenlidir. Bu neden önemlidir? - daha fazla değil, ama Windows XP Media Player 16 bit WAV dosyalarını, ancak sadece FMT yığın Extended (18+ bayt) sürümüyse 24 bit WAV dosyalarını çalabildi. "Windows 24-bit WAV dosyalarını çalmıyor" diye bir çok şikayette bulunmaktaydı, fakat eğer bir 18 baytlık FMT öyküsü varsa ... Microsoft, Windows 7'nin ilk günlerinde bunu düzeltdi. 16 bit FMT dosyaları ile 24 bit şimdi iyi çalışıyor.

  3. (Yeni eklenenler) Garip boyutları olan yığın boyutları oldukça sık olarak ortaya çıkar. Çoğunlukla 24 bit mono dosya yapıldığında görülür. Spesifik olmayan verilerden belirsizdir, fakat yığın büyüklüğü gerçek veri uzunluğunu (tek değer) belirtir ve yığından sonra ve bir sonraki parçanın başlamasından önce bir tampon bayt (sıfır) eklenir. Böylece parçalar her zaman sınırlar üzerinde başlar, ancak yığın büyüklüğünün kendisi gerçek tek değer olarak saklanır.

İlgili konular