An LV2 host library for Rust.

Overview

Livi

crates.io docs.rs

License: MIT Tests

A library for hosting LV2 plugins.

Note: This is a work in progress and has not yet been full tested.

Supported LV2 Features

LV2 has a simple core interface but is accompanied by extensions that can add lots of functionality. This library aims to support as many features as possible out of the box.

Quickstart

Below is an example on how to run the mda EPiano plugin.

(1, world.midi_urid(), &play_note_data) .unwrap(); s }; // Where parameters can be set. We initialize to the plugin's default values. let params: Vec = plugin .ports_with_type(livi::PortType::ControlInput) .map(|p| p.default_value) .collect(); // This is where the audio data will be stored. let mut outputs = [ vec![0.0; MAX_BLOCK_SIZE], // For mda EPiano, this is the left channel. vec![0.0; MAX_BLOCK_SIZE], // For mda EPiano, this is the right channel. ]; // Set up the port configuration and run the plugin! // The results will be stored in `outputs`. let ports = EmptyPortConnections::new(MAX_BLOCK_SIZE) .with_atom_sequence_inputs(std::iter::once(&input)) .with_audio_outputs(outputs.iter_mut().map(|output| output.as_mut_slice())) .with_control_inputs(params.iter()); unsafe { instance.run(ports).unwrap() }; ">
use livi;

let mut world = livi::World::new();
const MIN_BLOCK_SIZE: usize = 1;
const MAX_BLOCK_SIZE: usize = 256;
const SAMPLE_RATE: f64 = 44100.0;
world
    .initialize_block_length(MIN_BLOCK_SIZE, MAX_BLOCK_SIZE)
    .unwrap();
let plugin = world
    // This is the URI for mda EPiano. You can use the `lv2ls` command line
    // utility to see all available LV2 plugins.
    .plugin_by_uri("http://drobilla.net/plugins/mda/EPiano")
    .expect("Plugin not found.");
let mut instance = unsafe {
    plugin
        .instantiate(SAMPLE_RATE)
        .expect("Could not instantiate plugin.")
};

// Where midi events will be read from.
let input = {
    let mut s = livi::event::LV2AtomSequence::new(1024);
    let play_note_data = [0x90, 0x40, 0x7f];
    s.push_midi_event::<3>(1, world.midi_urid(), &play_note_data)
        .unwrap();
    s
};

// Where parameters can be set. We initialize to the plugin's default values.
let params: Vec<f32> = plugin
    .ports_with_type(livi::PortType::ControlInput)
    .map(|p| p.default_value)
    .collect();
// This is where the audio data will be stored.
let mut outputs = [
    vec![0.0; MAX_BLOCK_SIZE], // For mda EPiano, this is the left channel.
    vec![0.0; MAX_BLOCK_SIZE], // For mda EPiano, this is the right channel.
];

// Set up the port configuration and run the plugin!
// The results will be stored in `outputs`.
let ports = EmptyPortConnections::new(MAX_BLOCK_SIZE)
    .with_atom_sequence_inputs(std::iter::once(&input))
    .with_audio_outputs(outputs.iter_mut().map(|output| output.as_mut_slice()))
    .with_control_inputs(params.iter());
unsafe { instance.run(ports).unwrap() };

Building, Testing, and Running

  • Build - cargo build
  • Test - cargo test, requires mda LV2 plugins.
  • Run livi-jack - cargo run --example livi-jack --release -- --plugin-uri=http://drobilla.net/plugins/mda/EPiano.
Comments
  • LV2 Worker feature

    LV2 Worker feature

    Hey there,

    Thank you for this repo!

    I am pretty new to both Rust and LV2, but this is my attempt to add support for the LV2 Worker feature. It's still a work in progress and not yet adequately tested (like, not at all) but I figured now would be a good time to open a draft PR and potentially get some feedback if possible as the basic structure is there.

    Honestly this was pretty challenging and I had to use a lot of unsafe code that made me a bit uncomfortable. But I guess the worker feature is unsafe by nature - running the plugin in the real-time thread while it simultaneously does work in another thread is pretty much the definition of unsafe.

    Here is some pseudo-code of how I intended it to work:

    let instance = plugin.instantiate(...)
    let worker = instance.get_worker().unwrap()
    
    // In real-time thread:
    plugin.run()
    
    // In another thread
    while(plugin_is_alive) {
      worker.do_work()
    }
    

    I am not yet sure how to make sure the worker does not outlive the plugin instance - I have a bit to learn yet on rust lifetimes.

    opened by ctsexton 13
  • Handle atom sequences properly.

    Handle atom sequences properly.

    This:

    • Adds urid to sequence types.
    • Passes atom#Chunks instead of atom#Sequence to plugins. Plugins should take atom#Chunks and mutate it into atom#Sequence.
    opened by wmedrano 2
  • mark Worker as Send

    mark Worker as Send

    Quick follow up PR on the Worker stuff. I realized when actually using the WorkerManager that it was not possible to pass off to another thread due to the Worker struct containing *mut c_void fields. So, this should take care of that.

    opened by ctsexton 1
  • Crash

    Crash

    Before:

    wmedrano@donuts:~/code/livi-rs$ git checkout main
    Already on 'main'
    Your branch is up to date with 'origin/main'.
    wmedrano@donuts:~/code/livi-rs$ pw-jack cargo run --example livi-jack
       Compiling livi v0.5.3 (/home/wmedrano/code/livi-rs)
        Finished dev [unoptimized + debuginfo] target(s) in 2.17s
         Running `target/debug/examples/livi-jack`
    [2022-04-10T03:29:27Z INFO  livi] Creating World with supported features {"http://lv2plug.in/ns/ext/urid#map", "http://lv2plug.in/ns/ext/buf-size#boundedBlockLength", "http://lv2plug.in/ns/ext/urid#unmap", "http://lv2plug.in/ns/ext/options#options", "http://lv2plug.in/ns/ext/worker#schedule"}
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Ambience: http://drobilla.net/plugins/mda/Ambience
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Bandisto: http://drobilla.net/plugins/mda/Bandisto
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA BeatBox: http://drobilla.net/plugins/mda/BeatBox
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Combo: http://drobilla.net/plugins/mda/Combo
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA DX10: http://drobilla.net/plugins/mda/DX10
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA De-ess: http://drobilla.net/plugins/mda/DeEss
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Degrade: http://drobilla.net/plugins/mda/Degrade
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Delay: http://drobilla.net/plugins/mda/Delay
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Detune: http://drobilla.net/plugins/mda/Detune
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Dither: http://drobilla.net/plugins/mda/Dither
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA DubDelay: http://drobilla.net/plugins/mda/DubDelay
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Dynamics: http://drobilla.net/plugins/mda/Dynamics
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA ePiano: http://drobilla.net/plugins/mda/EPiano
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Image: http://drobilla.net/plugins/mda/Image
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA JX10: http://drobilla.net/plugins/mda/JX10
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Leslie: http://drobilla.net/plugins/mda/Leslie
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Limiter: http://drobilla.net/plugins/mda/Limiter
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Loudness: http://drobilla.net/plugins/mda/Loudness
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA MultiBand: http://drobilla.net/plugins/mda/MultiBand
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Overdrive: http://drobilla.net/plugins/mda/Overdrive
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Piano: http://drobilla.net/plugins/mda/Piano
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA RePsycho!: http://drobilla.net/plugins/mda/RePsycho
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA RezFilter: http://drobilla.net/plugins/mda/RezFilter
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA RingMod: http://drobilla.net/plugins/mda/RingMod
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA RoundPan: http://drobilla.net/plugins/mda/RoundPan
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Shepard: http://drobilla.net/plugins/mda/Shepard
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Splitter: http://drobilla.net/plugins/mda/Splitter
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Stereo: http://drobilla.net/plugins/mda/Stereo
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA SubSynth: http://drobilla.net/plugins/mda/SubSynth
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA TalkBox: http://drobilla.net/plugins/mda/TalkBox
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA TestTone: http://drobilla.net/plugins/mda/TestTone
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA ThruZero: http://drobilla.net/plugins/mda/ThruZero
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Tracker: http://drobilla.net/plugins/mda/Tracker
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Transient: http://drobilla.net/plugins/mda/Transient
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA VocInput: http://drobilla.net/plugins/mda/VocInput
    [2022-04-10T03:29:27Z INFO  livi] Found plugin MDA Vocoder: http://drobilla.net/plugins/mda/Vocoder
    [2022-04-10T03:29:27Z INFO  livi] Found plugin Simple Amplifier: http://lv2plug.in/plugins/eg-amp
    [2022-04-10T03:29:27Z INFO  livi] Found plugin Example Fifths: http://lv2plug.in/plugins/eg-fifths
    [2022-04-10T03:29:27Z INFO  livi] Found plugin Example Metronome: http://lv2plug.in/plugins/eg-metro
    [2022-04-10T03:29:27Z INFO  livi] Found plugin Example MIDI Gate: http://lv2plug.in/plugins/eg-midigate
    [2022-04-10T03:29:27Z INFO  livi] Found plugin Example Parameters: http://lv2plug.in/plugins/eg-params
    [2022-04-10T03:29:27Z WARN  livi] Plugin http://lv2plug.in/plugins/eg-sampler requires unsupported features: [Node("<http://lv2plug.in/ns/ext/urid#map>"), Node("<http://lv2plug.in/ns/ext/state#loadDefaultState>"), Node("<http://lv2plug.in/ns/ext/worker#schedule>")]
    [2022-04-10T03:29:27Z INFO  livi] Found plugin Example Scope (Mono): http://lv2plug.in/plugins/eg-scope#Mono
    [2022-04-10T03:29:27Z INFO  livi] Found plugin Example Scope (Stereo): http://lv2plug.in/plugins/eg-scope#Stereo
    [2022-04-10T03:29:27Z INFO  livi_jack] Created jack client Client { name: "MDA ePiano", sample_rate: 48000, buffer_size: 1024, cpu_usage: "0%", frame_time: 226758108 } with status (empty).
    [2022-04-10T03:29:27Z INFO  livi_jack] Initializing audio output Left Out.
    [2022-04-10T03:29:27Z INFO  livi_jack] Initializing audio output Right Out.
    ^C
    

    After:

    wmedrano@donuts:~/code/livi-rs$ git checkout wmedrano
    Switched to branch 'wmedrano'
    wmedrano@donuts:~/code/livi-rs$ pw-jack cargo run --example livi-jack
       Compiling livi v0.5.3 (/home/wmedrano/code/livi-rs)
        Finished dev [unoptimized + debuginfo] target(s) in 1.28s
         Running `target/debug/examples/livi-jack`
    [2022-04-10T03:29:37Z INFO  livi] Creating World with supported features {"http://lv2plug.in/ns/ext/worker#schedule", "http://lv2plug.in/ns/ext/urid#map", "http://lv2plug.in/ns/ext/options#options", "http://lv2plug.in/ns/ext/buf-size#boundedBlockLength", "http://lv2plug.in/ns/ext/urid#unmap"}
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Ambience: http://drobilla.net/plugins/mda/Ambience
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Bandisto: http://drobilla.net/plugins/mda/Bandisto
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA BeatBox: http://drobilla.net/plugins/mda/BeatBox
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Combo: http://drobilla.net/plugins/mda/Combo
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA DX10: http://drobilla.net/plugins/mda/DX10
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA De-ess: http://drobilla.net/plugins/mda/DeEss
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Degrade: http://drobilla.net/plugins/mda/Degrade
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Delay: http://drobilla.net/plugins/mda/Delay
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Detune: http://drobilla.net/plugins/mda/Detune
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Dither: http://drobilla.net/plugins/mda/Dither
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA DubDelay: http://drobilla.net/plugins/mda/DubDelay
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Dynamics: http://drobilla.net/plugins/mda/Dynamics
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA ePiano: http://drobilla.net/plugins/mda/EPiano
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Image: http://drobilla.net/plugins/mda/Image
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA JX10: http://drobilla.net/plugins/mda/JX10
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Leslie: http://drobilla.net/plugins/mda/Leslie
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Limiter: http://drobilla.net/plugins/mda/Limiter
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Loudness: http://drobilla.net/plugins/mda/Loudness
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA MultiBand: http://drobilla.net/plugins/mda/MultiBand
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Overdrive: http://drobilla.net/plugins/mda/Overdrive
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Piano: http://drobilla.net/plugins/mda/Piano
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA RePsycho!: http://drobilla.net/plugins/mda/RePsycho
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA RezFilter: http://drobilla.net/plugins/mda/RezFilter
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA RingMod: http://drobilla.net/plugins/mda/RingMod
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA RoundPan: http://drobilla.net/plugins/mda/RoundPan
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Shepard: http://drobilla.net/plugins/mda/Shepard
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Splitter: http://drobilla.net/plugins/mda/Splitter
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Stereo: http://drobilla.net/plugins/mda/Stereo
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA SubSynth: http://drobilla.net/plugins/mda/SubSynth
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA TalkBox: http://drobilla.net/plugins/mda/TalkBox
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA TestTone: http://drobilla.net/plugins/mda/TestTone
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA ThruZero: http://drobilla.net/plugins/mda/ThruZero
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Tracker: http://drobilla.net/plugins/mda/Tracker
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Transient: http://drobilla.net/plugins/mda/Transient
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA VocInput: http://drobilla.net/plugins/mda/VocInput
    [2022-04-10T03:29:37Z INFO  livi] Found plugin MDA Vocoder: http://drobilla.net/plugins/mda/Vocoder
    [2022-04-10T03:29:37Z INFO  livi] Found plugin Simple Amplifier: http://lv2plug.in/plugins/eg-amp
    [2022-04-10T03:29:37Z INFO  livi] Found plugin Example Fifths: http://lv2plug.in/plugins/eg-fifths
    [2022-04-10T03:29:37Z INFO  livi] Found plugin Example Metronome: http://lv2plug.in/plugins/eg-metro
    [2022-04-10T03:29:37Z INFO  livi] Found plugin Example MIDI Gate: http://lv2plug.in/plugins/eg-midigate
    [2022-04-10T03:29:37Z INFO  livi] Found plugin Example Parameters: http://lv2plug.in/plugins/eg-params
    [2022-04-10T03:29:37Z WARN  livi] Plugin http://lv2plug.in/plugins/eg-sampler requires unsupported features: [Node("<http://lv2plug.in/ns/ext/state#loadDefaultState>"), Node("<http://lv2plug.in/ns/ext/urid#map>"), Node("<http://lv2plug.in/ns/ext/worker#schedule>")]
    [2022-04-10T03:29:37Z INFO  livi] Found plugin Example Scope (Mono): http://lv2plug.in/plugins/eg-scope#Mono
    [2022-04-10T03:29:37Z INFO  livi] Found plugin Example Scope (Stereo): http://lv2plug.in/plugins/eg-scope#Stereo
    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: LoadLibraryError("The resquested symbol was missing: jack_get_internal_client_name\u{0}")', examples/livi-jack.rs:36:81
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    
    opened by wmedrano 0
  • Refactor features.

    Refactor features.

    • No longer required that world is mut.
    • Features no longer locked to world. Recreating world is no longer required to remake parameters.
    • URID feature uses Pin for more safety.
    • Worker's add themselves automatically.
    opened by wmedrano 0
Releases(0.5.2)
Owner
Will
I love developing in Rust!
Will
The gRPC library for Rust built on C Core library and futures

gRPC-rs gRPC-rs is a Rust wrapper of gRPC Core. gRPC is a high performance, open source universal RPC framework that puts mobile and HTTP/2 first. Sta

TiKV Project 1.6k Jan 7, 2023
A µTP (Micro/uTorrent Transport Library) library implemented in Rust

rust-utp A Micro Transport Protocol library implemented in Rust. API documentation Overview The Micro Transport Protocol is a reliable transport proto

Ricardo Martins 134 Dec 11, 2022
A library to work with CIDRs in rust

ipnetwork This is a library to work with IPv4 and IPv6 CIDRs in Rust Run Clippy by doing rustup component add clippy cargo clippy Installation This c

Abhishek Chanda 98 Dec 12, 2022
Nanomsg library for Rust

Nanomsg Documentation Nanomsg is a modern messaging library that is the successor to ZeroMQ, written in C by Martin Sustrik and colleagues. The nanoms

Daniel Fagnan 371 Nov 18, 2022
A Constrained Application Protocol(CoAP) library implemented in Rust.

coap-rs A fast and stable Constrained Application Protocol(CoAP) library implemented in Rust. Features: CoAP core protocol RFC 7252 CoAP Observe optio

Covertness 170 Dec 19, 2022
Backroll is a pure Rust implementation of GGPO rollback networking library.

backroll-rs Backroll is a pure Rust implementation of GGPO rollback networking library. Development Status This is still in an untested alpha stage. A

Hourai Teahouse 273 Dec 28, 2022
A Rust library for parsing the SOME/IP network protocol (without payload interpretation).

someip_parse A Rust library for parsing the SOME/IP network protocol (without payload interpretation). Usage Add the following to your Cargo.toml: [de

Julian Schmid 18 Oct 31, 2022
A library for easily creating WebRTC data channel connections in Rust

Cyberdeck A library for easily creating WebRTC data channel connections in Rust.

RICHΛRD ΛNΛYΛ 34 Nov 10, 2022
Modrinth API is a simple library for using Modrinth's API in Rust projects

Ferinth is a simple library for using the Modrinth API in Rust projects. It uses reqwest as its HTTP(S) client and deserialises responses to typed structs using serde.

null 20 Dec 8, 2022
A Rust compiler plugin and support library to annotate overflow behavior

overflower This project contains a compiler plugin and supporting library to allow the programmer to annotate their code to declare how integer overfl

null 104 Nov 28, 2022
Dav-server-rs - Rust WebDAV server library. A fork of the webdav-handler crate.

dav-server-rs A fork of the webdav-handler-rs project. Generic async HTTP/Webdav handler Webdav (RFC4918) is defined as HTTP (GET/HEAD/PUT/DELETE) plu

messense 30 Dec 29, 2022
Peer-to-peer communications library for Rust based on QUIC protocol

qp2p Crate Documentation MaidSafe website SAFE Dev Forum SAFE Network Forum Overview This library provides an API to simplify common tasks when creati

MaidSafe 337 Dec 14, 2022
A BitTorrent V1 engine library for Rust (and currently Linux)

cratetorrent Cratetorrent is a Rust crate implementing the BitTorrent version 1 protocol. It can be used as a library and also provides a simple examp

null 401 Dec 28, 2022
Rust library that helps you change the domain of the link to another domain 🦀🔐

Rust library that helps you to change the domain of the link to another domain, the library helps with privacy. It can be used to change the domain of sites that do not care about privacy to another that does.

TheAwiteb 2 Mar 28, 2022
A generic Rust based Bigtable connection library implemented using gRPC

A generic Rust based Bigtable connection library refactored out the solana mono-repo so that can be shared for different applications.

Lijun Wang 3 Sep 25, 2022
A high performance/low-overhead OpenMetrics library for Rust

* * * EXPERIMENTAL * * * discreet-metrics A high-performance/low-overhead metrics library aiming to conform with OpenMetrics and to satisfy the follow

null 2 Sep 14, 2022
A library for writing type-safe Durable Objects in Rust.

do-proxy A library for writing type-safe Durable Objects (DOs) in Rust. With do-proxy you can: Easily write type-safe APIs for Durable Objects. Abstra

Fisher Darling 12 Dec 4, 2022
📡 Rust mDNS library designed with user interfaces in mind

?? Searchlight Searchlight is an mDNS server & client library designed to be simple, lightweight and easy to use, even if you just have basic knowledg

William 5 Jan 8, 2023
This is a UPnP client library for Rust.

UPnP Client This is a UPNP client library for Rust. Usage Add this to your Cargo.toml: [dependencies] upnp-client = "0.1" Example This example will pr

Tsiry Sandratraina 7 Feb 20, 2023