diff --git a/components/audiobench/src/engine/codegen.rs b/components/audiobench/src/engine/codegen.rs index 955b597..274d800 100644 --- a/components/audiobench/src/engine/codegen.rs +++ b/components/audiobench/src/engine/codegen.rs @@ -133,8 +133,8 @@ impl<'a> CodeGenerator<'a> { " static_index += 1\n", // grumble grumble " global_input = GlobalInput(midi_controls, pitch_wheel, bpm, elapsed_time, ", "elapsed_beats)\n", - " start_trigger = Trigger(note_input.start_trigger, repeat([false], buffer_length - 1)...)\n", - " release_trigger = Trigger(note_input.release_trigger, repeat([false], buffer_length - 1)...)\n", + " start_trigger = Trigger(reshape([note_input.start_trigger, repeat([false], buffer_length - 1)...], (1, buffer_length)))\n", + " release_trigger = Trigger(reshape([note_input.release_trigger, repeat([false], buffer_length - 1)...], (1, buffer_length)))\n", " note_output = NoteOutput()\n", " context = NoteContext(global_input, note_input, note_output)\n", " view = ()\n", @@ -143,7 +143,7 @@ impl<'a> CodeGenerator<'a> { for _ in 0..feedback_widget_selectors.len() { exec_body.push_str("Vector{Float32}(), "); } - exec_body.push_str(")\n\n @. context.note_out.audio = 0.0\n"); + exec_body.push_str(")\n\n context.note_out.audio .= 0f0\n"); let automation_code = AutomationCode { ordered_modules: ordered_modules.clone(), }; @@ -181,7 +181,7 @@ impl<'a> CodeGenerator<'a> { exec_body.push_str(" if do_feedback\n"); } exec_body.push_str(&format!( - " push!(feedback.m{}w{}, m{}c{}[%, 1, 1])\n", + " push!(feedback.m{}w{}, m{}c{}[1, 1])\n", index, widget_index, index, control_index )); } diff --git a/components/audiobench/src/engine/julia_thread.rs b/components/audiobench/src/engine/julia_thread.rs index 1426f9a..0adf4db 100644 --- a/components/audiobench/src/engine/julia_thread.rs +++ b/components/audiobench/src/engine/julia_thread.rs @@ -175,6 +175,7 @@ impl JuliaThread { "Encountered Julia error while executing, see message log for details.\n\n{}", err ); + eprintln!("{}", err); // This error is "recoverable" self.report_julia_error(message); } @@ -212,6 +213,7 @@ impl JuliaThread { "Encountered Julia error while executing, see message log for details.\n\n{}", err ); + eprintln!("{}", err); self.report_julia_error(message); // This error is "recoverable" None diff --git a/components/audiobench/src/engine/program_wrapper.rs b/components/audiobench/src/engine/program_wrapper.rs index 6fbe6c9..80f78f2 100644 --- a/components/audiobench/src/engine/program_wrapper.rs +++ b/components/audiobench/src/engine/program_wrapper.rs @@ -236,8 +236,8 @@ impl AudiobenchExecutor { parameters: &GlobalParameters, ) -> Result { let mut base = ExecutionEngine::new(); - base.add_global_code(julia_helper::include_packed_library!("StaticArrays")) - .unwrap(); + // base.add_global_code(julia_helper::include_packed_library!("StaticArrays")) + // .unwrap(); let mut this = AudiobenchExecutor { base, // This is a quick and dirty way of getting the executor to rebuild when we use diff --git a/components/factory_library/lib.lib.jl b/components/factory_library/lib.lib.jl index 9eb1be8..bef3517 100644 --- a/components/factory_library/lib.lib.jl +++ b/components/factory_library/lib.lib.jl @@ -8,83 +8,187 @@ end if isinteractive() @eval Main Parameters = Main.Registry.Factory.Lib.TestParameters - @eval Main module UnpackedDependencies using StaticArrays end end using Main.Parameters -using Main.UnpackedDependencies.StaticArrays -const num_midi_controls = 128 -const default_graph_resolution = 42 - -const MonoSample = SArray{Tuple{1},Float32,1,1} -const StereoSample = SArray{Tuple{channels},Float32,1,channels} -const StaticMonoAudio = SArray{Tuple{1,1},Float32,2,1} -const StaticStereoAudio = SArray{Tuple{channels,1},Float32,2,channels} -const MonoAudio = SArray{Tuple{1,buffer_length},Float32,2,buffer_length} -const StereoAudio = SArray{Tuple{channels,buffer_length},Float32,2,channels * buffer_length} -const StaticControlSignal = StaticMonoAudio -const ControlSignal = MonoAudio - -const StaticTrigger = SArray{Tuple{1,1},Bool,2,1} -const Trigger = SArray{Tuple{1,buffer_length},Bool,2,buffer_length} -const Waveform = Function +struct FixedArray{T,D,ND,NI} <: AbstractArray{T,ND} + data::Array{T,ND} +end +FixedArray{T,D,ND,NI}(value::T) where {T,D,ND,NI} = + FixedArray{T,D,ND,NI}(repeat([value], D.parameters...)) -const flat_waveform = (phase::Float32, _buffer_pos::Integer) -> 0f0 -const ramp_up_waveform = (phase::Float32, _buffer_pos::Integer) -> phase * 2 - 1 -const ramp_down_waveform = (phase::Float32, _buffer_pos::Integer) -> 1 - phase * 2 -const sine_waveform = (phase::Float32, _buffer_pos::Integer) -> sin(phase * pi * 2f0) +import Base.similar +@generated function similar(array::Type{FixedArray{T,D,ND,NI}})::FixedArray{T,D,ND,NI} where {T,D,ND,NI} + quote + FixedArray{$T,$D,$ND,$NI}(similar(Array{$T,$ND}, $(tuple(D.parameters...)))) + end +end +@generated function similar(array::FixedArray{T,D,ND,NI})::FixedArray{T,D,ND,NI} where {T,D,ND,NI} + quote + similar($array) + end +end -function mutable(type::DataType)::DataType - typeof(similar(type)) +import Base.getindex +@generated function getindex(array::FixedArray{T,D,ND,NI}, index::Vararg{Integer,ND})::T where {T,D,ND,NI} + real_indices = [ + if dim_value == 1 + quote 1 end + else + quote index[$dim_index] end + end + for (dim_index, dim_value) ∈ enumerate(D.parameters) + ] + quote + getindex(array.data, $(real_indices...)) + end +end +function getindex(array::FixedArray{T,D,ND,NI}, index::CartesianIndex{ND})::T where {T,D,ND,NI} + getindex(array, Tuple(index)...) end -function maybe_mutable(type::DataType) - Union{type, mutable(type)} +import Base.setindex! +@generated function setindex!(array::FixedArray{T,D,ND,NI}, value::I, index::Vararg{Integer,ND}) where {T,D,ND,NI,I} + real_indices = [ + if dim_value == 1 + quote 1 end + else + quote index[$dim_index] end + end + for (dim_index, dim_value) ∈ enumerate(D.parameters) + ] + quote + setindex!(array.data, value, $(real_indices...)) + end +end +function setindex!(array::FixedArray{T,D,ND,NI}, value::I, index::CartesianIndex{ND}) where {T,D,ND,NI,I} + setindex!(array, value, Tuple(index)...) end -function maybe_mutable_type(type::DataType) - Union{Type{type}, Type{mutable(type)}} +Base.Broadcast.broadcastable(array::FixedArray{T,D,ND,NI}) where {T,D,ND,NI} = array +@generated Base.Broadcast.ndims(array::Type{FixedArray{T,D,ND,NI}}) where {T,D,ND,NI} = ND +@generated Base.Broadcast.size(array::FixedArray{T,D,ND,NI}) where {T,D,ND,NI} = :($(tuple(D.parameters...))) +@generated Base.Broadcast.size(array::FixedArray{T,D,ND,NI}, dim::Integer) where {T,D,ND,NI} = quote $(D.parameters)[dim] end +# Adapted from StaticArrays: https://github.com/JuliaArrays/StaticArrays.jl/blob/5cb521a5ff2b7a1625ecad03c1ac756dacabb346/src/broadcast.jl +import Base.Broadcast: +BroadcastStyle, AbstractArrayStyle, Broadcasted, DefaultArrayStyle, materialize! +struct FixedArrayStyle{N} <: AbstractArrayStyle{N} end +FixedArrayStyle{M}(::Val{N}) where {M,N} = FixedArrayStyle{N}() +BroadcastStyle(::Type{FixedArray{T, D, ND, NI}}) where {T,D,ND,NI} = FixedArrayStyle{ND}() +BroadcastStyle(::FixedArrayStyle{N}, ::FixedArrayStyle{M}) where {M,N} = + FixedArrayStyle{max(M, N)}() +BroadcastStyle(fix::FixedArrayStyle{M}, ::DefaultArrayStyle{N}) where {M,N} = + if N == 0 fix else DefaultArrayStyle(Val(max(M, N))) end +BroadcastStyle(::DefaultArrayStyle{N}, fix::FixedArrayStyle{M}) where {M,N} = + if N == 0 fix else DefaultArrayStyle(Val(max(M, N))) end +scalar_getindex(x) = x +scalar_getindex(x::Ref) = x[] +# copy overload +@inline function Base.copy(B::Broadcasted{FixedArrayStyle{M}}) where M + flat = Broadcast.flatten(B); as = flat.args; f = flat.f + argsizes = map(Base.Broadcast.size, as) + destsize = Base.Broadcast.broadcast_shape(argsizes...) + _broadcast(f, Val(destsize), Val(argsizes), as...) +end +@inline function Base.copyto!(dest::FixedArray{T,D,M,NI}, B::Broadcasted{FixedArrayStyle{M}}) where {M,T,D,NI} + flat = Broadcast.flatten(B); as = flat.args; f = flat.f + argsizes = map(Base.Broadcast.size, as) + destsize = Base.Broadcast.broadcast_shape(Base.Broadcast.size(dest), argsizes...) + _broadcast!(f, Val(destsize), dest, Val(argsizes), as...) end +@generated function _broadcast(f, _newsize::Val{newsize}, s::Val{sizes}, a...) where {newsize, sizes} + first_staticarray = a[findfirst(ai -> ai <: FixedArray, a)] -# Sometimes Julia's typeof() gives an answer which is too specific. -function typeof2(data::AbstractArray{Float32,1}) - dims = size(data) - if dims[1] === 1 - MonoSample - elseif dims[1] === channels - StereoSample - else - @assert false "Invalid sample type" + if prod(newsize) == 0 + @assert false "We don't handle this yet." + end + + first_expr_values = [ + begin + if !(a[i] <: AbstractArray || a[i] <: Tuple) + :(scalar_getindex(a[$i])) + else + all_ones = [:(1) for _ = 1:length(sizes[i])] + :(a[$i][$(all_ones...)]) + end + end + for i = 1:length(sizes) + ] + first_expr = :(f($(first_expr_values...))) + + return quote + @inbounds result = similar(FixedArray{typeof($first_expr),Tuple{$(newsize...)},$(length(newsize)),$(prod(newsize))}) + _broadcast!(f, _newsize, result, s, a...) + @inbounds return result end end +@generated function _broadcast!(f, ::Val{newsize}, dest, s::Val{sizes}, a...) where {newsize, sizes} + first_staticarray = a[findfirst(ai -> ai <: FixedArray, a)] -function typeof2(data::AbstractArray{Float32,2}) - dims = size(data) - if dims === (1, 1) - StaticMonoAudio # Equivalent to StaticControlSignal - elseif dims === (channels, 1) - StaticStereoAudio - elseif dims === (1, buffer_length) - MonoAudio # Equivalent to ControlSignal - elseif dims === (channels, buffer_length) - StereoAudio - else - @assert false "Invalid audio or control signal type" + if prod(newsize) == 0 + @assert false "We don't handle this yet." + end + + index_symbols = [Symbol(:idx, dimidx) for dimidx = 1:length(newsize)] + exprs_vals = [ + begin + if !(a[i] <: AbstractArray || a[i] <: Tuple) + :(scalar_getindex(a[$i])) + else + :(a[$i][$([index_symbols[i] for i = 1:length(sizes[i])]...)]) + end + end + for i = 1:length(sizes) + ] + compute_expr = :(dest[$(index_symbols...)] = f($(exprs_vals...))) + for (dimidx, dim) in enumerate(newsize) + compute_expr = quote + for $(index_symbols[dimidx]) = 1:$(newsize[dimidx]) + $compute_expr + end + end + end + + return quote + @inbounds $compute_expr + @inbounds return dest end end -function typeof2(data::AbstractArray{Bool,2}) - dims = size(data) - if dims === (1, 1) - StaticTrigger - elseif dims === (1, buffer_length) - Trigger - else - @assert false "Invalid trigger type" +@generated function fixed_array_type(typ::Type, dimensions::Type)::Type{FixedArray} + eltype = typ.parameters[1] + dims = [convert(Int64, x) for x ∈ dimensions.parameters[1].parameters] + dims = Tuple{dims...} + ndims = length(dims.parameters) + nitems = reduce(*, dims.parameters) + quote + FixedArray{$eltype, $dims, $ndims, $nitems} end end +const num_midi_controls = 128 +const default_graph_resolution = 42 + +const MonoSample = fixed_array_type(Float32, Tuple{1}) +const StereoSample = fixed_array_type(Float32, Tuple{2}) + +const StaticMonoAudio = fixed_array_type(Float32, Tuple{1, 1}) +const StaticStereoAudio = fixed_array_type(Float32, Tuple{channels, 1}) +const MonoAudio = fixed_array_type(Float32, Tuple{1, buffer_length}) +const StereoAudio = fixed_array_type(Float32, Tuple{channels, buffer_length}) +const StaticControlSignal = StaticMonoAudio +const ControlSignal = MonoAudio + +const StaticTrigger = fixed_array_type(Bool, Tuple{1, 1}) +const Trigger = fixed_array_type(Bool, Tuple{1, buffer_length}) +const Waveform = Function + +const flat_waveform = (phase::Float32, _buffer_pos::Integer) -> 0f0 +const ramp_up_waveform = (phase::Float32, _buffer_pos::Integer) -> phase * 2 - 1 +const ramp_down_waveform = (phase::Float32, _buffer_pos::Integer) -> 1 - phase * 2 +const sine_waveform = (phase::Float32, _buffer_pos::Integer) -> sin(phase * pi * 2f0) + struct GlobalInput midi_controls::Vector{Float32} pitch_wheel::Float32 @@ -103,7 +207,7 @@ struct NoteInput end mutable struct NoteOutput - audio::mutable(StereoAudio) + audio::StereoAudio end function NoteOutput() @@ -135,7 +239,7 @@ end # Timing modes: # Bit 1 controls note (false) vs song (true) # Bit 2 controls seconds (false) vs beats (true) -function get_timing(context::NoteContext, mode::Integer)::mutable(ControlSignal) +function get_timing(context::NoteContext, mode::Integer)::ControlSignal result = similar(ControlSignal) global_source::Bool = timing_mode_source_is_global(mode) beat_units::Bool = timing_mode_unit_is_beats(mode) @@ -169,51 +273,43 @@ at2st(_audio_type::Type{StaticMonoAudio})::Type{MonoSample} = MonoSample at2st(_audio_type::Type{StaticStereoAudio})::Type{StereoSample} = StereoSample at2st(_audio_type::Type{MonoAudio})::Type{MonoSample} = MonoSample at2st(_audio_type::Type{StereoAudio})::Type{StereoSample} = StereoSample -at2st(_audio_type::Type{mutable(StaticMonoAudio)})::Type{MonoSample} = MonoSample -at2st(_audio_type::Type{mutable(StaticStereoAudio)})::Type{StereoSample} = StereoSample -at2st(_audio_type::Type{mutable(MonoAudio)})::Type{MonoSample} = MonoSample -at2st(_audio_type::Type{mutable(StereoAudio)})::Type{StereoSample} = StereoSample # Sample type to audio type st2at(_sample_type::Type{MonoSample})::Type{MonoAudio} = MonoAudio st2at(_sample_type::Type{StereoSample})::Type{StereoAudio} = StereoAudio -st2at(_sample_type::Type{mutable(MonoSample)})::Type{MonoAudio} = MonoAudio -st2at(_sample_type::Type{mutable(StereoSample)})::Type{StereoAudio} = StereoAudio # Sample type to static audio type st2sat(_sample_type::Type{MonoSample})::Type{StaticMonoAudio} = StaticMonoAudio st2sat(_sample_type::Type{StereoSample})::Type{StaticStereoAudio} = StaticStereoAudio -st2sat(_sample_type::Type{mutable(MonoSample)})::Type{StaticMonoAudio} = StaticMonoAudio -st2sat(_sample_type::Type{mutable(StereoSample)})::Type{StaticStereoAudio} = StaticStereoAudio # Audio to control signal -a2cs(audio::maybe_mutable(StereoAudio))::ControlSignal = ControlSignal(sum(audio; dims=1) ./ 2) -a2cs(audio::maybe_mutable(MonoAudio))::ControlSignal = ControlSignal(audio) -a2cs(audio::maybe_mutable(StaticStereoAudio))::StaticControlSignal = StaticControlSignal((audio[1] + audio[2]) / 2) -a2cs(audio::maybe_mutable(StaticMonoAudio))::StaticControlSignal = StaticControlSignal(audio) +a2cs(audio::StereoAudio)::ControlSignal = ControlSignal(sum(audio; dims=1) ./ 2) +a2cs(audio::MonoAudio)::ControlSignal = ControlSignal(audio) +a2cs(audio::StaticStereoAudio)::StaticControlSignal = StaticControlSignal((audio[1] + audio[2]) / 2) +a2cs(audio::StaticMonoAudio)::StaticControlSignal = StaticControlSignal(audio) -function assert_is_sample_type(_type::maybe_mutable_type(MonoSample)) end -function assert_is_sample_type(_type::maybe_mutable_type(StereoSample)) end +function assert_is_sample_type(_type::Type{MonoSample}) end +function assert_is_sample_type(_type::Type{StereoSample}) end function assert_is_sample_type(type) throw(AssertionError("$type is not a valid sample type.")) end -function assert_is_control_signal_type(_type::maybe_mutable_type(StaticControlSignal)) end -function assert_is_control_signal_type(_type::maybe_mutable_type(ControlSignal)) end +function assert_is_control_signal_type(_type::Type{StaticControlSignal}) end +function assert_is_control_signal_type(_type::Type{ControlSignal}) end function assert_is_control_signal_type(type) throw(AssertionError("$type is not a valid control signal type.")) end -function assert_is_audio_type(_type::maybe_mutable_type(StaticMonoAudio)) end -function assert_is_audio_type(_type::maybe_mutable_type(StaticStereoAudio)) end -function assert_is_audio_type(_type::maybe_mutable_type(MonoAudio)) end -function assert_is_audio_type(_type::maybe_mutable_type(StereoAudio)) end +function assert_is_audio_type(_type::Type{StaticMonoAudio}) end +function assert_is_audio_type(_type::Type{StaticStereoAudio}) end +function assert_is_audio_type(_type::Type{MonoAudio}) end +function assert_is_audio_type(_type::Type{StereoAudio}) end function assert_is_audio_type(type) throw(AssertionError("$type is not a valid audio type.")) end -function assert_is_trigger_type(_type::maybe_mutable_type(StaticTrigger)) end -function assert_is_trigger_type(_type::maybe_mutable_type(Trigger)) end +function assert_is_trigger_type(_type::Type{StaticTrigger}) end +function assert_is_trigger_type(_type::Type{Trigger}) end function assert_is_trigger_type(type) throw(AssertionError("$type is not a valid trigger type.")) end @@ -228,68 +324,35 @@ function assert_is_waveform(func::Function) end end -# Allows indexing smaller buffers as if they were their full-sized counterparts. -Base.getindex(from::maybe_mutable(StereoAudio), _::typeof(%), channelidx::Integer, sampleidx::Integer)::Float32 = from[channelidx, sampleidx] -Base.getindex(from::maybe_mutable(MonoAudio), _::typeof(%), channelidx::Integer, sampleidx::Integer)::Float32 = from[1, sampleidx] -Base.getindex(from::maybe_mutable(StaticStereoAudio), _::typeof(%), channelidx::Integer, sampleidx::Integer)::Float32 = from[channelidx, 1] -Base.getindex(from::maybe_mutable(StaticMonoAudio), _::typeof(%), channelidx::Integer, sampleidx::Integer)::Float32 = from[1, 1] - -Base.getindex(from::maybe_mutable(StereoAudio), _::typeof(%), _::typeof(:), sampleidx::Integer) = from[:, sampleidx] -Base.getindex(from::maybe_mutable(MonoAudio), _::typeof(%), _::typeof(:), sampleidx::Integer) = from[:, sampleidx] -Base.getindex(from::maybe_mutable(StaticStereoAudio), _::typeof(%), _::typeof(:), sampleidx::Integer) = from[:, 1] -Base.getindex(from::maybe_mutable(StaticMonoAudio), _::typeof(%), _::typeof(:), sampleidx::Integer) = from[:, 1] - -Base.getindex(from::maybe_mutable(StereoSample), _::typeof(%), channelidx::Integer)::Float32 = from[channelidx] -Base.getindex(from::maybe_mutable(MonoSample), _::typeof(%), channelidx::Integer)::Float32 = from[1] - -Base.getindex(from::maybe_mutable(Trigger), _::typeof(%), channelidx::Integer, sampleidx::Integer)::Bool = from[1, sampleidx] -Base.getindex(from::maybe_mutable(StaticTrigger), _::typeof(%), channelidx::Integer, sampleidx::Integer)::Bool = from[1] - # Allows accessing static data as a smaller data type. Cannot view small data as a bigger type. -viewas(data::Union{maybe_mutable(MonoSample), maybe_mutable(StereoSample)}, type::maybe_mutable_type(MonoSample)) = @view data[1:1] -viewas(data::maybe_mutable(StereoSample), type::maybe_mutable_type(StereoSample)) = @view data[:] +viewas(data::Union{(MonoSample), (StereoSample)}, type::(MonoSample)) = @view data[1:1] +viewas(data::(StereoSample), type::(StereoSample)) = @view data[:] -viewas(data::Union{maybe_mutable(StaticTrigger), maybe_mutable(Trigger)}, type::maybe_mutable_type(StaticTrigger)) = @view data[1:1] -viewas(data::maybe_mutable(Trigger), type::maybe_mutable_type(Trigger)) = @view data[:] +viewas(data::Union{(StaticTrigger), (Trigger)}, type::(StaticTrigger)) = @view data[1:1] +viewas(data::(Trigger), type::(Trigger)) = @view data[:] -viewas(data::Union{maybe_mutable(StaticMonoAudio), maybe_mutable(StaticStereoAudio), maybe_mutable(MonoAudio), maybe_mutable(StereoAudio)}, type::Type{StaticMonoAudio}) = @view data[1:1, 1:1] -viewas(data::Union{maybe_mutable(StaticStereoAudio), maybe_mutable(StereoAudio)}, type::Type{StaticStereoAudio}) = @view data[:, 1:1] -viewas(data::Union{maybe_mutable(MonoAudio), maybe_mutable(StereoAudio)}, type::Type{MonoAudio}) = @view data[1:1, :] -viewas(data::maybe_mutable(StereoAudio), type::Type{StereoAudio}) = @view data[:, :] +viewas(data::Union{(StaticMonoAudio), (StaticStereoAudio), (MonoAudio), (StereoAudio)}, type::Type{StaticMonoAudio}) = @view data[1:1, 1:1] +viewas(data::Union{(StaticStereoAudio), (StereoAudio)}, type::Type{StaticStereoAudio}) = @view data[:, 1:1] +viewas(data::Union{(MonoAudio), (StereoAudio)}, type::Type{MonoAudio}) = @view data[1:1, :] +viewas(data::(StereoAudio), type::Type{StereoAudio}) = @view data[:, :] # Allows manually iterating over audio. -sample_indices(_buf::SArray{Tuple{C, S}, Float32, 2, N}) where {C, S, N} = Base.OneTo(S) -sample_indices(_buf::MArray{Tuple{C, S}, Float32, 2, N}) where {C, S, N} = Base.OneTo(S) -sample_indices(_buf::SizedArray{Tuple{C, S}, Float32, 2, N}) where {C, S, N} = Base.OneTo(S) -sample_indices(_buf::Type{SArray{Tuple{C, S}, Float32, 2, N}}) where {C, S, N} = Base.OneTo(S) -sample_indices(_buf::Type{MArray{Tuple{C, S}, Float32, 2, N}}) where {C, S, N} = Base.OneTo(S) -sample_indices(_buf::Type{SizedArray{Tuple{C, S}, Float32, 2, N}}) where {C, S, N} = Base.OneTo(S) -channel_indices(_buf::SArray{Tuple{C, S}, Float32, 2, N}) where {C, S, N} = Base.OneTo(C) -channel_indices(_buf::MArray{Tuple{C, S}, Float32, 2, N}) where {C, S, N} = Base.OneTo(C) -channel_indices(_buf::SizedArray{Tuple{C, S}, Float32, 2, N}) where {C, S, N} = Base.OneTo(C) -channel_indices(_buf::Type{SArray{Tuple{C, S}, Float32, 2, N}}) where {C, S, N} = Base.OneTo(C) -channel_indices(_buf::Type{MArray{Tuple{C, S}, Float32, 2, N}}) where {C, S, N} = Base.OneTo(C) -channel_indices(_buf::Type{SizedArray{Tuple{C, S}, Float32, 2, N}}) where {C, S, N} = Base.OneTo(C) +sample_indices(_buf::FixedArray{Float32, Tuple{C, S}, 2, N}) where {C, S, N} = Base.OneTo(S) +sample_indices(_buf::Type{FixedArray{Float32, Tuple{C, S}, 2, N}}) where {C, S, N} = Base.OneTo(S) +channel_indices(_buf::FixedArray{Float32, Tuple{C, S}, 2, N}) where {C, S, N} = Base.OneTo(C) +channel_indices(_buf::Type{FixedArray{Float32, Tuple{C, S}, 2, N}}) where {C, S, N} = Base.OneTo(C) # ...samples. -channel_indices(_buf::SArray{Tuple{C}, Float32, 1, N}) where {C, N} = Base.OneTo(C) -channel_indices(_buf::MArray{Tuple{C}, Float32, 1, N}) where {C, N} = Base.OneTo(C) -channel_indices(_buf::SizedArray{Tuple{C}, Float32, 1, N}) where {C, N} = Base.OneTo(C) -channel_indices(_buf::Type{SArray{Tuple{C}, Float32, 1, N}}) where {C, N} = Base.OneTo(C) -channel_indices(_buf::Type{MArray{Tuple{C}, Float32, 1, N}}) where {C, N} = Base.OneTo(C) -channel_indices(_buf::Type{SizedArray{Tuple{C}, Float32, 1, N}}) where {C, N} = Base.OneTo(C) +channel_indices(_buf::FixedArray{Float32, Tuple{C}, 1, N}) where {C, N} = Base.OneTo(C) +channel_indices(_buf::Type{FixedArray{Float32, Tuple{C}, 1, N}}) where {C, N} = Base.OneTo(C) # ...triggers. -sample_indices(_buf::SArray{Tuple{1, S}, Bool, 2, N}) where {S, N} = Base.OneTo(S) -sample_indices(_buf::MArray{Tuple{1, S}, Bool, 2, N}) where {S, N} = Base.OneTo(S) -sample_indices(_buf::SizedArray{Tuple{1, S}, Bool, 2, N}) where {S, N} = Base.OneTo(S) -sample_indices(_buf::Type{SArray{Tuple{1, S}, Bool, 2, N}}) where {S, N} = Base.OneTo(S) -sample_indices(_buf::Type{MArray{Tuple{1, S}, Bool, 2, N}}) where {S, N} = Base.OneTo(S) -sample_indices(_buf::Type{SizedArray{Tuple{1, S}, Bool, 2, N}}) where {S, N} = Base.OneTo(S) +sample_indices(_buf::FixedArray{Bool, Tuple{C, S}, 2, N}) where {C, S, N} = Base.OneTo(S) +sample_indices(_buf::Type{FixedArray{Bool, Tuple{C, S}, 2, N}}) where {C, S, N} = Base.OneTo(S) # View outputs, for sending data going across wires back to the GUI so that it can be displayed # to the user. function make_pitch_view_data(pitch)::Vector{Float32} assert_is_control_signal_type(typeof(pitch)) - [pitch[%, 1, 1]] + [pitch[1, 1]] end function make_audio_view_data(audio)::Vector{Float32} assert_is_audio_type(typeof(audio)) @@ -329,7 +392,7 @@ lerp(from, to, amount) = to * amount + from * (1 - amount) # export all # https://discourse.julialang.org/t/exportall/4970/16 -for m in (@__MODULE__, Main.UnpackedDependencies.StaticArrays, Main.Parameters) +for m in (@__MODULE__, Main.Parameters) for n in names(m; all=true) if Base.isidentifier(n) && n ∉ (Symbol(@__MODULE__), :eval, :include) @eval export $n diff --git a/components/factory_library/modules/control/Envelope.module.jl b/components/factory_library/modules/control/Envelope.module.jl index ce1c332..78e166f 100644 --- a/components/factory_library/modules/control/Envelope.module.jl +++ b/components/factory_library/modules/control/Envelope.module.jl @@ -14,10 +14,10 @@ function exec() for s in sample_indices(MonoAudio) if !static.releasing - if reset_trigger[%, 1, s] + if reset_trigger[1, s] static.start = timing[1, s] end - if release_trigger[%, 1, s] + if release_trigger[1, s] static.start = timing[1, s] static.releasing = true end @@ -26,20 +26,20 @@ function exec() now = timing[1, s] - static.start value = Float32(0) if static.releasing - if now < release_time[%, 1, s] - value = lerp(static.last_value, 0f0, now / release_time[%, 1, s]) + if now < release_time[1, s] + value = lerp(static.last_value, 0f0, now / release_time[1, s]) else value = 0f0 end else - if now < attack_time[%, 1, s] - value = now / attack_time[%, 1, s] + if now < attack_time[1, s] + value = now / attack_time[1, s] else - now = now - attack_time[%, 1, s] - if now < decay_time[%, 1, s] - value = lerp(1f0, sustain[%, 1, s], now / decay_time[%, 1, s]) + now = now - attack_time[1, s] + if now < decay_time[1, s] + value = lerp(1f0, sustain[1, s], now / decay_time[1, s]) else - value = sustain[%, 1, s] + value = sustain[1, s] end end static.last_value = value @@ -51,12 +51,12 @@ function exec() if do_feedback now_time = timing[1, 1] - static.start if static.releasing - now_time = now_time + attack_time[%, 1, 1] + decay_time[%, 1, 1] - if now_time > attack_time[%, 1, 1] + decay_time[%, 1, 1] + release_time[%, 1, 1] - now_time = attack_time[%, 1, 1] + decay_time[%, 1, 1] + release_time[%, 1, 1] + now_time = now_time + attack_time[1, 1] + decay_time[1, 1] + if now_time > attack_time[1, 1] + decay_time[1, 1] + release_time[1, 1] + now_time = attack_time[1, 1] + decay_time[1, 1] + release_time[1, 1] end - elseif now_time > attack_time[%, 1, 1] + decay_time[%, 1, 1] - now_time = attack_time[%, 1, 1] + decay_time[%, 1, 1] + elseif now_time > attack_time[1, 1] + decay_time[1, 1] + now_time = attack_time[1, 1] + decay_time[1, 1] end multiplier = 1f0 if timing_mode_unit_is_beats(timing_mode) diff --git a/components/factory_library/modules/control/LFO.module.jl b/components/factory_library/modules/control/LFO.module.jl index 09ae038..9489c4d 100644 --- a/components/factory_library/modules/control/LFO.module.jl +++ b/components/factory_library/modules/control/LFO.module.jl @@ -14,8 +14,8 @@ function exec() timing = get_timing(context, timing_mode) for s in sample_indices(MonoAudio) - phase = (timing[%, 1, s] / cycle_time[%, 1, s] + offset[%, 1, s] + 1f0) % 1f0 - sample = apply_strength(waveform(phase, s), strength[%, 1, s], strength_mode) + phase = (timing[1, s] / cycle_time[1, s] + offset[1, s] + 1f0) % 1f0 + sample = apply_strength(waveform(phase, s), strength[1, s], strength_mode) audio[1, s] = sample end @@ -26,7 +26,7 @@ function exec() push!(graph_feedback, last(audio)) for s in 1:default_graph_resolution phase = ((s - 1) / Float32(default_graph_resolution - 1) + offset) % 1f0 - sample = apply_strength(waveform(phase, s), strength[%, 1, s], strength_mode) + sample = apply_strength(waveform(phase, s), strength[1, s], strength_mode) push!(graph_feedback, sample) end end diff --git a/components/factory_library/modules/control/TriggerSequence.module.jl b/components/factory_library/modules/control/TriggerSequence.module.jl index 7fd5a04..1cb488c 100644 --- a/components/factory_library/modules/control/TriggerSequence.module.jl +++ b/components/factory_library/modules/control/TriggerSequence.module.jl @@ -18,11 +18,11 @@ function exec() end for s in sample_indices(Trigger) - if reset[%, 1, s] - static.base_time = timing[%, 1, s] + if reset[1, s] + static.base_time = timing[1, s] end current_step = - floor(Int32, (timing[%, 1, s] - static.base_time) / first(step_time)) % num_steps + floor(Int32, (timing[1, s] - static.base_time) / first(step_time)) % num_steps if static.last_step != current_step static.last_step = current_step out_trigger[1, s] = sequence[current_step + 1] # grumble grumble diff --git a/components/factory_library/modules/control/ValueSequence.module.jl b/components/factory_library/modules/control/ValueSequence.module.jl index b2eac53..b9b65a4 100644 --- a/components/factory_library/modules/control/ValueSequence.module.jl +++ b/components/factory_library/modules/control/ValueSequence.module.jl @@ -18,19 +18,19 @@ function exec() end for s in sample_indices(Trigger) - if reset[%, 1, s] - static.base_time = timing[%, 1, s] + if reset[1, s] + static.base_time = timing[1, s] end - sequence_time = (timing[%, 1, s] - static.base_time) / first(step_time) % Float32(num_steps) + sequence_time = (timing[1, s] - static.base_time) / first(step_time) % Float32(num_steps) step_index = floor(Int32, sequence_time) step_progress = sequence_time % 1f0 - ramp_start = 1f0 - ramping[%, 1, s] + ramp_start = 1f0 - ramping[1, s] if step_progress <= ramp_start # Every time I have to add a +1 I die a little inside. out_value[1, s] = sequence[step_index + 1] else next_index = (step_index + Int32(1)) % num_steps - ramp_amount = (step_progress - ramp_start) / ramping[%, 1, s] + ramp_amount = (step_progress - ramp_start) / ramping[1, s] out_value[1, s] = lerp(sequence[step_index + 1], sequence[next_index + 1], ramp_amount) end end diff --git a/components/factory_library/modules/synthesis/FmOscillator.module.jl b/components/factory_library/modules/synthesis/FmOscillator.module.jl index 077c16c..afd71b0 100644 --- a/components/factory_library/modules/synthesis/FmOscillator.module.jl +++ b/components/factory_library/modules/synthesis/FmOscillator.module.jl @@ -1,5 +1,5 @@ mutable struct StaticData - phase::mutable(StereoSample) + phase::StereoSample end function static_init() @@ -19,12 +19,12 @@ function exec() for s in sample_indices(MonoAudio) @. sample = 0f0 - @. pitch_here = pitch[%, 1, s] * (fm_signal[%, :, s] * fm_strength[%, 1, s] + 1f0) + @. pitch_here = pitch[1, s] * (fm_signal[:, s] * fm_strength[1, s] + 1f0) @. phase_delta = pitch_here / sample_rate / Float32(oversampling) + 1f0 for subsample in 1:oversampling @. sample += waveform(phase, (s,)) @. phase = (phase + phase_delta) % 1f0 end - @. audio[:, s] = sample * amplitude[%, 1, s] / Float32(oversampling) + @. audio[:, s] = sample * amplitude[1, s] / Float32(oversampling) end end diff --git a/components/factory_library/modules/synthesis/Noise.module.jl b/components/factory_library/modules/synthesis/Noise.module.jl index 3d07118..ddb75fb 100644 --- a/components/factory_library/modules/synthesis/Noise.module.jl +++ b/components/factory_library/modules/synthesis/Noise.module.jl @@ -15,8 +15,8 @@ function exec() timing = get_timing(context, 0) # 0 = note time in seconds. for s in sample_indices(MonoAudio) - delay_now = max_delay[%, 1, s] * delay_mul[%, 1, s] - time_now = timing[%, 1, s] + delay_now = max_delay[1, s] * delay_mul[1, s] + time_now = timing[1, s] if delay_now <= 1f0 / sample_rate static.old_value = static.new_value static.new_value = rand() @@ -35,13 +35,13 @@ function exec() value_now = static.new_value * factor + static.old_value * (1f0 - factor) end end - audio[1, s] = (value_now * 2f0 - 1f0) * amplitude[%, 1, s] + audio[1, s] = (value_now * 2f0 - 1f0) * amplitude[1, s] end if do_feedback # We use this instead of rand() so that the waveform display doesn't violently flicker # every time it is updated. - dummy_waveform = SA_F32[ + dummy_waveform = [ 0.3988945908f0, 0.8954911673f0, 0.0116554042f0, 0.0909389386f0, 0.0893340926f0, 0.4953123474f0, 0.5784687653f0, 0.2548134842f0, 0.1776265054f0, 0.3360827756f0, 0.3734218081f0, 0.6334027459f0, 0.8120340729f0, 0.1525260985f0, 0.0720461340f0, 0.3180398718f0, 0.3208139232f0, 0.9439490845f0, diff --git a/components/factory_library/modules/synthesis/Oscillator.module.jl b/components/factory_library/modules/synthesis/Oscillator.module.jl index f762359..94b369f 100644 --- a/components/factory_library/modules/synthesis/Oscillator.module.jl +++ b/components/factory_library/modules/synthesis/Oscillator.module.jl @@ -12,11 +12,11 @@ function exec() for s in sample_indices(MonoAudio) sample = 0f0 - phase_delta = pitch[%, 1, s] / sample_rate / Float32(oversampling) + phase_delta = pitch[1, s] / sample_rate / Float32(oversampling) for subsample in 1:oversampling sample += waveform(static.phase, s) static.phase = (static.phase + phase_delta) % 1f0 end - audio[1, s] = sample * amplitude[%, 1, s] / Float32(oversampling) + audio[1, s] = sample * amplitude[1, s] / Float32(oversampling) end end diff --git a/components/factory_library/modules/waveform/BasicShape.module.jl b/components/factory_library/modules/waveform/BasicShape.module.jl index fe52929..284eb26 100644 --- a/components/factory_library/modules/waveform/BasicShape.module.jl +++ b/components/factory_library/modules/waveform/BasicShape.module.jl @@ -1,6 +1,6 @@ # Chosen by fair dice roll # Guaranteed to be random -const NOISE_TABLE = SA_F32[ +const NOISE_TABLE = [ 0.5823733057f0, 0.9205345657f0, 0.6571298125f0, 0.2043478687f0, 0.0512690046f0, 0.7195207713f0, 0.2310517188f0, 0.9231509820f0, 0.4526838440f0, 0.0260594523f0, 0.0544928862f0, 0.1159314989f0, 0.6362018928f0, 0.9405871960f0, 0.6264985620f0, 0.2124264443f0, 0.9875042798f0, 0.5048588941f0, 0.4584656122f0, 0.0476017861f0, 0.0288889159f0, 0.6630403250f0, 0.1075030279f0, 0.3067326273f0, 0.9282126274f0, 0.0489180574f0, 0.1897491045f0, 0.1516381450f0, 0.6479914077f0, 0.5966626517f0, 0.4706642329f0, 0.9586591313f0, 0.6251782560f0, 0.0076425701f0, 0.3360183227f0, 0.6176969668f0, 0.6220597604f0, 0.7335311112f0, 0.7775641042f0, 0.1995585811f0, 0.5877232887f0, 0.6093249228f0, 0.6400574417f0, 0.6418364406f0, 0.0491156122f0, 0.8943623489f0, 0.9641463493f0, 0.1186706294f0, 0.8703927354f0, 0.5001623698f0, 0.0056789845f0, 0.8819698228f0, 0.7906626464f0, 0.9055616444f0, 0.4516002782f0, 0.5868636653f0, 0.3968439417f0, 0.7977244927f0, 0.1008400526f0, 0.8151222552f0, 0.9223453225f0, 0.3447953142f0, 0.1643775846f0, 0.3701962001f0, 0.8219128569f0, 0.1433063551f0, 0.1008244504f0, 0.2759908145f0, 0.3385794923f0, 0.3720992678f0, 0.5390982539f0, 0.4045148123f0, 0.3129078015f0, 0.0905247719f0, 0.3636522633f0, 0.1815615606f0, 0.5866425786f0, 0.3635969325f0, 0.7118137809f0, 0.9413958251f0, 0.8462901983f0, 0.7086461944f0, 0.2116179209f0, 0.9558813899f0, 0.7712728058f0, 0.7436435592f0, 0.1174792689f0, 0.4402326317f0, 0.3300473314f0, 0.7258179635f0, 0.6852669441f0, 0.8268835500f0, 0.9414339859f0, 0.9698785453f0, 0.2198778432f0, 0.4993178479f0, 0.9458775302f0, 0.4651419338f0, 0.9475727742f0, 0.7516902440f0, 0.5921997640f0, 0.5647117736f0, 0.3013841324f0, 0.6477727130f0, 0.3548120713f0, 0.6580563145f0, 0.4555038559f0, 0.1969513888f0, 0.3823855544f0, 0.4797867289f0, 0.6586882920f0, 0.8334892165f0, 0.6235895636f0, 0.9407863655f0, 0.0075790714f0, 0.6499866010f0, 0.6128024268f0, 0.3511917680f0, 0.7220454357f0, 0.0042356840f0, 0.4927558066f0, 0.3164420912f0, 0.8955885591f0, 0.3571066220f0, 0.4981550359f0, 0.0989912997f0, 0.3712778393f0, 0.3266162353f0, 0.7478513378f0, 0.6276269444f0, 0.8296789683f0, 0.4610411459f0, 0.0314418181f0, 0.4869558270f0, 0.7031032450f0, 0.2127670626f0, 0.0680087881f0, 0.8889250267f0, 0.8341707040f0, 0.1246116881f0, 0.0000052800f0, 0.7495491208f0, 0.0503713413f0, 0.4544045378f0, 0.8691414718f0, 0.9367935365f0, 0.5428859015f0, 0.2169552000f0, 0.6843056289f0, 0.9442384416f0, 0.1008210213f0, 0.4999945500f0, 0.9061539286f0, 0.5784019420f0, 0.6173300311f0, 0.5558337790f0, 0.2850517636f0, 0.1011729350f0, 0.1542427349f0, 0.0508586863f0, 0.3768541338f0, 0.0972577220f0, 0.7144678479f0, 0.5576695303f0, 0.8094812596f0, 0.2209368912f0, 0.5436705058f0, 0.3966998104f0, 0.5256404117f0, 0.5910491356f0, 0.7311703302f0, 0.3424387399f0, 0.3120802337f0, 0.7003970577f0, 0.1556768013f0, 0.5504151494f0, 0.6198858838f0, 0.5639975408f0, 0.6005967693f0, 0.3505180292f0, 0.5527126085f0, 0.6887327524f0, 0.5782618174f0, 0.4792736810f0, 0.3127952238f0, 0.1107229101f0, 0.5514905204f0, 0.5568147747f0, 0.7904348876f0, 0.6297142275f0, 0.9969978076f0, 0.8145126198f0, @@ -9,7 +9,7 @@ const NOISE_TABLE = SA_F32[ function exec() waveform = function (phase::Float32, buffer_pos::Integer) - parameter_here = parameter[%, 1, buffer_pos] + parameter_here = parameter[1, buffer_pos] if choice == 0 sin(phase * pi * 2) elseif choice == 1 diff --git a/components/factory_library/modules/waveform/HarmonicCasserole.module.jl b/components/factory_library/modules/waveform/HarmonicCasserole.module.jl index cb95219..764f908 100644 --- a/components/factory_library/modules/waveform/HarmonicCasserole.module.jl +++ b/components/factory_library/modules/waveform/HarmonicCasserole.module.jl @@ -2,23 +2,23 @@ function exec() out_wave = function (phase::Float32, buffer_pos::Integer) result = 0f0 - this_phase = (phase * 1 + 1f0 + phase1[%, 1, buffer_pos]) % 1f0 - result += base_wave(this_phase, buffer_pos) * amp1[%, 1, buffer_pos] - this_phase = (phase * 2 + 1f0 + phase2[%, 1, buffer_pos]) % 1f0 - result += base_wave(this_phase, buffer_pos) * amp2[%, 1, buffer_pos] - this_phase = (phase * 3 + 1f0 + phase3[%, 1, buffer_pos]) % 1f0 - result += base_wave(this_phase, buffer_pos) * amp3[%, 1, buffer_pos] - this_phase = (phase * 4 + 1f0 + phase4[%, 1, buffer_pos]) % 1f0 - result += base_wave(this_phase, buffer_pos) * amp4[%, 1, buffer_pos] - this_phase = (phase * 5 + 1f0 + phase5[%, 1, buffer_pos]) % 1f0 - result += base_wave(this_phase, buffer_pos) * amp5[%, 1, buffer_pos] - this_phase = (phase * 6 + 1f0 + phase6[%, 1, buffer_pos]) % 1f0 - result += base_wave(this_phase, buffer_pos) * amp6[%, 1, buffer_pos] - this_phase = (phase * 7 + 1f0 + phase7[%, 1, buffer_pos]) % 1f0 - result += base_wave(this_phase, buffer_pos) * amp7[%, 1, buffer_pos] - this_phase = (phase * 8 + 1f0 + phase8[%, 1, buffer_pos]) % 1f0 - result += base_wave(this_phase, buffer_pos) * amp8[%, 1, buffer_pos] + this_phase = (phase * 1 + 1f0 + phase1[1, buffer_pos]) % 1f0 + result += base_wave(this_phase, buffer_pos) * amp1[1, buffer_pos] + this_phase = (phase * 2 + 1f0 + phase2[1, buffer_pos]) % 1f0 + result += base_wave(this_phase, buffer_pos) * amp2[1, buffer_pos] + this_phase = (phase * 3 + 1f0 + phase3[1, buffer_pos]) % 1f0 + result += base_wave(this_phase, buffer_pos) * amp3[1, buffer_pos] + this_phase = (phase * 4 + 1f0 + phase4[1, buffer_pos]) % 1f0 + result += base_wave(this_phase, buffer_pos) * amp4[1, buffer_pos] + this_phase = (phase * 5 + 1f0 + phase5[1, buffer_pos]) % 1f0 + result += base_wave(this_phase, buffer_pos) * amp5[1, buffer_pos] + this_phase = (phase * 6 + 1f0 + phase6[1, buffer_pos]) % 1f0 + result += base_wave(this_phase, buffer_pos) * amp6[1, buffer_pos] + this_phase = (phase * 7 + 1f0 + phase7[1, buffer_pos]) % 1f0 + result += base_wave(this_phase, buffer_pos) * amp7[1, buffer_pos] + this_phase = (phase * 8 + 1f0 + phase8[1, buffer_pos]) % 1f0 + result += base_wave(this_phase, buffer_pos) * amp8[1, buffer_pos] - result * post_amp[%, 1, buffer_pos] + result * post_amp[1, buffer_pos] end end diff --git a/components/factory_library/modules/waveform/PhaseMod.module.jl b/components/factory_library/modules/waveform/PhaseMod.module.jl index 2571d30..f4d0aea 100644 --- a/components/factory_library/modules/waveform/PhaseMod.module.jl +++ b/components/factory_library/modules/waveform/PhaseMod.module.jl @@ -1,6 +1,6 @@ function exec() waveform = function (phase::Float32, buffer_pos::Integer) - offset = modulator(phase, buffer_pos) * intensity[%, 1, buffer_pos] + offset = modulator(phase, buffer_pos) * intensity[1, buffer_pos] carrier((phase + offset + 1f0) % 1f0, buffer_pos) end end diff --git a/components/factory_library/modules/waveform/PhaseOffset.module.jl b/components/factory_library/modules/waveform/PhaseOffset.module.jl index 396855a..555fc48 100644 --- a/components/factory_library/modules/waveform/PhaseOffset.module.jl +++ b/components/factory_library/modules/waveform/PhaseOffset.module.jl @@ -1,5 +1,5 @@ function exec() waveform = function (phase::Float32, buffer_pos::Integer) - carrier((phase + offset[%, 1, buffer_pos] + 1f0) % 1f0, buffer_pos) + carrier((phase + offset[1, buffer_pos] + 1f0) % 1f0, buffer_pos) end end diff --git a/components/factory_library/modules/waveform/amplitude_mod.module.jl b/components/factory_library/modules/waveform/amplitude_mod.module.jl index 090e841..b08214b 100644 --- a/components/factory_library/modules/waveform/amplitude_mod.module.jl +++ b/components/factory_library/modules/waveform/amplitude_mod.module.jl @@ -1,6 +1,6 @@ function exec() waveform = function (phase::Float32, buffer_pos::Integer) - amplitude = lerp(1f0, modulator(phase, buffer_pos), intensity[%, 1, buffer_pos]) + amplitude = lerp(1f0, modulator(phase, buffer_pos), intensity[1, buffer_pos]) carrier(phase, buffer_pos) * amplitude end end diff --git a/components/julia_helper/src/lib.rs b/components/julia_helper/src/lib.rs index b73f1b2..b599783 100644 --- a/components/julia_helper/src/lib.rs +++ b/components/julia_helper/src/lib.rs @@ -444,9 +444,9 @@ Scope End"# "using Main.UnpackedDependencies.StaticArrays", ); ee.add_global_code(code).unwrap(); - let code = GeneratedCode::from_unique_source("asdf", "SA_F32[1, 2, 3]"); + let code = GeneratedCode::from_unique_source("asdf", "[1, 2, 3]"); ee.add_global_code(code).unwrap(); - let code = GeneratedCode::from_unique_source("asdf", "SA_F32[1, 2, 3][4]"); + let code = GeneratedCode::from_unique_source("asdf", "[1, 2, 3][4]"); let error = ee.add_global_code(code).unwrap_err(); assert!(error.contains("packed/StaticArrays/SVector.jl:40")); assert!(error.contains("__global_code_0__.jl:15")); diff --git a/docs/book/src/making_libraries/audiobench_and_julia.md b/docs/book/src/making_libraries/audiobench_and_julia.md index 01e7cf0..501b2bb 100644 --- a/docs/book/src/making_libraries/audiobench_and_julia.md +++ b/docs/book/src/making_libraries/audiobench_and_julia.md @@ -62,7 +62,7 @@ static[1, 1] = false # place to make sure that places where Audiobench expects a valid waveform are # actually being given a valid waveform. waveform = function(phase::Float32, buffer_pos::Integer) - sin(phase + some_control[%, 1, buffer_pos]) + sin(phase + some_control[1, buffer_pos]) end # This is not a valid waveform. It's going through a rebellious phase. not_waveform = function(something::String, irrelevant::Float64) @@ -138,7 +138,7 @@ The `buffer_pos` argument is there if you want to include some controls which can be automated by the user in the definition of your waveform: ```julia square_wave = function(phase::Float32, buffer_pos::Integer) - if phase < duty_cycle[%, 1, buffer_pos] 1f0 else -1f0 end + if phase < duty_cycle[1, buffer_pos] 1f0 else -1f0 end end ``` Unlike with control signals and audio, the `phase` argument *must* be between @@ -153,26 +153,26 @@ important to account for this possibility. For example, consider this code: second_sample = some_control[1, 2] ``` This could fail if `some_control` is one of the `Static` variants which only -contains a single value, indicating that it did not change during the span of +contains a single value, indicating that it did not change during the span of time it represents. The factory library extends indexing in a custom way to make -situations like this easier. By adding a percent sign `%` as the first index, -any Audiobench-related data can be accessed as if it were the biggest possible -type for that kind of data. For example: +situations like this easier. Any Audiobench-related data can be accessed as if +it were the biggest possible type for that kind of data. For example: ```julia -full_audio_data = similar(StereoAudio) -# Note that the % trick won't work on the left hand side of an equal sign, as -# its behavior would be misleading. -full_audio_data[2, 3] = 1f0 -@assert full_audio_data[%, 2, 3] == 1f0 # This represents audio data which is the same on all channels and during the # entire 'length' of the buffer, only storing a single value. static_audio_data = similar(StaticMonoAudio) static_audio_data[1, 1] = 1f0 # Access it as if it were stereo and changed over time. Any access will return # the same value. -@assert static_audio_data[%, 2, 3] == 1f0 -@assert static_audio_data[%, 2, 19] == 1f0 -@assert static_audio_data[%, 1, 1] == 1f0 +@assert static_audio_data[2, 3] == 1f0 +@assert static_audio_data[2, 19] == 1f0 +@assert static_audio_data[1, 1] == 1f0 +# Note that this won't work on the left hand side of an equal sign, as +# its behavior would be misleading. +full_audio_data = similar(StereoAudio) +# This would be an error if we used some type other than StereoAudio. +full_audio_data[2, 3] = 1f0 +@assert full_audio_data[2, 3] == 1f0 ``` If you want to manually iterate over data, the `channel_indices` and `sample_indices` functions are quite useful: @@ -188,7 +188,7 @@ for s in sample_indices(input) for c in channel_indices(input) # Using the percent trick is optional here since we already know for # sure that `s` and `c` are valid for this array. - output[c, s] = 2f0 * input[%, c, s] + output[c, s] = 2f0 * input[c, s] end end ``` @@ -202,7 +202,7 @@ for s in sample_indices(input) # case, we want a slice of all the channels for the particular sample `s`. # Also note the use of the .= since a slice is not a scalar, we want to # assign into it instead of over it. - output[:, s] .= input[%, :, s] .* sin(static.phase) + output[:, s] .= input[:, s] .* sin(static.phase) # 1.0 / sample_rate is the amount of time represented by a single audio # sample. static.phase += 1.0 / sample_rate @@ -214,10 +214,10 @@ support having multiple channels. ```julia data = similar(Trigger) data[1, 2] = true -@assert data[%, 1, 2] == true +@assert data[1, 2] == true static_data = similar(StaticTrigger) static_data[1, 1] = true -@assert static_data[%, 1, 81] == true +@assert static_data[1, 81] == true ``` Waveforms are functions, so using them only involves passing the correct arguments. The first argument is the phase you want to look up in the waveform, @@ -233,7 +233,7 @@ a simple oscillator: output = similar(MonoAudio) for s in sample_indices(MonoAudio) output[1, s] = waveform(static.phase % 1f0, s) - static.phase += pitch[%, 1, s] / sample_rate + static.phase += pitch[1, s] / sample_rate end ``` The value of the phase argument *must* be between `0f0` and `1f0` or you may @@ -280,19 +280,11 @@ function was called. # Linear interpolation. @assert lerp(from, to, 1f0) == to @assert lerp(0f0, 5f0, 0.2f0) == 1f0 -# mutable() returns the mutable version of a data type. mutable struct StaticData - echo_memory: mutable(StereoAudio) + echo_memory::StereoAudio end # viewas() lets you treat bigger data like it was smaller data. echo_memory = viewas(static.echo_memory, typeof(mono_audio)) echo_memory .+= mono_audio echo_memory .*= 0.5f0 -# typeof2() is useful for explicitly getting the Audiobench-defined datatype of -# something since Julia considers the immutable and mutable data types to be -# different. -@assert typeof(incoming_audio) == mutable(StereoAudio) -@assert typeof(incoming_audio) != StereoAudio -@assert typeof2(incoming_audio) == StereoAudio -@assert at2st(typeof2(incoming_audio)) == StereoSample ``` diff --git a/docs/book/src/making_libraries/custom_modules_julia.md b/docs/book/src/making_libraries/custom_modules_julia.md index e207d05..51aa6d6 100644 --- a/docs/book/src/making_libraries/custom_modules_julia.md +++ b/docs/book/src/making_libraries/custom_modules_julia.md @@ -11,7 +11,7 @@ Audiobench will look for three items defined in this file: ```julia # Optional mutable struct StaticData - echo_buffer::mutable(StereoAudio) + echo_buffer::StereoAudio end # Optional diff --git a/docs/book/src/making_libraries/factory_library_source.md b/docs/book/src/making_libraries/factory_library_source.md index cc3e53c..a8b2254 100644 --- a/docs/book/src/making_libraries/factory_library_source.md +++ b/docs/book/src/making_libraries/factory_library_source.md @@ -164,8 +164,8 @@ function exec() timing = get_timing(context, timing_mode) for s in sample_indices(MonoAudio) - phase = (timing[%, 1, s] / cycle_time[%, 1, s] + offset[%, 1, s] + 1f0) % 1f0 - sample = apply_strength(waveform(phase, s), strength[%, 1, s], strength_mode) + phase = (timing[1, s] / cycle_time[1, s] + offset[1, s] + 1f0) % 1f0 + sample = apply_strength(waveform(phase, s), strength[1, s], strength_mode) audio[1, s] = sample end @@ -176,7 +176,7 @@ function exec() push!(graph_feedback, last(audio)) for s in 1:default_graph_resolution phase = ((s - 1) / Float32(default_graph_resolution - 1) + offset) % 1f0 - sample = apply_strength(waveform(phase, s), strength[%, 1, s], strength_mode) + sample = apply_strength(waveform(phase, s), strength[1, s], strength_mode) push!(graph_feedback, sample) end end diff --git a/docs/book/src/making_libraries/introduction_to_julia.md b/docs/book/src/making_libraries/introduction_to_julia.md index e1398f8..d170265 100644 --- a/docs/book/src/making_libraries/introduction_to_julia.md +++ b/docs/book/src/making_libraries/introduction_to_julia.md @@ -36,14 +36,6 @@ vanilla = [1, 2, 3] @assert vanilla[1] == 1 push!(vanilla, 15) @assert vanilla[4] == 15 -# Fixed-size array used in many parts of Audiobench, added by the Static Arrays -# library available at https://github.com/JuliaArrays/StaticArrays.jl -# It is automatically imported into Audiobench so it can be used anywhere without -# manually importing it. -using StaticArrays; # Only required if running code outside Audiobench. -fixed = SA_F32[1, 2, 3] -@assert fixed[1] == 1f0 -push!(fixed, 4) # This causes a compile error. # FUNCTIONS function add_one(input) @@ -104,13 +96,13 @@ mutable_number_holder.value = 456 # Prefixing an operator with a . means it is 'vectorized' such that it will # operate on individual elements of each operand. This also serves as a # compilation hint to use vectorized instructions like those in SIMD and AVX. -@assert SA_F32[10, 10, 10] .+ SA_F32[11, 12, 13] == SA_F32[21, 22, 23] +@assert [10, 10, 10] .+ [11, 12, 13] == [21, 22, 23] # This also works for functions, so that functions do not have to worry about # implementing the details of iterating over different array types. -@assert abs.(SA_F32[1, -2, -3]) == SA_F32[1, 2, 3] +@assert abs.([1, -2, -3]) == [1, 2, 3] # Vectorized operators can also automatically increase the size of a piece of # data so that it maches another operand. -@assert 10 .+ SA_F32[11, 12, 13] == SA_F32[21, 22, 23] +@assert 10 .+ [11, 12, 13] == [21, 22, 23] # Vectorized assignment will write data directly to a variable instead of # collecting it in an intermediate value and then assigning that value to the # variable. @@ -121,24 +113,11 @@ container .= data1 .+ data2 # Results of individual sums are written directly to # individual elements of container # The @. macro will replace all operators on a line or in an expression with # their vectorized versions. -value = @. abs(SA_F32[1, -2, -3]) + 10 -@assert value == SA_F32[11, 12, 13] +value = @. abs([1, -2, -3]) + 10 +@assert value == [11, 12, 13] value = similar(value) @. value = 42 -@assert value == SA_F32[42, 42, 42] +@assert value == [42, 42, 42] # Frequent use of these features allow Julia to more thorougly optimize the code, # yielding better performance. - -# OTHER IMPORTANT FEATURES -# The similar() function makes a new mutable instance of some data type which -# itself may not be mutable. For example: -not_mutable = SA_F32[1, 2, 3] -not_mutable[1] = 10 # This will cause an error! -mutable = similar(not_mutable) -mutable[1] = 10 # This works fine. -# Note that the data is not copied by the similar() function: -@assert mutable[3] != not_mutable[3] -# This function can also be used with the type aliases defined by Audiobench: -data = similar(StereoAudioBuffer) -data[1, 1] = 0.2f0 ``` \ No newline at end of file diff --git a/docs/book/src/making_libraries/module_controls.md b/docs/book/src/making_libraries/module_controls.md index d81c1d1..236d266 100644 --- a/docs/book/src/making_libraries/module_controls.md +++ b/docs/book/src/making_libraries/module_controls.md @@ -107,7 +107,7 @@ on bpm.) It can be used in code like this: # timing is a ControlSignal timing = get_timing(context, name_of_timing_mode_control) for s in sample_indices(MonoAudio) - time_now = timing[%, 1, s] + time_now = timing[1, s] lfo_value = sin(time_now) end ```