Module: Muze::Display
- Defined in:
- lib/muze/display/specshow.rb
Constant Summary collapse
- BASE64_ALPHABET =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
Class Method Summary collapse
-
.onsetshow(onset_envelope, sr: 22_050, hop_length: 512, output: nil, width: 800, height: 160, normalize: true) ⇒ String
SVG content.
-
.specshow(data, sr: 22_050, hop_length: 512, x_axis: :time, y_axis: :linear, output: nil, width: 800, height: 400, cmap: :heat, vmin: nil, vmax: nil, fragment: false, render: :auto) ⇒ String
SVG content.
-
.waveshow(y, sr: 22_050, output: nil, width: 800, height: 240, normalize: true, channels: :overlay) ⇒ String
SVG content.
Class Method Details
.onsetshow(onset_envelope, sr: 22_050, hop_length: 512, output: nil, width: 800, height: 160, normalize: true) ⇒ String
Returns SVG content.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/muze/display/specshow.rb', line 92 def onsetshow(onset_envelope, sr: 22_050, hop_length: 512, output: nil, width: 800, height: 160, normalize: true) raise Muze::ParameterError, "width and height must be positive" unless width.positive? && height.positive? raise Muze::ParameterError, "sr and hop_length must be positive" unless sr.positive? && hop_length.positive? raise Muze::ParameterError, "normalize must be true or false" unless [true, false].include?(normalize) envelope = Numo::SFloat.cast(onset_envelope).to_a.flatten raise Muze::ParameterError, "onset envelope must contain only finite values" unless envelope.all? { |value| value.respond_to?(:finite?) && value.finite? } peak = envelope.map(&:abs).max.to_f values = normalize && peak.positive? ? envelope.map { |value| value / peak } : envelope width = width.to_f height = height.to_f = width / [values.length, 1].max = values.each_with_index.map do |value, index| scaled = [[value, 0.0].max, 1.0].min = scaled * height x = index * y = height - "<rect x='#{x.round(3)}' y='#{y.round(3)}' width='#{[, 0.1].max.round(3)}' height='#{.round(3)}' fill='#22d3ee' />" end svg = [ "<svg xmlns='http://www.w3.org/2000/svg' width='#{width.to_i}' height='#{height.to_i}' viewBox='0 0 #{width.to_i} #{height.to_i}'>", "<rect width='100%' height='100%' fill='#111827' />", "<g data-kind='onset' data-sr='#{sr}' data-hop-length='#{hop_length}'>", .join, "</g>", "</svg>" ].join write_output(output, svg) if output svg end |
.specshow(data, sr: 22_050, hop_length: 512, x_axis: :time, y_axis: :linear, output: nil, width: 800, height: 400, cmap: :heat, vmin: nil, vmax: nil, fragment: false, render: :auto) ⇒ String
Returns SVG content.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/muze/display/specshow.rb', line 14 def specshow(data, sr: 22_050, hop_length: 512, x_axis: :time, y_axis: :linear, output: nil, width: 800, height: 400, cmap: :heat, vmin: nil, vmax: nil, fragment: false, render: :auto) validate_axis!(x_axis:, y_axis:) raise Muze::ParameterError, "width and height must be positive" unless width.positive? && height.positive? raise Muze::ParameterError, "sr and hop_length must be positive" unless sr.positive? && hop_length.positive? raise Muze::ParameterError, "render must be :auto, :rects, or :image" unless %i[auto rects image].include?(render) matrix = Numo::SFloat.cast(data) matrix = matrix.(1) if matrix.ndim == 1 validate_matrix!(matrix) validate_color_bounds!(vmin:, vmax:) render = image_render?(matrix, render:) ? :image : :rects matrix = downsample_matrix(matrix, max_cells: 12_000) if render == :rects rows, cols = matrix.shape width = width.to_f height = height.to_f min = vmin || matrix.min max = vmax || matrix.max range = [max - min, 1.0e-12].max content = render == :image ? image_element(matrix, width:, height:, min:, range:, cmap:) : rect_elements(matrix, rows:, cols:, width:, height:, min:, range:, cmap:, y_axis:, x_axis:, sr:, hop_length:) body = [ "<g data-x-axis='#{x_axis}' data-y-axis='#{y_axis}' data-sr='#{sr}' data-hop-length='#{hop_length}' data-render='#{render}'>", content, "</g>" ].join svg = if fragment body else [ "<svg xmlns='http://www.w3.org/2000/svg' width='#{width.to_i}' height='#{height.to_i}' viewBox='0 0 #{width.to_i} #{height.to_i}'>", "<rect width='100%' height='100%' fill='#0b132b' />", body, "</svg>" ].join end write_output(output, svg) if output svg end |
.waveshow(y, sr: 22_050, output: nil, width: 800, height: 240, normalize: true, channels: :overlay) ⇒ String
Returns SVG content.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/muze/display/specshow.rb', line 61 def waveshow(y, sr: 22_050, output: nil, width: 800, height: 240, normalize: true, channels: :overlay) raise Muze::ParameterError, "width and height must be positive" unless width.positive? && height.positive? raise Muze::ParameterError, "sr must be positive" unless sr.positive? raise Muze::ParameterError, "normalize must be true or false" unless [true, false].include?(normalize) raise Muze::ParameterError, "channels must be :overlay or :split" unless %i[overlay split].include?(channels) signal = Muze::Core::Audio.validate_audio!(y, allow_empty: true).to_a channel_data = signal.first.is_a?(Array) ? transpose_channels(signal) : [signal] channel_data = channel_data.map { |channel| normalize ? normalize_wave(channel) : channel } width = width.to_f height = height.to_f middle = height / 2.0 paths = channel_data.each_with_index.map do |channel, index| top, lane_height = channel_lane(index, channel_data.length, height, channels:) envelope_path(channel, width:, top:, height: lane_height) end svg = [ "<svg xmlns='http://www.w3.org/2000/svg' width='#{width.to_i}' height='#{height.to_i}' viewBox='0 0 #{width.to_i} #{height.to_i}'>", "<rect width='100%' height='100%' fill='#111827' />", "<g data-sr='#{sr}' data-channels='#{channels}' transform='translate(0 #{middle * 0.0})'>", paths.join, "</g>", "</svg>" ].join write_output(output, svg) if output svg end |