Skip to content

Commit

Permalink
Merge pull request #134 from rust3ds/improve/docs
Browse files Browse the repository at this point in the history
Improve documentation and examples
  • Loading branch information
Meziu authored Aug 6, 2023
2 parents 54c6359 + 869cd50 commit 3e89922
Show file tree
Hide file tree
Showing 57 changed files with 3,066 additions and 791 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
matrix:
toolchain:
# Run against a "known good" nightly
- nightly-2023-01-13
- nightly-2023-05-31
# Check for breakage on latest nightly
- nightly

Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
strategy:
matrix:
toolchain:
- nightly-2023-01-13
- nightly-2023-05-31
- nightly
continue-on-error: ${{ matrix.toolchain == 'nightly' }}
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ Cargo.lock

# IDE files
.idea
.vscode
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = ["ctru-rs", "ctru-sys", "ctru-sys/bindgen-ctru-sys"]
default-members = ["ctru-rs", "ctru-sys"]
resolver = "2"

[patch.'https://github.com/rust3ds/ctru-rs']
# Make sure all dependencies use the local ctru-sys package
Expand Down
32 changes: 13 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
# ctru-rs

A Rust wrapper library for smealum's [ctrulib](https://github.com/smealum/ctrulib).
This repository is home of the `ctru-rs` project, which aims to bring full control of the Nintendo 3DS console to homebrew developers using Rust.

## Structure

This repository is organized as follows:

* `ctru-rs`: Safe, idiomatic wrapper around `ctru-sys`
* [`ctru-rs`](./ctru-rs/) - Safe, idiomatic wrapper around [`ctru-sys`](./ctru-sys/).
* [`ctru-sys`](./ctru-sys/) - Low-level, unsafe bindings to [`libctru`](https://github.com/devkitPro/libctru).

* `ctru-sys`: Low-level, unsafe bindings to ctrulib.
## Getting Started

This crate's version changes according to the version of `libctru`
used to generate the bindings, with the following convention:

* `libctru` version `X.Y.Z-W`
* `ctru-sys` version `XY.Z.P+X.Y.Z-W`

where `P` is usually 0 but may be incremented for fixes in e.g.
binding generation, `libc` dependency bump, etc.

It may be possible to build this crate against a different version of `libctru`,
but you may encounter linker errors or ABI issues. A build-time Cargo warning
(displayed when built with `-vv`) will be issued if the build script detects
a mismatch or is unable to check the installed `libctru` version.
Specific information about how to use the crates is present in the individual README for each package.
Have a look at `ctru-rs`' [README.md](./ctru-rs/README.md) for a broad overview.

## Original version

This project is based on the efforts the original authors:
* [Eidolon](https://github.com/HybridEidolon)
* [FenrirWolf](https://github.com/FenrirWolf)
This project is based on the efforts of the original authors:
* [Eidolon](https://github.com/HybridEidolon)
* [FenrirWolf](https://github.com/FenrirWolf)

The old version is archived [here](https://github.com/rust3ds/ctru-rs-old).

## License

This project is distributed under the Zlib license.
19 changes: 14 additions & 5 deletions ctru-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
[package]
authors = ["Rust3DS Org", "Ronald Kinard <[email protected]>"]
description = "A safe wrapper around smealum's ctrulib."
license = "Zlib"
name = "ctru-rs"
version = "0.7.1"
authors = ["Rust3DS Org", "Ronald Kinard <[email protected]>"]
description = "A safe wrapper around libctru"
repository = "https://github.com/rust3ds/ctru-rs"
keywords = ["3ds", "libctru"]
categories = ["os", "api-bindings", "hardware-support"]
exclude = ["examples"]
license = "Zlib"
edition = "2021"
rust-version = "1.64"
rust-version = "1.70"

[lib]
crate-type = ["rlib"]
Expand All @@ -18,7 +22,7 @@ const-zero = "0.1.0"
shim-3ds = { git = "https://github.com/rust3ds/shim-3ds.git" }
pthread-3ds = { git = "https://github.com/rust3ds/pthread-3ds.git" }
libc = "0.2.121"
bitflags = "1.0.0"
bitflags = "2.3.3"
widestring = "0.2.2"

[build-dependencies]
Expand All @@ -45,6 +49,11 @@ std-threads = []
[package.metadata.cargo-3ds]
romfs_dir = "examples/romfs"

[package.metadata.docs.rs]
default-target = "armv6k-nintendo-3ds"
targets = []
cargo-args = ["-Z", "build-std"]

[[example]]
name = "thread-basic"
required-features = ["std-threads"]
Expand Down
22 changes: 22 additions & 0 deletions ctru-rs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ctru-rs

Safe and idiomatic Rust wrapper around [`libctru`](https://github.com/devkitPro/libctru).

## Getting Started

Thoroughly read the [`ctru-rs` wiki](https://github.com/rust3ds/ctru-rs/wiki/Getting-Started) to meet the requirements
and to understand what it takes to develop homebrew software on the Nintendo 3DS family of consoles.
After that, you can simply add the crate as a dependency to your project and build your final binary by using [`cargo-3ds`](https://github.com/rust3ds/cargo-3ds)
or by manually compiling for the `armv6k-nintendo-3ds` target.

## Examples

Many examples to demonstrate the `ctru-rs` functionality are available in the [`examples`](./examples/) folder. Simply run them via

```bash
cargo 3ds run --example <example-name>
```

## License

This project is distributed under the Zlib license.
39 changes: 28 additions & 11 deletions ctru-rs/examples/audio-filters.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
//! Audio Filters example.
//!
//! This example showcases basic audio functionality using [`Ndsp`].
#![feature(allocator_api)]

use std::f32::consts::PI;

use ctru::linear::LinearAllocator;
use ctru::prelude::*;
use ctru::services::ndsp::{
wave::{Wave, WaveStatus},
wave::{Status, Wave},
AudioFormat, AudioMix, InterpolationType, Ndsp, OutputMode,
};

// Configuration for the NDSP process and channels.
const SAMPLE_RATE: usize = 22050;
const SAMPLES_PER_BUF: usize = SAMPLE_RATE / 10; // 2205
const BYTES_PER_SAMPLE: usize = AudioFormat::PCM16Stereo.size();
const AUDIO_WAVE_LENGTH: usize = SAMPLES_PER_BUF * BYTES_PER_SAMPLE;

// Note Frequencies
// Note frequencies.
const NOTEFREQ: [f32; 7] = [220., 440., 880., 1760., 3520., 7040., 14080.];

// The audio format is Stereo PCM16
// As such, a sample is made up of 2 "Mono" samples (2 * i16 = u32), one for each channel (left and right)
fn fill_buffer(audio_data: &mut [u8], frequency: f32) {
// The audio format is Stereo PCM16.
// As such, a sample is made up of 2 "Mono" samples (2 * i16), one for each channel (left and right).
let formatted_data = bytemuck::cast_slice_mut::<_, [i16; 2]>(audio_data);

for (i, chunk) in formatted_data.iter_mut().enumerate() {
Expand All @@ -44,8 +49,7 @@ fn main() {

let mut note: usize = 4;

// Filters

// Filter names to display.
let filter_names = [
"None",
"Low-Pass",
Expand All @@ -60,19 +64,26 @@ fn main() {
// We set up two wave buffers and alternate between the two,
// effectively streaming an infinitely long sine wave.

// We create a buffer on the LINEAR memory that will hold our audio data.
// It's necessary for the buffer to live on the LINEAR memory sector since it needs to be accessed by the DSP processor.
let mut audio_data1 = Box::new_in([0u8; AUDIO_WAVE_LENGTH], LinearAllocator);

// Fill the buffer with the first set of data. This simply writes a sine wave into the buffer.
fill_buffer(audio_data1.as_mut_slice(), NOTEFREQ[4]);

// Clone the original buffer to obtain an equal buffer on the LINEAR memory used for double buffering.
let audio_data2 = audio_data1.clone();

// Setup two wave info objects with the correct configuration and ownership of the audio data.
let mut wave_info1 = Wave::new(audio_data1, AudioFormat::PCM16Stereo, false);
let mut wave_info2 = Wave::new(audio_data2, AudioFormat::PCM16Stereo, false);

let mut ndsp = Ndsp::new().expect("Couldn't obtain NDSP controller");
// Setup the NDSP service and its configuration.

// This line isn't needed since the default NDSP configuration already sets the output mode to `Stereo`
let mut ndsp = Ndsp::new().expect("Couldn't obtain NDSP controller");
ndsp.set_output_mode(OutputMode::Stereo);

// Channel configuration. We use channel zero but any channel would do just fine.
let mut channel_zero = ndsp.channel(0).unwrap();
channel_zero.set_interpolation(InterpolationType::Linear);
channel_zero.set_sample_rate(SAMPLE_RATE as f32);
Expand All @@ -82,6 +93,7 @@ fn main() {
let mix = AudioMix::default();
channel_zero.set_mix(&mix);

// First set of queueing for the two buffers. The second one will only play after the first one has ended.
channel_zero.queue_wave(&mut wave_info1).unwrap();
channel_zero.queue_wave(&mut wave_info2).unwrap();

Expand All @@ -93,6 +105,8 @@ fn main() {
filter_names[filter as usize]
);

println!("\x1b[29;16HPress Start to exit");

let mut altern = true; // true is wave_info1, false is wave_info2

while apt.main_loop() {
Expand All @@ -101,14 +115,16 @@ fn main() {

if keys_down.contains(KeyPad::START) {
break;
} // break in order to return to hbmenu
}

// Note frequency controller using the buttons.
if keys_down.intersects(KeyPad::DOWN) {
note = note.saturating_sub(1);
} else if keys_down.intersects(KeyPad::UP) {
note = std::cmp::min(note + 1, NOTEFREQ.len() - 1);
}

// Filter controller using the buttons.
let mut update_params = false;
if keys_down.intersects(KeyPad::LEFT) {
filter -= 1;
Expand Down Expand Up @@ -139,22 +155,23 @@ fn main() {
}
}

// Double buffer alternation depending on the one used.
let current: &mut Wave = if altern {
&mut wave_info1
} else {
&mut wave_info2
};

// If the current buffer has finished playing, we can refill it with new data and re-queue it.
let status = current.status();
if let WaveStatus::Done = status {
if let Status::Done = status {
fill_buffer(current.get_buffer_mut().unwrap(), NOTEFREQ[note]);

channel_zero.queue_wave(current).unwrap();

altern = !altern;
}

//Wait for VBlank
gfx.wait_for_vblank();
}
}
24 changes: 14 additions & 10 deletions ctru-rs/examples/buttons.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! Buttons example.
//!
//! This example showcases how to retrieve button inputs from the console's HID.
use ctru::prelude::*;

fn main() {
Expand All @@ -18,28 +22,28 @@ fn main() {
// Scan for user input on the current frame.
hid.scan_input();

// Get information about which keys were held down on this frame
// Get information about which keys were held down on this frame.
let keys = hid.keys_held();

// We only want to print when the keys we're holding now are different
// from what they were on the previous frame
// from what they were on the previous frame.
if keys != old_keys {
// Clear the screen
// Clear the screen.
console.clear();

// We print these again because we just cleared the screen above
// We print these again because we just cleared the screen above.
println!("Hi there! Try pressing a button");
println!("\x1b[29;16HPress Start to exit");

// Move the cursor back to the top of the screen
// Move the cursor back to the top of the screen.
println!("\x1b[3;0H");

// Print to the screen depending on which keys were held.
//
// The .contains() method checks for all of the provided keys,
// and the .intersects() method checks for any of the provided keys.
// The `.contains()` method checks for all of the provided keys,
// and the `.intersects()` method checks for any of the provided keys.
//
// You can also use the .bits() method to do direct comparisons on
// You can also use the `.bits()` method to do direct comparisons on
// the underlying bits

if keys.contains(KeyPad::A) {
Expand All @@ -54,13 +58,13 @@ fn main() {
if keys.intersects(KeyPad::L | KeyPad::R | KeyPad::ZL | KeyPad::ZR) {
println!("You held a shoulder button!");
}
if keys.intersects(KeyPad::START) {
if keys.contains(KeyPad::START) {
println!("See ya!");
break;
}
}

// Save our current key presses for the next frame
// Save our current key presses for the next frame.
old_keys = keys;

gfx.wait_for_vblank();
Expand Down
Loading

0 comments on commit 3e89922

Please sign in to comment.