/// <summary> /// Code from these gents /// https://answers.unity.com/questions/189886/displaying-an-audio-waveform-in-the-editor.html /// </summary> public Texture2D PaintWaveformSpectrum(AudioClip audio, int width, int height, Color col) { if (Event.current.type != EventType.Repaint) { return(null); } Texture2D tex = new Texture2D(width, height, TextureFormat.RGBA32, false); float[] samples = new float[audio.samples * audio.channels]; // Copy sample data to array audio.GetData(samples, 0); Color lightShade = new Color(0.3f, 0.3f, 0.3f); int halfHeight = height / 2; float leftValue = AudioPlaybackToolEditor.CalculateZoomedLeftValue(); float rightValue = AudioPlaybackToolEditor.CalculateZoomedRightValue(); int leftSide = Mathf.RoundToInt(leftValue * samples.Length); int rightSide = Mathf.RoundToInt(rightValue * samples.Length); float zoomLevel = AudioPlaybackToolEditor.scrollZoom / AudioPlaybackToolEditor.MAX_SCROLL_ZOOM; int packSize = Mathf.RoundToInt((int)samples.Length / (int)width * (float)zoomLevel) + 1; int s = 0; int limit = Mathf.Min(rightSide, samples.Length); // Build waveform data float[] waveform = new float[limit]; for (int i = leftSide; i < limit; i += packSize) { waveform[s] = Mathf.Abs(samples[i]); s++; } if (myScript.loopMode == LoopMode.LoopWithLoopPoints) { float beginning = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, myScript.loopStart / audio.length); float ending = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, myScript.loopEnd / audio.length); float loopStart = beginning * width; float loopEnd = ending * width; for (int x = 0; x < width; x++) { // Here we limit the scope of the area based on loop points if (x < loopStart || x > loopEnd) { for (int y = 0; y < height; y++) { tex.SetPixel(x, y, Color.black); } } else { for (int y = 0; y < height; y++) { tex.SetPixel(x, y, lightShade); } } } } else { for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { tex.SetPixel(x, y, lightShade); } } } for (int x = 0; x < Mathf.Clamp(rightSide, 0, width); x++) { // Scale the wave vertically relative to half the rect height and the relative volume float heightLimit = waveform[x] * halfHeight * myScript.relativeVolume; for (int y = (int)heightLimit; y >= 0; y--) { Color currentPixelColour = tex.GetPixel(x, halfHeight + y); tex.SetPixel(x, halfHeight + y, currentPixelColour + col * 0.75f); // Get data from upper half offset by 1 unit due to int truncation currentPixelColour = tex.GetPixel(x, halfHeight - (y + 1)); // Draw bottom half with data from upper half tex.SetPixel(x, halfHeight - (y + 1), currentPixelColour + col * 0.75f); } } tex.Apply(); return(tex); }
/// <summary> /// Code from these gents /// https://answers.unity.com/questions/189886/displaying-an-audio-waveform-in-the-editor.html /// </summary> public Texture2D PaintWaveformSpectrum(AudioClip audio, int width, int height, Color col) { if (Event.current.type != EventType.Repaint) { return(null); } Texture2D tex = new Texture2D(width, height, TextureFormat.RGBA32, false); float[] samples = new float[audio.samples * audio.channels]; // Copy sample data to array audio.GetData(samples, 0); float leftValue = AudioPlaybackToolEditor.CalculateZoomedLeftValue(); float rightValue = AudioPlaybackToolEditor.CalculateZoomedRightValue(); int leftSide = Mathf.RoundToInt(leftValue * samples.Length); int rightSide = Mathf.RoundToInt(rightValue * samples.Length); float zoomLevel = AudioPlaybackToolEditor.scrollZoom / AudioPlaybackToolEditor.MAX_SCROLL_ZOOM; int packSize = Mathf.RoundToInt((int)samples.Length / (int)width * (float)zoomLevel) + 1; int s = 0; int limit = Mathf.Min(rightSide, samples.Length); // Build waveform data float[] waveform = new float[limit]; for (int i = leftSide; i < limit; i += packSize) { waveform[s] = Mathf.Abs(samples[i]); s++; } float fadeInDuration = asset.fadeInDuration; float fadeOutDuration = asset.fadeOutDuration; Color lightShade = new Color(0.3f, 0.3f, 0.3f); int halfHeight = height / 2; // The halved height limit of the wave at the left/right extremes float fadeStart = leftValue * halfHeight; float fadeEnd = rightValue * halfHeight; switch (asset.fadeMode) { case FadeMode.None: { for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { tex.SetPixel(x, y, lightShade); } } } break; case FadeMode.FadeIn: // Paint the fade-in area { // Scope lol { // Scale our fadeIn value by the current zoom float fadeInRelative = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, fadeInDuration); float fadeStartRelative = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, 0); // Get the length of the whole fade shape from the scaled fade duration relative to the rect width float fadeWidth = width * fadeInRelative; // Offset the lerp value by how much the left side bar is obscuring the start of the fade int offset = Mathf.RoundToInt(width * Mathf.Abs(fadeStartRelative)); // Clamp the limit in case fadeWidth exceeds the right side of the bar for (int x = 0; x + offset < Mathf.Clamp(fadeWidth, 0, width) + offset; x++) { // Paint amount of vertical black depending on progress // Lerp from 0 to half height as those are the extremes we're working with // amountToPaint is the amount of float lerpValue = (float)(x + offset) / (fadeWidth + offset); int amountToPaint = (int)Mathf.Lerp(0, halfHeight, lerpValue); for (int y = halfHeight; y >= 0; y--) { switch (amountToPaint) { case 0: tex.SetPixel(x, y, Color.black); break; default: tex.SetPixel(x, y, lightShade); amountToPaint--; break; } } // Paint the same on the lower half for (int y = halfHeight; y < height; y++) { tex.SetPixel(x, halfHeight - y, tex.GetPixel(x, y - halfHeight)); } } for (int x = (int)Mathf.Clamp(fadeWidth, 0, width); x < width; x++) { for (int y = 0; y < height; y++) { tex.SetPixel(x, y, lightShade); } } } } break; case FadeMode.FadeOut: { float fadeStartRelative = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, 1 - fadeOutDuration); int fadeStartX = Mathf.RoundToInt(width * fadeStartRelative); for (int x = 0; x < fadeStartX; x++) { for (int y = 0; y < height; y++) { tex.SetPixel(x, y, lightShade); } } float fadeEndRelative = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, 1); float fadeWidth = width * (fadeEndRelative - fadeStartRelative); for (int x = fadeStartX; x < width; x++) { float lerpValue = (float)(x - fadeStartX) / (fadeWidth); int amountToPaint = (int)Mathf.Lerp(halfHeight, 0, lerpValue); for (int y = halfHeight; y >= 0; y--) { switch (amountToPaint) { case 0: tex.SetPixel(x, y, Color.black); break; default: tex.SetPixel(x, y, lightShade); break; } amountToPaint = Mathf.Clamp(amountToPaint - 1, 0, height); } for (int y = halfHeight; y < height; y++) { tex.SetPixel(x, halfHeight - y, tex.GetPixel(x, y - halfHeight)); } } } break; case FadeMode.FadeInAndOut: { // Scale our fadeIn value by the current zoom float fadeInRelative = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, fadeInDuration); float fadeStartRelative = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, 0); // Get the length of the whole fade shape from the scaled fade duration relative to the rect width float fadeWidth = width * fadeInRelative; // Offset the lerp value by how much the left side bar is obscuring the start of the fade int offset = Mathf.RoundToInt(width * Mathf.Abs(fadeStartRelative)); for (int x = 0; x + offset < Mathf.Clamp(fadeWidth, 0, width) + offset; x++) { float lerpValue = (float)(x + offset) / (fadeWidth + offset); int amountToPaint = (int)Mathf.Lerp(0, halfHeight, lerpValue); for (int y = halfHeight; y >= 0; y--) { switch (amountToPaint) { case 0: tex.SetPixel(x, y, Color.black); break; default: tex.SetPixel(x, y, lightShade); break; } amountToPaint = Mathf.Clamp(amountToPaint - 1, 0, height); } for (int y = halfHeight; y < height; y++) { tex.SetPixel(x, halfHeight - y, tex.GetPixel(x, y - halfHeight)); } } fadeStartRelative = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, 1 - fadeOutDuration); float fadeStartX = width * fadeStartRelative; // Paint the middle rectangle for (int x = (int)fadeWidth; x < Mathf.Clamp(fadeStartX, 0, width); x++) { for (int y = 0; y < height; y++) { tex.SetPixel(x, y, lightShade); } } float fadeEndRelative = JSAMExtensions.InverseLerpUnclamped(leftValue, rightValue, 1); fadeWidth = width * (fadeEndRelative - fadeStartRelative); // Paint the right-side triangle for (int x = (int)fadeStartX; x < width; x++) { float lerpValue = (float)(x - fadeStartX) / (fadeWidth); int amountToPaint = (int)Mathf.Lerp(halfHeight, 0, lerpValue); for (int y = halfHeight; y >= 0; y--) { switch (amountToPaint) { case 0: tex.SetPixel(x, y, Color.black); break; default: tex.SetPixel(x, y, lightShade); break; } amountToPaint = Mathf.Clamp(amountToPaint - 1, 0, height); } for (int y = halfHeight; y < height; y++) { tex.SetPixel(x, halfHeight - y, tex.GetPixel(x, y - halfHeight)); } } } break; } for (int x = 0; x < Mathf.Clamp(rightSide, 0, width); x++) { // Scale the wave vertically relative to half the rect height and the relative volume float heightLimit = waveform[x] * halfHeight * asset.relativeVolume; for (int y = (int)heightLimit; y >= 0; y--) { Color currentPixelColour = tex.GetPixel(x, halfHeight + y); if (currentPixelColour == Color.black) { continue; } tex.SetPixel(x, halfHeight + y, lightShade + col * 0.75f); // Get data from upper half offset by 1 unit due to int truncation tex.SetPixel(x, halfHeight - (y + 1), lightShade + col * 0.75f); } } tex.Apply(); return(tex); }