Skip to content

Commit 10f47f1

Browse files
authored
Merge pull request #113 from Neotron-Compute/add-more-apps
Add neoplay and snake.
2 parents 9ecb42c + 078a69d commit 10f47f1

File tree

12 files changed

+1051
-1
lines changed

12 files changed

+1051
-1
lines changed

Cargo.lock

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[workspace]
22
members = [
33
"neotron-os",
4-
"utilities/flames",
4+
"utilities/*",
55
]
66
resolver = "2"
77
exclude = [

nbuild/src/main.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,37 @@ pub struct NBuildApp {
4242

4343
fn packages() -> Vec<nbuild::Package> {
4444
vec![
45+
// *** build system ***
4546
nbuild::Package {
4647
name: "nbuild",
4748
path: std::path::Path::new("./nbuild/Cargo.toml"),
4849
output_template: None,
4950
kind: nbuild::PackageKind::NBuild,
5051
testable: nbuild::Testable::All,
5152
},
53+
// *** utilities ***
5254
nbuild::Package {
5355
name: "flames",
5456
path: std::path::Path::new("./utilities/flames/Cargo.toml"),
5557
output_template: Some("./target/{target}/{profile}/flames"),
5658
kind: nbuild::PackageKind::Utility,
5759
testable: nbuild::Testable::No,
5860
},
61+
nbuild::Package {
62+
name: "neoplay",
63+
path: std::path::Path::new("./utilities/neoplay/Cargo.toml"),
64+
output_template: Some("./target/{target}/{profile}/neoplay"),
65+
kind: nbuild::PackageKind::Utility,
66+
testable: nbuild::Testable::No,
67+
},
68+
nbuild::Package {
69+
name: "snake",
70+
path: std::path::Path::new("./utilities/snake/Cargo.toml"),
71+
output_template: Some("./target/{target}/{profile}/snake"),
72+
kind: nbuild::PackageKind::Utility,
73+
testable: nbuild::Testable::No,
74+
},
75+
// *** OS ***
5976
nbuild::Package {
6077
name: "Neotron OS",
6178
path: std::path::Path::new("./neotron-os/Cargo.toml"),

utilities/neoplay/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "neoplay"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "MIT OR Apache-2.0"
6+
authors = ["Jonathan 'theJPster' Pallant <neotron@thejpster.org.uk>"]
7+
description = "4-channel ProTracker player for Neotro"
8+
9+
[dependencies]
10+
grounded = { version = "0.2.0", features = ["critical-section", "cas"] }
11+
neotracker = { git = "https://github.com/thejpster/neotracker.git", rev = "2ee7a85006a9461b876bdf47e45b6105437a38f6" }
12+
neotron-sdk = { workspace = true }
13+
14+
# See workspace for profile settings

utilities/neoplay/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Neoplay
2+
3+
A ProTracker MOD player for the Neotron Pico.
4+
5+
Runs at 11,025 Hz, quadrupling samples for the audio codec which runs at 44,100 Hz.
6+
7+
```console
8+
$ cargo build --release --target=thumbv6m-none-eabi
9+
$ cp ../target/thumbv6m-none-eabi/release/neoplay /media/USER/SDCARD/NEOPLAY.ELF
10+
11+
```
12+
13+
```console
14+
> load neoplay.elf
15+
> run airwolf.mod
16+
Loading "airwolf.mod"
17+
audio 44100, SixteenBitStereo
18+
Playing "airwolf.mod"
19+
20+
000 000000 12 00fe 0f04|-- ---- ----|-- ---- ----|-- ---- ----|
21+
000 000001 -- ---- ----|-- ---- ----|-- ---- ----|-- ---- ----|
22+
000 000002 -- ---- ----|-- ---- ----|-- ---- ----|-- ---- ----|
23+
000 000003 -- ---- ----|-- ---- ----|-- ---- ----|-- ---- ----|
24+
etc
25+
```
26+
27+
Here's a video of it in action: https://youtu.be/ONZhDrZsmDU

utilities/neoplay/build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
println!("cargo:rustc-link-arg-bin=neoplay=-Tneotron-cortex-m.ld");
3+
}

utilities/neoplay/src/main.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#![cfg_attr(target_os = "none", no_std)]
2+
#![cfg_attr(target_os = "none", no_main)]
3+
4+
use core::{fmt::Write, ptr::addr_of_mut};
5+
6+
const FILE_BUFFER_LEN: usize = 192 * 1024;
7+
static mut FILE_BUFFER: [u8; FILE_BUFFER_LEN] = [0u8; FILE_BUFFER_LEN];
8+
9+
mod player;
10+
11+
#[cfg(not(target_os = "none"))]
12+
fn main() {
13+
neotron_sdk::init();
14+
}
15+
16+
#[no_mangle]
17+
extern "C" fn neotron_main() -> i32 {
18+
if let Err(e) = real_main() {
19+
let mut stdout = neotron_sdk::stdout();
20+
let _ = writeln!(stdout, "Error: {:?}", e);
21+
1
22+
} else {
23+
0
24+
}
25+
}
26+
27+
fn real_main() -> Result<(), neotron_sdk::Error> {
28+
let mut stdout = neotron_sdk::stdout();
29+
let stdin = neotron_sdk::stdin();
30+
let Some(filename) = neotron_sdk::arg(0) else {
31+
return Err(neotron_sdk::Error::InvalidArg);
32+
};
33+
let _ = writeln!(stdout, "Loading {:?}...", filename);
34+
let path = neotron_sdk::path::Path::new(&filename)?;
35+
let f = neotron_sdk::File::open(path, neotron_sdk::Flags::empty())?;
36+
let file_buffer = unsafe {
37+
let file_buffer = &mut *addr_of_mut!(FILE_BUFFER);
38+
let n = f.read(file_buffer)?;
39+
&file_buffer[0..n]
40+
};
41+
drop(f);
42+
// Set 16-bit stereo, 44.1 kHz
43+
let dsp_path = neotron_sdk::path::Path::new("AUDIO:")?;
44+
let dsp = neotron_sdk::File::open(dsp_path, neotron_sdk::Flags::empty())?;
45+
if dsp.ioctl(1, 3 << 60 | 44100).is_err() {
46+
let _ = writeln!(stdout, "Failed to configure audio");
47+
return neotron_sdk::Result::Err(neotron_sdk::Error::DeviceSpecific);
48+
}
49+
50+
let mut player = match player::Player::new(file_buffer, 44100) {
51+
Ok(player) => player,
52+
Err(e) => {
53+
let _ = writeln!(stdout, "Failed to create player: {:?}", e);
54+
return Err(neotron_sdk::Error::InvalidArg);
55+
}
56+
};
57+
58+
let _ = writeln!(stdout, "Playing {:?}...", filename);
59+
let mut sample_buffer = [0u8; 1024];
60+
// loop some some silence to give us a head-start
61+
for _i in 0..11 {
62+
let _ = dsp.write(&sample_buffer);
63+
}
64+
65+
loop {
66+
for chunk in sample_buffer.chunks_exact_mut(4) {
67+
let (left, right) = player.next_sample(&mut stdout);
68+
let left_bytes = left.to_le_bytes();
69+
let right_bytes = right.to_le_bytes();
70+
chunk[0] = left_bytes[0];
71+
chunk[1] = left_bytes[1];
72+
chunk[2] = right_bytes[0];
73+
chunk[3] = right_bytes[1];
74+
}
75+
let _ = dsp.write(&sample_buffer);
76+
let mut in_buf = [0u8; 1];
77+
if player.is_finished() {
78+
break;
79+
}
80+
if stdin.read(&mut in_buf).is_ok() && in_buf[0].to_ascii_lowercase() == b'q' {
81+
break;
82+
}
83+
}
84+
85+
let _ = writeln!(stdout, "Bye!");
86+
87+
Ok(())
88+
}

0 commit comments

Comments
 (0)