vizcore 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +70 -117
- data/docs/.nojekyll +0 -0
- data/docs/assets/playground-worker.js +373 -0
- data/docs/assets/playground.css +440 -0
- data/docs/assets/playground.js +652 -0
- data/docs/assets/site.css +744 -0
- data/docs/assets/vizcore-demo.gif +0 -0
- data/docs/assets/vizcore-poster.png +0 -0
- data/docs/assets/vj-tunnel.js +159 -0
- data/docs/index.html +225 -0
- data/docs/playground.html +81 -0
- data/docs/shape_dsl.md +269 -0
- data/examples/README.md +59 -0
- data/examples/assets/README.md +19 -0
- data/examples/audio_inspector.rb +34 -0
- data/examples/club_intro_drop.rb +78 -0
- data/examples/kansai_rubykaigi_visual.rb +70 -0
- data/examples/live_coding_minimal.rb +22 -0
- data/examples/midi_controller_show.rb +78 -0
- data/examples/midi_scene_switch.rb +3 -1
- data/examples/parser_visualizer.rb +48 -0
- data/examples/readme_demo.rb +17 -0
- data/examples/rhythm_geometry.rb +34 -0
- data/examples/ruby_crystal_show.rb +35 -0
- data/examples/shader_playground.rb +18 -0
- data/examples/unyo_liquid.rb +59 -0
- data/examples/vj_ambient_chill_room.rb +124 -0
- data/examples/vj_dnb_jungle.rb +170 -0
- data/examples/vj_festival_mainstage.rb +245 -0
- data/examples/vj_festival_mainstage.yml +17 -0
- data/examples/vj_glitch_industrial.rb +164 -0
- data/examples/vj_hiphop_cipher.rb +167 -0
- data/examples/vj_jpop_idol_live.rb +210 -0
- data/examples/vj_synthwave_retro.rb +173 -0
- data/examples/vj_techno_warehouse.rb +195 -0
- data/frontend/index.html +494 -2
- data/frontend/src/audio-inspector.js +40 -0
- data/frontend/src/custom-shape-param-controls.js +106 -0
- data/frontend/src/live-controls.js +131 -0
- data/frontend/src/main.js +1060 -16
- data/frontend/src/mapping-target-selector.js +109 -0
- data/frontend/src/midi-learn.js +194 -0
- data/frontend/src/performance-monitor.js +183 -0
- data/frontend/src/plugin-runtime.js +130 -0
- data/frontend/src/projector-mode.js +56 -0
- data/frontend/src/renderer/engine.js +157 -3
- data/frontend/src/renderer/layer-manager.js +442 -30
- data/frontend/src/renderer/shader-manager.js +26 -0
- data/frontend/src/runtime-control-preset.js +11 -0
- data/frontend/src/shader-error-overlay.js +29 -0
- data/frontend/src/shader-param-controls.js +93 -0
- data/frontend/src/shaders/builtins.js +380 -2
- data/frontend/src/shaders/post-effects.js +52 -0
- data/frontend/src/shape-editor-controls.js +157 -0
- data/frontend/src/visual-regression.js +67 -0
- data/frontend/src/visual-settings-preset.js +103 -0
- data/frontend/src/visuals/geometry.js +666 -0
- data/frontend/src/visuals/image-renderer.js +291 -0
- data/frontend/src/visuals/particle-system.js +56 -10
- data/frontend/src/visuals/shape-renderer.js +475 -0
- data/frontend/src/visuals/spectrogram-renderer.js +226 -0
- data/frontend/src/visuals/svg-arc.js +104 -0
- data/frontend/src/visuals/text-renderer.js +112 -11
- data/frontend/src/websocket-client.js +12 -1
- data/lib/vizcore/analysis/adaptive_normalizer.rb +70 -0
- data/lib/vizcore/analysis/beat_detector.rb +4 -2
- data/lib/vizcore/analysis/bpm_estimator.rb +8 -0
- data/lib/vizcore/analysis/feature_recorder.rb +159 -0
- data/lib/vizcore/analysis/feature_replay.rb +84 -0
- data/lib/vizcore/analysis/pipeline.rb +235 -11
- data/lib/vizcore/analysis/tap_tempo.rb +74 -0
- data/lib/vizcore/analysis.rb +4 -0
- data/lib/vizcore/audio/dummy_sine_input.rb +1 -1
- data/lib/vizcore/audio/fixture_input.rb +65 -0
- data/lib/vizcore/audio/input_manager.rb +4 -2
- data/lib/vizcore/audio/mic_input.rb +24 -8
- data/lib/vizcore/audio/portaudio_ffi.rb +106 -1
- data/lib/vizcore/audio.rb +1 -0
- data/lib/vizcore/cli/doctor.rb +159 -0
- data/lib/vizcore/cli/dsl_reference.rb +99 -0
- data/lib/vizcore/cli/layer_docs.rb +46 -0
- data/lib/vizcore/cli/scene_diagnostics.rb +23 -0
- data/lib/vizcore/cli/scene_inspector.rb +136 -0
- data/lib/vizcore/cli/scene_validator.rb +337 -0
- data/lib/vizcore/cli/shader_template.rb +68 -0
- data/lib/vizcore/cli/shader_uniform_docs.rb +54 -0
- data/lib/vizcore/cli.rb +689 -18
- data/lib/vizcore/config.rb +103 -2
- data/lib/vizcore/control_preset.rb +68 -0
- data/lib/vizcore/dsl/engine.rb +277 -5
- data/lib/vizcore/dsl/layer_builder.rb +1280 -23
- data/lib/vizcore/dsl/layer_group_builder.rb +112 -0
- data/lib/vizcore/dsl/mapping_resolver.rb +290 -7
- data/lib/vizcore/dsl/mapping_transform_builder.rb +71 -0
- data/lib/vizcore/dsl/reaction_builder.rb +44 -0
- data/lib/vizcore/dsl/scene_builder.rb +61 -5
- data/lib/vizcore/dsl/shader_source_resolver.rb +67 -6
- data/lib/vizcore/dsl/style_builder.rb +68 -0
- data/lib/vizcore/dsl/timeline_builder.rb +138 -0
- data/lib/vizcore/dsl/transition_controller.rb +77 -0
- data/lib/vizcore/dsl.rb +5 -1
- data/lib/vizcore/layer_catalog.rb +275 -0
- data/lib/vizcore/project_manifest.rb +152 -0
- data/lib/vizcore/renderer/png_writer.rb +57 -0
- data/lib/vizcore/renderer/render_sequence.rb +153 -0
- data/lib/vizcore/renderer/scene_frame_source.rb +132 -0
- data/lib/vizcore/renderer/scene_serializer.rb +36 -3
- data/lib/vizcore/renderer/snapshot.rb +38 -0
- data/lib/vizcore/renderer/snapshot_renderer.rb +938 -0
- data/lib/vizcore/renderer.rb +5 -0
- data/lib/vizcore/server/frame_broadcaster.rb +143 -8
- data/lib/vizcore/server/gallery_app.rb +155 -0
- data/lib/vizcore/server/gallery_page.rb +100 -0
- data/lib/vizcore/server/gallery_runner.rb +48 -0
- data/lib/vizcore/server/rack_app.rb +203 -4
- data/lib/vizcore/server/runner.rb +391 -22
- data/lib/vizcore/server/scene_dependency_watcher.rb +79 -0
- data/lib/vizcore/server/websocket_handler.rb +60 -10
- data/lib/vizcore/server.rb +4 -0
- data/lib/vizcore/shape.rb +719 -0
- data/lib/vizcore/sync/osc_message.rb +103 -0
- data/lib/vizcore/sync/osc_receiver.rb +68 -0
- data/lib/vizcore/sync.rb +4 -0
- data/lib/vizcore/templates/midi_control_scene.rb +3 -1
- data/lib/vizcore/templates/plugin_layer.rb +20 -0
- data/lib/vizcore/templates/plugin_readme.md +23 -0
- data/lib/vizcore/templates/plugin_renderer.js +43 -0
- data/lib/vizcore/templates/plugin_scene.rb +14 -0
- data/lib/vizcore/templates/project_readme.md +7 -23
- data/lib/vizcore/templates/rubykaigi_scene.rb +30 -0
- data/lib/vizcore/version.rb +1 -1
- data/lib/vizcore.rb +28 -0
- data/scripts/browser_capture.mjs +75 -0
- data/sig/vizcore.rbs +461 -0
- metadata +94 -3
- data/docs/GETTING_STARTED.md +0 -105
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "base64"
|
|
3
4
|
require "pathname"
|
|
4
5
|
|
|
5
6
|
module Vizcore
|
|
6
7
|
module DSL
|
|
7
|
-
# Replaces layer
|
|
8
|
+
# Replaces external layer source paths with browser-ready inline payloads.
|
|
8
9
|
class ShaderSourceResolver
|
|
10
|
+
MEDIA_MIME_TYPES = {
|
|
11
|
+
".gif" => "image/gif",
|
|
12
|
+
".jpg" => "image/jpeg",
|
|
13
|
+
".jpeg" => "image/jpeg",
|
|
14
|
+
".png" => "image/png",
|
|
15
|
+
".svg" => "image/svg+xml",
|
|
16
|
+
".webp" => "image/webp",
|
|
17
|
+
".mp4" => "video/mp4",
|
|
18
|
+
".ogv" => "video/ogg",
|
|
19
|
+
".webm" => "video/webm"
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
9
22
|
# @param definition [Hash] DSL definition payload
|
|
10
23
|
# @param scene_file [String, Pathname] source scene file
|
|
11
|
-
# @raise [ArgumentError] when a referenced
|
|
12
|
-
# @return [Hash] deep-copied definition with
|
|
24
|
+
# @raise [ArgumentError] when a referenced source file is missing
|
|
25
|
+
# @return [Hash] deep-copied definition with resolved layer source entries
|
|
13
26
|
def resolve(definition:, scene_file:)
|
|
14
27
|
scene_path = Pathname.new(scene_file.to_s).expand_path
|
|
15
28
|
base_dir = scene_path.dirname
|
|
@@ -29,8 +42,13 @@ module Vizcore
|
|
|
29
42
|
def resolve_layer(layer, base_dir:)
|
|
30
43
|
layer_hash = symbolize_hash(layer)
|
|
31
44
|
shader_path = layer_hash[:glsl]
|
|
32
|
-
|
|
45
|
+
layer_hash = resolve_shader_layer(layer_hash, base_dir: base_dir) if shader_path
|
|
46
|
+
layer_hash = resolve_media_layer(layer_hash, base_dir: base_dir) if media_layer?(layer_hash)
|
|
47
|
+
layer_hash
|
|
48
|
+
end
|
|
33
49
|
|
|
50
|
+
def resolve_shader_layer(layer_hash, base_dir:)
|
|
51
|
+
shader_path = layer_hash.fetch(:glsl)
|
|
34
52
|
full_path = resolve_path(base_dir: base_dir, shader_path: shader_path)
|
|
35
53
|
raise ArgumentError, "GLSL file not found: #{shader_path}" unless full_path.file?
|
|
36
54
|
|
|
@@ -39,8 +57,51 @@ module Vizcore
|
|
|
39
57
|
layer_hash
|
|
40
58
|
end
|
|
41
59
|
|
|
42
|
-
def
|
|
43
|
-
|
|
60
|
+
def resolve_media_layer(layer_hash, base_dir:)
|
|
61
|
+
params = symbolize_hash(layer_hash[:params] || {})
|
|
62
|
+
media_path = params[:file]
|
|
63
|
+
return layer_hash unless media_path
|
|
64
|
+
|
|
65
|
+
full_path = resolve_path(base_dir: base_dir, relative_path: media_path)
|
|
66
|
+
raise ArgumentError, "#{media_layer_label(layer_hash)} file not found: #{media_path}" unless full_path.file?
|
|
67
|
+
|
|
68
|
+
mime_type = MEDIA_MIME_TYPES[full_path.extname.downcase]
|
|
69
|
+
raise ArgumentError, "Unsupported #{media_layer_label(layer_hash)} file extension: #{media_path}" unless mime_type
|
|
70
|
+
raise ArgumentError, "Unsupported SVG file extension: #{media_path}" if svg_layer?(layer_hash) && mime_type != "image/svg+xml"
|
|
71
|
+
raise ArgumentError, "Unsupported Image file extension: #{media_path}" if image_layer?(layer_hash) && !mime_type.start_with?("image/")
|
|
72
|
+
raise ArgumentError, "Unsupported Video file extension: #{media_path}" if video_layer?(layer_hash) && !mime_type.start_with?("video/")
|
|
73
|
+
|
|
74
|
+
params[:file] = media_path.to_s
|
|
75
|
+
params[:src] = "data:#{mime_type};base64,#{Base64.strict_encode64(full_path.binread)}"
|
|
76
|
+
layer_hash[:params] = params
|
|
77
|
+
layer_hash
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def svg_layer?(layer_hash)
|
|
81
|
+
%i[svg svg_layer].include?(layer_hash[:type]&.to_sym)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def image_layer?(layer_hash)
|
|
85
|
+
%i[image image_layer photo].include?(layer_hash[:type]&.to_sym)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def video_layer?(layer_hash)
|
|
89
|
+
%i[video video_layer footage].include?(layer_hash[:type]&.to_sym)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def media_layer?(layer_hash)
|
|
93
|
+
svg_layer?(layer_hash) || image_layer?(layer_hash) || video_layer?(layer_hash)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def media_layer_label(layer_hash)
|
|
97
|
+
return "SVG" if svg_layer?(layer_hash)
|
|
98
|
+
return "Video" if video_layer?(layer_hash)
|
|
99
|
+
|
|
100
|
+
"Image"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def resolve_path(base_dir:, relative_path: nil, shader_path: nil)
|
|
104
|
+
path = Pathname.new((relative_path || shader_path).to_s)
|
|
44
105
|
return path.expand_path if path.absolute?
|
|
45
106
|
|
|
46
107
|
base_dir.join(path).expand_path
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vizcore
|
|
4
|
+
module DSL
|
|
5
|
+
# Collects reusable layer parameter presets for the `style` DSL.
|
|
6
|
+
class StyleBuilder
|
|
7
|
+
# @param name [Symbol, String] style identifier
|
|
8
|
+
# @param kind [String] user-facing DSL kind for error messages
|
|
9
|
+
def initialize(name:, kind: "style")
|
|
10
|
+
@name = name.to_sym
|
|
11
|
+
@kind = kind
|
|
12
|
+
@params = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Evaluate a style block.
|
|
16
|
+
#
|
|
17
|
+
# @yield Style parameter declarations
|
|
18
|
+
# @return [Vizcore::DSL::StyleBuilder]
|
|
19
|
+
def evaluate(&block)
|
|
20
|
+
instance_eval(&block) if block
|
|
21
|
+
raise ArgumentError, "#{@kind} #{@name} requires at least one parameter" if @params.empty?
|
|
22
|
+
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Hash] serialized style payload
|
|
27
|
+
def to_h
|
|
28
|
+
{
|
|
29
|
+
name: @name,
|
|
30
|
+
params: @params.dup
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Store an ordered color palette for styles and themes.
|
|
35
|
+
#
|
|
36
|
+
# @param colors [Array<String, Array<String>>] color values such as "#00ffff"
|
|
37
|
+
# @raise [ArgumentError] when no non-blank colors are supplied
|
|
38
|
+
# @return [Array<String>]
|
|
39
|
+
def palette(*colors)
|
|
40
|
+
@params[:palette] = normalize_palette(colors)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Stores one-argument style setters into `params`.
|
|
44
|
+
# @api private
|
|
45
|
+
def method_missing(method_name, *args, &block)
|
|
46
|
+
if block.nil? && args.length == 1
|
|
47
|
+
@params[method_name.to_sym] = args.first
|
|
48
|
+
return args.first
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def respond_to_missing?(_method_name, _include_private = false)
|
|
55
|
+
true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def normalize_palette(colors)
|
|
61
|
+
values = colors.flatten.map { |color| color.to_s.strip }.reject(&:empty?)
|
|
62
|
+
raise ArgumentError, "#{@kind} #{@name} palette requires at least one color" if values.empty?
|
|
63
|
+
|
|
64
|
+
values
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vizcore
|
|
4
|
+
module DSL
|
|
5
|
+
# Collects ordered timeline scene markers and converts them to transitions.
|
|
6
|
+
class TimelineBuilder
|
|
7
|
+
DEFAULT_BEATS_PER_BAR = 4
|
|
8
|
+
|
|
9
|
+
Point = Struct.new(:value, :unit, keyword_init: true)
|
|
10
|
+
|
|
11
|
+
def initialize(beats_per_bar: DEFAULT_BEATS_PER_BAR)
|
|
12
|
+
@beats_per_bar = positive_integer(beats_per_bar, "beats_per_bar")
|
|
13
|
+
@entries = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Evaluate a timeline block.
|
|
17
|
+
#
|
|
18
|
+
# @yield Timeline marker definitions
|
|
19
|
+
# @return [Vizcore::DSL::TimelineBuilder]
|
|
20
|
+
def evaluate(&block)
|
|
21
|
+
instance_eval(&block) if block
|
|
22
|
+
validate_entries!
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Add a scene marker at a timeline position.
|
|
27
|
+
#
|
|
28
|
+
# @param position [Numeric, Point] seconds by default, or a value from `seconds`, `beats`, or `bars`
|
|
29
|
+
# @param scene [Symbol, String] scene to activate at the position
|
|
30
|
+
# @return [Hash]
|
|
31
|
+
def at(position, scene:)
|
|
32
|
+
point = normalize_position(position)
|
|
33
|
+
entry = {
|
|
34
|
+
at: point.value,
|
|
35
|
+
unit: point.unit,
|
|
36
|
+
scene: scene.to_sym
|
|
37
|
+
}
|
|
38
|
+
@entries << entry
|
|
39
|
+
entry
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @param value [Numeric] seconds from the timeline start
|
|
43
|
+
# @return [Point]
|
|
44
|
+
def seconds(value)
|
|
45
|
+
Point.new(value: non_negative_float(value, "timeline seconds"), unit: :seconds)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @param value [Numeric] beats from the timeline start
|
|
49
|
+
# @return [Point]
|
|
50
|
+
def beats(value)
|
|
51
|
+
Point.new(value: non_negative_float(value, "timeline beats"), unit: :beats)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param value [Numeric] bars from the timeline start
|
|
55
|
+
# @param beats_per_bar [Integer, nil] meter override
|
|
56
|
+
# @return [Point]
|
|
57
|
+
def bars(value, beats_per_bar: nil)
|
|
58
|
+
beats_per_measure = beats_per_bar.nil? ? @beats_per_bar : positive_integer(beats_per_bar, "beats_per_bar")
|
|
59
|
+
beats(non_negative_float(value, "timeline bars") * beats_per_measure)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Array<Hash>] serialized marker definitions
|
|
63
|
+
def to_h
|
|
64
|
+
@entries.map(&:dup)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [Array<Hash>] generated scene transitions
|
|
68
|
+
def transitions
|
|
69
|
+
@entries.each_cons(2).map do |from_entry, to_entry|
|
|
70
|
+
delta = to_entry.fetch(:at) - from_entry.fetch(:at)
|
|
71
|
+
{
|
|
72
|
+
from: from_entry.fetch(:scene),
|
|
73
|
+
to: to_entry.fetch(:scene),
|
|
74
|
+
trigger: trigger_for(delta, from_entry.fetch(:unit))
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def normalize_position(position)
|
|
82
|
+
return position if position.is_a?(Point)
|
|
83
|
+
|
|
84
|
+
seconds(position)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def trigger_for(delta, unit)
|
|
88
|
+
case unit
|
|
89
|
+
when :seconds
|
|
90
|
+
proc { seconds >= delta }
|
|
91
|
+
when :beats
|
|
92
|
+
proc { beat_count >= delta }
|
|
93
|
+
else
|
|
94
|
+
proc { false }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def validate_entries!
|
|
99
|
+
return if @entries.length < 2
|
|
100
|
+
|
|
101
|
+
unit = @entries.first.fetch(:unit)
|
|
102
|
+
@entries.each_cons(2) do |from_entry, to_entry|
|
|
103
|
+
raise ArgumentError, "timeline entries must use the same unit" unless to_entry.fetch(:unit) == unit
|
|
104
|
+
|
|
105
|
+
from_position = from_entry.fetch(:at)
|
|
106
|
+
to_position = to_entry.fetch(:at)
|
|
107
|
+
raise ArgumentError, "timeline positions must increase" unless to_position > from_position
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def non_negative_float(value, name)
|
|
112
|
+
numeric = parse_float(value, name)
|
|
113
|
+
raise ArgumentError, "#{name} must be non-negative" if numeric.negative?
|
|
114
|
+
|
|
115
|
+
numeric
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def positive_integer(value, name)
|
|
119
|
+
numeric = parse_integer(value, name)
|
|
120
|
+
raise ArgumentError, "#{name} must be positive" unless numeric.positive?
|
|
121
|
+
|
|
122
|
+
numeric
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def parse_float(value, name)
|
|
126
|
+
Float(value)
|
|
127
|
+
rescue ArgumentError, TypeError
|
|
128
|
+
raise ArgumentError, "#{name} must be numeric"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def parse_integer(value, name)
|
|
132
|
+
Integer(value)
|
|
133
|
+
rescue ArgumentError, TypeError
|
|
134
|
+
raise ArgumentError, "#{name} must be an integer"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -4,6 +4,8 @@ module Vizcore
|
|
|
4
4
|
module DSL
|
|
5
5
|
# Evaluates transition rules and returns scene-change payloads.
|
|
6
6
|
class TransitionController
|
|
7
|
+
DEFAULT_FRAME_RATE = 60.0
|
|
8
|
+
|
|
7
9
|
# @param scenes [Array<Hash>]
|
|
8
10
|
# @param transitions [Array<Hash>]
|
|
9
11
|
def initialize(scenes:, transitions:)
|
|
@@ -108,6 +110,8 @@ module Vizcore
|
|
|
108
110
|
def initialize(audio, frame_count:)
|
|
109
111
|
@audio = symbolize_hash(audio)
|
|
110
112
|
@bands = symbolize_hash(@audio[:bands])
|
|
113
|
+
@onsets = symbolize_hash(@audio[:onsets])
|
|
114
|
+
@drums = symbolize_hash(@audio[:drums])
|
|
111
115
|
@frame_count = Integer(frame_count)
|
|
112
116
|
rescue StandardError
|
|
113
117
|
@frame_count = 0
|
|
@@ -124,16 +128,84 @@ module Vizcore
|
|
|
124
128
|
@bands[name.to_sym].to_f
|
|
125
129
|
end
|
|
126
130
|
|
|
131
|
+
# @return [Float]
|
|
132
|
+
def sub
|
|
133
|
+
frequency_band(:sub)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# @return [Float]
|
|
137
|
+
def low
|
|
138
|
+
frequency_band(:low)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @return [Float]
|
|
142
|
+
def bass
|
|
143
|
+
frequency_band(:low)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @return [Float]
|
|
147
|
+
def mid
|
|
148
|
+
frequency_band(:mid)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# @return [Float]
|
|
152
|
+
def high
|
|
153
|
+
frequency_band(:high)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# @return [Float]
|
|
157
|
+
def treble
|
|
158
|
+
frequency_band(:high)
|
|
159
|
+
end
|
|
160
|
+
|
|
127
161
|
# @return [Array<Float>]
|
|
128
162
|
def fft_spectrum
|
|
129
163
|
Array(@audio[:fft])
|
|
130
164
|
end
|
|
131
165
|
|
|
166
|
+
# @param name [Symbol, String, nil]
|
|
167
|
+
# @return [Float]
|
|
168
|
+
def onset(name = nil)
|
|
169
|
+
return @audio[:onset].to_f if name.nil?
|
|
170
|
+
|
|
171
|
+
@onsets[name.to_sym].to_f
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# @return [Float]
|
|
175
|
+
def kick
|
|
176
|
+
@drums[:kick].to_f
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# @return [Float]
|
|
180
|
+
def snare
|
|
181
|
+
@drums[:snare].to_f
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# @return [Float]
|
|
185
|
+
def hihat
|
|
186
|
+
@drums[:hihat].to_f
|
|
187
|
+
end
|
|
188
|
+
|
|
132
189
|
# @return [Boolean]
|
|
133
190
|
def beat?
|
|
134
191
|
!!@audio[:beat]
|
|
135
192
|
end
|
|
136
193
|
|
|
194
|
+
# @return [Boolean]
|
|
195
|
+
def beat
|
|
196
|
+
beat?
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# @return [Float]
|
|
200
|
+
def beat_confidence
|
|
201
|
+
@audio[:beat_confidence].to_f
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @return [Float]
|
|
205
|
+
def beat_pulse
|
|
206
|
+
@audio[:beat_pulse].to_f
|
|
207
|
+
end
|
|
208
|
+
|
|
137
209
|
# @return [Integer]
|
|
138
210
|
def beat_count
|
|
139
211
|
Integer(@audio[:beat_count] || 0)
|
|
@@ -151,6 +223,11 @@ module Vizcore
|
|
|
151
223
|
@frame_count
|
|
152
224
|
end
|
|
153
225
|
|
|
226
|
+
# @return [Float] scene-local elapsed seconds at the default runtime frame rate
|
|
227
|
+
def seconds
|
|
228
|
+
@frame_count / DEFAULT_FRAME_RATE
|
|
229
|
+
end
|
|
230
|
+
|
|
154
231
|
private
|
|
155
232
|
|
|
156
233
|
def symbolize_hash(value)
|
data/lib/vizcore/dsl.rb
CHANGED
|
@@ -6,11 +6,15 @@ module Vizcore
|
|
|
6
6
|
end
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
require_relative "dsl/layer_builder"
|
|
10
9
|
require_relative "dsl/file_watcher"
|
|
11
10
|
require_relative "dsl/mapping_resolver"
|
|
11
|
+
require_relative "dsl/mapping_transform_builder"
|
|
12
12
|
require_relative "dsl/midi_map_executor"
|
|
13
|
+
require_relative "dsl/reaction_builder"
|
|
14
|
+
require_relative "dsl/style_builder"
|
|
15
|
+
require_relative "dsl/layer_builder"
|
|
13
16
|
require_relative "dsl/scene_builder"
|
|
14
17
|
require_relative "dsl/shader_source_resolver"
|
|
18
|
+
require_relative "dsl/timeline_builder"
|
|
15
19
|
require_relative "dsl/transition_controller"
|
|
16
20
|
require_relative "dsl/engine"
|