private void ApplyID3Tag(ID3TagData tag) { if (tag == null) return; if (!string.IsNullOrEmpty(tag.Title)) _lame.ID3SetTitle(tag.Title); if (!string.IsNullOrEmpty(tag.Artist)) _lame.ID3SetArtist(tag.Artist); if (!string.IsNullOrEmpty(tag.Album)) _lame.ID3SetAlbum(tag.Album); if (!string.IsNullOrEmpty(tag.Year)) _lame.ID3SetYear(tag.Year); if (!string.IsNullOrEmpty(tag.Comment)) _lame.ID3SetComment(tag.Comment); if (!string.IsNullOrEmpty(tag.Genre)) _lame.ID3SetGenre(tag.Genre); if (!string.IsNullOrEmpty(tag.Track)) _lame.ID3SetTrack(tag.Track); if (!string.IsNullOrEmpty(tag.Subtitle)) _lame.ID3SetFieldValue(string.Format("TIT3={0}", tag.Subtitle)); if (!string.IsNullOrEmpty(tag.AlbumArtist)) _lame.ID3SetFieldValue(string.Format("TPE2={0}", tag.AlbumArtist)); if (tag.AlbumArt != null && tag.AlbumArt.Length > 0 && tag.AlbumArt.Length < 131072) _lame.ID3SetAlbumArt(tag.AlbumArt); }
/// <summary>Setup ID3 tag with supplied information</summary> /// <param name="tag">ID3 data</param> private void ApplyID3Tag(ID3TagData tag) { if (tag == null) { return; } // Apply standard ID3 fields if (!string.IsNullOrEmpty(tag.Title)) { _lame.ID3SetTitle(tag.Title); } if (!string.IsNullOrEmpty(tag.Artist)) { _lame.ID3SetArtist(tag.Artist); } if (!string.IsNullOrEmpty(tag.Album)) { _lame.ID3SetAlbum(tag.Album); } if (!string.IsNullOrEmpty(tag.Year)) { _lame.ID3SetYear(tag.Year); } if (!string.IsNullOrEmpty(tag.Comment)) { _lame.ID3SetComment(tag.Comment); } if (!string.IsNullOrEmpty(tag.Genre)) { _lame.ID3SetGenre(tag.Genre); } if (!string.IsNullOrEmpty(tag.Track)) { _lame.ID3SetTrack(tag.Track); } // Apply standard ID3 fields that are not directly supported by LAME if (!string.IsNullOrEmpty(tag.Subtitle)) { _lame.ID3SetFieldValue(string.Format("TIT3={0}", tag.Subtitle)); } if (!string.IsNullOrEmpty(tag.AlbumArtist)) { _lame.ID3SetFieldValue(string.Format("TPE2={0}", tag.AlbumArtist)); } // Add user-defined tags if present // NB: LAME handles the replacement of duplicates. if (tag.UserDefinedTags?.Length > 0) { foreach (var userDefinedTag in tag.UserDefinedTags) { _lame.ID3SetFieldValue(string.Format("TXXX={0}", userDefinedTag)); } } // Set the album art if supplied and within size limits if (tag.AlbumArt?.Length > 0 && tag.AlbumArt.Length < 131072) { _lame.ID3SetAlbumArt(tag.AlbumArt); } }
/// <summary>Create MP3FileWriter to write to supplied stream</summary> /// <param name="outStream">Stream to write encoded data to</param> /// <param name="format">Input WaveFormat</param> /// <param name="bitRate">Output bit rate in kbps</param> /// <param name="id3">Optional ID3 data block</param> public LameMP3FileWriter(Stream outStream, WaveFormat format, int bitRate, ID3TagData id3 = null) : base() { Loader.Init(); //if (!Loader.Initialized) // Loader.Initialized = false; // sanity check if (outStream == null) { throw new ArgumentNullException("outStream"); } if (format == null) { throw new ArgumentNullException("format"); } // check for unsupported wave formats if (format.Channels != 1 && format.Channels != 2) { throw new ArgumentException(string.Format("Unsupported number of channels {0}", format.Channels), "format"); } if (format.Encoding != WaveFormatEncoding.Pcm && format.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException(string.Format("Unsupported encoding format {0}", format.Encoding.ToString()), "format"); } if (format.Encoding == WaveFormatEncoding.Pcm && format.BitsPerSample != 16) { throw new ArgumentException(string.Format("Unsupported PCM sample size {0}", format.BitsPerSample), "format"); } if (format.Encoding == WaveFormatEncoding.IeeeFloat && format.BitsPerSample != 32) { throw new ArgumentException(string.Format("Unsupported Float sample size {0}", format.BitsPerSample), "format"); } if (format.SampleRate < 8000 || format.SampleRate > 48000) { throw new ArgumentException(string.Format("Unsupported Sample Rate {0}", format.SampleRate), "format"); } // select encoder function that matches data format if (format.Encoding == WaveFormatEncoding.Pcm) { if (format.Channels == 1) { _encode = encode_pcm_16_mono; } else { _encode = encode_pcm_16_stereo; } } else { if (format.Channels == 1) { _encode = encode_float_mono; } else { _encode = encode_float_stereo; } } // Set base properties this.inputFormat = format; this.outStream = outStream; this.disposeOutput = false; // Allocate buffers based on sample rate this.inBuffer = new ArrayUnion(format.AverageBytesPerSecond); this.outBuffer = new byte[format.SampleRate * 5 / 4 + 7200]; // Initialize lame library this._lame = new LibMp3Lame(); this._lame.InputSampleRate = format.SampleRate; this._lame.NumChannels = format.Channels; this._lame.BitRate = bitRate; if (id3 != null) { ApplyID3Tag(id3); } this._lame.InitParams(); }
/// <summary>Create MP3FileWriter to write to a file on disk</summary> /// <param name="outFileName">Name of file to create</param> /// <param name="format">Input WaveFormat</param> /// <param name="bitRate">Output bit rate in kbps</param> /// <param name="id3">Optional ID3 data block</param> public LameMP3FileWriter(string outFileName, WaveFormat format, int bitRate, ID3TagData id3 = null) : this(File.Create(outFileName), format, bitRate, id3) { this.disposeOutput = true; }
/// <summary>Create MP3FileWriter to write to a file on disk</summary> /// <param name="outFileName">Name of file to create</param> /// <param name="format">Input WaveFormat</param> /// <param name="quality">LAME quality preset</param> /// <param name="id3">Optional ID3 data block</param> public LameMP3FileWriter(string outFileName, WaveFormat format, NAudio.Lame.LAMEPreset quality, ID3TagData id3 = null) : this(File.Create(outFileName), format, quality, id3) { this.disposeOutput = true; }
/// <summary>Create MP3FileWriter to write to supplied stream</summary> /// <param name="outStream">Stream to write encoded data to</param> /// <param name="format">Input WaveFormat</param> /// <param name="quality">LAME quality preset</param> /// <param name="id3">Optional ID3 data block</param> public LameMP3FileWriter(Stream outStream, WaveFormat format, LAMEPreset quality, ID3TagData id3 = null) : base() { if (format == null) { throw new ArgumentNullException("format"); } // check for unsupported wave formats if (format.Channels != 1 && format.Channels != 2) { throw new ArgumentException($"Unsupported number of channels {format.Channels}", "format"); } if (format.Encoding != WaveFormatEncoding.Pcm && format.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException($"Unsupported encoding format {format.Encoding}", "format"); } if (format.Encoding == WaveFormatEncoding.Pcm && format.BitsPerSample != 16) { throw new ArgumentException($"Unsupported PCM sample size {format.BitsPerSample}", "format"); } if (format.Encoding == WaveFormatEncoding.IeeeFloat && format.BitsPerSample != 32) { throw new ArgumentException($"Unsupported Float sample size {format.BitsPerSample}", "format"); } if (format.SampleRate < 8000 || format.SampleRate > 48000) { throw new ArgumentException($"Unsupported Sample Rate {format.SampleRate}", "format"); } // select encoder function that matches data format if (format.Encoding == WaveFormatEncoding.Pcm) { if (format.Channels == 1) { _encode = Encode_pcm_16_mono; } else { _encode = Encode_pcm_16_stereo; } } else { if (format.Channels == 1) { _encode = Encode_float_mono; } else { _encode = Encode_float_stereo; } } // Set base properties _inputFormat = format; _outStream = outStream ?? throw new ArgumentNullException("outStream"); _disposeOutput = false; // Allocate buffers based on sample rate _inBuffer = new ArrayUnion(format.AverageBytesPerSecond); _outBuffer = new byte[format.SampleRate * 5 / 4 + 7200]; // Initialize lame library _lame = new LibMp3Lame { InputSampleRate = format.SampleRate, NumChannels = format.Channels }; _lame.SetPreset((int)quality); if (id3 != null) { ApplyID3Tag(id3); } _lame.InitParams(); }
/// <summary> /// Decode frames from ID3 tag /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="size"></param> /// <param name="flags"></param> /// <returns></returns> private static ID3TagData InternalDecode(byte[] buffer, int offset, int size, byte flags) { // copy tag body data into array and remove unsynchronization padding if present byte[] bytes = new byte[size]; Array.Copy(buffer, offset, bytes, 0, size); if ((flags & 0x80) != 0) { bytes = UnsyncBytes(bytes); } var res = new ID3TagData(); int pos = 0; // skip extended header if present if ((flags & 0x40) != 0) { var ehSize = DecodeBEInt32(bytes, pos); pos += ehSize + 4; } // load all frames from the tag buffer for (var frame = ID3FrameData.ReadFrame(bytes, pos, out int frameSize); frameSize > 0 && frame != null; frame = ID3FrameData.ReadFrame(bytes, pos, out frameSize)) { switch (frame.FrameID) { case "TIT2": res.Title = frame.ParseString(); break; case "TPE1": res.Artist = frame.ParseString(); break; case "TALB": res.Album = frame.ParseString(); break; case "TYER": res.Year = frame.ParseString(); break; case "COMM": res.Comment = frame.ParseCommentText(); break; case "TCON": res.Genre = frame.ParseString(); break; case "TRCK": res.Track = frame.ParseString(); break; case "TIT3": res.Subtitle = frame.ParseString(); break; case "TPE2": res.AlbumArtist = frame.ParseString(); break; case "TXXX": { var udt = frame.ParseUserDefinedText(); res.UserDefinedText[udt.Key] = udt.Value; break; } case "APIC": { var pic = frame.ParseAPIC(); res.AlbumArt = pic?.ImageBytes; break; } default: break; } pos += frameSize; } return(res); }
/// <summary>Setup ID3 tag with supplied information</summary> /// <param name="tag">ID3 data</param> private void ApplyID3Tag(ID3TagData tag) { if (tag == null) { return; } _lame.ID3Init(); // Apply standard ID3 fields if (!string.IsNullOrEmpty(tag.Title)) { _lame.ID3SetTitle(tag.Title); } if (!string.IsNullOrEmpty(tag.Artist)) { _lame.ID3SetArtist(tag.Artist); } if (!string.IsNullOrEmpty(tag.Album)) { _lame.ID3SetAlbum(tag.Album); } if (!string.IsNullOrEmpty(tag.Year)) { _lame.ID3SetYear(tag.Year); } if (!string.IsNullOrEmpty(tag.Comment)) { _lame.ID3SetComment(tag.Comment); } if (!string.IsNullOrEmpty(tag.Genre)) { _lame.ID3SetGenre(tag.Genre); } if (!string.IsNullOrEmpty(tag.Track)) { _lame.ID3SetTrack(tag.Track); } // Apply standard ID3 fields that are not directly supported by LAME if (!string.IsNullOrEmpty(tag.Subtitle)) { _lame.ID3SetFieldValue($"TIT3={tag.Subtitle}"); } if (!string.IsNullOrEmpty(tag.AlbumArtist)) { _lame.ID3SetFieldValue($"TPE2={tag.AlbumArtist}"); } // Add user-defined tags if present foreach (var kv in tag.UserDefinedText) { _lame.ID3SetFieldValue($"TXXX={kv.Key}={kv.Value}"); } // Set the album art if supplied if (tag.AlbumArt?.Length > 0) { _lame.ID3SetAlbumArt(tag.AlbumArt); } // check size of ID3 tag, if too large write it ourselves. byte[] data = _lame.ID3GetID3v2Tag(); if (data.Length >= 32768) { _lame.ID3WriteTagAutomatic = false; outStream.Write(data, 0, data.Length); } }
/// <summary>Create MP3FileWriter to write to supplied stream</summary> /// <param name="outStream">Stream to write encoded data to</param> /// <param name="format">Input WaveFormat</param> /// <param name="quality">LAME quality preset</param> /// <param name="id3">Optional ID3 data block</param> public LameMP3FileWriter(Stream outStream, WaveFormat format, LAMEPreset quality, ID3TagData id3 = null) : this(outStream, format, new LameConfig { Preset = quality, ID3 = id3 }) { }
/// <summary>Create MP3FileWriter to write to supplied stream</summary> /// <param name="outStream">Stream to write encoded data to</param> /// <param name="format">Input WaveFormat</param> /// <param name="bitRate">Output bit rate in kbps</param> /// <param name="id3">Optional ID3 data block</param> public LameMP3FileWriter(Stream outStream, WaveFormat format, int bitRate, ID3TagData id3 = null) : this(outStream, format, new LameConfig { BitRate = bitRate, ID3 = id3 }) { }