void onUpdate() { if (lastScrollTime.HasValue && lastScrollTime.Value + 1_000_000 < Stopwatch.GetTimestamp()) { lastScrollTime = null; waveformRenderer.clearMinMaxCache(); Repaint(); } }
void OnGUI() { var clip = selectedAudioClip; var text = (clip != null ? clip.name : "No audio clip selected"); EditorGUILayout.BeginHorizontal(); GUILayout.Label("Audio editor: " + text); if (clip != null && selectionRenderer.hasSelection) { if (GUILayout.Button("Fade in")) { volumeGraph.applyFadeIn(selectionRenderer.selectionFrom, selectionRenderer.selectionTo); Repaint(); } if (GUILayout.Button("Fade out")) { volumeGraph.applyFadeOut(selectionRenderer.selectionFrom, selectionRenderer.selectionTo); Repaint(); } ; if (GUILayout.Button("Silence")) { volumeGraph.applySilence(selectionRenderer.selectionFrom, selectionRenderer.selectionTo); Repaint(); } if (selectionRenderer.hasSelectionRange) { if (GUILayout.Button("Crop")) { cropAudio(selectionRenderer.selectionFrom, selectionRenderer.selectionTo); Repaint(); } } else { if (GUILayout.Button("crop from")) { cropAudio(selectionRenderer.selectionFrom, to: 1); Repaint(); } if (GUILayout.Button("crop to")) { cropAudio(from: 0, selectionRenderer.selectionTo); Repaint(); } } } if (GUILayout.Button("Export")) { exportAudio(); Repaint(); } if (clip != null) { if (AudioUtilBindings.isClipPlaying(clip)) { if (GUILayout.Button("Stop")) { AudioUtilBindings.stopAllClips(); isPaused = false; } if (!isPaused) { if (GUILayout.Button("Pause")) { isPaused = true; AudioUtilBindings.pauseClip(clip); } } else { if (GUILayout.Button("Play")) { isPaused = false; AudioUtilBindings.resumeClip(clip); } } } else { if (GUILayout.Button("Play")) { isPaused = false; AudioUtilBindings.stopAllClips(); AudioUtilBindings.playClip(clip); if (selectionRenderer.hasSelection) { var sampleFrom = Mathf.FloorToInt(selectionRenderer.selectionFrom * currentAudioData.sampleCount); AudioUtilBindings.setClipSamplePosition(clip, sampleFrom); } } } EditorGUILayout.PrefixLabel("Playback volume"); playbackVolume = EditorGUILayout.Slider(playbackVolume, 0, 1); } EditorGUILayout.EndHorizontal(); if (clip == null) { return; } if (AudioUtilBindings.isClipPlaying(clip) && selectionRenderer.hasSelectionRange) { var currentSample = AudioUtilBindings.getClipSamplePosition(clip); var sampleFrom = Mathf.FloorToInt(selectionRenderer.selectionFrom * currentAudioData.sampleCount); var sampleTo = Mathf.FloorToInt(selectionRenderer.selectionTo * currentAudioData.sampleCount); if (currentSample >= sampleTo) { AudioUtilBindings.stopAllClips(); } } var mouseInScrollView = contentRect.containsPoint(Event.current.mousePosition); scrollPos = GUI.BeginScrollView(contentRect, scrollPos, scrollRect, alwaysShowHorizontal: true, alwaysShowVertical: false); if (volumeGraph.handleMouseEvent(Event.current, channelRect(0, currentAudioData.channels))) { Event.current.Use(); Repaint(); } else if (selectionRenderer.handleEvent(Event.current, scrollRect)) { Event.current.Use(); Repaint(); } else { switch (Event.current.type) { case EventType.Repaint: repaint(); break; case EventType.ScrollWheel: if (mouseInScrollView) { // var mouseBefore = Mathf.InverseLerp(0, scrollRect.width, Event.current.mousePosition.x); scale = Mathf.Clamp(scale - Event.current.delta.y * 0.1f, min: 1, max: 40); // var mouseAfter = Mathf.InverseLerp(0, scrollRect.width, Event.current.mousePosition.x); // Debug.Log($"Before: {mouseBefore}, after: {mouseAfter}"); // scrollPos.x += (mouseAfter - mouseBefore); lastScrollTime = Stopwatch.GetTimestamp(); Repaint(); Event.current.Use(); } break; case EventType.KeyDown: if (Event.current.keyCode == KeyCode.Space) { if (clip == null) { break; } Event.current.Use(); if (AudioUtilBindings.isClipPlaying(clip)) { if (!isPaused) { isPaused = true; AudioUtilBindings.pauseClip(clip); } else { isPaused = false; AudioUtilBindings.resumeClip(clip); } } else { isPaused = false; AudioUtilBindings.stopAllClips(); AudioUtilBindings.playClip(clip); if (selectionRenderer.hasSelection) { var sampleFrom = Mathf.FloorToInt(selectionRenderer.selectionFrom * currentAudioData.sampleCount); AudioUtilBindings.setClipSamplePosition(clip, sampleFrom); } } } else if (Event.current.keyCode == KeyCode.LeftArrow) { Event.current.Use(); selectionRenderer.moveSelection(seconds: -1, clipLength: clip.length, moveTo: (Event.current.modifiers & EventModifiers.Shift) == 0); Repaint(); } else if (Event.current.keyCode == KeyCode.RightArrow) { Event.current.Use(); selectionRenderer.moveSelection(seconds: 1, clipLength: clip.length, moveTo: (Event.current.modifiers & EventModifiers.Shift) == 0); Repaint(); } break; } } GUI.EndScrollView(); }
/// <summary>Waits for the associated process to exit.</summary> /// <param name="millisecondsTimeout">The amount of time to wait, or -1 to wait indefinitely.</param> /// <returns>true if the process exited; false if the timeout occurred.</returns> internal bool WaitForExit(int millisecondsTimeout) { Debug.Assert(!Monitor.IsEntered(_gate)); // Track the time the we start waiting. long startTime = Stopwatch.GetTimestamp(); // Polling loop while (true) { bool createdTask = false; CancellationTokenSource cts = null; Task waitTask; // We're in a polling loop... determine how much time remains int remainingTimeout = millisecondsTimeout == Timeout.Infinite ? Timeout.Infinite : (int)Math.Max(millisecondsTimeout - ((Stopwatch.GetTimestamp() - startTime) / (double)Stopwatch.Frequency * 1000), 0); lock (_gate) { // If we already know that the process exited, we're done. if (_exited) { return(true); } // If a timeout of 0 was supplied, then we simply need to poll // to see if the process has already exited. if (remainingTimeout == 0) { // If there's currently a wait-in-progress, then we know the other process // hasn't exited (barring races and the polling interval). if (_waitInProgress != null) { return(false); } // No one else is checking for the process' exit... so check. // We're currently holding the _gate lock, so we don't want to // allow CheckForExit to block indefinitely. CheckForExit(); return(_exited); } // The process has not yet exited (or at least we don't know it yet) // so we need to wait for it to exit, outside of the lock. // If there's already a wait in progress, we'll do so later // by waiting on that existing task. Otherwise, we'll spin up // such a task. if (_waitInProgress != null) { waitTask = _waitInProgress; } else { createdTask = true; CancellationToken token = remainingTimeout == Timeout.Infinite ? CancellationToken.None : (cts = new CancellationTokenSource(remainingTimeout)).Token; waitTask = WaitForExitAsync(token); // PERF NOTE: // At the moment, we never call CheckForExit(true) (which in turn allows // waitpid to block until the child has completed) because we currently call it while // holdling the _gate lock. This is probably unnecessary in some situations, and in particular // here if remainingTimeout == Timeout.Infinite. In that case, we should be able to set // _waitInProgress to be a TaskCompletionSource task, and then below outside of the lock // we could do a CheckForExit(blockingAllowed:true) and complete the TaskCompletionSource // after that. We would just need to make sure that there's no risk of the other state // on this instance experiencing torn reads. } } // lock(_gate) if (createdTask) { // We created this task, and it'll get canceled automatically after our timeout. // This Wait should only wake up when either the process has exited or the timeout // has expired. Either way, we'll loop around again; if the process exited, that'll // be caught first thing in the loop where we check _exited, and if it didn't exit, // our remaining time will be zero, so we'll do a quick remaining check and bail. waitTask.Wait(); if (cts != null) { cts.Dispose(); } } else { // It's someone else's task. We'll wait for it to complete. This could complete // either because our remainingTimeout expired or because the task completed, // which could happen because the process exited or because whoever created // that task gave it a timeout. In any case, we'll loop around again, and the loop // will catch these cases, potentially issuing another wait to make up any // remaining time. waitTask.Wait(remainingTimeout); } } }