using NAudio.Wave.SampleProviders; using Svg; using System.Windows.Media.Imaging; namespace QuikDawEditor.EditingClasses; public partial class Clip { internal string UniqueWaveFormImageDirectory { get { return clipType == ClipType.Midi ? "" : Path.GetFileNameWithoutExtension(ClipSourceFileName) + "_" + GetUniquePngWaveformDirGuidFromAudioFile(ClipSourceFileName); } } internal string UniqueWaveFormImageDirectoryFullPath { get { return ProjectWaveformImagesDirectory + "\\" + UniqueWaveFormImageDirectory; } } public string WaveFormatString = ""; internal List GetWavePeaks2(string thisClipWavePeakString) { List wpeaks = new List(); if (thisClipWavePeakString != "") for (int wno = 0; wno < thisClipWavePeakString.Length - 1; wno += 2) wpeaks.Add(Convert.ToInt16(thisClipWavePeakString.Substring(wno, 2), 16)); return wpeaks; } internal List GetWavePeaks4(string thisClipWavePeakString) { List wpeaks = new List(); if (thisClipWavePeakString != "") for (int wno = 0; wno < thisClipWavePeakString.Length - 1; wno += 4) wpeaks.Add(Convert.ToInt16(thisClipWavePeakString.Substring(wno, 4), 16)); return wpeaks; } internal void ApplyWaveBitmapSourcesToImage() { WaveBitmapSource foundWBS = waveBitmapSources.FirstOrDefault(wbs => wbs.sourceFileDirName == this.UniqueWaveFormImageDirectory); if (foundWBS != null) { try { //Necessary because applying existing wavebitmap source to new clip needs a source length if (this.ClipSourceLenMilliseconds == 100) //(clip was newly created with the default 100ms len) { this.ClipSourceLenMilliseconds = foundWBS.sourceFileLengthMs; this.ClipWidthMs = this.ClipSourceLenMilliseconds; } else foundWBS.sourceFileLengthMs = this.ClipSourceLenMilliseconds; foreach (BitmapSource bsource in foundWBS.waveBmSources) this.ClipBackgroundSources.Add(new ClipBackgroundSource(bsource)); this.IsWaveformLoaded = true; } catch (Exception ex) { MessageBox.Show("error loading bmsources\n" + ex.Message); } } else { Debug.WriteLine("Could not find wavebitmapsource for: " + this.ClipSourceFileName); GetWaveformFromAudioFile(); } } public string BitmapSourceFileName; public void GetWaveformFromAudioFile() { if (ClipSourceLenMilliseconds <= 0 || !Directory.Exists(ProjectWaveformImagesDirectory + "\\" + UniqueWaveFormImageDirectory)) { switch (Path.GetExtension(this.ClipSourceFileName)) { case ".mp3": if (File.Exists(this.clipSourceFullPath)) GetMp3FileInfo(); break; case ".wav": if (File.Exists(this.clipSourceFullPath)) GetWavFileInfo(); break; } } WaveBitmapSource foundSource = waveBitmapSources.FirstOrDefault(wbs => wbs.sourceFileDirName == this.UniqueWaveFormImageDirectory); if (foundSource == null) Application.Current.Dispatcher.Invoke(() => { MakeWaveformImageSource(); }, DispatcherPriority.Background); } private double WaveFormVerticalRes = 1; private double pixelRelativeSamplingRes = 50; // greater res,more data (20 is sufficient res for 40x) private List WaveformPeaks = new List(); WaveBitmapSource newwavebitmapsource; internal void MakeWaveformImageSource() { if (WaveformPeaks.Count == 0) return; //Use xoffset to make fine adjustments to horizontal wave position on clip: double xoffset = -1; //First make empty image sources and get real ones off the thread int numImages = (int)(WaveformPeaks.Count / 28000) + 1; int splitCount = (int)Math.Truncate((double)WaveformPeaks.Count / numImages); double verticalSize = UnitHeight * WaveFormVerticalRes; Uri uriX = new Uri("pack://application:,,,/EDITING/images/LoadingEmpty.png"); BitmapImage holderSource = new BitmapImage(); holderSource.BeginInit(); holderSource.UriSource = uriX; holderSource.DecodePixelWidth = splitCount; holderSource.DecodePixelHeight = (int)verticalSize; holderSource.EndInit(); for (int i = 0; i < numImages; i++) ClipBackgroundSources.Add(new ClipBackgroundSource(holderSource)); //Time-consuming image creation task in the background Task.Run(() => { newwavebitmapsource = new WaveBitmapSource() { sourceFileDirName = UniqueWaveFormImageDirectory, sourceFileLengthMs = this.ClipSourceLenMilliseconds }; for (int i = 0; i < numImages; i++) { List splitPeaks = WaveformPeaks.Take(new Range(i * splitCount, (i + 1) * splitCount)).ToList(); System.Drawing.Bitmap bmap = GetBitmapSourceFromWavePeaksBackground(splitPeaks, verticalSize); if (bmap != null) // bmap may return null due to threading problem { using (MemoryStream memory = new MemoryStream()) { bmap.Save(memory, System.Drawing.Imaging.ImageFormat.Png); try { //Save png for future startup efficiency if (!Directory.Exists(UniqueWaveFormImageDirectoryFullPath)) Directory.CreateDirectory(UniqueWaveFormImageDirectoryFullPath); bmap.Save(ProjectWaveformImagesDirectory + "\\" + UniqueWaveFormImageDirectory + "\\img_" + i + ".png", System.Drawing.Imaging.ImageFormat.Png); } catch (Exception ex) { Debug.WriteLine("Error writing pngbitmapecoder: " + ClipSourceFileName + "\n" + ex.Message); } bmap.Dispose(); memory.Position = 0; Application.Current.Dispatcher.Invoke(() => { BitmapImage bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); ClipBackgroundSource cbSource = new ClipBackgroundSource(bitmapImage); cbSource.bSource.Freeze(); ClipBackgroundSources[i] = cbSource; newwavebitmapsource.waveBmSources.Add(cbSource.bSource); }, DispatcherPriority.ContextIdle); } } } waveBitmapSources.Add(newwavebitmapsource); //WaveformPeaks.Clear(); can't clear due to threading IsWaveformLoaded = true; }); } internal System.Drawing.Bitmap GetBitmapSourceFromWavePeaksBackground(List wavePeaks, double verticalSize) { int bmapWidth = wavePeaks.Count; double yScale = 1; StringBuilder sbuilder = new StringBuilder("\r\n\r\n" + "\r\n" + "\r\n\r\n\r\n"); SvgDocument svgDoc = SvgDocument.FromSvg(sbuilder.ToString()); //The time-consuming line System.Drawing.Bitmap bitmap = svgDoc.Draw(); if (bitmap == null) Debug.WriteLine("NULL BITMAP!"); ////Save svg file? //svgDoc.Write(ProjectWaveformImagesDirectory + "\\" + UniqueWaveFormImageDirectory + "\\svg_" + i + ".svg"); return bitmap; } public List GetWaveformPeakValues(Mp3FileReaderBase mp3reader) { //Get Waveform peak values List wavePeaks = new List(); VolumeSampleProvider vsp = new VolumeSampleProvider(mp3reader.ToSampleProvider()); int samplesPerPixel = (int)Math.Round(vsp.WaveFormat.SampleRate / PixelsPerSecond); int sampLen = (int)(vsp.WaveFormat.SampleRate * mp3reader.TotalTime.TotalSeconds * vsp.WaveFormat.Channels); vsp.Volume = 1; float[] totalReadFloats = new float[sampLen]; vsp.Read(totalReadFloats, 0, totalReadFloats.Length); int resolutionStride = (int)(samplesPerPixel / pixelRelativeSamplingRes); for (int sampno = 0; sampno < totalReadFloats.Length; sampno += resolutionStride * vsp.WaveFormat.Channels) { float avFloat = totalReadFloats[sampno]; float maxno = 0; //Get average from stride group for (int getmaxno = 0; getmaxno < Math.Min(resolutionStride, totalReadFloats.Length - sampno); getmaxno++) { if (Math.Abs(totalReadFloats[sampno + getmaxno]) > Math.Abs(maxno)) maxno = totalReadFloats[sampno + getmaxno]; } short thisShort = Convert.ToInt16((float)UnitHeight / 2 * maxno); wavePeaks.Add((short)((UnitHeight / 2 - thisShort - 1) * WaveFormVerticalRes)); } return wavePeaks; } //private string GetWaveformPeakValueString(ISampleProvider vsp, double totalMilliseconds) private List GetWaveformPeakValues(ISampleProvider vsp, double totalMilliseconds) { //Get Waveform peak values List wavePeaks = new List(); int samplesPerPixel = (int)Math.Round(vsp.WaveFormat.SampleRate / PixelsPerSecond); int sampLen = (int)(vsp.WaveFormat.SampleRate * totalMilliseconds / 1000D * vsp.WaveFormat.Channels); float[] totalReadFloats = new float[sampLen]; try { vsp.Read(totalReadFloats, 0, totalReadFloats.Length); } catch (Exception ex) { Debug.WriteLine("Couldn't read in file to get floats with Naudio..."); return wavePeaks; } int resolutionStride = (int)(samplesPerPixel / pixelRelativeSamplingRes); for (int sampno = 0; sampno < totalReadFloats.Length; sampno += resolutionStride * vsp.WaveFormat.Channels) { float avFloat = totalReadFloats[sampno]; float maxno = 0; for (int getmaxno = 0; getmaxno < Math.Min(resolutionStride, totalReadFloats.Length - sampno); getmaxno++) { if (Math.Abs(totalReadFloats[sampno + getmaxno]) > Math.Abs(maxno)) maxno = totalReadFloats[sampno + getmaxno]; //maxno = Math.Max(maxno, Math.Abs(totalReadFloats[sampno + getmaxno])); } short thisShort = Convert.ToInt16((float)UnitHeight / 2 * maxno); wavePeaks.Add((short)((UnitHeight / 2 - thisShort - 1) * WaveFormVerticalRes)); } return wavePeaks; } }