diff --git a/Cargo.lock b/Cargo.lock index e586521..64dff80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "example" version = "0.1.0" @@ -10,11 +16,86 @@ dependencies = [ "oxydsp-flowgraph", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "oxydsp-dsp" version = "0.1.0" dependencies = [ + "num", "oxydsp-flowgraph", + "rustfft", ] [[package]] @@ -41,6 +122,15 @@ dependencies = [ "syn", ] +[[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -59,6 +149,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustfft" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", +] + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + [[package]] name = "syn" version = "2.0.117" @@ -70,6 +180,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/example/src/main.rs b/example/src/main.rs index a89e7d0..5db951e 100644 --- a/example/src/main.rs +++ b/example/src/main.rs @@ -1,19 +1,24 @@ -use std::{fmt::Display, fs::File, io::Write}; +use std::fmt::Display; +use std::fs::File; +use std::io::Write; -use oxydsp_dsp::blocks::{math::basic::Adder, utilities::iter::IterSource}; -use oxydsp_flowgraph::{ - BlockIO, - block::{Block, BlockResult}, - edge::{In, PopIterable}, - flowgraph, - graph::FlowGraph, -}; +use oxydsp_dsp::blocks::math::basic::Adder; +use oxydsp_dsp::blocks::utilities::iter::IterSource; +use oxydsp_flowgraph::BlockIO; +use oxydsp_flowgraph::block::Block; +use oxydsp_flowgraph::block::BlockResult; +use oxydsp_flowgraph::edge::In; +use oxydsp_flowgraph::edge::PopIter; +use oxydsp_flowgraph::edge::PopIterable; +use oxydsp_flowgraph::flowgraph; +use oxydsp_flowgraph::graph::FlowGraph; +use oxydsp_flowgraph::stream::StreamReader; -#[derive(BlockIO)] +//#[derive(BlockIO)] pub struct Printer { - #[input] - input: In, + //#[input] + input: [In; 3], n: usize, } @@ -22,7 +27,8 @@ impl Printer { pub fn new(input: In) -> Self { - Self { input, n: 0 } + todo!() + //Self { input, n: 0 } } } @@ -32,7 +38,12 @@ where { fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult { - for x in self.input.pop_iter() + let mut k: Vec<_> = self.input.iter_mut().map(|x| x.read()).collect(); + + k[0].pop(); + k[1].next(); + + for x in self.input[0].pop_iter() { if self.n.is_multiple_of(2usize.pow(20)) { diff --git a/oxydsp-dsp/Cargo.toml b/oxydsp-dsp/Cargo.toml index e13b74a..323aeb8 100644 --- a/oxydsp-dsp/Cargo.toml +++ b/oxydsp-dsp/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] +num = "0.4.3" oxydsp-flowgraph = {path = "../oxydsp-flowgraph/"} +rustfft = "6.4.1" diff --git a/oxydsp-dsp/src/blocks.rs b/oxydsp-dsp/src/blocks.rs index ecff8e3..154ed1b 100644 --- a/oxydsp-dsp/src/blocks.rs +++ b/oxydsp-dsp/src/blocks.rs @@ -1,2 +1,3 @@ pub mod math; +pub mod synthesis; pub mod utilities; diff --git a/oxydsp-dsp/src/blocks/synthesis.rs b/oxydsp-dsp/src/blocks/synthesis.rs new file mode 100644 index 0000000..2b6152a --- /dev/null +++ b/oxydsp-dsp/src/blocks/synthesis.rs @@ -0,0 +1,81 @@ +use crate::units::DigitalFrequency; +use num::Complex; +use num::Float; +use oxydsp_flowgraph::BlockIO; +use oxydsp_flowgraph::block::Block; +use oxydsp_flowgraph::block::BlockResult; +use oxydsp_flowgraph::edge::In; +use oxydsp_flowgraph::edge::Out; +use oxydsp_flowgraph::edge::PopIterable; +use oxydsp_flowgraph::edge::stream; + +#[derive(BlockIO)] +pub struct OscillatorSource + 'static> +{ + nco: crate::synthesis::oscillator::Nco, + + #[output] + output: Out>, +} + +impl + 'static> OscillatorSource +{ + pub fn new(nco: crate::synthesis::oscillator::Nco) -> (Self, In>) + { + let (output, signal) = stream(); + (Self { nco, output }, signal) + } +} + +impl + 'static> Block for OscillatorSource +{ + fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult + { + self.output.push_iter(&mut self.nco); + BlockResult::Ok + } +} + +#[derive(BlockIO)] +pub struct Nco + 'static> +{ + nco: crate::synthesis::oscillator::Nco, + + #[input] + frequency: In, + + #[output] + output: Out>, +} + +impl + 'static> Nco +{ + pub fn new( + input: In, + nco: crate::synthesis::oscillator::Nco, + ) -> (Self, In>) + { + let (output, signal) = stream(); + ( + Self { + nco, + frequency: input, + output, + }, + signal, + ) + } +} + +impl + 'static> Block for Nco +{ + fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult + { + self.output + .push_iter(&mut self.frequency.pop_iter().map(|f| { + self.nco.set_frequency(f); + self.nco.next().unwrap() + })); + BlockResult::Ok + } +} diff --git a/oxydsp-dsp/src/blocks/utilities.rs b/oxydsp-dsp/src/blocks/utilities.rs index 9708a97..46b77fc 100644 --- a/oxydsp-dsp/src/blocks/utilities.rs +++ b/oxydsp-dsp/src/blocks/utilities.rs @@ -1 +1,2 @@ +pub mod adapters; pub mod iter; diff --git a/oxydsp-dsp/src/blocks/utilities/adapters.rs b/oxydsp-dsp/src/blocks/utilities/adapters.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/oxydsp-dsp/src/blocks/utilities/adapters.rs @@ -0,0 +1 @@ + diff --git a/oxydsp-dsp/src/lib.rs b/oxydsp-dsp/src/lib.rs index 049a8aa..768c266 100644 --- a/oxydsp-dsp/src/lib.rs +++ b/oxydsp-dsp/src/lib.rs @@ -1 +1,10 @@ +use num::Float; + pub mod blocks; +pub mod synthesis; +pub mod units; + +fn map(x: T, x_min: T, x_max: T, o_min: T, o_max: T) -> T +{ + ((x - x_min) / (x_max - x_min)) * (o_max - o_min) + o_min +} diff --git a/oxydsp-dsp/src/synthesis.rs b/oxydsp-dsp/src/synthesis.rs new file mode 100644 index 0000000..a0bd267 --- /dev/null +++ b/oxydsp-dsp/src/synthesis.rs @@ -0,0 +1 @@ +pub mod oscillator; diff --git a/oxydsp-dsp/src/synthesis/oscillator.rs b/oxydsp-dsp/src/synthesis/oscillator.rs new file mode 100644 index 0000000..2f97630 --- /dev/null +++ b/oxydsp-dsp/src/synthesis/oscillator.rs @@ -0,0 +1,91 @@ +use std::f32::consts::PI; +use std::marker::PhantomData; + +use num::Complex; +use num::Float; + +use crate::map; +use crate::units::DigitalFrequency; +use crate::units::Phase; + +#[derive(Clone, Copy)] +pub struct Nco +{ + // Current phase angle of the oscillator + phase: usize, + + // "Phase derivative": How much to increase the phase per sample + d_phase: usize, + _phantom: PhantomData, +} + +impl Nco +{ + pub fn new(frequency: DigitalFrequency) -> Self + { + Self { + phase: 0, + d_phase: frequency.0, + _phantom: Default::default(), + } + } + + pub fn with_phase(frequency: DigitalFrequency, phase: Phase) -> Self + { + Self { + phase: phase.0.0, + d_phase: frequency.0, + _phantom: Default::default(), + } + } + + pub fn set_phase(&mut self, phase: Phase) + { + self.phase = phase.0.0; + } + + pub fn set_frequency(&mut self, frequency: DigitalFrequency) + { + self.d_phase = frequency.0; + } + + pub fn step(&mut self) + { + let _ = self.phase.overflowing_add(self.d_phase); + } +} + +impl> Nco +{ + pub fn sample(&self) -> Complex + { + let t = map( + >::from(self.phase as f32), + >::from(0.0f32), + >::from(usize::MAX as f32), + >::from(0.0f32), + >::from(2. * PI), + ); + Complex::new(t.cos(), t.sin()) + } +} + +impl> Iterator for Nco +{ + type Item = Complex; + + fn next(&mut self) -> Option + { + let s = self.sample(); + self.step(); + Some(s) + } +} + +impl From for Nco +{ + fn from(value: DigitalFrequency) -> Self + { + Nco::new(value) + } +} diff --git a/oxydsp-dsp/src/units.rs b/oxydsp-dsp/src/units.rs new file mode 100644 index 0000000..1e4f042 --- /dev/null +++ b/oxydsp-dsp/src/units.rs @@ -0,0 +1,38 @@ +use std::f64::consts::PI; + +use crate::map; + +// Represents digital frequency +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub struct DigitalFrequency(pub usize); + +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub struct Phase(pub DigitalFrequency); + +impl DigitalFrequency +{ + pub fn from_rad(radians_per_sample: f64) -> Self + { + // Frequnecy wraps arround : Going at 2 pi radians per second + // Is like not oscillating at all + let f = radians_per_sample.rem_euclid(radians_per_sample); + + // Then we map the [0, 2*PI] range into the 0 to usize range + DigitalFrequency(map(f, 0., 2. * PI, 0., usize::MAX as f64).floor() as usize) + } + + pub fn from_time_frequency(hertz: f64, sample_rate: f64) -> Self + { + Self::from_rad(map(hertz, 0., sample_rate, 0., 2. * PI)) + } + + pub fn as_rad(&self) -> f64 + { + map(self.0 as f64, 0., usize::MAX as f64, 0., 2. * PI) + } + + pub fn as_time_frequency(&self, sample_rate: f64) -> f64 + { + map(self.0 as f64, 0., usize::MAX as f64, 0., sample_rate) + } +} diff --git a/oxydsp-flowgraph/oxydsp-flowgraph-macros/src/block_io.rs b/oxydsp-flowgraph/oxydsp-flowgraph-macros/src/block_io.rs new file mode 100644 index 0000000..f8176e3 --- /dev/null +++ b/oxydsp-flowgraph/oxydsp-flowgraph-macros/src/block_io.rs @@ -0,0 +1,279 @@ +use proc_macro::TokenStream; +use zyn::ToTokens; +use zyn::ext::AttrExt; +use zyn::ext::FieldsExt; +use zyn::syn::Attribute; +use zyn::syn::Index; +use zyn::syn::spanned::Spanned; + +pub enum BlockIOPort +{ + Field(TokenStream), + Array(TokenStream, TokenStream), +} + +pub struct BlockIOPorts +{ + inputs: Vec, + outputs: Vec, +} + +pub fn parse_ports(fields: zyn::syn::Fields, attribute: &str) -> Vec +{ + let mut ports = vec![]; + let fields = fields.as_named().unwrap(); + + for field in fields.named.iter() + { + if field.attrs.iter().any(|attr| attr.is(attribute)) + { + ports.append(&mut parse_port(field.clone())); + } + } + + ports.iter().for_each(|x| match x + { + BlockIOPort::Field(token_stream) => println!("field: {}", token_stream), + BlockIOPort::Array(token_stream, token_stream1) => + { + println!("array: {} [{}]", token_stream, token_stream1) + } + }); + + ports +} + +pub fn parse_port(field: zyn::syn::Field) -> Vec +{ + match field.ty + { + zyn::syn::Type::Path(type_path) => + { + println!("{:?}", type_path.path); + assert!(type_path.path.is_ident("In") || type_path.path.is_ident("Out")); + vec![BlockIOPort::Field( + field.ident.unwrap().to_token_stream().into(), + )] + } + zyn::syn::Type::Tuple(type_tuple) => + { + let mut output = vec![]; + for (i, _) in type_tuple.elems.iter().enumerate() + { + output.push(BlockIOPort::Field( + zyn::zyn!({{field.ident.clone().unwrap()}}.{{ Index::from(i) }}) + .to_token_stream() + .into(), + )); + } + output + } + zyn::syn::Type::Array(type_array) => + { + let b = BlockIOPort::Array( + field.ident.clone().unwrap().to_token_stream().into(), + type_array.len.to_token_stream().into(), + ); + vec![b] + } + _ => panic!("Unsupported port type."), + } +} + +#[zyn::element] +pub fn block_io_set_index(fields: zyn::syn::Fields) -> zyn::TokenStream +{ + let fields = fields.as_named().unwrap().named.clone(); + zyn::zyn!( + fn set_index(&self, block_index: usize) + { + use oxydsp_flowgraph::edge::BlockIOIndex; + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("input"))).enumerate()) + { + self.{{field.1.ident}}.set_block_index(BlockIOIndex {block_index, port_index: {{ field.0 }} }); + } + + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) + { + self.{{field.1.ident}}.set_block_index(BlockIOIndex {block_index, port_index: {{ field.0 }} }); + } + } + ) +} + +#[zyn::element] +pub fn block_io_get_successors(fields: zyn::syn::Fields) -> zyn::TokenStream +{ + let fields = fields.as_named().unwrap().named.clone(); + zyn::zyn!( + fn get_successors(&self) -> Vec + { + let mut output = vec![]; + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) + { + if let Some(block_index) = self.{{ field.1.ident }}.get_consumer_block() + { + output.push(block_index); + } + } + output + } + ) +} + +#[zyn::element] +pub fn block_io_get_meta(ident: zyn::syn::Ident, fields: zyn::syn::Fields) -> zyn::TokenStream +{ + let fields = fields.as_named().unwrap().named.clone(); + zyn::zyn!( + fn get_block_name(&self) -> &'static str + { + return {{ ident.to_string() }}; + } + + fn get_input_names(&self) -> Vec<&'static str> + { + let mut output = Vec::new(); + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("input"))).enumerate()) + { + output.push({{ field.1.ident.clone().unwrap().to_string() }}); + } + return output; + } + fn get_output_names(&self) -> Vec<&'static str> + { + let mut output = Vec::new(); + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) + { + output.push({{ field.1.ident.clone().unwrap().to_string() }}); + } + return output; + } + + fn get_output_type_names(&self) -> Vec<&'static str> + { + let mut output = Vec::new(); + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) + { + output.push(self.{{ field.1.ident.clone() }}.get_type_name()); + } + return output; + } + ) +} + +#[zyn::element] +pub fn block_io_counts(fields: zyn::syn::Fields) -> zyn::TokenStream +{ + let fields = fields.as_named().unwrap().named.clone(); + let input_count = fields + .iter() + .filter(|x| x.attrs.iter().any(|x| x.is("input"))) + .count(); + let output_count = fields + .iter() + .filter(|x| x.attrs.iter().any(|x| x.is("output"))) + .count(); + zyn::zyn!( + fn input_count(&self) -> usize + { + return { { input_count } }; + } + + fn output_count(&self) -> usize + { + return { { output_count } }; + } + ) +} + +#[zyn::element(debug = "pretty")] +pub fn block_io_set_streams(fields: zyn::syn::Fields) -> zyn::TokenStream +{ + zyn::zyn!( + #[allow(unreachable_code)] + fn set_anonymous_out_stream( + &mut self, + output_index: usize, + producer: oxydsp_flowgraph::edge::AnonymousStreamProducer, + ) + { + match output_index + { + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) + { + {{ field.0 }} => self.{{field.1.ident}}.set_anonymous_stream(producer), + } + _ => panic!("output_index out of bounds.") + }; + } + + #[allow(unreachable_code)] + fn set_anonymous_in_stream(&mut self, input_index: usize, consumer: oxydsp_flowgraph::edge::AnonymousStreamConsumer) + { + match input_index + { + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("input"))).enumerate()) + { + {{ field.0 }} => self.{{field.1.ident}}.set_anonymous_stream(consumer), + } + _ => panic!("output_index out of bounds.") + }; + } + ) +} + +#[zyn::element] +pub fn block_io_create_stream(fields: zyn::syn::Fields) -> zyn::TokenStream +{ + zyn::zyn!( + #[allow(unreachable_code)] + fn create_anonymous_stream_for( + &mut self, + output_index: usize, + capacity: usize + ) -> (oxydsp_flowgraph::edge::AnonymousStreamProducer, oxydsp_flowgraph::edge::AnonymousStreamConsumer) + { + let (tx, rx): (oxydsp_flowgraph::edge::AnonymousStreamProducer, oxydsp_flowgraph::edge::AnonymousStreamConsumer) + = match output_index + { + @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) + { + {{ field.0 }} => + { + let (tx, rx) = oxydsp_flowgraph::stream::bounded_queue::<@out_inner_type(ty = field.1.ty.clone())>(capacity); + (tx.into(), rx.into()) + }, + } + _ => panic!("output_index out of bounds.") + }; + + (tx, rx) + } + ) +} + +#[zyn::element] +pub fn out_inner_type(ty: zyn::syn::Type) -> zyn::TokenStream +{ + let out_ty = match ty + { + zyn::syn::Type::Path(type_path) => match &type_path.path.segments.last().unwrap().arguments + { + zyn::syn::PathArguments::AngleBracketed(args) => match args.args.first().unwrap() + { + zyn::syn::GenericArgument::Type(x) => Some(x.clone().to_token_stream()), + _ => None, + }, + _ => None, + }, + _ => None, + }; + + if out_ty.is_none() + { + bail!("Output type must be a Out type."; span = ty.span()); + } + + out_ty.unwrap() +} diff --git a/oxydsp-flowgraph/oxydsp-flowgraph-macros/src/lib.rs b/oxydsp-flowgraph/oxydsp-flowgraph-macros/src/lib.rs index cbe97db..434f031 100644 --- a/oxydsp-flowgraph/oxydsp-flowgraph-macros/src/lib.rs +++ b/oxydsp-flowgraph/oxydsp-flowgraph-macros/src/lib.rs @@ -8,6 +8,9 @@ use zyn::syn::Index; use zyn::syn::Lit; use zyn::syn::spanned::Spanned; +mod block_io; +use crate::block_io::*; + #[zyn::derive("BlockIO", attributes(input, output), debug = "pretty")] pub fn block_io( #[zyn(input)] ident: zyn::Extract, @@ -15,6 +18,7 @@ pub fn block_io( #[zyn(input)] fields: zyn::Fields, ) -> zyn::TokenStream { + //parse_ports(fields.clone(), "input"); let ident = ident.inner(); let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); zyn::zyn!( @@ -31,204 +35,6 @@ pub fn block_io( ) } -#[zyn::element] -fn block_io_set_index(fields: zyn::syn::Fields) -> zyn::TokenStream -{ - let fields = fields.as_named().unwrap().named.clone(); - zyn::zyn!( - fn set_index(&self, block_index: usize) - { - use oxydsp_flowgraph::edge::BlockIOIndex; - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("input"))).enumerate()) - { - self.{{field.1.ident}}.set_block_index(BlockIOIndex {block_index, port_index: {{ field.0 }} }); - } - - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) - { - self.{{field.1.ident}}.set_block_index(BlockIOIndex {block_index, port_index: {{ field.0 }} }); - } - } - ) -} - -#[zyn::element] -fn block_io_get_successors(fields: zyn::syn::Fields) -> zyn::TokenStream -{ - let fields = fields.as_named().unwrap().named.clone(); - zyn::zyn!( - fn get_successors(&self) -> Vec - { - let mut output = vec![]; - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) - { - if let Some(block_index) = self.{{ field.1.ident }}.get_consumer_block() - { - output.push(block_index); - } - } - output - } - ) -} - -#[zyn::element] -fn block_io_get_meta(ident: zyn::syn::Ident, fields: zyn::syn::Fields) -> zyn::TokenStream -{ - let fields = fields.as_named().unwrap().named.clone(); - zyn::zyn!( - fn get_block_name(&self) -> &'static str - { - return {{ ident.to_string() }}; - } - - fn get_input_names(&self) -> Vec<&'static str> - { - let mut output = Vec::new(); - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("input"))).enumerate()) - { - output.push({{ field.1.ident.clone().unwrap().to_string() }}); - } - return output; - } - fn get_output_names(&self) -> Vec<&'static str> - { - let mut output = Vec::new(); - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) - { - output.push({{ field.1.ident.clone().unwrap().to_string() }}); - } - return output; - } - - fn get_output_type_names(&self) -> Vec<&'static str> - { - let mut output = Vec::new(); - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) - { - output.push(self.{{ field.1.ident.clone() }}.get_type_name()); - } - return output; - } - ) -} - -#[zyn::element] -fn block_io_counts(fields: zyn::syn::Fields) -> zyn::TokenStream -{ - let fields = fields.as_named().unwrap().named.clone(); - let input_count = fields - .iter() - .filter(|x| x.attrs.iter().any(|x| x.is("input"))) - .count(); - let output_count = fields - .iter() - .filter(|x| x.attrs.iter().any(|x| x.is("output"))) - .count(); - zyn::zyn!( - fn input_count(&self) -> usize - { - return { { input_count } }; - } - - fn output_count(&self) -> usize - { - return { { output_count } }; - } - ) -} - -#[zyn::element(debug = "pretty")] -fn block_io_set_streams(fields: zyn::syn::Fields) -> zyn::TokenStream -{ - zyn::zyn!( - #[allow(unreachable_code)] - fn set_anonymous_out_stream( - &mut self, - output_index: usize, - producer: oxydsp_flowgraph::edge::AnonymousStreamProducer, - ) - { - match output_index - { - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) - { - {{ field.0 }} => self.{{field.1.ident}}.set_anonymous_stream(producer), - } - _ => panic!("output_index out of bounds.") - }; - } - - #[allow(unreachable_code)] - fn set_anonymous_in_stream(&mut self, input_index: usize, consumer: oxydsp_flowgraph::edge::AnonymousStreamConsumer) - { - match input_index - { - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("input"))).enumerate()) - { - {{ field.0 }} => self.{{field.1.ident}}.set_anonymous_stream(consumer), - } - _ => panic!("output_index out of bounds.") - }; - } - ) -} - -#[zyn::element] -fn block_io_create_stream(fields: zyn::syn::Fields) -> zyn::TokenStream -{ - zyn::zyn!( - #[allow(unreachable_code)] - fn create_anonymous_stream_for( - &mut self, - output_index: usize, - capacity: usize - ) -> (oxydsp_flowgraph::edge::AnonymousStreamProducer, oxydsp_flowgraph::edge::AnonymousStreamConsumer) - { - let (tx, rx): (oxydsp_flowgraph::edge::AnonymousStreamProducer, oxydsp_flowgraph::edge::AnonymousStreamConsumer) - = match output_index - { - @for (field in fields.iter().filter(|x| x.attrs.iter().any(|x| x.is("output"))).enumerate()) - { - {{ field.0 }} => - { - let (tx, rx) = oxydsp_flowgraph::stream::bounded_queue::<@out_inner_type(ty = field.1.ty.clone())>(capacity); - (tx.into(), rx.into()) - }, - } - _ => panic!("output_index out of bounds.") - }; - - (tx, rx) - } - ) -} - -#[zyn::element] -fn out_inner_type(ty: zyn::syn::Type) -> zyn::TokenStream -{ - let out_ty = match ty - { - zyn::syn::Type::Path(type_path) => match &type_path.path.segments.last().unwrap().arguments - { - zyn::syn::PathArguments::AngleBracketed(args) => match args.args.first().unwrap() - { - zyn::syn::GenericArgument::Type(x) => Some(x.clone().to_token_stream()), - _ => None, - }, - _ => None, - }, - _ => None, - }; - - if out_ty.is_none() - { - bail!("Output type must be a Out type."; span = ty.span()); - } - - out_ty.unwrap() -} - // Sync block #[zyn::attribute]