<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<?xml-stylesheet href="./_c74_ref.xsl" type="text/xsl"?>

<c74object name='spectrogram~'>

	<digest>
		<o>buffer~</o> based spectrogram analyzer, editor, and viewer
	</digest>
	<description>
		Use the <o>spectrogram~</o> object to analyze, edit, and view the spectral contents of a <o>buffer~</o>.
		<br />
		<br />
		<a href="https://www.youtube.com/playlist?list=PLHc927eu2oKYhvWtMZ7XhzYrs3WBCVVWj">Tutorial Videos on YouTube</a>
	</description>


	<!--METADATA-->
	<metadatalist>
		<metadata name='author'>Zachary Seldess</metadata>
		<metadata name='tag'>MSP</metadata>
		<metadata name='tag'>MSP U/I</metadata>
	</metadatalist>


	<!--INLETS-->
	<inletlist>
		<inlet id="0" type="float">
			<digest>Display Start (ms)</digest>
		</inlet>
		<inlet id="1" type="float">
			<digest>Display Length (ms)</digest>
		</inlet>
		<inlet id="2" type="float">
			<digest>Display Low (Hz)</digest>
		</inlet>
		<inlet id="3" type="float">
			<digest>Display High (Hz)</digest>
		</inlet>
		<inlet id="4" type="float">
			<digest>Display Selection Start (ms)</digest>
		</inlet>
		<inlet id="5" type="float">
			<digest>Display Selection End (ms)</digest>
		</inlet>
		<inlet id="6" type="float">
			<digest>Display Selection Low (Hz)</digest>
		</inlet>
		<inlet id="7" type="float">
			<digest>Display Selection High (Hz)</digest>
		</inlet>
		<inlet id="8" type="message">
			<digest>Link In (for multi-channel viewing)</digest>
		</inlet>
	</inletlist>


	<!--OUTLETS-->
	<outletlist>
		<outlet id="0" type="float">
			<digest>Display Start (ms)</digest>
		</outlet>
		<outlet id="1" type="float">
			<digest>Display Length (ms)</digest>
		</outlet>
		<outlet id="2" type="float">
			<digest>Display Low (Hz)</digest>
		</outlet>
		<outlet id="3" type="float">
			<digest>Display High (Hz)</digest>
		</outlet>
		<outlet id="4" type="float">
			<digest>Display Selection Start (ms)</digest>
		</outlet>
		<outlet id="5" type="float">
			<digest>Display Selection End (ms)</digest>
		</outlet>
		<outlet id="6" type="float">
			<digest>Display Selection Low (Hz)</digest>
		</outlet>
		<outlet id="7" type="float">
			<digest>Display Selection High (Hz)</digest>
		</outlet>
		<outlet id="8" type="message">
			<digest>Link Out (for multi-channel viewing)</digest>
		</outlet>
		<outlet id="9" type="message">
			<digest>Data at mouse location (time freq mag)</digest>
		</outlet>
	</outletlist>


	<!--ARGUMENTS-->
	<objarglist>
	</objarglist>


	<!--MESSAGES-->
	<methodlist>
		<method name="write">
			<arglist />
			<digest>
				Save spectrogram as full or custom-resolution PNG file (full resolution = 1 pixel per bin).
			</digest>
			<description>
				Save spectrogram as full or custom-resolution PNG file (full resolution = 1 pixel per bin).
				<br />
				Optional arguments:
				<ul>
					<li>
						<b>Version 1</b> - Width Height DPI (will open file chooser)
					</li>
					<li>
						<b>Version 2</b> - Filename Width Height DPI
					</li>
					Defaults:
					<ul>
						<li>
							Width = Current object width (patcher_rect or presentation_rect). Range 1 - 100000.
						</li>
						<li>
							Height = Current object height (patching_rect or presentation_rect). Range 1 - 100000.
						</li>
						<li>
							DPI = 72. Range 1 - 10000.
						</li>
					</ul>
					Providing a Width or Height of &lt;= 0 will fall back to full resolution
					<br />
					(i.e. Width = number of FFT windows, Height = number of FFT bins per window).
				</ul>
				<o>spectrogram~</o> will save to .png using its current display/color/etc. settings.
				<br />
				For example:
				<ul>
					<li>
						To save without time and frequency text, set @time_text and @freq_text to 0.
					</li>
					<li>
						To save using a specific magnitude scale or color scheme, set @mag_scale and @color_scheme accordingly.
					</li>
					<li>
						etc.
					</li>
				</ul>
			</description>
		</method>
		<method name="zoom_in">
			<arglist />
			<digest>
				Zoom in (time and frequency).
			</digest>
			<description>
				Zoom in (time and frequency).
			</description>
		</method>
		<method name="zoom_in_freq">
			<arglist />
			<digest>
				Zoom in (frequency).
			</digest>
			<description>
				Zoom in (frequency).
			</description>
		</method>
		<method name="zoom_in_time">
			<arglist />
			<digest>
				Zoom in (time).
			</digest>
			<description>
				Zoom in (time).
			</description>
		</method>

		<method name="zoom_out">
			<arglist />
			<digest>
				Zoom out (time and frequency).
			</digest>
			<description>
				Zoom out (time and frequency).
			</description>
		</method>
		<method name="zoom_out_freq">
			<arglist />
			<digest>
				Zoom out (frequency).
			</digest>
			<description>
				Zoom out (frequency).
			</description>
		</method>
		<method name="zoom_out_time">
			<arglist />
			<digest>
				Zoom out (time).
			</digest>
			<description>
				Zoom out (time).
			</description>
		</method>

		<method name="view_all">
			<arglist />
			<digest>
				Display entire buffer~ (time and frequency).
			</digest>
			<description>
				Display entire buffer~ (time and frequency).
			</description>
		</method>
		<method name="view_all_freq">
			<arglist />
			<digest>
				Display entire buffer~ (frequency).
			</digest>
			<description>
				Display entire buffer~ (frequency).
			</description>
		</method>
		<method name="view_all_time">
			<arglist />
			<digest>
				Display entire buffer~ (time).
			</digest>
			<description>
				Display entire buffer~ (time).
			</description>
		</method>
		<method name="view_selected">
			<arglist />
			<digest>
				Display selection (i.e. zoom to selection).
			</digest>
			<description>
				Display selection (i.e. zoom to selection).
			</description>
		</method>

		<method name="select_none">
			<arglist />
			<digest>
				Deselect all.
			</digest>
			<description>
				Deselect all.
			</description>
		</method>
		<method name="select_all">
			<arglist />
			<digest>
				Select entire buffer~ (time and frequency).
			</digest>
			<description>
				Select entire buffer~ (time and frequency).
			</description>
		</method>
		<method name="select_all_freq">
			<arglist />
			<digest>
				Select entire buffer~ (frequency).
			</digest>
			<description>
				Select entire buffer~ (frequency).
			</description>
		</method>
		<method name="select_all_time">
			<arglist />
			<digest>
				Select entire buffer~ (time).
			</digest>
			<description>
				Select entire buffer~ (time).
			</description>
		</method>
		<method name="select_window">
			<arglist />
			<digest>
				Select current window (time and frequency).
			</digest>
			<description>
				Select current window (time and frequency).
			</description>
		</method>
		<method name="select_window_freq">
			<arglist />
			<digest>
				Select current window (frequency).
			</digest>
			<description>
				Select current window (frequency).
			</description>
		</method>
		<method name="select_window_time">
			<arglist />
			<digest>
				Select current window (time).
			</digest>
			<description>
				Select current window (time).
			</description>
		</method>

		<method name="edit_shapes_isolate">
			<arglist />
			<digest>
				Keep audio inside drawn shapes, zero everything else.
			</digest>
			<description>
				Applies the drawn shapes (from <b>@mouse_mode 2</b>) as a spectral mask to the reference <o>buffer~</o>. STFT bins whose time/frequency coordinates fall inside any drawn shape are kept; all other bins are zeroed. The result is written back to the reference buffer via inverse FFT with overlap-add reconstruction.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, the inside/outside roles are swapped (bins outside shapes are kept, bins inside are zeroed).
				<br />
				<br />
				Requires at least one drawn shape with 3 or more points. If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently (i.e. the spectral mask is applied to each channel's own STFT data, not to the mixdown).
			</description>
		</method>
		<method name="edit_shapes_delete">
			<arglist />
			<digest>
				Remove audio inside drawn shapes, keep everything else.
			</digest>
			<description>
				The inverse of <b>edit_shapes_isolate</b>. Applies the drawn shapes (from <b>@mouse_mode 2</b>) as a spectral mask to the reference <o>buffer~</o>. STFT bins whose time/frequency coordinates fall inside any drawn shape are zeroed; all other bins are kept. The result is written back to the reference buffer via inverse FFT with overlap-add reconstruction.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, the inside/outside roles are swapped (bins inside shapes are kept, bins outside are zeroed).
				<br />
				<br />
				Requires at least one drawn shape with 3 or more points. If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently (i.e. the spectral mask is applied to each channel's own STFT data, not to the mixdown).
			</description>
		</method>
		<method name="edit_shapes_gain">
			<arglist>
				<arg name="inside_dB" type="float" optional="0" />
				<arg name="outside_dB" type="float" optional="1" />
			</arglist>
			<digest>
				Apply dB gain to bins inside (and optionally outside) drawn shapes.
			</digest>
			<description>
				Applies gain (specified in dB) to STFT bins based on the drawn shapes (from <b>@mouse_mode 2</b>). With one argument, the gain is applied to bins inside the drawn shapes while bins outside are left unchanged. With two arguments, the first gain is applied to bins inside the shapes and the second gain is applied to bins outside.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, the inside/outside gain values are swapped (the first gain applies to bins outside the shapes, the second to bins inside).
				<br />
				<br />
				For example, <b>edit_shapes_gain -6</b> attenuates audio inside the shapes by 6 dB. <b>edit_shapes_gain 0 -inf</b> is equivalent to <b>edit_shapes_isolate</b>. <b>edit_shapes_gain -inf 0</b> is equivalent to <b>edit_shapes_delete</b>.
				<br />
				<br />
				Requires at least one drawn shape with 3 or more points. If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently (i.e. the spectral mask is applied to each channel's own STFT data, not to the mixdown).
			</description>
		</method>
		<method name="edit_shapes_clear">
			<arglist />
			<digest>
				Clear all drawn shapes.
			</digest>
			<description>
				Removes all drawn shapes created in Draw mode (<b>@mouse_mode 2</b>).
			</description>
		</method>
		<method name="edit_shapes_clearlast">
			<arglist />
			<digest>
				Remove the most recently drawn shape.
			</digest>
			<description>
				Removes the most recently drawn shape created in Draw mode (<b>@mouse_mode 2</b>). Can be sent repeatedly to undo shapes one at a time (most recent first).
			</description>
		</method>
		<method name="edit_selection_isolate">
			<arglist />
			<digest>
				Keep audio inside selection rectangle, zero everything else.
			</digest>
			<description>
				Applies the selection rectangle (from <b>@mouse_mode 0</b>) as a spectral mask to the reference <o>buffer~</o>. STFT bins whose time/frequency coordinates fall inside the selection rectangle are kept; all other bins are zeroed. The result is written back to the reference buffer via inverse FFT with overlap-add reconstruction.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, the inside/outside roles are swapped (bins outside the selection are kept, bins inside are zeroed).
				<br />
				<br />
				Requires an active selection (selection start != end and selection low != high). If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently (i.e. the spectral mask is applied to each channel's own STFT data, not to the mixdown).
			</description>
		</method>
		<method name="edit_selection_delete">
			<arglist />
			<digest>
				Remove audio inside selection rectangle, keep everything else.
			</digest>
			<description>
				The inverse of <b>edit_selection_isolate</b>. Applies the selection rectangle (from <b>@mouse_mode 0</b>) as a spectral mask to the reference <o>buffer~</o>. STFT bins whose time/frequency coordinates fall inside the selection rectangle are zeroed; all other bins are kept. The result is written back to the reference buffer via inverse FFT with overlap-add reconstruction.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, the inside/outside roles are swapped (bins inside the selection are kept, bins outside are zeroed).
				<br />
				<br />
				Requires an active selection (selection start != end and selection low != high). If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently (i.e. the spectral mask is applied to each channel's own STFT data, not to the mixdown).
			</description>
		</method>
		<method name="edit_selection_gain">
			<arglist>
				<arg name="inside_dB" type="float" optional="0" />
				<arg name="outside_dB" type="float" optional="1" />
			</arglist>
			<digest>
				Apply dB gain to bins inside (and optionally outside) selection rectangle.
			</digest>
			<description>
				Applies gain (specified in dB) to STFT bins based on the selection rectangle (from <b>@mouse_mode 0</b>). With one argument, the gain is applied to bins inside the selection rectangle while bins outside are left unchanged. With two arguments, the first gain is applied to bins inside the selection and the second gain is applied to bins outside.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, the inside/outside gain values are swapped (the first gain applies to bins outside the selection, the second to bins inside).
				<br />
				<br />
				For example, <b>edit_selection_gain -6</b> attenuates audio inside the selection by 6 dB. <b>edit_selection_gain 0 -inf</b> is equivalent to <b>edit_selection_isolate</b>. <b>edit_selection_gain -inf 0</b> is equivalent to <b>edit_selection_delete</b>.
				<br />
				<br />
				Requires an active selection (selection start != end and selection low != high). If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently (i.e. the spectral mask is applied to each channel's own STFT data, not to the mixdown).
			</description>
		</method>
		<method name="edit_selection_freq_shift">
			<arglist>
				<arg name="shift_Hz" type="float" optional="0" />
				<arg name="gain_dB" type="float" optional="1" />
				<arg name="mode" type="int" optional="1" />
			</arglist>
			<digest>
				Frequency-shift bins inside selection rectangle by a constant Hz offset.
			</digest>
			<description>
				Shifts STFT bins inside the selection rectangle (from <b>@mouse_mode 0</b>) by a constant frequency offset specified in Hz. Positive values shift up, negative values shift down. Phase is corrected via rotation to maintain coherence across frames. Unlike <b>edit_selection_pitch_shift</b>, this applies an additive offset so harmonic relationships are not preserved (e.g. a harmonic series becomes inharmonic).
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, bins <i>outside</i> the selection are shifted instead.
				<br />
				<br />
				The optional second argument specifies gain in dB applied to the shifted content. If omitted, defaults to <b>0</b> (no change). Positive values boost, negative values attenuate. Useful for blending shifted content more naturally (e.g. shift up an octave and reduce by 6 dB).
				<br />
				<br />
				The optional third argument specifies the shift mode. If omitted, defaults to <b>0</b> (Move &amp; Mix):
				<ul>
					<li>
						<b>0</b> (or <b>move-mix</b>) - Move &amp; Mix. Source bins are zeroed; shifted energy is added to destination bins.
					</li>
					<li>
						<b>1</b> (or <b>move-replace</b>) - Move &amp; Replace. Source bins are zeroed; destination bins are cleared before writing shifted energy.
					</li>
					<li>
						<b>2</b> (or <b>copy-mix</b>) - Copy &amp; Mix. Source bins are kept; shifted energy is added to destination bins.
					</li>
					<li>
						<b>3</b> (or <b>copy-replace</b>) - Copy &amp; Replace. Source bins are kept; destination bins are cleared before writing shifted energy.
					</li>
				</ul>
				For example, <b>edit_selection_freq_shift 500</b> shifts the selection up by 500 Hz using Move &amp; Mix with no gain change. <b>edit_selection_freq_shift -200 -6 2</b> copies the selection down by 200 Hz at -6 dB, keeping the original.
				<br />
				<br />
				Requires an active selection (selection start != end and selection low != high). If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently.
			</description>
		</method>
		<method name="edit_selection_pitch_shift">
			<arglist>
				<arg name="semitones" type="float" optional="0" />
				<arg name="gain_dB" type="float" optional="1" />
				<arg name="mode" type="int" optional="1" />
			</arglist>
			<digest>
				Pitch-shift bins inside selection rectangle by semitones.
			</digest>
			<description>
				Shifts STFT bins inside the selection rectangle (from <b>@mouse_mode 0</b>) by a musical interval specified in semitones. Uses a phase vocoder with instantaneous frequency estimation for high-quality pitch shifting that preserves harmonic relationships (each frequency is multiplied by the same ratio). Positive values shift up, negative values shift down. Fractional semitones are supported.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, bins <i>outside</i> the selection are shifted instead.
				<br />
				<br />
				The optional second argument specifies gain in dB applied to the shifted content. If omitted, defaults to <b>0</b> (no change). Positive values boost, negative values attenuate.
				<br />
				<br />
				The optional third argument specifies the shift mode. If omitted, defaults to <b>0</b> (Move &amp; Mix):
				<ul>
					<li>
						<b>0</b> (or <b>move-mix</b>) - Move &amp; Mix. Source bins are zeroed; shifted energy is added to destination bins.
					</li>
					<li>
						<b>1</b> (or <b>move-replace</b>) - Move &amp; Replace. Source bins are zeroed; destination bins are cleared before writing shifted energy.
					</li>
					<li>
						<b>2</b> (or <b>copy-mix</b>) - Copy &amp; Mix. Source bins are kept; shifted energy is added to destination bins.
					</li>
					<li>
						<b>3</b> (or <b>copy-replace</b>) - Copy &amp; Replace. Source bins are kept; destination bins are cleared before writing shifted energy.
					</li>
				</ul>
				For example, <b>edit_selection_pitch_shift 12</b> shifts the selection up one octave using Move &amp; Mix with no gain change. <b>edit_selection_pitch_shift -7 -6 2</b> copies the selection down a perfect fifth at -6 dB, keeping the original.
				<br />
				<br />
				Requires an active selection (selection start != end and selection low != high). If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently.
			</description>
		</method>
		<method name="edit_shapes_freq_shift">
			<arglist>
				<arg name="shift_Hz" type="float" optional="0" />
				<arg name="gain_dB" type="float" optional="1" />
				<arg name="mode" type="int" optional="1" />
			</arglist>
			<digest>
				Frequency-shift bins inside drawn shapes by a constant Hz offset.
			</digest>
			<description>
				Shifts STFT bins inside the drawn shapes (from <b>@mouse_mode 2</b>) by a constant frequency offset specified in Hz. Positive values shift up, negative values shift down. Phase is corrected via rotation to maintain coherence across frames. Unlike <b>edit_shapes_pitch_shift</b>, this applies an additive offset so harmonic relationships are not preserved.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, bins <i>outside</i> the shapes are shifted instead.
				<br />
				<br />
				The optional second argument specifies gain in dB applied to the shifted content. If omitted, defaults to <b>0</b> (no change). Positive values boost, negative values attenuate.
				<br />
				<br />
				The optional third argument specifies the shift mode. If omitted, defaults to <b>0</b> (Move &amp; Mix):
				<ul>
					<li>
						<b>0</b> (or <b>move-mix</b>) - Move &amp; Mix. Source bins are zeroed; shifted energy is added to destination bins.
					</li>
					<li>
						<b>1</b> (or <b>move-replace</b>) - Move &amp; Replace. Source bins are zeroed; destination bins are cleared before writing shifted energy.
					</li>
					<li>
						<b>2</b> (or <b>copy-mix</b>) - Copy &amp; Mix. Source bins are kept; shifted energy is added to destination bins.
					</li>
					<li>
						<b>3</b> (or <b>copy-replace</b>) - Copy &amp; Replace. Source bins are kept; destination bins are cleared before writing shifted energy.
					</li>
				</ul>
				For example, <b>edit_shapes_freq_shift 500</b> shifts the shapes up by 500 Hz using Move &amp; Mix with no gain change. <b>edit_shapes_freq_shift -200 -6 2</b> copies the shapes down by 200 Hz at -6 dB, keeping the original.
				<br />
				<br />
				Requires at least one drawn shape with 3 or more points. If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently.
			</description>
		</method>
		<method name="edit_shapes_pitch_shift">
			<arglist>
				<arg name="semitones" type="float" optional="0" />
				<arg name="gain_dB" type="float" optional="1" />
				<arg name="mode" type="int" optional="1" />
			</arglist>
			<digest>
				Pitch-shift bins inside drawn shapes by semitones.
			</digest>
			<description>
				Shifts STFT bins inside the drawn shapes (from <b>@mouse_mode 2</b>) by a musical interval specified in semitones. Uses a phase vocoder with instantaneous frequency estimation for high-quality pitch shifting that preserves harmonic relationships. Positive values shift up, negative values shift down. Fractional semitones are supported.
				<br />
				<br />
				When <b>@edit_inverted</b> is enabled, bins <i>outside</i> the shapes are shifted instead.
				<br />
				<br />
				The optional second argument specifies a gain in dB applied to the shifted content. Positive values boost, negative values attenuate. If omitted, defaults to <b>0</b> (no gain change).
				<br />
				<br />
				The optional third argument specifies the shift mode. If omitted, defaults to <b>0</b> (Move &amp; Mix):
				<ul>
					<li>
						<b>0</b> (or <b>move-mix</b>) - Move &amp; Mix. Source bins are zeroed; shifted energy is added to destination bins.
					</li>
					<li>
						<b>1</b> (or <b>move-replace</b>) - Move &amp; Replace. Source bins are zeroed; destination bins are cleared before writing shifted energy.
					</li>
					<li>
						<b>2</b> (or <b>copy-mix</b>) - Copy &amp; Mix. Source bins are kept; shifted energy is added to destination bins.
					</li>
					<li>
						<b>3</b> (or <b>copy-replace</b>) - Copy &amp; Replace. Source bins are kept; destination bins are cleared before writing shifted energy.
					</li>
				</ul>
				For example, <b>edit_shapes_pitch_shift 12</b> shifts the shapes up one octave using Move &amp; Mix with no gain change. <b>edit_shapes_pitch_shift -7 -6 2</b> copies the shapes down a perfect fifth at -6 dB, keeping the original.
				<br />
				<br />
				Requires at least one drawn shape with 3 or more points. If <b>@buffer_chan</b> is set to a specific channel, only that channel is processed. If <b>@buffer_chan</b> is 0 (mixdown), each channel of the reference buffer is processed independently.
			</description>
		</method>
		<method name="undo">
			<arglist />
			<digest>
				Undo the last buffer~ edit operation.
			</digest>
			<description>
				Restores the reference <o>buffer~</o> audio to the state before the most recent edit operation (<b>edit_selection_isolate</b>, <b>edit_selection_delete</b>, <b>edit_selection_gain</b>, <b>edit_selection_freq_shift</b>, <b>edit_selection_pitch_shift</b>, <b>edit_shapes_isolate</b>, <b>edit_shapes_delete</b>, <b>edit_shapes_gain</b>, <b>edit_shapes_freq_shift</b>, or <b>edit_shapes_pitch_shift</b>). Up to 20 levels of undo are supported.
				<br />
				<br />
				After an undo, the undone state is pushed onto the redo stack so it can be re-applied with <b>redo</b>. Making a new edit after an undo clears the redo history. Changing the <b>@buffer_name</b> attribute clears both undo and redo history.
				<br />
				<br />
				Also bound to <b>Cmd-Z</b> (macOS) or <b>Ctrl-Z</b> (Windows) when the <o>spectrogram~</o> object has keyboard focus (click into it first; otherwise Max routes the shortcut to patcher-level undo).
			</description>
		</method>
		<method name="redo">
			<arglist />
			<digest>
				Redo the last undone buffer~ edit operation.
			</digest>
			<description>
				Re-applies the most recently undone edit operation, restoring the reference <o>buffer~</o> audio to the state after the edit. Up to 20 levels of redo are supported.
				<br />
				<br />
				Redo is only available after one or more <b>undo</b> operations. Making a new edit clears the redo history. Changing the <b>@buffer_name</b> attribute clears both undo and redo history.
				<br />
				<br />
				Also bound to <b>Cmd-Shift-Z</b> (macOS) or <b>Ctrl-Shift-Z</b> (Windows) when the <o>spectrogram~</o> object has keyboard focus (click into it first; otherwise Max routes the shortcut to patcher-level redo).
			</description>
		</method>
		<method name="clear_history">
			<arglist />
			<digest>
				Drop both undo and redo stacks.
			</digest>
			<description>
				Clears both the undo and redo stacks, releasing the audio snapshots they hold. After this message, <b>undo</b> and <b>redo</b> have no history to act on until the next edit operation pushes a new snapshot.
				<br />
				<br />
				Useful when a control patch swaps the contents of the reference <o>buffer~</o> in place (without changing <b>@buffer_name</b>) so existing snapshots no longer correspond to the current audio, or to free snapshot memory once a series of edits is considered final. Changing <b>@buffer_name</b> already clears history automatically — <b>clear_history</b> is for cases where the buffer name doesn't change.
			</description>
		</method>
	</methodlist>


	<!--ATTRIBUTES-->
	<attributelist>
		<!--Behavior Category-->
		<attribute name='buffer_name' get='1' set='1' type='symbol' size='1'>
			<digest>
				buffer~ name
			</digest>
			<description>
				buffer~ name.
			</description>
		</attribute>
		<attribute name='buffer_chan' get='1' set='1' type='int' size='1'>
			<digest>
				buffer~ channel to view.
			</digest>
			<description>
				buffer~ channel to view:
				<ul>
					<li>
						<b>0 = mixdown of all channels</b>
					</li>
					<li>
						<b>1 = 1st channel</b> (default)
					</li>
					<li>
						<b>2 = 2nd channel</b>
					</li>
					<li>
						<b>etc...</b>
					</li>
				</ul>
			</description>
		</attribute>
		<attribute name='win_size' get='1' set='1' type='int' size='1'>
			<digest>
				FFT window size (samples).
			</digest>
			<description>
				FFT window size. Range 32 - 32768. Must be a power of 2. (default 2048 samples)
			</description>
		</attribute>
		<attribute name='win_zero_pad' get='1' set='1' type='int' size='1'>
			<digest>
				FFT window zero padding.
			</digest>
			<description>
				FFT window zero padding. Range 1 - 64. (default 1)
				<br />
				<b>Note</b>: Window zero padding is clamped based on the FFT window size, keeping the applied window size (i.e. window size * window zero padding) to less than or equal to 32768 samples.
				<br />
				<b>Warning</b>: Setting window zero padding high with large buffers can considerably increase computation time. Use with care.
			</description>
		</attribute>
		<attribute name='win_overlap' get='1' set='1' type='int' size='1'>
			<digest>
				FFT window overlap.
			</digest>
			<description>
				FFT window overlap. Range 1 - 64. (default 2)
				<br />
				<b>Warning</b>: Setting window overlap high with large buffers can considerably increase computation time. Use with care.
			</description>
		</attribute>
		<attribute name='win_type' get='1' set='1' type='int' size='1'>
			<digest>
				FFT window type.
			</digest>
			<description>
				FFT window type: <a href="https://en.wikipedia.org/wiki/Window_function">about</a>
				<ul>
					<li>
						<b>Rect (0)</b> - Rectangular window
					</li>
					<li>
						<b>Tri (1)</b> - Triangular window
					</li>
					<li>
						<b>Bart (2)</b> - Bartlett window
					</li>
					<li>
						<b>Sine (3)</b> - Sine window
					</li>
					<li>
						<b>Hann (4)</b> - Hanning window (default)
					</li>
					<li>
						<b>Hamm (5)</b> - Hamming window
					</li>
					<li>
						<b>Bart-Hann (6)</b> - Bartlett-Hanning window
					</li>
					<li>
						<b>Black (7)</b> - Blackman window
					</li>
					<li>
						<b>Nutt (8)</b> - Nuttall window
					</li>
					<li>
						<b>Black-Nutt (9)</b> - Blackman-Nuttall window
					</li>
					<li>
						<b>Black-Harr (10)</b> - Blackman-Harris window
					</li>
					<li>
						<b>Gaus (11)</b> - Gaussian window
					</li>
					<li>
						<b>Sqrt (12)</b> - Square Root window
					</li>
				</ul>
			</description>
		</attribute>
		<attribute name='mag_scale' get='1' set='1' type='int' size='1'>
			<digest>
				FFT magnitude scale.
			</digest>
			<description>
				FFT magnitude scale:
				<ul>
					<li>
						<b>Lin (0)</b> - Linear scaling
					</li>
					<li>
						<b>Mel (1)</b> - Mel scaling (default) <a href="https://en.wikipedia.org/wiki/Mel_scale">about</a>
					</li>
					<li>
						<b>Log (2)</b> - Logarithmic scaling
					</li>
					<li>
						<b>Bark (3)</b> - Bark scaling <a href="https://en.wikipedia.org/wiki/Bark_scale">about</a>
					</li>
					<li>
						<b>ERB (4)</b> - Equivalent Rectangular Bandwidth scaling <a href="https://en.wikipedia.org/wiki/Equivalent_rectangular_bandwidth">about</a>
					</li>
				</ul>
			</description>
		</attribute>
		<attribute name='freq_reassign' get='1' set='1' type='int' size='1'>
			<digest>
				Frequency reassignment on/off.
			</digest>
			<description>
				Frequency reassignment uses phase differences between consecutive FFT frames to estimate the instantaneous frequency of spectral components, providing improved frequency resolution especially for low frequencies.
				<ul>
					<li>
						<b>0</b> - Off (default)
					</li>
					<li>
						<b>1</b> - On
					</li>
				</ul>
				When enabled, energy is "scattered" to its true frequency location rather than being confined to FFT bin centers, resulting in sharper spectral lines and better separation of closely-spaced partials.
			</description>
		</attribute>
		<attribute name='mouse_mode' get='1' set='1' type='int' size='1'>
			<digest>
				Mouse mode.
			</digest>
			<description>
				Mouse mode:
				<ul>
					<li>
						<b>Select (0)</b> (default)
						<ol>
							<li>
								Click-and-drag to create selection rectangle (click on unselected portion to deselect).
							</li>
							<li>
								Click-and-drag a side or corner of the selection rectangle to resize it.
							</li>
							<li>
								Click-and-drag inside the selection rectangle to move it.
							</li>
							<li>
								Shift-click-and-drag inside the selection rectangle to resize symmetrically from the center.
								<ul>
									<li>
										Dragging right/left resizes larger/smaller on the time axis (horizontally).
									</li>
									<li>
										Dragging up/down resizes larger/smaller on the frequency axis (vertically).
									</li>
								</ul>
							</li>
						</ol>
					</li>
					<li>
						<b>Move (1)</b>
						<ol>
							<li>
								Click-and-drag to adjust the displayed time window.
								<ul>
									<li>
										Dragging up/down zooms in/out on the time axis (horizontally).
									</li>
									<li>
										Dragging left/right scrolls left/right on the time axis (horizontally).
									</li>
								</ul>
							</li>
							<li>
								Shift-click-and-drag to adjust the displayed frequency window.
								<ul>
									<li>
										Dragging right/left zooms in/out on the frequency axis (vertically).
									</li>
									<li>
										Dragging up/down scrolls up/down on the frequency axis (vertically).
									</li>
								</ul>
							</li>
						</ol>
					</li>
					<li>
						<b>Draw (2)</b>
						<ol>
							<li>
								Click-and-drag to draw closed shapes on the spectrogram. Up to 64 shapes can be drawn, with a maximum of 4096 total points across all shapes.
							</li>
							<li>
								Shift-click inside a drawn shape to remove it.
							</li>
							<li>
								Use the <b>edit_shapes_isolate</b>, <b>edit_shapes_delete</b>, <b>edit_shapes_gain</b>, <b>edit_shapes_freq_shift</b>, and <b>edit_shapes_pitch_shift</b> messages to apply spectral edits to the drawn shapes region of the reference buffer.
							</li>
						</ol>
					</li>
				</ul>
				In all modes, right-clicking opens a context menu with quick access to undo/redo, selection edit operations (isolate, delete, gain, frequency shift, pitch shift), shape edit operations (isolate, delete, gain, frequency shift, pitch shift), invert selection/shapes toggle, and shape management (clear shapes, clear last shape). Menu items are enabled or disabled based on the current state (e.g. whether a selection or shapes exist, and whether undo/redo history is available). Gain items prompt for inside and outside gain values in dB. Frequency shift items prompt for the Hz offset and shift mode (move-mix, move-replace, copy-mix, or copy-replace). Pitch shift items prompt for semitones and shift mode.
			</description>
		</attribute>

		<!--Display Category-->
		<attribute name='mag_min' get='1' set='1' type='float' size='1'>
			<digest>
				FFT minimum magnitude (dB).
			</digest>
			<description>
				FFT minimum magnitude sets the magnitude that maps to the coldest color in the active <b>color_scheme</b>. All magnitudes below this threshold will visually clamp to the coldest color. Range -120 - 120. (default -100 dB)
			</description>
		</attribute>
		<attribute name='mag_max' get='1' set='1' type='float' size='1'>
			<digest>
				FFT maximum magnitude (dB).
			</digest>
			<description>
				FFT maximum magnitude sets the magnitude that maps to the hottest color in the active <b>color_scheme</b>. All magnitudes above this threshold will visually clamp to the hottest color. Range -120 - 120. (default 0 dB)
			</description>
		</attribute>
		<attribute name='freq_text' get='1' set='1' type='int' size='1'>
			<digest>
				Frequency text on/off.
			</digest>
			<description>
				Frequency text on/off. (default 1)
			</description>
		</attribute>
		<attribute name='time_text' get='1' set='1' type='int' size='1'>
			<digest>
				Time text on/off.
			</digest>
			<description>
				Time text on/off. (default 1)
			</description>
		</attribute>
		<attribute name='textsize' get='1' set='1' type='int' size='1'>
			<digest>
				Text size.
			</digest>
			<description>
				Text size. Range 1 - 72. (default 12)
			</description>
		</attribute>
		<attribute name='time_format' get='1' set='1' type='int' size='1'>
			<digest>
				Time format for time axis labels.
			</digest>
			<description>
				Time format for time axis labels:
				<ul>
					<li>
						<b>H:MM:SS (0)</b>
					</li>
					<li>
						<b>Milliseconds (1)</b>
					</li>
					<li>
						<b>Seconds (2)</b> (default)
					</li>
					<li>
						<b>Min:Sec (3)</b>
					</li>
					<li>
						<b>Min:Sec.ms (4)</b>
					</li>
					<li>
						<b>Samples (5)</b>
					</li>
				</ul>
			</description>
		</attribute>
		<attribute name='selection_linewidth' get='1' set='1' type='float' size='1'>
			<digest>
				Selection box line width.
			</digest>
			<description>
				Selection box line width. Range 1 - 20. (default 2)
			</description>
		</attribute>
		<attribute name='draw_linewidth' get='1' set='1' type='float' size='1'>
			<digest>
				Draw mode line width.
			</digest>
			<description>
				Line width for drawn shapes in Draw mode (<b>@mouse_mode 2</b>). Range 1 - 20. (default 2)
			</description>
		</attribute>
		<attribute name='edit_win_size' get='1' set='1' type='int' size='1'>
			<digest>
				Edit STFT window size (samples).
			</digest>
			<description>
				STFT window size used for analysis-synthesis when applying spectral edits via <b>edit_selection_isolate</b>, <b>edit_selection_delete</b>, <b>edit_selection_gain</b>, <b>edit_selection_freq_shift</b>, <b>edit_selection_pitch_shift</b>, <b>edit_shapes_isolate</b>, <b>edit_shapes_delete</b>, <b>edit_shapes_gain</b>, <b>edit_shapes_freq_shift</b>, and <b>edit_shapes_pitch_shift</b>. Uses sqrt(Hann) window with fixed 2x overlap for perfect reconstruction. Range 32 - 8192. Must be a power of 2. (default 2048 samples)
			</description>
		</attribute>
		<attribute name='edit_inverted' get='1' set='1' type='int' size='1'>
			<digest>
				Invert edit region (apply edits outside selection/shapes instead of inside).
			</digest>
			<description>
				When enabled, spectral edit operations (<b>edit_selection_isolate</b>, <b>edit_selection_delete</b>, <b>edit_selection_gain</b>, <b>edit_selection_freq_shift</b>, <b>edit_selection_pitch_shift</b>, <b>edit_shapes_isolate</b>, <b>edit_shapes_delete</b>, <b>edit_shapes_gain</b>, <b>edit_shapes_freq_shift</b>, <b>edit_shapes_pitch_shift</b>) act on bins <i>outside</i> the selection rectangle or drawn shapes instead of inside. For example, with <b>@edit_inverted 1</b>, <b>edit_selection_isolate</b> keeps audio outside the selection and zeroes audio inside it.
				<br />
				<br />
				When inverted, the selection rectangle and drawn shapes display a cross-hatch pattern overlay to provide visual feedback that the inverted region (outside) is the active edit target.
				<ul>
					<li>
						<b>0</b> - Off (default). Edits apply inside the selection/shapes.
					</li>
					<li>
						<b>1</b> - On. Edits apply outside the selection/shapes.
					</li>
				</ul>
				Can also be toggled via the right-click context menu (<b>Invert Selection/Shapes</b>).
			</description>
		</attribute>

		<!--Color Category-->
		<attribute name='color_scheme' get='1' set='1' type='int' size='1'>
			<digest>
				Color scheme
			</digest>
			<description>
				Color scheme. Schemes 0-5 are perceptually uniform and colorblind-friendly. Schemes 6-7 are not perceptually uniform but offer high visual contrast. Inverted variants (10-19) reverse the mapping direction.
				<ul>
					<li>
						<b>Roseus (0)</b> - Perceptually uniform, colorblind-friendly. Dark-to-pink. (default)
					</li>
					<li>
						<b>Viridis (1)</b> - Perceptually uniform, colorblind-friendly. Dark purple to yellow-green. The matplotlib default.
					</li>
					<li>
						<b>Magma (2)</b> - Perceptually uniform, colorblind-friendly. Black to light yellow through purple-pink.
					</li>
					<li>
						<b>Inferno (3)</b> - Perceptually uniform, colorblind-friendly. Black to light yellow through red-orange.
					</li>
					<li>
						<b>Plasma (4)</b> - Perceptually uniform, colorblind-friendly. Dark blue to yellow through purple-pink.
					</li>
					<li>
						<b>Cividis (5)</b> - Perceptually uniform, optimized for color vision deficiency. Dark blue to yellow.
					</li>
					<li>
						<b>Turbo (6)</b> - High-contrast rainbow. Not perceptually uniform. Improved replacement for Jet with smoother gradation and better colorblind performance.
					</li>
					<li>
						<b>Hot (7)</b> - Thermal/heat colormap. Not perceptually uniform. Black to white through red and yellow. Classic spectrogram color scheme.
					</li>
					<li>
						<b>Greyscale (8)</b> - Simple luminance ramp. Black to white.
					</li>
					<li>
						<b>Custom (9)</b> - User-defined 5-point color gradient. Set via <b>@custom_color1</b> through <b>@custom_color5</b>.
					</li>
					<li>
						<b>Roseus - Inverted (10)</b>
					</li>
					<li>
						<b>Viridis - Inverted (11)</b>
					</li>
					<li>
						<b>Magma - Inverted (12)</b>
					</li>
					<li>
						<b>Inferno - Inverted (13)</b>
					</li>
					<li>
						<b>Plasma - Inverted (14)</b>
					</li>
					<li>
						<b>Cividis - Inverted (15)</b>
					</li>
					<li>
						<b>Turbo - Inverted (16)</b>
					</li>
					<li>
						<b>Hot - Inverted (17)</b>
					</li>
					<li>
						<b>Greyscale - Inverted (18)</b>
					</li>
					<li>
						<b>Custom - Inverted (19)</b>
					</li>
				</ul>
			</description>
		</attribute>
		<attribute name='custom_color1' get='1' set='1' type='rgba' size='1'>
			<digest>
				Custom color scheme: color 1 (lowest magnitude).
			</digest>
			<description>
				Custom color scheme: color 1 (lowest magnitude). (default 0. 0. 0. 1.)
			</description>
		</attribute>
		<attribute name='custom_color2' get='1' set='1' type='rgba' size='1'>
			<digest>
				Custom color scheme: color 2.
			</digest>
			<description>
				Custom color scheme: color 2. (default 0.361 0.039 0.706, 1.)
			</description>
		</attribute>
		<attribute name='custom_color3' get='1' set='1' type='rgba' size='1'>
			<digest>
				Custom color scheme: color 3.
			</digest>
			<description>
				Custom color scheme: color 3. (default 0.851 0.118 0.298 1.)
			</description>
		</attribute>
		<attribute name='custom_color4' get='1' set='1' type='rgba' size='1'>
			<digest>
				Custom color scheme: color 4.
			</digest>
			<description>
				Custom color scheme: color 4. (default 1. 0.486 0. 1.)
			</description>
		</attribute>
		<attribute name='custom_color5' get='1' set='1' type='rgba' size='1'>
			<digest>
				Custom color scheme: color 5 (highest magnitude).
			</digest>
			<description>
				Custom color scheme: color 5 (highest magnitude). (default 1. 1. 1. 1.)
			</description>
		</attribute>
		<attribute name='selection_linecolor' get='1' set='1' type='rgba' size='1'>
			<digest>
				Selection box line color.
			</digest>
			<description>
				Selection box line color. (default 0. 1. 0. 1.)
			</description>
		</attribute>
		<attribute name='draw_linecolor' get='1' set='1' type='rgba' size='1'>
			<digest>
				Draw mode line color.
			</digest>
			<description>
				Line color for drawn shapes in Draw mode (<b>@mouse_mode 2</b>). (default 1. 1. 1. 1.)
			</description>
		</attribute>
		<attribute name='textcolor' get='1' set='1' type='rgba' size='1'>
			<digest>
				Text color.
			</digest>
			<description>
				Text color. (default 1. 1. 1. 1.)
			</description>
		</attribute>
	</attributelist>


	<!--RELATED-->
	<seealsolist>
		<seealso name="spectrogram.colorbar"/>
		<seealso name="buffer~"/>
		<seealso name="waveform~"/>
	</seealsolist>


	<misc name="Input">
		<entry name="float">
			<description>
				1st inlet: The display start time in milliseconds.
				<br />
				<br />
				2nd inlet: The display length in milliseconds.
				<br />
				<br />
				3rd inlet: The display low frequency in hertz.
				<br />
				<br />
				4th inlet: The display high frequency in hertz.
				<br />
				<br />
				5th inlet: The start time of the selection range in milliseconds.
				<br />
				<br />
				6th inlet: The end time of the selection range in milliseconds.
				<br />
				<br />
				7th inlet: The low frequency of the selection range in hertz.
				<br />
				<br />
				8th inlet: The high frequency of the selection range in hertz.
			</description>
		</entry>
		<entry name="list">
			<description>
				9th inlet: Link In. See description for 9th outlet.
			</description>
		</entry>
	</misc>

	<misc name="Output">
		<entry name="float">
			<description>
				1st outlet: The display start time in milliseconds.
				<br />
				<br />
				2nd outlet: The display length in milliseconds.
				<br />
				<br />
				3rd outlet: The display low frequency in hertz.
				<br />
				<br />
				4th outlet: The display high frequency in hertz.
				<br />
				<br />
				5th outlet: The start time of the selection range in milliseconds.
				<br />
				<br />
				6th outlet: The end time of the selection range in milliseconds.
				<br />
				<br />
				7th outlet: The low frequency of the selection range in hertz.
				<br />
				<br />
				8th outlet: The high frequency of the selection range in hertz.
			</description>
		</entry>
		<entry name="list">
			<description>
				9th outlet: This outlet sends a list that contains values from the 1st through 8th outlets, and is used for linking multiple <o>spectrogram~</o> objects together (i.e. each object displaying a different channel). Any update to an inlet or mouse interaction inside the body of the <o>spectrogram~</o> object results in a list being sent out of this outlet.
			</description>
		</entry>
		<entry name="list">
			<description>
				10th outlet: This outlet sends a list that contains the data at the mousedown/mousedrag point in the spectrogram. The list is formatted as: Time (ms) Frequency (Hz) Magnitude (dB).
			</description>
		</entry>
	</misc>
</c74object>
