This repository has been archived by the owner on Sep 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Petr Horacek <[email protected]>
- Loading branch information
Showing
2 changed files
with
118 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
//! Manipulate persistent buffers with lengths that are exceeding memory capacity. | ||
//! | ||
//! # Requirements | ||
//! | ||
//! * Can handle loops from 32 sample length up to tens of minutes. | ||
//! * Allows simultaneous playback and recording. | ||
//! * Recorded audio is being saved even while recording is in progress. | ||
//! * Can immediatelly jump to the beginning of the sample and start playing. | ||
//! * Saving and loading can be done in another routine. | ||
//! | ||
//! # Architecture | ||
//! | ||
//! * No pages are owned by the buffer. | ||
//! * Caller is responsible for: | ||
//! * Providing new empty pages on request. | ||
//! * Persisting returned buffers. | ||
//! * Doing the two listed above with RT guarantees. | ||
//! * Each of the page contains: | ||
//! * Fixed-size array of data. | ||
//! * "Dirty" flag. | ||
//! * Length of recorded data. | ||
//! * Start address, relative to the parent sample. | ||
//! | ||
//! # Flow starting from fresh | ||
//! | ||
//! 1. Caller initializes empty page on Pool and passes Box to the buffer. | ||
//! 2. Buffer stores the Box in its struct. | ||
//! 3. Caller passes input audio, info about armed channels. | ||
//! 4. Buffer writes the audio into its active page and returns output audio. | ||
//! 5. Caller asks the buffer whether it is full, if it is, it takes its page and passes | ||
//! a fresh one to it again. | ||
//! 6. Since this was the first page, caller stores it in its cache. | ||
//! 7. Caller passess the dirty page to SD save queue. | ||
//! 8. Buffer continues recording, until its full again, swaps the page. | ||
//! 9. Caller passes the dirty page to save queue. | ||
//! 10. This continues for some time, until position reset is triggered. | ||
//! 11. With reset armed, caller will force buffer to return its current buffer, | ||
//! and it will pass a clone of the start page to it. | ||
//! 12. Caller recognizes that the next page is available on SD, it will send | ||
//! a request for SD loader to pull it. It should be eventually available | ||
//! in a loaded queue. | ||
//! 13. The loaded page is then passed to buffer instead of empty pages used before. | ||
//! 14. At some point, midway through the sample, recording stops. | ||
//! 15. Any new samples will be returned like before, except now they will not | ||
//! be dirty and thus just thrown away. | ||
//! | ||
//! # Flow starting from a loaded sample | ||
//! | ||
//! 1. The caller recognizes there is a sample available and it reads its length. | ||
//! 2. The caller loads the first page, queues fetching of the second one, if there is one. | ||
//! 3. The caller passes the first page to the buffer. | ||
//! 4. Business as usual. | ||
//! | ||
//! # Working with samples shorter than a single page | ||
//! | ||
//! 1. Buffer gets an inpulse to reset midway through the first page. | ||
//! 2. The caller takes page from the buffer, clones it for save queue, clones it for | ||
//! its own cache and passes it back to the buffer. | ||
//! | ||
//! # Optimizations | ||
//! | ||
//! * There may be a queue of empty pages, initialized in the background so they | ||
//! are readily available. | ||
|
||
// const MINIMAL_BLOCK_LENGTH = 32; | ||
|
||
// type Frame = ((f32, f32), (f32, f32), (f32, f32), (f32, f32)); | ||
|
||
struct DynamicBuffer { | ||
// first_block: Block, | ||
// // TODO: Keep moving blocks outside, so the dynamic buffer can be used | ||
// // with reference to populated block while the other one is being populated. | ||
// moving_blocks: (Block, Block), | ||
// active_block: ActiveBlock, | ||
// active_block_position: usize, | ||
// sample_length: usize, | ||
} | ||
|
||
impl DynamicBuffer { | ||
fn new() -> Self { | ||
Self {} | ||
} | ||
|
||
fn load(&mut self) {} | ||
} | ||
|
||
// struct Block { | ||
// length: (), | ||
// buffer: (), | ||
// } | ||
|
||
// enum ActiveBlock { | ||
// First, | ||
// Low, | ||
// High, | ||
// } | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn full_end_to_end() { | ||
let mut dynamic_buffer = DynamicBuffer::new(); | ||
|
||
let save_exists = true; | ||
if save_exists { | ||
dynamic_buffer.load(); | ||
} | ||
|
||
// TODO: play two passes | ||
// TODO: start recording while playing, pass input and say to which channels it should go | ||
// TODO: stop recording | ||
// TODO: play two passes, this time with recorded stuff too | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
#![no_std] | ||
|
||
mod dynamic_buffer; | ||
|
||
use core::convert::TryFrom; | ||
use core::fmt; | ||
|
||
|