lv2-atom-2.0.0/.cargo_vcs_info.json0000644000000001121374457216100125360ustar { "git": { "sha1": "c93bfe859a849c5189a27c62c659bd3b724a39b6" } } lv2-atom-2.0.0/Cargo.toml0000644000000022731374457216100105460ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "lv2-atom" version = "2.0.0" authors = ["Jan-Oliver 'Janonard' Opdenhövel "] description = "rust-lv2's Atom handling library" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/RustAudio/rust-lv2" [dependencies.lv2-core] version = "3.0.0" optional = true [dependencies.lv2-sys] version = "2.0.0" [dependencies.lv2-units] version = "0.1.3" [dependencies.urid] version = "0.1.0" [dev-dependencies.lv2-urid] version = "2.1.0" [features] default = ["lv2-core"] [badges.maintenance] status = "passively-maintained" [badges.travis-ci] branch = "master" repository = "RustAudio/rust-lv2" lv2-atom-2.0.0/Cargo.toml.orig010064400017500001750000000011641374457150200142340ustar 00000000000000[package] name = "lv2-atom" version = "2.0.0" authors = ["Jan-Oliver 'Janonard' Opdenhövel "] edition = "2018" license = "MIT OR Apache-2.0" description = "rust-lv2's Atom handling library" readme = "README.md" repository = "https://github.com/RustAudio/rust-lv2" [badges] travis-ci = { repository = "RustAudio/rust-lv2", branch = "master" } maintenance = { status = "passively-maintained" } [dependencies] lv2-sys = "2.0.0" lv2-units = "0.1.3" urid = "0.1.0" [dependencies.lv2-core] version = "3.0.0" optional = true [dev-dependencies] lv2-urid = "2.1.0" [features] default = ["lv2-core"]lv2-atom-2.0.0/LICENSE-APACHE010064400017500001731000000251371364504410600132710ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. lv2-atom-2.0.0/LICENSE-MIT010064400017500001731000000017761364504410600130040ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.lv2-atom-2.0.0/README.md010064400017500001750000000025741374457150200126320ustar 00000000000000# Rust-LV2's Atom handling library. A library for reading and writing [LV2's](https://lv2plug.in/) Atom type system, used by [`rust-lv2`](https://crates.io/crates/lv2), a safe, fast, and ergonomic framework to create [LV2 plugins](http://lv2plug.in/) for audio processing, written in Rust. LV2 has it's own type system to make data exchange between plugins as versatile and portable as possible. Basic integer and float types are supported as well as vectors, event sequences, and URID->Atom maps. ## Documentation The original LV2 API (in the `C` programming language) is documented by ["the LV2 book"](https://lv2plug.in/book/). This book is in the process of being translated to Rust along with the development of `rust-lv2` [(link)](https://janonard.github.io/rust-lv2-book/) and describes how to properly use `rust-lv2`. ## Features Like any other crate of `rust-lv2`, this crate has the optional `host` feature. Some of the types defined by some crates are only useful for testing or LV2 hosts. Since the goal of this framework is to provide an easy way to create plugins, these aren't necessary and therefore gated behind that feature. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option.lv2-atom-2.0.0/src/chunk.rs010064400017500001731000000066451364504410600136150ustar 00000000000000//! An atom containing memory of undefined type. //! //! This contents of this atom is considered as a simple blob of data. It used, for example, by the host to transmit the size of a writable atom port. Since it is so simple, it does not need a reading or writing parameter. //! //! # Example //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let in_chunk: &[u8] = ports.input.read(urids.chunk, ()).unwrap(); //! let mut out_chunk: FramedMutSpace = ports.output.init(urids.chunk, ()).unwrap(); //! //! out_chunk.write_raw(in_chunk, false).unwrap(); //! } //! ``` //! //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Chunk](http://lv2plug.in/ns/ext/atom/atom.html#Chunk) use crate::space::*; use crate::Atom; use urid::UriBound; /// An atom containing memory of undefined type. /// /// [See also the module documentation.](index.html) pub struct Chunk; unsafe impl UriBound for Chunk { const URI: &'static [u8] = sys::LV2_ATOM__Chunk; } impl<'a, 'b> Atom<'a, 'b> for Chunk where 'a: 'b, { type ReadParameter = (); type ReadHandle = &'a [u8]; type WriteParameter = (); type WriteHandle = FramedMutSpace<'a, 'b>; fn read(space: Space<'a>, _: ()) -> Option<&'a [u8]> { space.data() } fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { Some(frame) } } #[cfg(test)] mod tests { use crate::chunk::*; use crate::*; use std::mem::size_of; use urid::*; #[test] fn test_chunk_and_slice_writer() { const SLICE_LENGTH: usize = 42; let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing { let mut space = RootMutSpace::new(raw_space.as_mut()); let mut writer = (&mut space as &mut dyn MutSpace) .init(urids.chunk, ()) .unwrap(); for (i, value) in writer .allocate(SLICE_LENGTH - 1, false) .map(|(_, data)| data) .unwrap() .into_iter() .enumerate() { *value = i as u8; } (&mut writer as &mut dyn MutSpace) .write(&41u8, false) .unwrap(); } // verifying { let raw_space = raw_space.as_ref(); let (atom, data) = raw_space.split_at(size_of::()); let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; assert_eq!(atom.size as usize, SLICE_LENGTH); assert_eq!(atom.type_, urids.chunk.get()); let data = data.split_at(SLICE_LENGTH).0; for i in 0..SLICE_LENGTH { assert_eq!(data[i] as usize, i); } } // reading { let space = Space::from_reference(raw_space.as_ref()); let data = Chunk::read(space.split_atom_body(urids.chunk).unwrap().0, ()).unwrap(); assert_eq!(data.len(), SLICE_LENGTH); for (i, value) in data.iter().enumerate() { assert_eq!(*value as usize, i); } } } } lv2-atom-2.0.0/src/lib.rs010064400017500001750000000210551374457150200132510ustar 00000000000000//! General data IO for LV2 plugins. //! //! This crate empowers LV2 plugins to communicate using a common type system, which is defined in the [LV2 Atom Specification](http://lv2plug.in/ns/ext/atom/atom.html). Many plugin standards only provide audio and MIDI IO, but LV2 plugins can read and write anything from simple integers over vectors and strings to event sequences using this specification. //! //! # How to use atoms //! //! The foundation of this crate is the [`Atom`](trait.Atom.html) trait. This trait provides type definitions and methods to read and write atoms. However, you will never handle these types directly, only via handles and generics. //! //! Your entry point to the atom system are the [`PortReader`](port/struct.PortReader.html) and [`PortWriter`](port/struct.PortWriter.html) structs provided by your plugin's ports. If you provide them the URID of the desired atom type as a atom-specific parameter, they will try to retrieve a handle that either lets you access the contents of an atom or write additional data to it. This is a general pattern in this crate; you will encounter it several times. From there, you use the handles as documented. //! //! # Example //! //! ``` //! use lv2_atom::prelude::*; //! use lv2_core::prelude::*; //! use lv2_units::prelude::*; //! use urid::*; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! #[derive(URIDCollection)] //! struct MyURIDs { //! atom: AtomURIDCollection, //! units: UnitURIDCollection, //! } //! //! /// Something like a plugin's run method. //! fn run(ports: &mut MyPorts, urids: &MyURIDs) { //! // Get the read handle to the sequence. //! let input_sequence = ports.input.read( //! urids.atom.sequence, //! urids.units.beat //! ).unwrap(); //! //! // Get the write handle to the sequence. //! let mut output_sequence = ports.output.init( //! urids.atom.sequence, //! TimeStampURID::Frames(urids.units.frame) //! ).unwrap(); //! //! // Iterate through all events in the input sequence. //! for event in input_sequence { //! // An event contains a timestamp and an atom. //! let (timestamp, atom) = event; //! // If the read atom is a 32-bit integer... //! if let Some(integer) = atom.read(urids.atom.int, ()) { //! // Multiply it by two and write it to the sequence. //! output_sequence.init(timestamp, urids.atom.int, integer * 2).unwrap(); //! } else { //! // Forward the atom to the sequence without a change. //! output_sequence.forward(timestamp, atom).unwrap(); //! } //! } //! } //! ``` //! //! # Internals //! //! Internally, all atoms are powered by the structs in the [`space`](space/index.html) module. They safely abstract the reading and writing process and assure that no memory is improperly accessed or leaked and that alignments are upheld. If you simply want to use the atoms in this crate, you don't need to deal with. They are only interesting if you want to create your own atom types. extern crate lv2_sys as sys; extern crate lv2_units as units; pub mod chunk; pub mod object; pub mod scalar; pub mod sequence; pub mod space; pub mod string; pub mod tuple; pub mod vector; #[cfg(feature = "lv2-core")] pub mod port; /// Prelude of `lv2_atom` for wildcard usage. pub mod prelude { use crate::*; pub use crate::{Atom, AtomURIDCollection, UnidentifiedAtom}; pub use chunk::Chunk; pub use object::{Object, ObjectHeader, PropertyHeader}; pub use port::AtomPort; pub use scalar::{AtomURID, Bool, Double, Float, Int, Long}; pub use sequence::{Sequence, TimeStamp, TimeStampURID}; pub use space::{FramedMutSpace, MutSpace, Space}; pub use string::{Literal, LiteralInfo, String}; pub use tuple::Tuple; pub use vector::Vector; } use space::*; use urid::*; #[derive(Clone, URIDCollection)] /// Collection with the URIDs of all `UriBound`s in this crate. pub struct AtomURIDCollection { pub blank: URID, pub double: URID, pub float: URID, pub int: URID, pub long: URID, pub urid: URID, pub bool: URID, vector: URID>, pub chunk: URID, pub literal: URID, pub object: URID, pub property: URID, pub string: URID, pub tuple: URID, pub sequence: URID, } impl AtomURIDCollection { pub fn vector(&self) -> URID> { unsafe { URID::new_unchecked(self.vector.get()) } } } /// Atom type. /// /// This is the foundation of this crate: Types that implement `Atom` define the reading and writing functions for an atom type. However, these types will never be constructed; They are only names to be used for generic type arguments. /// /// This trait has two lifetime parameters: The first one is the lifetime of the atom in memory. In practice, this will often be `'static`, but it's good to keep it generic for testing purposes. The second parameter is the lifetime of the `MutSpace` borrowed by the `FramedMutSpace` parameter in the `write` method. Since the `WriteParameter` may contain this `FramedMutSpace`, it has to be assured that it lives long enough. Since the referenced `MutSpace` also has to borrow the atom, it may not live longer than the atom. pub trait Atom<'a, 'b>: UriBound where 'a: 'b, { /// The atom-specific parameter of the `read` function. /// /// If your atom does not need a reading parameter, you may set it to `()`. type ReadParameter; /// The return value of the `read` function. /// /// It may contain a reference to the atom and therefore may not outlive it. type ReadHandle: 'a; /// The atom-specific parameter of the `write` function. /// /// If your atom does not need a writing parameter, you may set it to `()`. type WriteParameter; /// The return value of the `write` function. /// /// It may contain a reference to a `MutSpace` and therefore may not outlive it. type WriteHandle: 'b; /// Read the body of the atom. /// /// The passed space exactly covers the body of the atom, excluding the header. You may assume that the body is actually of your atom type, since the URID of the atom was checked beforehand. /// /// If the atom is malformed, you may not panic and return `None` instead. fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option; /// Initialize the body of the atom. /// /// In this method, the atom is prepared for the writing handle. Usually, the atom will not be /// valid when initializied; Users have to use the write handle to make it valid. /// /// The frame of the atom was already initialized, containing the URID. /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. fn init( frame: FramedMutSpace<'a, 'b>, parameter: Self::WriteParameter, ) -> Option; } /// An atom of yet unknown type. /// /// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it. #[derive(Clone, Copy)] pub struct UnidentifiedAtom<'a> { space: Space<'a>, } impl<'a> UnidentifiedAtom<'a> { /// Construct a new unidentified atom. /// /// The space actually has to contain an atom. If it doesn't, crazy (but not undefined) things can happen. pub fn new(space: Space<'a>) -> Self { Self { space } } /// Try to read the atom. /// /// To identify the atom, it's URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. pub fn read<'b, A: Atom<'a, 'b>>( self, urid: URID, parameter: A::ReadParameter, ) -> Option { self.space .split_atom_body(urid) .map(|(body, _)| body) .and_then(|body| A::read(body, parameter)) } /// Retrieve the type URID of the atom. /// /// This can be used to identify atoms without actually reading them. pub fn type_urid(self) -> Option { self.space .split_type::() .and_then(|(header, _)| URID::new(header.type_)) } } lv2-atom-2.0.0/src/object.rs010064400017500001750000000336041374457150200137540ustar 00000000000000//! An atom containing multiple key-value pairs. //! //! This module is centered on the [`Object`](struct.Object.html) atom type. An object is the atomized form of an RDF instance: It has an (optional) id, a type and multiple properties declared as URID/Atom pairs. Both the id and the type are URIDs too. //! //! # Example //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! use urid::*; //! //! #[uri("urn:object-class")] //! struct ObjectClass; //! //! #[uri("urn:property-a")] //! struct PropertyA; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! #[derive(URIDCollection)] //! struct MyURIDs { //! atom: AtomURIDCollection, //! object_class: URID, //! property_a: URID, //! } //! //! fn run(ports: &mut MyPorts, urids: &MyURIDs) { //! // Create the reading handle. //! // We don't need the header now. //! let (_header, object_reader) = ports.input.read(urids.atom.object, ()).unwrap(); //! //! /// Iterate through all properties of the object. //! for (property_header, atom) in object_reader { //! // If the property is an integer... //! if let Some(integer) = atom.read(urids.atom.int, ()) { //! // Print it! //! println!( //! "Property No. {} has integer value {}", //! property_header.key.get(), //! integer //! ); //! } else { //! // Print that is not an integer. //! println!( //! "Property No. {} is not an integer", //! property_header.key.get() //! ); //! } //! } //! //! // Initialize the object. //! let mut object_writer = ports.output.init( //! urids.atom.object, //! ObjectHeader { //! id: None, //! otype: urids.object_class.into_general(), //! } //! ).unwrap(); //! //! // Write a property to the object. //! object_writer.init(urids.property_a, urids.atom.int, 42).unwrap(); //! } //! ``` //! //! # Specification //! [http://lv2plug.in/ns/ext/atom/atom.html#Object](http://lv2plug.in/ns/ext/atom/atom.html#Object). use crate::space::*; use crate::*; use std::convert::TryFrom; use std::iter::Iterator; use urid::UriBound; use urid::URID; /// An atom containing multiple key-value pairs. /// /// [See also the module documentation.](index.html) pub struct Object; unsafe impl UriBound for Object { const URI: &'static [u8] = sys::LV2_ATOM__Object; } /// Information about an object atom. pub struct ObjectHeader { /// The id of the object to distinguish different objects of the same type. /// /// If you don't need it, you should set it to `None`. pub id: Option, /// The type of the object (same as `rdf:type`). pub otype: URID, } impl<'a, 'b> Atom<'a, 'b> for Object where 'a: 'b, { type ReadParameter = (); type ReadHandle = (ObjectHeader, ObjectReader<'a>); type WriteParameter = ObjectHeader; type WriteHandle = ObjectWriter<'a, 'b>; fn read(body: Space<'a>, _: ()) -> Option<(ObjectHeader, ObjectReader<'a>)> { let (header, body) = body.split_type::()?; let header = ObjectHeader { id: URID::try_from(header.id).ok(), otype: URID::try_from(header.otype).ok()?, }; let reader = ObjectReader { space: body }; Some((header, reader)) } fn init( mut frame: FramedMutSpace<'a, 'b>, header: ObjectHeader, ) -> Option> { { let frame = &mut frame as &mut dyn MutSpace; frame.write( &sys::LV2_Atom_Object_Body { id: header.id.map(|urid| urid.get()).unwrap_or(0), otype: header.otype.get(), }, true, ); } Some(ObjectWriter { frame }) } } /// Alias of `Object`, used by older hosts. /// /// A blank object is an object that isn't an instance of a class. The [specification recommends](https://lv2plug.in/ns/ext/atom/atom.html#Blank) to use an [`Object`](struct.Object.html) with an id of `None`, but some hosts still use it and therefore, it's included in this library. /// /// If you want to read an object, you should also support `Blank`s, but if you want to write an object, you should always use `Object`. pub struct Blank; unsafe impl UriBound for Blank { const URI: &'static [u8] = sys::LV2_ATOM__Blank; } impl<'a, 'b> Atom<'a, 'b> for Blank where 'a: 'b, { type ReadParameter = >::ReadParameter; type ReadHandle = >::ReadHandle; type WriteParameter = >::WriteParameter; type WriteHandle = >::WriteHandle; #[allow(clippy::unit_arg)] fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option { Object::read(body, parameter) } fn init( frame: FramedMutSpace<'a, 'b>, parameter: Self::WriteParameter, ) -> Option { Object::init(frame, parameter) } } /// An iterator over all properties in an object. /// /// Each iteration item is the header of the property, as well as the space occupied by the value atom. You can use normal `read` methods on the returned space. pub struct ObjectReader<'a> { space: Space<'a>, } impl<'a> Iterator for ObjectReader<'a> { type Item = (PropertyHeader, UnidentifiedAtom<'a>); fn next(&mut self) -> Option<(PropertyHeader, UnidentifiedAtom<'a>)> { let (header, value, space) = Property::read_body(self.space)?; self.space = space; Some((header, UnidentifiedAtom::new(value))) } } /// Writing handle for object properties. /// /// This handle is a safeguard to assure that a object is always a series of properties. pub struct ObjectWriter<'a, 'b> { frame: FramedMutSpace<'a, 'b>, } impl<'a, 'b> ObjectWriter<'a, 'b> { /// Initialize a new property with a context. /// /// This method does the same as [`init`](#method.init), but also sets the context URID. pub fn init_with_context<'c, K: ?Sized, T: ?Sized, A: Atom<'a, 'c>>( &'c mut self, key: URID, context: URID, child_urid: URID, parameter: A::WriteParameter, ) -> Option { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; (&mut self.frame as &mut dyn MutSpace).init(child_urid, parameter) } /// Initialize a new property. /// /// This method writes out the header of a property and returns a reference to the space, so the property values can be written. /// /// Properties also have a context URID internally, which is rarely used. If you want to add one, use [`init_with_context`](#method.init_with_context). pub fn init<'c, K: ?Sized, A: Atom<'a, 'c>>( &'c mut self, key: URID, child_urid: URID, parameter: A::WriteParameter, ) -> Option { Property::write_header::(&mut self.frame, key, None)?; (&mut self.frame as &mut dyn MutSpace).init(child_urid, parameter) } } /// An atom containing a key-value pair. /// /// A property represents a single URID -> atom mapping. Additionally and optionally, you may also define a context in which the property is valid. For more information, visit the [specification](http://lv2plug.in/ns/ext/atom/atom.html#Property). /// /// Most of the time, properties are a part of an [`Object`](struct.Object.html) atom and therefore, you don't need to read or write them directly. However, they could in theory appear on their own too, which is why reading and writing methods are still provided. pub struct Property; unsafe impl UriBound for Property { const URI: &'static [u8] = sys::LV2_ATOM__Property; } /// Information about a property atom. #[derive(Clone, Copy)] pub struct PropertyHeader { /// The key of the property. pub key: URID, /// URID of the context (generally `None`). pub context: Option, } impl Property { /// Read the body of a property atom from a space. /// /// This method assumes that the space actually contains the body of a property atom, without the header. It returns the property header, containing the key and optional context of the property, the body of the actual atom, and the space behind the atom. fn read_body(space: Space) -> Option<(PropertyHeader, Space, Space)> { #[repr(C)] #[derive(Clone, Copy)] /// A custom version of the property body that does not include the value atom header. /// /// We will retrieve it separately. struct StrippedPropertyBody { key: u32, context: u32, } let (header, space) = space.split_type::()?; let header = PropertyHeader { key: URID::try_from(header.key).ok()?, context: URID::try_from(header.context).ok(), }; let (atom, space) = space.split_atom()?; Some((header, atom, space)) } /// Write out the header of a property atom. /// /// This method simply writes out the content of the header to the space and returns `Some(())` if it's successful. fn write_header( space: &mut dyn MutSpace, key: URID, context: Option>, ) -> Option<()> { space.write(&key.get(), true)?; space.write(&context.map(|urid| urid.get()).unwrap_or(0), false)?; Some(()) } } #[cfg(test)] mod tests { use crate::prelude::*; use crate::space::*; use std::mem::size_of; use urid::*; #[test] fn test_object() { let map = HashURIDMapper::new(); let urids = AtomURIDCollection::from_map(&map).unwrap(); let object_type = map .map_uri(Uri::from_bytes_with_nul(b"urn:my-type\0").unwrap()) .unwrap(); let first_key = map .map_uri(Uri::from_bytes_with_nul(b"urn:value-a\0").unwrap()) .unwrap(); let first_value: i32 = 17; let second_key = map .map_uri(Uri::from_bytes_with_nul(b"urn:value-b\0").unwrap()) .unwrap(); let second_value: f32 = 42.0; let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing { let mut space = RootMutSpace::new(raw_space.as_mut()); let frame = FramedMutSpace::new(&mut space as &mut dyn MutSpace, urids.object).unwrap(); let mut writer = Object::init( frame, ObjectHeader { id: None, otype: object_type, }, ) .unwrap(); { writer.init(first_key, urids.int, first_value).unwrap(); } { writer.init(second_key, urids.float, second_value).unwrap(); } } // verifying { // Header let (atom, space) = raw_space.split_at(size_of::()); let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; assert_eq!(atom.type_, urids.object); assert_eq!( atom.size as usize, size_of::() + size_of::() + 2 * size_of::() + size_of::() + size_of::() ); // Object. let (object, space) = space.split_at(size_of::()); let object = unsafe { &*(object.as_ptr() as *const sys::LV2_Atom_Object_Body) }; assert_eq!(object.id, 0); assert_eq!(object.otype, object_type); // First property. let (property, space) = space.split_at(size_of::()); let property = unsafe { &*(property.as_ptr() as *const sys::LV2_Atom_Property_Body) }; assert_eq!(property.key, first_key); assert_eq!(property.context, 0); assert_eq!(property.value.type_, urids.int); assert_eq!(property.value.size as usize, size_of::()); let (value, space) = space.split_at(size_of::()); let value = unsafe { *(value.as_ptr() as *const i32) }; assert_eq!(value, first_value); let (_, space) = space.split_at(size_of::()); // Second property. let (property, space) = space.split_at(size_of::()); let property = unsafe { &*(property.as_ptr() as *const sys::LV2_Atom_Property_Body) }; assert_eq!(property.key, second_key); assert_eq!(property.context, 0); assert_eq!(property.value.type_, urids.float); assert_eq!(property.value.size as usize, size_of::()); let (value, _) = space.split_at(size_of::()); let value = unsafe { *(value.as_ptr() as *const f32) }; assert_eq!(value, second_value); } // reading { let space = Space::from_slice(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.object).unwrap(); let (header, iter) = Object::read(body, ()).unwrap(); assert_eq!(header.otype, object_type); assert_eq!(header.id, None); let properties: Vec<(PropertyHeader, UnidentifiedAtom)> = iter.collect(); let (header, atom) = properties[0]; assert_eq!(header.key, first_key); assert_eq!(atom.read::(urids.int, ()).unwrap(), first_value); let (header, atom) = properties[1]; assert_eq!(header.key, second_key); assert_eq!(atom.read::(urids.float, ()).unwrap(), second_value); } } } lv2-atom-2.0.0/src/port.rs010064400017500001731000000117551364504410600134670ustar 00000000000000//! Integration for plugin ports. //! //! This module contains a `PortType` for plugin ports that supports atom IO. This will most common //! way to use atoms and is also used in most examples. //! //! # Example //! //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! /// Something like a plugin's run method. //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! // Read an integer from the port and print it. //! println!("My input is: {}", ports.input.read(urids.int, ()).unwrap()); //! // Write the integer `42` to the port. //! ports.output.init(urids.int, 42).unwrap(); //! } //! ``` use crate::space::*; use lv2_core::port::PortType; use std::ffi::c_void; use std::ptr::NonNull; use urid::URID; /// A handle to read atoms from a port. /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to read atoms. pub struct PortReader<'a> { space: Space<'a>, } impl<'a> PortReader<'a> { /// Create a new port reader. fn new(space: Space<'a>) -> Self { Self { space } } /// Read an atom. /// /// In order to identify the atom, the reader needs to know it's URID. Also, some atoms require a parameter. However, you can simply pass `()` in most cases. /// /// This method returns `None` if the atom is malformed or simply isn't of the specified type. pub fn read<'b, A: crate::Atom<'a, 'b>>( &'b self, urid: URID, parameter: A::ReadParameter, ) -> Option { A::read(self.space.split_atom_body(urid)?.0, parameter) } } /// A handle to write atoms into a port. /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to write atoms. pub struct PortWriter<'a> { space: RootMutSpace<'a>, has_been_written: bool, } impl<'a> PortWriter<'a> { /// Create a new port writer. fn new(space: RootMutSpace<'a>) -> Self { Self { space, has_been_written: false, } } /// Write an atom. /// /// In order to write an atom to a port, you need to pass the URID of the atom and an atom-specific parameter. /// /// Please note that you can call this method once only, because any atoms written behind the first one will not be identified. /// /// This method returns `None` if the space of the port isn't big enough or if the method was called multiple times. pub fn init<'b, A: crate::Atom<'a, 'b>>( &'b mut self, urid: URID, parameter: A::WriteParameter, ) -> Option { if !self.has_been_written { self.has_been_written = true; (&mut self.space as &mut dyn MutSpace).init(urid, parameter) } else { None } } } /// The port type for Atom IO. /// /// Port types should not include `Port`, but in this case it is needed since it would collide with the `Atom` trait. Therefore, this port type is named `AtomPort`. /// /// [See also the module documentation.](index.html) pub struct AtomPort; impl PortType for AtomPort { type InputPortType = PortReader<'static>; type OutputPortType = PortWriter<'static>; unsafe fn input_from_raw(pointer: NonNull, _sample_count: u32) -> PortReader<'static> { let space = Space::from_atom(pointer.cast().as_ref()); PortReader::new(space) } unsafe fn output_from_raw(pointer: NonNull, _sample_count: u32) -> PortWriter<'static> { let space = RootMutSpace::from_atom(pointer.cast().as_mut()); PortWriter::new(space) } } #[cfg(test)] mod tests { use crate::prelude::*; use crate::space::*; use lv2_core::prelude::*; use std::mem::size_of; use std::ptr::NonNull; use urid::*; #[test] fn test_atom_port() { let map = HashURIDMapper::new(); let urids = AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing a chunk to indicate the size of the space. { let mut space = RootMutSpace::new(raw_space.as_mut()); let mut writer = (&mut space as &mut dyn MutSpace) .init(urids.chunk, ()) .unwrap(); writer .allocate(256 - size_of::(), false) .unwrap(); } // Getting a writer with the port. { let mut writer = unsafe { AtomPort::output_from_raw(NonNull::from(raw_space.as_mut()).cast(), 0) }; writer.init::(urids.int, 42).unwrap(); } // Reading { let reader = unsafe { AtomPort::input_from_raw(NonNull::from(raw_space.as_mut()).cast(), 0) }; assert_eq!(reader.read::(urids.int, ()).unwrap(), 42); } } } lv2-atom-2.0.0/src/scalar.rs010064400017500001731000000145611364504410600137460ustar 00000000000000//! Scalar, single-value atoms. //! //! These atoms are the simplest of them all: They are simply represented by an internal type and their values can simply be copied. Due to this common behaviour, there is another trait called [`ScalarAtom`](trait.ScalarAtom.html) which provides this behaviour. Every type that implements `ScalarAtom` also implements `Atom`. //! //! Unlike other atoms, scalars do not need to be written after the initialization. However, you still can modify the scalar after it was initialized. //! //! # Example //! //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! /// Something like a plugin's run method. //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! // Scalar atoms don't need a reading parameter. //! let read_value: f32 = ports.input.read(urids.float, ()).unwrap(); //! //! // Writing is done with the value of the atom. //! // You can modify it afterwards. //! let written_value: &mut f32 = ports.output.init(urids.float, 17.0).unwrap(); //! } //! ``` //! //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Number](http://lv2plug.in/ns/ext/atom/atom.html#Number) use crate::space::*; use crate::*; use std::marker::Unpin; use urid::UriBound; use urid::URID; /// An atom that only contains a single, scalar value. /// /// Since scalar values are so simple, the reading and writing methods are exactly the same. pub trait ScalarAtom: UriBound { /// The internal representation of the atom. /// /// For example, the `Int` atom has the internal type of `i32`, which is `i32` on most platforms. type InternalType: Unpin + Copy + Send + Sync + Sized + 'static; /// Try to read the atom from a space. /// /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. fn read_scalar(body: Space) -> Option { body.split_type::() .map(|(value, _)| *value) } /// Try to write the atom into a space. /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. fn write_scalar<'a, 'b>( mut frame: FramedMutSpace<'a, 'b>, value: Self::InternalType, ) -> Option<&'a mut Self::InternalType> { (&mut frame as &mut dyn MutSpace).write(&value, true) } } impl<'a, 'b, A: ScalarAtom> Atom<'a, 'b> for A where 'a: 'b, { type ReadParameter = (); type ReadHandle = A::InternalType; type WriteParameter = A::InternalType; type WriteHandle = &'a mut A::InternalType; fn read(body: Space<'a>, _: ()) -> Option { ::read_scalar(body) } fn init( frame: FramedMutSpace<'a, 'b>, value: A::InternalType, ) -> Option<&'a mut A::InternalType> { ::write_scalar(frame, value) } } /// Macro to atomate the definition of scalar atoms. macro_rules! make_scalar_atom { ($atom:ty, $internal:ty, $uri:expr, $urid:expr) => { unsafe impl UriBound for $atom { const URI: &'static [u8] = $uri; } impl ScalarAtom for $atom { type InternalType = $internal; } }; } /// A scalar atom containing a `f64` (`f64` on most platforms). pub struct Double; make_scalar_atom!( Double, f64, sys::LV2_ATOM__Double, |urids: &AtomURIDCollection| urids.double ); /// A scalar atom containing a `f32` (`f32` on most platforms). pub struct Float; make_scalar_atom!( Float, f32, sys::LV2_ATOM__Float, |urids: &AtomURIDCollection| { urids.float } ); /// A scalar atom containing a `i64` (`i64` on most platforms). pub struct Long; make_scalar_atom!( Long, i64, sys::LV2_ATOM__Long, |urids: &AtomURIDCollection| { urids.long } ); /// A scalar atom containing a `i32` (`i32` on most platforms). pub struct Int; make_scalar_atom!( Int, i32, sys::LV2_ATOM__Int, |urids: &AtomURIDCollection| { urids.int } ); /// A scalar atom representing a boolean. /// /// Internally, this atom is represented by a `i32`, which is `==0` for `false` and `>= 1` for `true` pub struct Bool; make_scalar_atom!( Bool, i32, sys::LV2_ATOM__Bool, |urids: &AtomURIDCollection| { urids.bool } ); /// A scalar atom containing a URID. pub struct AtomURID; make_scalar_atom!( AtomURID, URID, sys::LV2_ATOM__URID, |urids: &AtomURIDCollection| urids.urid ); #[cfg(test)] mod tests { use crate::prelude::*; use crate::scalar::ScalarAtom; use crate::space::*; use std::convert::TryFrom; use std::mem::size_of; use urid::*; fn test_scalar(value: A::InternalType) where A::InternalType: PartialEq, A::InternalType: std::fmt::Debug, { let map = HashURIDMapper::new(); let urid: URID = map.map_type().unwrap(); let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing { let mut space = RootMutSpace::new(raw_space.as_mut()); (&mut space as &mut dyn MutSpace).init(urid, value).unwrap(); } // verifying { /// Generic version of the scalar atom structs. #[repr(C)] struct Scalar { atom: sys::LV2_Atom, body: B, } let (scalar, _) = raw_space.split_at(size_of::()); let scalar = unsafe { &*(scalar.as_ptr() as *const Scalar) }; assert_eq!(scalar.atom.type_, urid); assert_eq!(scalar.atom.size as usize, size_of::()); assert_eq!(scalar.body, value); } // reading { let space = Space::from_slice(raw_space.as_ref()); let (body, _) = space.split_atom_body(urid).unwrap(); assert_eq!(A::read(body, ()).unwrap(), value); } } #[test] fn test_scalars() { test_scalar::(42.0); test_scalar::(42.0); test_scalar::(42); test_scalar::(42); test_scalar::(1); test_scalar::(URID::try_from(1).unwrap()); } } lv2-atom-2.0.0/src/sequence.rs010064400017500001731000000305561364504410600143130ustar 00000000000000//! An atom containing a sequence of time-stamped events. //! //! These events are atoms again. Atoms passed in a sequence can be handled with frame-perfect timing and therefore is the prefered way to transmit events like MIDI messages. However, MIDI messages are implemented in separate crate. //! //! # Example //! //! ``` //! use lv2_core::prelude::*; //! use lv2_units::prelude::*; //! use lv2_atom::prelude::*; //! use lv2_atom::sequence::*; //! use urid::*; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! #[derive(URIDCollection)] //! struct MyURIDs { //! atom: AtomURIDCollection, //! units: UnitURIDCollection, //! } //! //! /// Something like a plugin's run method. //! fn run(ports: &mut MyPorts, urids: &MyURIDs) { //! // Get the read handle to the sequence. //! // The reading method needs the URID of the BPM unit to tell if the time stamp //! // is measured in beats or in frames. If the atom doesn't says that it's measured //! // in beats, it is assumed that it is measured in frames. //! let input_sequence: SequenceIterator = ports.input.read( //! urids.atom.sequence, //! urids.units.beat //! ).unwrap(); //! //! // Get the write handle to the sequence. //! // You have to provide the unit of the time stamps. //! let mut output_sequence: SequenceWriter = ports.output.init( //! urids.atom.sequence, //! TimeStampURID::Frames(urids.units.frame) //! ).unwrap(); //! //! // Iterate through all events in the input sequence. //! // //! // The specifications don't require the time stamps to be monotonic, your algorithms should //! // be able to handle older events written after younger events. //! // //! // The sequence writer, however, assures that the written time stamps are monotonic. //! for event in input_sequence { //! // An event contains a timestamp and an atom. //! let (timestamp, atom): (TimeStamp, UnidentifiedAtom) = event; //! // If the read atom is a 32-bit integer... //! if let Some(integer) = atom.read(urids.atom.int, ()) { //! // Multiply it by two and write it to the sequence. //! output_sequence.init(timestamp, urids.atom.int, integer * 2).unwrap(); //! } else { //! // Forward the atom to the sequence without a change. //! output_sequence.forward(timestamp, atom).unwrap(); //! } //! } //! } //! ``` //! //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence) use crate::space::*; use crate::*; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; use units::prelude::*; use urid::*; /// An atom containing a sequence of time-stamped events. /// /// [See also the module documentation.](index.html) pub struct Sequence; unsafe impl UriBound for Sequence { const URI: &'static [u8] = sys::LV2_ATOM__Sequence; } impl<'a, 'b> Atom<'a, 'b> for Sequence where 'a: 'b, { type ReadParameter = URID; type ReadHandle = SequenceIterator<'a>; type WriteParameter = TimeStampURID; type WriteHandle = SequenceWriter<'a, 'b>; fn read(body: Space, bpm_urid: URID) -> Option { let (header, body) = body.split_type::()?; let unit = if header.unit == bpm_urid { TimeStampUnit::BeatsPerMinute } else { TimeStampUnit::Frames }; Some(SequenceIterator { space: body, unit }) } fn init( mut frame: FramedMutSpace<'a, 'b>, unit: TimeStampURID, ) -> Option> { { let frame = &mut frame as &mut dyn MutSpace; let header = sys::LV2_Atom_Sequence_Body { unit: match unit { TimeStampURID::BeatsPerMinute(urid) => urid.get(), TimeStampURID::Frames(urid) => urid.get(), }, pad: 0, }; frame.write(&header, true)?; } Some(SequenceWriter { frame, unit: unit.into(), last_stamp: None, }) } } /// The measuring units of time stamps. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum TimeStampUnit { Frames, BeatsPerMinute, } /// An event time stamp. #[derive(Clone, Copy, Debug)] pub enum TimeStamp { Frames(i64), BeatsPerMinute(f64), } /// The measuring units of time stamps, with their URIDs. #[derive(Clone, Copy)] pub enum TimeStampURID { Frames(URID), BeatsPerMinute(URID), } impl From for TimeStampUnit { fn from(urid: TimeStampURID) -> TimeStampUnit { match urid { TimeStampURID::Frames(_) => TimeStampUnit::Frames, TimeStampURID::BeatsPerMinute(_) => TimeStampUnit::BeatsPerMinute, } } } impl TimeStamp { pub fn as_frames(self) -> Option { match self { Self::Frames(frame) => Some(frame), _ => None, } } pub fn as_bpm(self) -> Option { match self { Self::BeatsPerMinute(bpm) => Some(bpm), _ => None, } } } /// An iterator over all events in a sequence. pub struct SequenceIterator<'a> { space: Space<'a>, unit: TimeStampUnit, } impl<'a> SequenceIterator<'a> { pub fn unit(&self) -> TimeStampUnit { self.unit } } impl<'a> Iterator for SequenceIterator<'a> { type Item = (TimeStamp, UnidentifiedAtom<'a>); fn next(&mut self) -> Option<(TimeStamp, UnidentifiedAtom<'a>)> { let (raw_stamp, space) = self.space.split_type::()?; let stamp = match self.unit { TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, TimeStampUnit::BeatsPerMinute => unsafe { TimeStamp::BeatsPerMinute(raw_stamp.beats) }, }; let (atom, space) = space.split_atom()?; self.space = space; Some((stamp, UnidentifiedAtom::new(atom))) } } /// The writing handle for sequences. pub struct SequenceWriter<'a, 'b> { frame: FramedMutSpace<'a, 'b>, unit: TimeStampUnit, last_stamp: Option, } impl<'a, 'b> SequenceWriter<'a, 'b> { /// Write out the time stamp and update `last_stamp`. /// /// This method returns `Ǹone` if: /// * The time stamp is not measured in our unit. /// * The last time stamp is younger than the time stamp. /// * Space is insufficient. fn write_time_stamp(&mut self, stamp: TimeStamp) -> Option<()> { let raw_stamp = match self.unit { TimeStampUnit::Frames => { let frames = stamp.as_frames()?; if let Some(last_stamp) = self.last_stamp { if last_stamp.as_frames().unwrap() > frames { return None; } } RawTimeStamp { frames } } TimeStampUnit::BeatsPerMinute => { let beats = stamp.as_bpm()?; if let Some(last_stamp) = self.last_stamp { if last_stamp.as_bpm().unwrap() > beats { return None; } } RawTimeStamp { beats } } }; self.last_stamp = Some(stamp); (&mut self.frame as &mut dyn MutSpace) .write(&raw_stamp, true) .map(|_| ()) } /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. pub fn init<'c, A: Atom<'a, 'c>>( &'c mut self, stamp: TimeStamp, urid: URID, parameter: A::WriteParameter, ) -> Option { self.write_time_stamp(stamp)?; (&mut self.frame as &mut dyn MutSpace).init(urid, parameter) } /// Forward an unidentified atom to the sequence. /// /// If your cannot identify the type of the atom but have to write it, you can simply forward it. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. pub fn forward(&mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> { let data = atom.space.data()?; self.write_time_stamp(stamp)?; self.frame.write_raw(data, true).map(|_| ()) } } #[cfg(test)] mod tests { use crate::prelude::*; use crate::sequence::*; use std::mem::size_of; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; #[derive(URIDCollection)] struct TestURIDCollection { atom: AtomURIDCollection, units: UnitURIDCollection, } #[test] fn test_sequence() { let map = HashURIDMapper::new(); let urids = TestURIDCollection::from_map(&map).unwrap(); let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing { let mut space = RootMutSpace::new(raw_space.as_mut()); let mut writer = (&mut space as &mut dyn MutSpace) .init( urids.atom.sequence, TimeStampURID::Frames(urids.units.frame), ) .unwrap(); writer .init::(TimeStamp::Frames(0), urids.atom.int, 42) .unwrap(); writer .init::(TimeStamp::Frames(1), urids.atom.long, 17) .unwrap(); } // verifying { let (sequence, space) = raw_space.split_at(size_of::()); let sequence = unsafe { &*(sequence.as_ptr() as *const sys::LV2_Atom_Sequence) }; assert_eq!(sequence.atom.type_, urids.atom.sequence); assert_eq!( sequence.atom.size as usize, size_of::() + size_of::() + size_of::() + 4 + size_of::() + size_of::() ); assert_eq!(sequence.body.unit, urids.units.frame); let (stamp, space) = space.split_at(size_of::()); let stamp = unsafe { *(stamp.as_ptr() as *const RawTimeStamp) }; assert_eq!(unsafe { stamp.frames }, 0); let (int, space) = space.split_at(size_of::()); let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Int) }; assert_eq!(int.atom.type_, urids.atom.int); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); let (_, space) = space.split_at(4); let (stamp, space) = space.split_at(size_of::()); let stamp = unsafe { *(stamp.as_ptr() as *const RawTimeStamp) }; assert_eq!(unsafe { stamp.frames }, 1); let (int, _) = space.split_at(size_of::()); let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Long) }; assert_eq!(int.atom.type_, urids.atom.long); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 17); } // reading { let space = Space::from_slice(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.atom.sequence).unwrap(); let mut reader = Sequence::read(body, urids.units.beat).unwrap(); assert_eq!(reader.unit(), TimeStampUnit::Frames); let (stamp, atom) = reader.next().unwrap(); match stamp { TimeStamp::Frames(frames) => assert_eq!(frames, 0), _ => panic!("Invalid time stamp!"), } assert_eq!(atom.read::(urids.atom.int, ()).unwrap(), 42); let (stamp, atom) = reader.next().unwrap(); match stamp { TimeStamp::Frames(frames) => assert_eq!(frames, 1), _ => panic!("Invalid time stamp!"), } assert_eq!(atom.read::(urids.atom.long, ()).unwrap(), 17); assert!(reader.next().is_none()); } } } lv2-atom-2.0.0/src/space.rs010064400017500001731000000554231364504410600135760ustar 00000000000000//! Smart pointers with safe atom reading and writing methods. //! //! # Safety //! //! The only unsafe things that happen in this module is when either space is created from a reference to a `sys::LV2_Atom` and when space is re-interpreted as typed data. //! //! In the first case, we have to trust that the space behind the atom header is accessible since we have no way to check whether it is or not. Therefore, we have to assume that it is sound. //! //! The second case is sound since a) the data is contained in a slice and therefore is accessible, b) generic type parameter bounds assure that the type is plain-old-data and c) 64-bit padding is assured. use crate::Atom; use std::cell::Cell; use std::marker::Unpin; use std::mem::{size_of, size_of_val}; use urid::URID; /// Specialized smart pointer to retrieve struct instances from a slice of memory. /// /// The accessor methods of this struct all behave in a similar way: If the internal slice is big enough, they create a reference to the start of the slice with the desired type and create a new space object that contains the space after the references instance. #[derive(Clone, Copy)] pub struct Space<'a> { data: Option<&'a [u8]>, } impl<'a> Space<'a> { /// Create a new space from an atom pointer. /// /// The method creates a space that contains the atom as well as it's body. /// /// # Safety /// /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. #[allow(clippy::trivially_copy_pass_by_ref)] pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> Self { let size = atom.size as usize; let data = std::slice::from_raw_parts( atom as *const sys::LV2_Atom as *const u8, size + size_of::(), ); Self::from_slice(data) } /// Create a new space from a slice. /// /// Since everything regarding atoms is 64-bit-aligned, this method panics if the data slice is not 64-bit-aligned. pub fn from_slice(data: &'a [u8]) -> Self { Space { data: Some(data) } } /// Try to retrieve a slice of bytes. /// /// This method basically splits off the lower part of the internal bytes slice and creates a new atom space pointer of the upper part. Since atoms have to be 64-bit-aligned, there might be a padding space that's neither in the lower nor in the upper part. pub fn split_raw(self, size: usize) -> Option<(&'a [u8], Self)> { let data = self.data?; if size > data.len() { return None; } let (lower_space, upper_space) = data.split_at(size); // Apply padding. let padding = if size % 8 == 0 { 0 } else { 8 - size % 8 }; let upper_space = if padding <= upper_space.len() { let upper_space = upper_space.split_at(padding).1; Some(upper_space) } else { None }; let upper_space = Self { data: upper_space }; Some((lower_space, upper_space)) } /// Try to retrieve space. /// /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. pub fn split_space(self, size: usize) -> Option<(Self, Self)> { self.split_raw(size) .map(|(data, rhs)| (Self::from_slice(data), rhs)) } /// Try to retrieve a reference to a sized type. /// /// This method retrieves a slice of memory using the [`split_raw`](#method.split_raw) method and interprets it as an instance of `T`. Since there is no way to check that the memory is actually a valid instance of `T`, this method is unsafe. The second return value is the space after the instance of `T`. pub fn split_type(self) -> Option<(&'a T, Self)> where T: Unpin + Copy + Send + Sync + Sized + 'static, { self.split_raw(size_of::()) .map(|(data, rhs)| (unsafe { &*(data.as_ptr() as *const T) }, rhs)) } /// Try to retrieve the space occupied by an atom. /// /// This method assumes that the space contains an atom and retrieves the space occupied by the atom, including the atom header. The second return value is the rest of the space behind the atom. /// /// The difference to [`split_atom_body`](#method.split_atom_body) is that the returned space contains the header of the atom and that the type of the atom is not checked. pub fn split_atom(self) -> Option<(Self, Self)> { let (header, _) = self.split_type::()?; self.split_space(size_of::() + header.size as usize) } /// Try to retrieve the body of the atom. /// /// This method retrieves the header of the atom. If the type URID in the header matches the given URID, it returns the body of the atom. If not, it returns `None`. The first space is the body of the atom, the second one is the space behind it. /// /// The difference to [`split_atom`](#method.split_atom) is that the returned space does not contain the header of the atom and that the type of the atom is checked. pub fn split_atom_body(self, urid: URID) -> Option<(Self, Self)> { let (header, space) = self.split_type::()?; if header.type_ != urid.get() { return None; } space.split_space(header.size as usize) } /// Create a space from a reference. pub fn from_reference(instance: &'a T) -> Self { let data = unsafe { std::slice::from_raw_parts(instance as *const T as *const u8, size_of_val(instance)) }; assert_eq!(data.as_ptr() as usize % 8, 0); Space { data: Some(data) } } /// Concatenate two spaces. /// /// There are situations where a space is split too often and you might want to reunite these two adjacent spaces. This method checks if the given spaces are adjacent, which means that the left space has to end exactly where the right one begins. In this case, the concatenated space is returned. If this is not the case, this method returns `None`. pub fn concat(lhs: Self, rhs: Self) -> Option { let lhs_data = match lhs.data { Some(data) => data, None => return Some(rhs), }; let rhs_data = match rhs.data { Some(data) => data, None => return Some(lhs), }; if unsafe { lhs_data.as_ptr().add(lhs_data.len()) } == rhs_data.as_ptr() { Some(Self::from_slice(unsafe { std::slice::from_raw_parts(lhs_data.as_ptr(), lhs_data.len() + rhs_data.len()) })) } else { None } } /// Return the internal slice of the space. pub fn data(&self) -> Option<&'a [u8]> { self.data } /// Return a mutable reference to the internal slice of the space. pub fn mut_data(&mut self) -> &mut Option<&'a [u8]> { &mut self.data } } /// A smart pointer that writes atom data to an internal slice. /// /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. pub trait MutSpace<'a> { /// Try to allocate memory on the internal data slice. /// /// If `apply_padding` is `true`, the method will assure that the allocated memory is 64-bit-aligned. The first return value is the number of padding bytes that has been used and the second return value is a mutable slice referencing the allocated data. /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])>; /// Try to write data to the internal data slice. /// /// The method allocates a slice with the [`allocate`](#tymethod.allocate) method and copies the data to the slice. fn write_raw(&mut self, data: &[u8], apply_padding: bool) -> Option<&'a mut [u8]> { self.allocate(data.len(), apply_padding).map(|(_, space)| { space.copy_from_slice(data); space }) } } /// A `MutSpace` that directly manages it's own internal data slice. pub struct RootMutSpace<'a> { space: Cell>, allocated_bytes: usize, } impl<'a> RootMutSpace<'a> { /// Create new space from an atom. /// /// The method creates a space that contains the atom as well as it's body. /// /// # Safety /// /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe. pub unsafe fn from_atom(atom: &mut sys::LV2_Atom) -> Self { let space = std::slice::from_raw_parts_mut( atom as *mut _ as *mut u8, atom.size as usize + size_of::(), ); Self::new(space) } /// Create a new instance. /// /// This method takes the space reserved for the value and interprets it as a slice of bytes (`&mut [u8]`). pub fn new(space: &'a mut [u8]) -> Self { RootMutSpace { space: Cell::new(Some(space)), allocated_bytes: 0, } } } impl<'a> MutSpace<'a> for RootMutSpace<'a> { fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])> { if self.space.get_mut().is_none() { return None; } let mut space = self.space.replace(None).unwrap(); let padding = if apply_padding { let alignment = self.allocated_bytes % 8; let padding = if alignment == 0 { 0 } else { 8 - alignment }; if padding > space.len() { return None; } space = space.split_at_mut(padding).1; self.allocated_bytes += padding; padding } else { 0 }; if size > space.len() { return None; } let (lower_slice, upper_slice) = space.split_at_mut(size); self.allocated_bytes += size; self.space.set(Some(upper_slice)); Some((padding, lower_slice)) } } /// Linked list element for dynamic atom writing. /// /// This struct works in conjunction with [`SpaceHead`](struct.SpaceHead.html) to provide a way to write atoms to dynamically allocated memory. pub struct SpaceElement { next: Option<(Box, Box<[u8]>)>, } impl Default for SpaceElement { fn default() -> Self { Self { next: None } } } impl SpaceElement { /// Append an element to the list. /// /// If this is the last element of the list, allocate a slice of the required length and append a new element to the list. If not, do nothing and return `None`. pub fn allocate(&mut self, size: usize) -> Option<(&mut Self, &mut [u8])> { if self.next.is_some() { return None; } let new_data = vec![0u8; size].into_boxed_slice(); let new_element = Box::new(Self::default()); self.next = Some((new_element, new_data)); self.next .as_mut() .map(|(new_element, new_data): &mut (Box, Box<[u8]>)| { (new_element.as_mut(), new_data.as_mut()) }) } /// Create a vector containing the data from all elements following this one. pub fn to_vec(&self) -> Vec { self.iter() .map(|slice| slice.iter()) .flatten() .cloned() .collect() } /// Return an iterator over the chunks of all elements following this one. pub fn iter(&self) -> impl Iterator { std::iter::successors(self.next.as_ref(), |element| element.0.next.as_ref()) .map(|(_, data)| data.as_ref()) } } /// A mutable space that dynamically allocates memory. /// /// This space uses a linked list of [`SpaceElement`s](struct.SpaceElement.html) to allocate memory. Every time `allocate` is called, a new element is appended to the list and a new byte slice is created. /// /// In order to use this space and retrieve the written data once it was written, you create a `SpaceElement` and create a new head with it. Then, you use the head like any other `MutSpace` and when you're done, you retrieve the written data by either calling [`to_vec`](struct.SpaceElement.html#method.to_vec) or [`iter`](struct.SpaceElement.html#iter). /// /// # Usage example /// /// ``` /// # use lv2_core::prelude::*; /// # use lv2_atom::prelude::*; /// # use lv2_atom::space::*; /// # use urid::*; /// # use std::pin::Pin; /// # let map = HashURIDMapper::new(); /// // URID cache creation is omitted. /// let urids: AtomURIDCollection = map.populate_collection().unwrap(); /// /// // Creating the first element in the list and the writing head. /// let mut element = SpaceElement::default(); /// let mut head = SpaceHead::new(&mut element); /// /// // Writing an integer. /// (&mut head as &mut dyn MutSpace).init(urids.int, 42).unwrap(); /// /// // Retrieving a continuos vector with the written data and verifying it's contents. /// let written_data: Vec = element.to_vec(); /// let atom = UnidentifiedAtom::new(Space::from_slice(written_data.as_ref())); /// assert_eq!(42, atom.read(urids.int, ()).unwrap()); /// ``` pub struct SpaceHead<'a> { element: Option<&'a mut SpaceElement>, allocated_space: usize, } impl<'a> SpaceHead<'a> { /// Create a new head that references the given element. pub fn new(element: &'a mut SpaceElement) -> Self { Self { element: Some(element), allocated_space: 0, } } fn internal_allocate(&mut self, size: usize) -> Option<&'a mut [u8]> { let element = self.element.take()?; let (new_element, new_space) = element.allocate(size)?; self.element = Some(new_element); self.allocated_space += size; Some(new_space) } } impl<'a> MutSpace<'a> for SpaceHead<'a> { fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])> { let padding: usize = if apply_padding { (8 - self.allocated_space % 8) % 8 } else { 0 }; if padding != 0 { self.internal_allocate(padding); } self.internal_allocate(size) .map(|new_space| (padding, new_space)) } } /// A `MutSpace` that notes the amount of allocated space in an atom header. pub struct FramedMutSpace<'a, 'b> { atom: &'a mut sys::LV2_Atom, parent: &'b mut dyn MutSpace<'a>, } impl<'a, 'b> FramedMutSpace<'a, 'b> { /// Create a new framed space with the given parent and type URID. pub fn new(parent: &'b mut dyn MutSpace<'a>, urid: URID) -> Option { let atom = sys::LV2_Atom { size: 0, type_: urid.get(), }; let atom: &'a mut sys::LV2_Atom = parent.write(&atom, true)?; Some(Self { atom, parent }) } } impl<'a, 'b> MutSpace<'a> for FramedMutSpace<'a, 'b> { fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])> { self.parent .allocate(size, apply_padding) .map(|(padding, data)| { self.atom.size += (size + padding) as u32; (padding, data) }) } } impl<'a, 'b> dyn MutSpace<'a> + 'b { /// Write a sized object to the space. /// /// If `apply_padding` is `true`, the method will assure that the written instance is 64-bit-aligned. pub fn write(&mut self, instance: &T, apply_padding: bool) -> Option<&'a mut T> where T: Unpin + Copy + Send + Sync + Sized + 'static, { let size = std::mem::size_of::(); let input_data = unsafe { std::slice::from_raw_parts(instance as *const T as *const u8, size) }; let output_data = self.write_raw(input_data, apply_padding)?; assert_eq!(size, output_data.len()); Some(unsafe { &mut *(output_data.as_mut_ptr() as *mut T) }) } /// Initialize a new atom in the space. pub fn init<'c, A: Atom<'a, 'c>>( &'c mut self, urid: URID, parameter: A::WriteParameter, ) -> Option { let new_space = FramedMutSpace::new(self, urid)?; A::init(new_space, parameter) } } #[cfg(test)] mod tests { use crate::space::*; use std::mem::{size_of, size_of_val}; use urid::*; #[test] fn test_space() { let mut vector: Vec = vec![0; 256]; for i in 0..128 { vector[i] = i as u8; } unsafe { let ptr = vector.as_mut_slice().as_mut_ptr().add(128) as *mut u32; *(ptr) = 0x42424242; } let space = Space::from_slice(vector.as_slice()); let (lower_space, space) = space.split_raw(128).unwrap(); for i in 0..128 { assert_eq!(lower_space[i], i as u8); } let (integer, _) = space.split_type::().unwrap(); assert_eq!(*integer, 0x42424242); } #[test] fn test_split_atom() { let mut data: Box<[u64]> = Box::new([0; 256]); let urid: URID = unsafe { URID::new_unchecked(17) }; // Writing an integer atom. unsafe { *(data.as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { atom: sys::LV2_Atom { size: size_of::() as u32, type_: urid.get(), }, body: 42, } } let space = Space::from_reference(data.as_ref()); let (atom, _) = space.split_atom().unwrap(); let (body, _) = atom.split_atom_body(urid).unwrap(); let body = body.data().unwrap(); assert_eq!(size_of::(), size_of_val(body)); assert_eq!(42, unsafe { *(body.as_ptr() as *const i32) }); } #[test] fn test_from_reference() { let value: u64 = 0x42424242; let space = Space::from_reference(&value); assert_eq!(value, *space.split_type::().unwrap().0); } #[test] fn test_concat() { let data: Box<[u64]> = Box::new([0; 64]); let space = Space::from_reference(data.as_ref()); let (lhs, rhs) = space.split_space(8).unwrap(); let concated_space = Space::concat(lhs, rhs).unwrap(); assert_eq!( space.data().unwrap().as_ptr(), concated_space.data().unwrap().as_ptr() ); assert_eq!( space.data().unwrap().len(), concated_space.data().unwrap().len() ); } fn test_mut_space<'a, S: MutSpace<'a>>(mut space: S) { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); let mut test_data: Vec = vec![0; 24]; for i in 0..test_data.len() { test_data[i] = i as u8; } match space.write_raw(test_data.as_slice(), true) { Some(written_data) => assert_eq!(test_data.as_slice(), written_data), None => panic!("Writing failed!"), } let test_atom = sys::LV2_Atom { size: 42, type_: 1 }; let written_atom = (&mut space as &mut dyn MutSpace) .write(&test_atom, true) .unwrap(); assert_eq!(written_atom.size, test_atom.size); assert_eq!(written_atom.type_, test_atom.type_); let created_space = unsafe { RootMutSpace::from_atom(written_atom) } .space .take() .unwrap(); assert_eq!( created_space.as_ptr() as usize, written_atom as *mut _ as usize ); assert_eq!(created_space.len(), size_of::() + 42); let mut atom_frame = FramedMutSpace::new(&mut space as &mut dyn MutSpace, urids.chunk).unwrap(); let mut test_data: Vec = vec![0; 24]; for i in 0..test_data.len() { test_data[i] = i as u8; } let written_data = atom_frame.write_raw(test_data.as_slice(), true).unwrap(); assert_eq!(test_data.as_slice(), written_data); assert_eq!(atom_frame.atom.size, test_data.len() as u32); let test_atom = sys::LV2_Atom { size: 42, type_: 1 }; let borrowed_frame = &mut atom_frame as &mut dyn MutSpace; let written_atom = borrowed_frame.write(&test_atom, true).unwrap(); assert_eq!(written_atom.size, test_atom.size); assert_eq!(written_atom.type_, test_atom.type_); assert_eq!( atom_frame.atom.size as usize, test_data.len() + size_of_val(&test_atom) ); } #[test] fn test_root_mut_space() { const MEMORY_SIZE: usize = 256; let mut memory: [u64; MEMORY_SIZE] = [0; MEMORY_SIZE]; let frame: RootMutSpace = RootMutSpace::new(unsafe { std::slice::from_raw_parts_mut( (&mut memory).as_mut_ptr() as *mut u8, MEMORY_SIZE * size_of::(), ) }); test_mut_space(frame); } #[test] fn test_space_head() { let mut space = SpaceElement::default(); let head = SpaceHead::new(&mut space); test_mut_space(head); } #[test] fn test_padding_inside_frame() { const MEMORY_SIZE: usize = 256; let mut memory: [u64; MEMORY_SIZE] = [0; MEMORY_SIZE]; let raw_space: &mut [u8] = unsafe { std::slice::from_raw_parts_mut( (&mut memory).as_mut_ptr() as *mut u8, MEMORY_SIZE * size_of::(), ) }; // writing { let mut root: RootMutSpace = RootMutSpace::new(raw_space); let mut frame = FramedMutSpace::new(&mut root as &mut dyn MutSpace, URID::<()>::new(1).unwrap()) .unwrap(); { let frame = &mut frame as &mut dyn MutSpace; frame.write::(&42, true).unwrap(); frame.write::(&17, true).unwrap(); } } // checking { let (atom, space) = raw_space.split_at(size_of::()); let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; assert_eq!(atom.type_, 1); assert_eq!(atom.size as usize, 12); let (value, space) = space.split_at(size_of::()); let value = unsafe { *(value.as_ptr() as *const u32) }; assert_eq!(value, 42); let (_, space) = space.split_at(4); let (value, _) = space.split_at(size_of::()); let value = unsafe { *(value.as_ptr() as *const u32) }; assert_eq!(value, 17); } } #[test] fn unaligned_root_write() { let mut raw_space = Box::new([0u8; 8]); { let mut root_space = RootMutSpace::new(&mut raw_space[3..]); (&mut root_space as &mut dyn MutSpace) .write(&42u8, true) .unwrap(); } assert_eq!(&[0, 0, 0, 42, 0, 0, 0, 0], raw_space.as_ref()); } } lv2-atom-2.0.0/src/string.rs010064400017500001731000000213521364504410600140030ustar 00000000000000//! String handling atoms. //! //! This module contains two different atoms: The [`String`](struct.String.html) and the [`Literal`](struct.Literal.html). The former is for simple, non-localized UTF-8 strings, like URIs or paths, and the later is either for localized text, e.g. descriptions in the user interface, or RDF literals. //! //! Reading and writing these atoms is pretty simple: They don't require a parameter and return a either a `&str` or the literal info and a `&str`. Writing is done with a writing handle which can append strings to the string/literal. When dropped, the handle will append the null character, you therefore don't have to handle it on your own. //! //! # Example //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! use lv2_atom::string::StringWriter; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let input: &str = ports.input.read(urids.string, ()).unwrap(); //! let mut writer: StringWriter = ports.output.init(urids.string, ()).unwrap(); //! writer.append(input).unwrap(); //! } //! ``` //! //! # Specifications //! //! [http://lv2plug.in/ns/ext/atom/atom.html#String](http://lv2plug.in/ns/ext/atom/atom.html#String) //! [http://lv2plug.in/ns/ext/atom/atom.html#Literal](http://lv2plug.in/ns/ext/atom/atom.html#Literal) use crate::prelude::*; use crate::space::*; use urid::*; /// An atom containing either a localized string or an RDF literal. /// /// [See also the module documentation.](index.html) pub struct Literal; unsafe impl UriBound for Literal { const URI: &'static [u8] = sys::LV2_ATOM__Literal; } #[derive(Clone, Copy, PartialEq, Eq, Debug)] /// The type or language URID of a literal. pub enum LiteralInfo { Language(URID), Datatype(URID), } impl<'a, 'b> Atom<'a, 'b> for Literal where 'a: 'b, { type ReadParameter = (); type ReadHandle = (LiteralInfo, &'a str); type WriteParameter = LiteralInfo; type WriteHandle = StringWriter<'a, 'b>; fn read(body: Space<'a>, _: ()) -> Option<(LiteralInfo, &'a str)> { let (header, body) = body.split_type::()?; let info = if header.lang != 0 && header.datatype == 0 { LiteralInfo::Language(URID::new(header.lang)?) } else if header.lang == 0 && header.datatype != 0 { LiteralInfo::Datatype(URID::new(header.datatype)?) } else { return None; }; let data = body.data()?; std::str::from_utf8(&data[0..data.len() - 1]) .or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()])) .ok() .map(|string| (info, string)) } fn init(mut frame: FramedMutSpace<'a, 'b>, info: LiteralInfo) -> Option> { (&mut frame as &mut dyn MutSpace).write( &match info { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { lang: lang.get(), datatype: 0, }, LiteralInfo::Datatype(datatype) => sys::LV2_Atom_Literal_Body { lang: 0, datatype: datatype.get(), }, }, true, )?; Some(StringWriter { frame }) } } /// An atom containing a UTF-8 encoded string. /// /// [See also the module documentation.](index.html) pub struct String; unsafe impl UriBound for String { const URI: &'static [u8] = sys::LV2_ATOM__String; } impl<'a, 'b> Atom<'a, 'b> for String where 'a: 'b, { type ReadParameter = (); type ReadHandle = &'a str; type WriteParameter = (); type WriteHandle = StringWriter<'a, 'b>; fn read(body: Space<'a>, _: ()) -> Option<&'a str> { body.data() .and_then(|data| std::str::from_utf8(data).ok()) .map(|string| &string[..string.len() - 1]) // removing the null-terminator } fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { Some(StringWriter { frame }) } } /// Handle to append strings to a string or literal. pub struct StringWriter<'a, 'b> { frame: FramedMutSpace<'a, 'b>, } impl<'a, 'b> StringWriter<'a, 'b> { /// Append a string. /// /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. /// /// If the internal space for the atom is not big enough, this method returns `None`. pub fn append(&mut self, string: &str) -> Option<&mut str> { let data = string.as_bytes(); let space = self.frame.write_raw(data, false)?; unsafe { Some(std::str::from_utf8_unchecked_mut(space)) } } } impl<'a, 'b> Drop for StringWriter<'a, 'b> { fn drop(&mut self) { // Null terminator. (&mut self.frame as &mut dyn MutSpace).write(&0u8, false); } } #[cfg(test)] mod tests { use crate::prelude::*; use crate::space::*; use std::ffi::CStr; use std::mem::{size_of, size_of_val}; use urid::*; struct German; unsafe impl UriBound for German { const URI: &'static [u8] = b"http://lexvo.org/id/iso639-1/de\0"; } #[derive(URIDCollection)] pub struct TestURIDs { atom: AtomURIDCollection, german: URID, } const SAMPLE0: &str = "Da steh ich nun, ich armer Tor! "; const SAMPLE1: &str = "Und bin so klug als wie zuvor;"; #[test] fn test_literal() { let map = HashURIDMapper::new(); let urids = TestURIDs::from_map(&map).unwrap(); let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing { let mut space = RootMutSpace::new(raw_space.as_mut()); let mut writer = (&mut space as &mut dyn MutSpace) .init( urids.atom.literal, LiteralInfo::Language(urids.german.into_general()), ) .unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } // verifying { let (atom, space) = raw_space.split_at(size_of::()); let literal = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom_Literal) }; assert_eq!(literal.atom.type_, urids.atom.literal.get()); assert_eq!( literal.atom.size as usize, size_of::() + size_of_val(SAMPLE0) + size_of_val(SAMPLE1) + 1 ); assert_eq!(literal.body.lang, urids.german.get()); assert_eq!(literal.body.datatype, 0); let size = literal.atom.size as usize - size_of::(); let string = CStr::from_bytes_with_nul(space.split_at(size).0) .unwrap() .to_str() .unwrap(); assert_eq!(SAMPLE0.to_owned() + SAMPLE1, string); } // reading { let space = Space::from_slice(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.atom.literal).unwrap(); let (info, text) = Literal::read(body, ()).unwrap(); assert_eq!(info, LiteralInfo::Language(urids.german.into_general())); assert_eq!(text, SAMPLE0.to_owned() + SAMPLE1); } } #[test] fn test_string() { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing { let mut space = RootMutSpace::new(raw_space.as_mut()); let mut writer = (&mut space as &mut dyn MutSpace) .init(urids.string, ()) .unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } // verifying { let (string, space) = raw_space.split_at(size_of::()); let string = unsafe { &*(string.as_ptr() as *const sys::LV2_Atom_String) }; assert_eq!(string.atom.type_, urids.string); assert_eq!(string.atom.size as usize, SAMPLE0.len() + SAMPLE1.len() + 1); let string = std::str::from_utf8(space.split_at(string.atom.size as usize).0).unwrap(); assert_eq!(string[..string.len() - 1], SAMPLE0.to_owned() + SAMPLE1); } // reading { let space = Space::from_slice(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.string).unwrap(); let string = String::read(body, ()).unwrap(); assert_eq!(string, SAMPLE0.to_owned() + SAMPLE1); } } } lv2-atom-2.0.0/src/tuple.rs010064400017500001731000000127641364504410600136350ustar 00000000000000//! An atom containg a series of other atoms. //! //! This atom is just like a [sequence](../sequence/index.html), only without time stamps: It contains multiple arbitrary atoms which you can either iterate through or write in sequence. //! //! # Example //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! use lv2_atom::tuple::{TupleIterator, TupleWriter}; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let input: TupleIterator = ports.input.read(urids.tuple, ()).unwrap(); //! let mut output: TupleWriter = ports.output.init(urids.tuple, ()).unwrap(); //! for atom in input { //! if let Some(integer) = atom.read(urids.int, ()) { //! output.init(urids.int, integer * 2).unwrap(); //! } else { //! output.init(urids.int, -1).unwrap(); //! } //! } //! } //! ``` //! //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Tuple](http://lv2plug.in/ns/ext/atom/atom.html#Tuple) use crate::space::*; use crate::*; use urid::*; /// An atom containing a series of other atoms. /// /// [See also the module documentation.](index.html) pub struct Tuple; unsafe impl UriBound for Tuple { const URI: &'static [u8] = sys::LV2_ATOM__Tuple; } impl<'a, 'b> Atom<'a, 'b> for Tuple where 'a: 'b, { type ReadParameter = (); type ReadHandle = TupleIterator<'a>; type WriteParameter = (); type WriteHandle = TupleWriter<'a, 'b>; fn read(body: Space<'a>, _: ()) -> Option> { Some(TupleIterator { space: body }) } fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { Some(TupleWriter { frame }) } } /// An iterator over all atoms in a tuple. /// /// The item of this iterator is simply the space a single atom occupies. pub struct TupleIterator<'a> { space: Space<'a>, } impl<'a> Iterator for TupleIterator<'a> { type Item = UnidentifiedAtom<'a>; fn next(&mut self) -> Option> { let (atom, space) = self.space.split_atom()?; self.space = space; Some(UnidentifiedAtom::new(atom)) } } /// The writing handle to add atoms to a tuple. pub struct TupleWriter<'a, 'b> { frame: FramedMutSpace<'a, 'b>, } impl<'a, 'b> TupleWriter<'a, 'b> { /// Initialize a new tuple element. pub fn init<'c, A: Atom<'a, 'c>>( &'c mut self, child_urid: URID, child_parameter: A::WriteParameter, ) -> Option { (&mut self.frame as &mut dyn MutSpace).init(child_urid, child_parameter) } } #[cfg(test)] mod tests { use crate::prelude::*; use crate::space::*; use std::mem::size_of; use urid::*; #[test] fn test_tuple() { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing { let mut space = RootMutSpace::new(raw_space.as_mut()); let mut writer = (&mut space as &mut dyn MutSpace) .init(urids.tuple, ()) .unwrap(); { let mut vector_writer = writer.init::>(urids.vector, urids.int).unwrap(); vector_writer.append(&[17; 9]).unwrap(); } writer.init::(urids.int, 42).unwrap(); } // verifying { let (atom, space) = raw_space.split_at(size_of::()); let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; assert_eq!(atom.type_, urids.tuple); assert_eq!( atom.size as usize, size_of::() + size_of::() * 9 + 4 + size_of::() ); let (vector, space) = space.split_at(size_of::()); let vector = unsafe { &*(vector.as_ptr() as *const sys::LV2_Atom_Vector) }; assert_eq!(vector.atom.type_, urids.vector); assert_eq!( vector.atom.size as usize, size_of::() + size_of::() * 9 ); assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int); let (vector_items, space) = space.split_at(size_of::() * 9); let vector_items = unsafe { std::slice::from_raw_parts(vector_items.as_ptr() as *const i32, 9) }; assert_eq!(vector_items, &[17; 9]); let (_, space) = space.split_at(4); let (int, _) = space.split_at(size_of::()); let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Int) }; assert_eq!(int.atom.type_, urids.int); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); } // reading { let space = Space::from_slice(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.tuple).unwrap(); let items: Vec = Tuple::read(body, ()).unwrap().collect(); assert_eq!(items[0].read(urids.vector, urids.int).unwrap(), [17; 9]); assert_eq!(items[1].read(urids.int, ()).unwrap(), 42); } } } lv2-atom-2.0.0/src/vector.rs010064400017500001731000000155171364504410600140050ustar 00000000000000//! An atom containg an array of scalar atom bodies. //! //! This atom is able to handle arrays (aka slices) of the internal types of scalar atoms. //! //! Reading a vector requires the URID fo the scalar that's been used and the reading process fails if the vector does not contain the requested scalar atom. The return value of the reading process is a slice of the internal type. //! //! Writing a vector is done with a writer that appends slices to the atom. //! //! # Example //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! use lv2_atom::vector::VectorWriter; //! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, //! output: OutputPort, //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let input: &[i32] = ports.input.read(urids.vector(), urids.int).unwrap(); //! let mut output: VectorWriter = ports.output.init(urids.vector(), urids.int).unwrap(); //! output.append(input).unwrap(); //! } //! ``` //! //! You may note that, unlike other atoms, the vector's URID is retrieved by calling the `vector` method. This is because two vectors with a different item type are considered two different types, and therefore would have the different URIDs. In reality, however, all vectors have the same URID and the `vector` method returns it with the fitting type. //! //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Vector](http://lv2plug.in/ns/ext/atom/atom.html#Vector) use crate::scalar::ScalarAtom; use crate::space::*; use crate::*; use std::marker::PhantomData; use std::mem::size_of; use urid::*; /// An atom containg an array of scalar atom bodies. /// /// [See also the module documentation.](index.html) pub struct Vector { child: PhantomData, } unsafe impl UriBound for Vector { const URI: &'static [u8] = sys::LV2_ATOM__Vector; } impl<'a, 'b, C: ScalarAtom> Atom<'a, 'b> for Vector where 'a: 'b, C: 'b, { type ReadParameter = URID; type ReadHandle = &'a [C::InternalType]; type WriteParameter = URID; type WriteHandle = VectorWriter<'a, 'b, C>; fn read(body: Space<'a>, child_urid: URID) -> Option<&'a [C::InternalType]> { let (header, body) = body.split_type::()?; if header.child_type != child_urid || header.child_size as usize != size_of::() { return None; } let data = body.data()?; assert_eq!(data.len() % size_of::(), 0); let children_count = data.len() / size_of::(); let children = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const C::InternalType, children_count) }; Some(children) } fn init( mut frame: FramedMutSpace<'a, 'b>, child_urid: URID, ) -> Option> { let body = sys::LV2_Atom_Vector_Body { child_type: child_urid.get(), child_size: size_of::() as u32, }; (&mut frame as &mut dyn MutSpace).write(&body, false)?; Some(VectorWriter { frame, type_: PhantomData, }) } } /// Handle to append elements to a vector. /// /// This works by allocating a slice of memory behind the vector and then writing your data to it. pub struct VectorWriter<'a, 'b, A: ScalarAtom> { frame: FramedMutSpace<'a, 'b>, type_: PhantomData, } impl<'a, 'b, A: ScalarAtom> VectorWriter<'a, 'b, A> { /// Push a single value to the vector. pub fn push(&mut self, child: A::InternalType) -> Option<&mut A::InternalType> { (&mut self.frame as &mut dyn MutSpace).write(&child, false) } /// Append a slice of undefined memory to the vector. /// /// Using this method, you don't need to have the elements in memory before you can write them. pub fn allocate(&mut self, size: usize) -> Option<&mut [A::InternalType]> { self.frame .allocate(size_of::() * size, false) .map(|(_, data)| unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut A::InternalType, size) }) } /// Append multiple elements to the vector. pub fn append(&mut self, data: &[A::InternalType]) -> Option<&mut [A::InternalType]> { let raw_data = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, std::mem::size_of_val(data)) }; self.frame .allocate(raw_data.len(), false) .map(|(_, space)| unsafe { space.copy_from_slice(raw_data); std::slice::from_raw_parts_mut( space.as_mut_ptr() as *mut A::InternalType, data.len(), ) }) } } #[cfg(test)] mod tests { use crate::prelude::*; use crate::space::*; use std::mem::size_of; use urid::*; #[test] fn test_vector() { const CHILD_COUNT: usize = 17; let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space: Box<[u8]> = Box::new([0; 256]); // writing { let mut space = RootMutSpace::new(raw_space.as_mut()); let mut writer = (&mut space as &mut dyn MutSpace) .init(urids.vector(), urids.int) .unwrap(); writer.append(&[42; CHILD_COUNT - 1]); writer.push(1); } // verifying { let (vector, children) = raw_space.split_at(size_of::()); let vector = unsafe { &*(vector.as_ptr() as *const sys::LV2_Atom_Vector) }; assert_eq!(vector.atom.type_, urids.vector.get()); assert_eq!( vector.atom.size as usize, size_of::() + size_of::() * CHILD_COUNT ); assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int.get()); let children = unsafe { std::slice::from_raw_parts(children.as_ptr() as *const i32, CHILD_COUNT) }; for value in &children[0..children.len() - 1] { assert_eq!(*value, 42); } assert_eq!(children[children.len() - 1], 1); } // reading { let space = Space::from_slice(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.vector).unwrap(); let children: &[i32] = Vector::::read(body, urids.int).unwrap(); assert_eq!(children.len(), CHILD_COUNT); for i in 0..children.len() - 1 { assert_eq!(children[i], 42); } assert_eq!(children[children.len() - 1], 1); } } } lv2-atom-2.0.0/tests/atom_integration.rs010064400017500001750000000126151374457150200164230ustar 00000000000000extern crate lv2_atom as atom; extern crate lv2_core as core; extern crate lv2_sys as sys; extern crate lv2_units as units; use atom::prelude::*; use core::prelude::*; use lv2_urid::*; use units::prelude::*; use urid::*; #[derive(PortCollection)] struct Ports { input: InputPort, output: OutputPort, } #[derive(FeatureCollection)] struct Features<'a> { map: LV2Map<'a>, } #[derive(URIDCollection)] struct URIDs { atom: AtomURIDCollection, units: UnitURIDCollection, } #[uri("urn:rust-lv2:atom-plugin")] struct AtomPlugin { urids: URIDs, } impl Plugin for AtomPlugin { type Ports = Ports; type InitFeatures = Features<'static>; type AudioFeatures = (); fn new(_plugin_info: &PluginInfo, features: &mut Features) -> Option { Some(Self { urids: features.map.populate_collection()?, }) } fn run(&mut self, ports: &mut Ports, _: &mut (), _: u32) { let sequence_reader = ports .input .read::(self.urids.atom.sequence, self.urids.units.beat) .unwrap(); let mut sequence_writer = ports .output .init::( self.urids.atom.sequence, TimeStampURID::Frames(self.urids.units.frame), ) .unwrap(); for (time_stamp, atom) in sequence_reader { match atom.read(self.urids.atom.int, ()) { Some(number) => { sequence_writer .init::(time_stamp, self.urids.atom.int, number * 2) .unwrap(); } None => { sequence_writer.forward(time_stamp, atom).unwrap(); } } } } } lv2_descriptors![AtomPlugin]; #[test] fn main() { use atom::space::*; use lv2_urid::*; use std::ffi::{c_void, CStr}; use std::mem::size_of; use std::pin::Pin; use urid::*; // Instantiating all features. let mut mapper: Pin>> = Box::pin(HashURIDMapper::new().into()); let map_interface = Box::pin(mapper.as_mut().make_map_interface()); let map = LV2Map::new(map_interface.as_ref().get_ref()); let mut map_feature_interface = Box::pin(mapper.as_mut().make_map_interface()); let map_feature = Box::pin(sys::LV2_Feature { URI: LV2Map::URI.as_ptr() as *const i8, data: map_feature_interface.as_mut().get_mut() as *mut _ as *mut c_void, }); let features_list: &[*const sys::LV2_Feature] = &[map_feature.as_ref().get_ref(), std::ptr::null()]; // Retrieving URIDs. let urids: URIDs = map.populate_collection().unwrap(); // Preparing the input atom. let mut input_atom_space: Box<[u8]> = Box::new([0; 256]); { let mut space = RootMutSpace::new(input_atom_space.as_mut()); let mut writer = (&mut space as &mut dyn MutSpace) .init( urids.atom.sequence, TimeStampURID::Frames(urids.units.frame), ) .unwrap(); writer .init(TimeStamp::Frames(0), urids.atom.int, 42) .unwrap(); writer .init(TimeStamp::Frames(1), urids.atom.long, 17) .unwrap(); writer .init(TimeStamp::Frames(2), urids.atom.int, 3) .unwrap(); } // preparing the output atom. let mut output_atom_space: Box<[u8]> = Box::new([0; 256]); { let mut space = RootMutSpace::new(output_atom_space.as_mut()); (&mut space as &mut dyn MutSpace) .init(urids.atom.chunk, ()) .unwrap() .allocate(256 - size_of::(), false) .unwrap(); } unsafe { // retrieving the descriptor. let plugin_descriptor = &*lv2_descriptor(0); assert_eq!( CStr::from_ptr(plugin_descriptor.URI).to_str().unwrap(), "urn:rust-lv2:atom-plugin" ); // Instantiating the plugin. let plugin = (plugin_descriptor.instantiate.unwrap())( plugin_descriptor, 44100.0, b"\0".as_ptr() as *const i8, features_list.as_ptr(), ); // connecting the ports. (plugin_descriptor.connect_port.unwrap())( plugin, 0, input_atom_space.as_mut_ptr() as *mut c_void, ); (plugin_descriptor.connect_port.unwrap())( plugin, 1, output_atom_space.as_mut_ptr() as *mut c_void, ); // Activate, run, deactivate. (plugin_descriptor.activate.unwrap())(plugin); (plugin_descriptor.run.unwrap())(plugin, 256); (plugin_descriptor.deactivate.unwrap())(plugin); // Cleanup. (plugin_descriptor.cleanup.unwrap())(plugin); } // Asserting the result let (sequence, _) = Space::from_slice(output_atom_space.as_ref()) .split_atom_body(urids.atom.sequence) .unwrap(); for (stamp, atom) in Sequence::read(sequence, urids.units.beat).unwrap() { let stamp = stamp.as_frames().unwrap(); match stamp { 0 => assert_eq!(atom.read(urids.atom.int, ()).unwrap(), 84), 1 => assert_eq!(atom.read(urids.atom.long, ()).unwrap(), 17), 2 => assert_eq!(atom.read(urids.atom.int, ()).unwrap(), 6), _ => panic!("Invalid time stamp in sequence!"), } } }