# `ot-tools-interfaces`

Interfaces to interact with Elektron Octatrack project data without as much boilerplate.

# Warnings

- There will be bugs.
- All `ot-tools*` crates only work with OS version 1.40 A/B/C.
- Every function/module/type is currently in an 'unstable' state and will 
  possibly (probably) change in the future.
- There will be bugs.

# License
All ot-tools crates are licensed under the 
[GNU GPL v3.0 license](https://www.gnu.org/licenses/gpl-3.0.en.html).

# `OctatrackProject`

- Reads all project files for a given Octatrack project. 
- Access to all data through `thing()`, `thing_ref()` and `thing_mut()` methods
- Can read existing projects, create new projects or clone existing projects.

```rust
use std::path::PathBuf;
use ot_tools_io::HasChecksumField;
use ot_tools_interfaces::projects::OctatrackProject;
use ot_tools_interfaces::projects::OtToolsInterfacesError;
use ot_tools_io::identifiers::{SavedState, BankId};

fn main() -> Result<(), OtToolsInterfacesError> {
      
    // -----------------------------------------------------------------------
    // read only inspection
    // -----------------------------------------------------------------------
    
    let project_readonly = OctatrackProject::load(&project_path)?;
    project_readonly.working_ref().validate()?;
    
    assert!(
        project_readonly
            .working_ref()
            .banks_ref()
            .id_ref(&BankId::A)
            .is_default()?
    );
    
    println!(
        "bank header: {:?}",
        project_readonly
            .working_ref()
            .banks_ref()
            .id_ref(&BankId::A)
            .data_ref()
            .header
    );
    
    // -----------------------------------------------------------------------
    // read and write
    // -----------------------------------------------------------------------
    
    let mut project_readwrite = OctatrackProject::load(&project_path).unwrap();
    project_readwrite.working_ref().validate()?;
    
    // change the first bank's checksum to an incorrect value
    let bank_data_mut = project_readwrite.working_mut().banks_mut().id_mut(&BankId::A);
    bank_data_mut.data_mut().checksum = 0;
    
    // should not pass validation
    assert!(project_readwrite.working_mut().validate().is_err());
    
    // but we can still write the data
    // -- checksums are updated on low-level file writes
    // -- note: file headers/patch versions etc. are NOT updated on low-level file writes!
    project_readwrite.save(false)?;
    
    // change another value in the first bank, calculate and reset the checksum value
    let bank_data_mut = project_readwrite.working_mut().banks_mut().id_mut(&BankId::A);
    bank_data_mut.data_mut().parts_edited_bitmask = 15;
    let new_checksum = bank_data_mut.data_mut().calculate_checksum()?;
    bank_data_mut.data_mut().checksum = new_checksum;
    
    // should pass validation
    assert!(project_readwrite.working_mut().validate().is_ok());
    
    // write the data
    project_readwrite.save(false)?;

    Ok(())
}

```

# Example: `AudioFile`

- Allows you to read in audio files based on the file extension
- note that reading stereo audio data will result in an interleaved samples field in the `AudioFile`

```rust
use ot_tools_interfaces::OtToolsInterfacesError;
use ot_tools_interfaces::audio::AudioFile;
use std::path::PathBuf;

fn main() -> Result<(), OtToolsInterfacesError> {

    // read aiff data
    let aiff = AudioFile::read_by_ext(&PathBuf::from("my_filename.aiff"))?;
    let aiff = AudioFile::read_aiff(&PathBuf::from("my_filename.aiff"))?;

    // read wav data
    let wav = AudioFile::read_by_ext(&PathBuf::from("my_filename.wav"))?;
    let wav = AudioFile::read_wav(&PathBuf::from("my_filename.wav"))?;

    Ok(())
}
```

# Example: `LazySampleFilePair`
Reading in hundreds of samples at the same time can be very costly memory-wise.
The `LazySampleFilePair` type allows you to "read" a sample file pair (audio file and `.ot` file)
with the underlying file data not read until accessed.

```rust
use ot_tools_interfaces::OtToolsInterfacesError;
use ot_tools_interfaces::sample_files::LazySampleFilePair;
use std::path::PathBuf;

fn main() -> Result<(), OtToolsInterfacesError> {
    let mut pair = LazySampleFilePair::load("my_audio_file");

    // file data read on first access to one of the "data" methods
    let audio_file_data = pair.audio_file_ref().data_ref()?;

    // uncache the data
    pair.uncache();
  
    Ok(())
}
```

# Todo: Sample Pools

I'd like to build on the `LazySampleFilePair` types etc to be able to provide an
interface for some `SamplePool` type, or two distinct types `AudioPool` and 
`ProjectPool`.

Trying to link an `AudioPool` to some `OctatrackProject` is fraught with 
dangerous "stuff".
It'd be nice to be able to do that, but i think it's probably best not to.
Also, reading in all sample files in a project directory is quite expensive when 
loading an `OctatrackProject`.

So, if i do get around to creating an interface for sample pool types, they will
probably be disconnected from any project types.