public static ISound ReadFromData(byte[] data, SoundType soundType) { switch (soundType) { case SoundType.Wav: return(WavSound.GetWavSound(data)); case SoundType.Mp3: return(null); default: return(null); } }
public static ISound ReadFromFile(string path) { if (!File.Exists(path)) { throw new FileNotFoundException($@"{path}: No such file or Directory"); } var fileExtension = Path.GetExtension(path); if (!AcceptedFormats.Contains(fileExtension)) // TODO: Check through other way using Linux Style :) { throw new Exception($@"{path}: Format not accepted"); // TODO: Define new type of exception } var bytes = File.ReadAllBytes(path); switch (fileExtension) { case ".wav": return(WavSound.GetWavSound(bytes)); case ".mp3": var sound2 = new Mp3Sound(); // TODO: ID3v2 //ID3v1 if (Encoding.UTF8.GetString(bytes.Slice(0, 3)) != "ID3") { var headerBits = bytes.Slice(0, 4).ToBitsArray(); /*var frameSync = headerBits.Take(11).ToList(); * if (frameSync.Sum() != frameSync.Count) * throw new Exception("Invalid MP3 file");*/ var mpegVersion = headerBits.Skip(11).Take(2).ToList(); if (mpegVersion[0] == 0) { sound2.MpegVersion = mpegVersion[1] == 0 ? MpegVersion.Mpeg25 : MpegVersion.Reserved; } else { sound2.MpegVersion = mpegVersion[1] == 0 ? MpegVersion.Mpeg2 : MpegVersion.Mpeg1; } var layerType = headerBits.Skip(13).Take(2).ToList(); if (layerType[0] == 0) { sound2.LayerType = layerType[1] == 0 ? LayerType.Reserved : LayerType.Layer3; } else { sound2.LayerType = layerType[1] == 0 ? LayerType.Layer2 : LayerType.Layer1; } var protectionBit = headerBits.Skip(15).Take(1).ToList()[0]; sound2.Protection = protectionBit == 0; var bitRateBits = headerBits.Skip(16).Take(4).ToList(); switch (bitRateBits) { case var _ when bitRateBits.IsEqualTo(new List <int> { 0, 0, 0, 0 }): sound2.BitRateIndex = 0; break; case var _ when bitRateBits.IsEqualTo(new List <int> { 0, 0, 0, 1 }): if ((sound2.MpegVersion == MpegVersion.Mpeg2 || sound2.MpegVersion == MpegVersion.Mpeg25) && (sound2.LayerType == LayerType.Layer2 || sound2.LayerType == LayerType.Layer3)) { sound2.BitRateIndex = 8; } else { sound2.BitRateIndex = 32; } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 0, 0, 1, 0 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 64 : 48; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 48 : 16; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 40 : 16; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 0, 0, 1, 1 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 96 : 56; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 56 : 24; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 48 : 24; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 0, 1, 0, 0 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 128 : 64; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 64 : 32; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 56 : 32; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 0, 1, 0, 1 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 160 : 80; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 80 : 40; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 64 : 40; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 0, 1, 1, 0 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 192 : 96; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 96 : 48; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 80 : 48; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 0, 1, 1, 1 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 224 : 112; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 112 : 56; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 96 : 56; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 1, 0, 0, 0 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 256 : 128; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 128 : 64; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 112 : 64; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 1, 0, 0, 1 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 288 : 144; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 160 : 80; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 128 : 80; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 1, 0, 1, 0 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 320 : 160; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 192 : 96; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 160 : 96; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 1, 0, 1, 1 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 352 : 176; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 224 : 112; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 192 : 112; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 1, 1, 0, 0 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 384 : 192; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 256 : 128; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 224 : 128; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 1, 1, 0, 1 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 416 : 224; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 320 : 144; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 256 : 144; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; case var _ when bitRateBits.IsEqualTo(new List <int> { 1, 1, 1, 0 }): switch (sound2.LayerType) { case LayerType.Layer1: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 448 : 256; break; case LayerType.Layer2: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 384 : 160; break; case LayerType.Layer3: sound2.BitRateIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 320 : 160; break; case LayerType.Reserved: break; default: throw new ArgumentOutOfRangeException(); } break; default: sound2.BitRateIndex = -1; break; } var frequencyIndexBits = headerBits.Skip(20).Take(2).ToList(); switch (frequencyIndexBits) { case var _ when frequencyIndexBits.IsEqualTo(new List <int> { 0, 0 }): sound2.RateFrequencyIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 44100 : sound2.MpegVersion == MpegVersion.Mpeg2 ? 22050 : 11025; break; case var _ when frequencyIndexBits.IsEqualTo(new List <int> { 0, 1 }): sound2.RateFrequencyIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 48000 : sound2.MpegVersion == MpegVersion.Mpeg2 ? 24000 : 12000; break; case var _ when frequencyIndexBits.IsEqualTo(new List <int> { 1, 0 }): sound2.RateFrequencyIndex = sound2.MpegVersion == MpegVersion.Mpeg1 ? 32000 : sound2.MpegVersion == MpegVersion.Mpeg2 ? 16000 : 8000; break; case var _ when frequencyIndexBits.IsEqualTo(new List <int> { 1, 1 }): sound2.RateFrequencyIndex = -1; break; } var paddingBit = headerBits.Skip(22).Take(1).ToList(); sound2.Padded = paddingBit[0] == 1; var privateBit = headerBits.Skip(23).Take(1).ToList(); sound2.PrivateBit = privateBit[0] == 1; var channelModeBits = headerBits.Skip(24).Take(2).ToList(); if (channelModeBits[0] == 0) { sound2.ChannelMode = channelModeBits[1] == 0 ? ChannelMode.Stereo : ChannelMode.JointStereo; } else { sound2.ChannelMode = channelModeBits[1] == 0 ? ChannelMode.DualChannel : ChannelMode.SingleChannel; } var modeExtenstionBits = headerBits.Skip(26).Take(2).ToList(); var copyrightBits = headerBits.Skip(28).Take(1).ToList(); sound2.Copyright = copyrightBits[0] == 1; var originalBits = headerBits.Skip(29).Take(1).ToList(); sound2.Original = originalBits[0] == 1; var emphasisBits = headerBits.Skip(30).ToList(); if (emphasisBits[0] == 0) { sound2.EmphasisType = emphasisBits[1] == 0 ? EmphasisType.None : EmphasisType._50_15ms; } else { sound2.EmphasisType = emphasisBits[1] == 0 ? EmphasisType.Reserved : EmphasisType.Ccitj17; } } else //ID3v2 { var majorVersion = BitConverter.ToInt32(bytes.Slice(3, 1, 4), 0); var minorVersion = BitConverter.ToInt32(bytes.Slice(4, 1, 4), 0); var flagsBits = bytes[5].ToBitsArray(); var unsynchronisationFlag = flagsBits[0] == 1; var extendedHeaderFlag = flagsBits[1] == 1; var experimentalIndicatorFlag = flagsBits[2] == 1; var footerPresentFlag = flagsBits[3] == 1; if (!flagsBits.Skip(4).ToList().IsEqualTo(new List <int> { 0, 0, 0, 0 })) { throw new Exception("MP3 Error"); } var size = bytes[9] + bytes[8] << 7 + bytes[6] << 14 + bytes[5] << 21; var currentByte = 10; if (extendedHeaderFlag) { var extendedHeaderSize = bytes[currentByte + 3] + bytes[currentByte + 2] << 7 + bytes[currentByte + 1] << 14 + bytes[currentByte] << 21; currentByte += extendedHeaderSize; //5; // skip flags size // TODO: parse extended header - optional // https://mutagen-specs.readthedocs.io/en/latest/id3/id3v2.4.0-structure.html } // TODO: Frames // TODO: Parse padding - optional // TODO: Parse footer - optional } // TODO: Data from mp3 var tagBytes = bytes.Reverse().Take(128).Reverse().ToArray(); var tagString = Encoding.UTF8.GetString(tagBytes.Slice(0, 3)); if (tagString.ToUpper() != "TAG") { return(sound2); } var tag = new Tag { Title = Encoding.UTF8.GetString(tagBytes.Slice(3, 30)).Trim('\0'), Artist = Encoding.UTF8.GetString(tagBytes.Slice(33, 30)).Trim('\0'), Album = Encoding.UTF8.GetString(tagBytes.Slice(63, 30)).Trim('\0'), Year = Encoding.UTF8.GetString(tagBytes.Slice(93, 4)).Trim('\0'), CommentV1 = Encoding.UTF8.GetString(tagBytes.Slice(97, 30)).Trim('\0'), CommentV2 = Encoding.UTF8.GetString(tagBytes.Slice(97, 28)).Trim('\0'), AlbumTrack = Encoding.UTF8.GetString(tagBytes.Slice(125, 2)).Trim('\0'), Genre = BitConverter.ToInt32(tagBytes.Slice(127, 1, 4), 0) }; sound2.Tag = tag; return(sound2); default: return(null); } }
public static WavSound GetWavSound(byte[] bytes) { var sound = new WavSound { RawBinary = bytes.ToList() }; var currentIndex = 0; sound.ChunkId = Encoding.UTF8.GetString(bytes.Slice(currentIndex, 4)); currentIndex += 4; bool bigEndian; switch (sound.ChunkId) { case SoundConstants.RIFF: bigEndian = false; break; case SoundConstants.RIFX: bigEndian = true; break; default: throw new Exception($"Unknown format: {sound.ChunkId}"); } sound.ChunkSize = bytes.Slice(currentIndex, 4).GetUInt(bigEndian) + 8; currentIndex += 4; sound.Format = Encoding.UTF8.GetString(bytes.Slice(currentIndex, 4)); currentIndex += 4; if (sound.Format != "WAVE") { throw new Exception("This is not a WAV File"); } var fmtChunkReceived = false; while (currentIndex < bytes.Length) { var chunkId = Encoding.UTF8.GetString(bytes.Slice(currentIndex, 4)); currentIndex += 4; switch (chunkId) { case SoundConstants.FMT_ID: fmtChunkReceived = true; sound.SubChunk1Id = chunkId; sound.SubChunk1Size = bytes.Slice(currentIndex, 4).GetUInt(bigEndian); currentIndex += 4; var bytesRead = 0; if (sound.SubChunk1Size < 16) { throw new Exception("Wrong WAV file format"); } sound.AudioFormat = bytes.Slice(currentIndex, 2, 4).GetUShort(bigEndian); currentIndex += 2; sound.NumChannels = bytes.Slice(currentIndex, 2, 4).GetUShort(bigEndian); currentIndex += 2; sound.SampleRate = bytes.Slice(currentIndex, 4).GetUInt(bigEndian); currentIndex += 4; sound.ByteRate = bytes.Slice(currentIndex, 4).GetUInt(bigEndian); currentIndex += 4; sound.BlockAlign = bytes.Slice(currentIndex, 2, 4).GetUShort(bigEndian); currentIndex += 2; sound.BitDepth = bytes.Slice(currentIndex, 2, 4).GetUShort(bigEndian); currentIndex += 2; bytesRead += 16; if (sound.AudioFormat == SoundConstants.WAVE_FORMAT_EXTENSIBLE && sound.SubChunk1Size >= 18) { sound.ExtraChunkSize = bytes.Slice(currentIndex, 2, 4).GetUShort(bigEndian); bytesRead += 2; currentIndex += 2; if (sound.ExtraChunkSize >= 22) { sound.ExtraData = bytes.Slice(currentIndex, 22, 4); currentIndex += 22; bytesRead += 22; var rawGuid = sound.ExtraData.Slice(6, 16, 4); var tail = bigEndian ? new byte[] { 0x0, 0x0, 0x0, 0x10, 0x80, 0x0, 0x0, 0xAA, 0x00, 0x38, 0x9B, 0x71 } : new byte[] { 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0xAA, 0x0, 0x38, 0x9B, 0x71 }; if (rawGuid.EndsWith(tail)) { sound.AudioFormat = rawGuid.Skip(rawGuid.Length - 4).ToArray().GetUShort(bigEndian); } } else { throw new Exception("Wave error"); } } if (!SoundConstants.KNOWN_WAVE_FORMATS.Contains(sound.AudioFormat)) { throw new Exception("Unknown wave format"); } if (sound.SubChunk1Size > bytesRead) { currentIndex += (int)(sound.SubChunk1Size - bytesRead); } if (!new List <ushort> { 8, 16, 32, 64, 96, 128 }.Contains(sound.BitDepth)) { throw new Exception($"Unsupported bit depth: {sound.BitDepth}-Bit"); } break; case SoundConstants.FACT_ID: if (currentIndex + 4 < bytes.Length) { currentIndex += 4 + bytes.Slice(currentIndex, 4, 4).GetUShort(bigEndian); } break; case SoundConstants.DATA_ID: if (!fmtChunkReceived) { throw new Exception("No ftm chunk before data"); } sound.SubChunk2Id = chunkId; sound.SubChunk2Size = bytes.Slice(currentIndex, 4, 4).GetUInt(bigEndian); currentIndex += 4; var bytesPerSample = (ushort)(sound.BitDepth / 8); var type = sound.BitDepth == 8 ? "u1" : (bigEndian ? ">" : "<") + (sound.AudioFormat == SoundConstants.WAVE_FORMAT_PCM ? "i" : "f") + bytesPerSample; sound.Data = bytes.Skip(currentIndex).Take((int)sound.SubChunk2Size).ToArray().FromBuffer(type); currentIndex += (int)sound.SubChunk2Size; // if (sound.NumChannels > 1) break; case SoundConstants.LIST_ID: if (currentIndex + 4 < bytes.Length) { currentIndex += 4 + bytes.Slice(currentIndex, 4, 4).GetUShort(bigEndian); } break; case var _ when new List <string> { SoundConstants.JUNK_ID, SoundConstants.FAKE_ID }.Contains(sound.SubChunk1Id): if (currentIndex + 4 < bytes.Length) { currentIndex += 4 + bytes.Slice(currentIndex, 4, 4).GetUShort(bigEndian); } break; default: // warning if (currentIndex + 4 < bytes.Length) { currentIndex += 4 + bytes.Slice(currentIndex, 4, 4).GetUShort(bigEndian); } break; } } sound.Duration = sound.ChunkSize * 1.0 / (sound.SampleRate * sound.NumChannels * sound.BitDepth / 8.0); return(sound); }