Skip to content

Commit 78b64b9

Browse files
committed
Add some Audio APIs
1 parent b6dda07 commit 78b64b9

File tree

1 file changed

+148
-26
lines changed

1 file changed

+148
-26
lines changed

src/program.rs

Lines changed: 148 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Program Loading and Execution
22
3-
use crate::{fs, osprintln, refcell::CsRefCell, FILESYSTEM};
3+
use neotron_api::FfiByteSlice;
4+
5+
use crate::{fs, osprintln, refcell::CsRefCell, API, FILESYSTEM};
46

57
#[allow(unused)]
68
static CALLBACK_TABLE: neotron_api::Api = neotron_api::Api {
@@ -41,6 +43,8 @@ pub enum OpenHandle {
4143
///
4244
/// This is the default state for handles.
4345
Closed,
46+
/// Represents the audio device,
47+
Audio,
4448
}
4549

4650
/// The open handle table
@@ -334,6 +338,17 @@ impl TransientProgramArea {
334338
}
335339
}
336340

341+
/// Store an open handle, or fail if we're out of space
342+
fn allocate_handle(h: OpenHandle) -> Result<usize, OpenHandle> {
343+
for (idx, slot) in OPEN_HANDLES.lock().iter_mut().enumerate() {
344+
if matches!(*slot, OpenHandle::Closed) {
345+
*slot = h;
346+
return Ok(idx);
347+
}
348+
}
349+
Err(h)
350+
}
351+
337352
/// Open a file, given a path as UTF-8 string.
338353
///
339354
/// If the file does not exist, or is already open, it returns an error.
@@ -344,6 +359,19 @@ extern "C" fn api_open(
344359
path: neotron_api::FfiString,
345360
_flags: neotron_api::file::Flags,
346361
) -> neotron_api::Result<neotron_api::file::Handle> {
362+
// Check for special devices
363+
if path.as_str().eq_ignore_ascii_case("AUDIO:") {
364+
match allocate_handle(OpenHandle::Audio) {
365+
Ok(n) => {
366+
return neotron_api::Result::Ok(neotron_api::file::Handle::new(n as u8));
367+
}
368+
Err(_f) => {
369+
return neotron_api::Result::Err(neotron_api::Error::OutOfMemory);
370+
}
371+
}
372+
}
373+
374+
// OK, let's assume it's a file relative to the root of our one and only volume
347375
let f = match FILESYSTEM.open_file(path.as_str(), embedded_sdmmc::Mode::ReadOnly) {
348376
Ok(f) => f,
349377
Err(fs::Error::Io(embedded_sdmmc::Error::NotFound)) => {
@@ -355,19 +383,9 @@ extern "C" fn api_open(
355383
};
356384

357385
// 1. Put the file into the open handles array and get the index (or return an error)
358-
let mut result = None;
359-
for (idx, slot) in OPEN_HANDLES.lock().iter_mut().enumerate() {
360-
if matches!(*slot, OpenHandle::Closed) {
361-
result = Some(idx);
362-
*slot = OpenHandle::File(f);
363-
break;
364-
}
365-
}
366-
367-
// 2. give the caller the index into the open handles array
368-
match result {
369-
Some(n) => neotron_api::Result::Ok(neotron_api::file::Handle::new(n as u8)),
370-
None => neotron_api::Result::Err(neotron_api::Error::OutOfMemory),
386+
match allocate_handle(OpenHandle::File(f)) {
387+
Ok(n) => neotron_api::Result::Ok(neotron_api::file::Handle::new(n as u8)),
388+
Err(_f) => neotron_api::Result::Err(neotron_api::Error::OutOfMemory),
371389
}
372390
}
373391

@@ -391,8 +409,11 @@ extern "C" fn api_write(
391409
buffer: neotron_api::FfiByteSlice,
392410
) -> neotron_api::Result<()> {
393411
let mut open_handles = OPEN_HANDLES.lock();
394-
match open_handles.get_mut(fd.value() as usize) {
395-
Some(OpenHandle::StdErr | OpenHandle::Stdout) => {
412+
let Some(h) = open_handles.get_mut(fd.value() as usize) else {
413+
return neotron_api::Result::Err(neotron_api::Error::BadHandle);
414+
};
415+
match h {
416+
OpenHandle::StdErr | OpenHandle::Stdout => {
396417
// Treat stderr and stdout the same
397418
let mut guard = crate::VGA_CONSOLE.lock();
398419
if let Some(console) = guard.as_mut() {
@@ -405,11 +426,27 @@ extern "C" fn api_write(
405426
}
406427
neotron_api::Result::Ok(())
407428
}
408-
Some(OpenHandle::File(f)) => match f.write(buffer.as_slice()) {
429+
OpenHandle::File(f) => match f.write(buffer.as_slice()) {
409430
Ok(_) => neotron_api::Result::Ok(()),
410431
Err(_e) => neotron_api::Result::Err(neotron_api::Error::DeviceSpecific),
411432
},
412-
Some(OpenHandle::StdIn | OpenHandle::Closed) | None => {
433+
OpenHandle::Audio => {
434+
let api = API.get();
435+
let mut slice = buffer.as_slice();
436+
// loop until we've sent all of it
437+
while !slice.is_empty() {
438+
let result = unsafe { (api.audio_output_data)(FfiByteSlice::new(slice)) };
439+
let this_time = match result {
440+
neotron_common_bios::FfiResult::Ok(n) => n,
441+
neotron_common_bios::FfiResult::Err(_e) => {
442+
return neotron_api::Result::Err(neotron_api::Error::DeviceSpecific);
443+
}
444+
};
445+
slice = &slice[this_time..];
446+
}
447+
neotron_api::Result::Ok(())
448+
}
449+
OpenHandle::StdIn | OpenHandle::Closed => {
413450
neotron_api::Result::Err(neotron_api::Error::BadHandle)
414451
}
415452
}
@@ -423,16 +460,19 @@ extern "C" fn api_read(
423460
mut buffer: neotron_api::FfiBuffer,
424461
) -> neotron_api::Result<usize> {
425462
let mut open_handles = OPEN_HANDLES.lock();
426-
match open_handles.get_mut(fd.value() as usize) {
427-
Some(OpenHandle::StdIn) => {
463+
let Some(h) = open_handles.get_mut(fd.value() as usize) else {
464+
return neotron_api::Result::Err(neotron_api::Error::BadHandle);
465+
};
466+
match h {
467+
OpenHandle::StdIn => {
428468
if let Some(buffer) = buffer.as_mut_slice() {
429469
let count = { crate::STD_INPUT.lock().get_data(buffer) };
430470
Ok(count).into()
431471
} else {
432472
neotron_api::Result::Err(neotron_api::Error::DeviceSpecific)
433473
}
434474
}
435-
Some(OpenHandle::File(f)) => {
475+
OpenHandle::File(f) => {
436476
let Some(buffer) = buffer.as_mut_slice() else {
437477
return neotron_api::Result::Err(neotron_api::Error::InvalidArg);
438478
};
@@ -441,7 +481,17 @@ extern "C" fn api_read(
441481
Err(_e) => neotron_api::Result::Err(neotron_api::Error::DeviceSpecific),
442482
}
443483
}
444-
Some(OpenHandle::Stdout | OpenHandle::StdErr | OpenHandle::Closed) | None => {
484+
OpenHandle::Audio => {
485+
let api = API.get();
486+
let result = unsafe { (api.audio_input_data)(buffer) };
487+
match result {
488+
neotron_common_bios::FfiResult::Ok(n) => neotron_api::Result::Ok(n),
489+
neotron_common_bios::FfiResult::Err(_e) => {
490+
neotron_api::Result::Err(neotron_api::Error::DeviceSpecific)
491+
}
492+
}
493+
}
494+
OpenHandle::Stdout | OpenHandle::StdErr | OpenHandle::Closed => {
445495
neotron_api::Result::Err(neotron_api::Error::BadHandle)
446496
}
447497
}
@@ -483,12 +533,84 @@ extern "C" fn api_rename(
483533
}
484534

485535
/// Perform a special I/O control operation.
536+
///
537+
/// # Audio Devices
538+
///
539+
/// * `0` - get output sample rate/format (0xN000_0000_<sample_rate_u32>) where N indicates the sample format
540+
/// * N = 0 => Eight bit mono, one byte per sample
541+
/// * N = 1 => Eight bit stereo, two byte per samples
542+
/// * N = 2 => Sixteen bit mono, two byte per samples
543+
/// * N = 3 => Sixteen bit stereo, four byte per samples
544+
/// * `1` - set output sample rate/format
545+
/// * As above
546+
/// * `2` - get output sample space available
547+
/// * Gets a value in bytes
486548
extern "C" fn api_ioctl(
487-
_fd: neotron_api::file::Handle,
488-
_command: u64,
489-
_value: u64,
549+
fd: neotron_api::file::Handle,
550+
command: u64,
551+
value: u64,
490552
) -> neotron_api::Result<u64> {
491-
neotron_api::Result::Err(neotron_api::Error::Unimplemented)
553+
let mut open_handles = OPEN_HANDLES.lock();
554+
let Some(h) = open_handles.get_mut(fd.value() as usize) else {
555+
return neotron_api::Result::Err(neotron_api::Error::BadHandle);
556+
};
557+
let api = API.get();
558+
match (h, command) {
559+
(OpenHandle::Audio, 0) => {
560+
// Getting sample rate
561+
let neotron_common_bios::FfiResult::Ok(config) = (api.audio_output_get_config)() else {
562+
return neotron_api::Result::Err(neotron_api::Error::DeviceSpecific);
563+
};
564+
let mut result: u64 = config.sample_rate_hz as u64;
565+
let nibble = match config.sample_format.make_safe() {
566+
Ok(neotron_common_bios::audio::SampleFormat::EightBitMono) => 0,
567+
Ok(neotron_common_bios::audio::SampleFormat::EightBitStereo) => 1,
568+
Ok(neotron_common_bios::audio::SampleFormat::SixteenBitMono) => 2,
569+
Ok(neotron_common_bios::audio::SampleFormat::SixteenBitStereo) => 3,
570+
_ => {
571+
return neotron_api::Result::Err(neotron_api::Error::DeviceSpecific);
572+
}
573+
};
574+
result |= nibble << 60;
575+
neotron_api::Result::Ok(result)
576+
}
577+
(OpenHandle::Audio, 1) => {
578+
// Setting sample rate
579+
let sample_rate = value as u32;
580+
let format = match value >> 60 {
581+
0 => neotron_common_bios::audio::SampleFormat::EightBitMono,
582+
1 => neotron_common_bios::audio::SampleFormat::EightBitStereo,
583+
2 => neotron_common_bios::audio::SampleFormat::SixteenBitMono,
584+
3 => neotron_common_bios::audio::SampleFormat::SixteenBitStereo,
585+
_ => {
586+
return neotron_api::Result::Err(neotron_api::Error::InvalidArg);
587+
}
588+
};
589+
let config = neotron_common_bios::audio::Config {
590+
sample_format: format.make_ffi_safe(),
591+
sample_rate_hz: sample_rate,
592+
};
593+
match (api.audio_output_set_config)(config) {
594+
neotron_common_bios::FfiResult::Ok(_) => {
595+
osprintln!("audio {}, {:?}", sample_rate, format);
596+
neotron_api::Result::Ok(0)
597+
}
598+
neotron_common_bios::FfiResult::Err(_) => {
599+
neotron_api::Result::Err(neotron_api::Error::DeviceSpecific)
600+
}
601+
}
602+
}
603+
(OpenHandle::Audio, 2) => {
604+
// Setting sample space
605+
match (api.audio_output_get_space)() {
606+
neotron_common_bios::FfiResult::Ok(n) => neotron_api::Result::Ok(n as u64),
607+
neotron_common_bios::FfiResult::Err(_) => {
608+
neotron_api::Result::Err(neotron_api::Error::DeviceSpecific)
609+
}
610+
}
611+
}
612+
_ => neotron_api::Result::Err(neotron_api::Error::InvalidArg),
613+
}
492614
}
493615

494616
/// Open a directory, given a path as a UTF-8 string.

0 commit comments

Comments
 (0)