Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Sound editor #9

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/bin/ui_demo/sprite_sheet.ppm

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/bin/ui_demo/sprite_sheet.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
70007777011D07770111077701110777700077777777777777777777777777770000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000FF0000FF0F00FF000F00FF000F0000FF0F000000FF000000000000000000000FF000000FFF00000F00000FFF0000FFFF0000FFF000000000000
000000000FFF0FF00FFF0FF00FFF00000000FFF00FF0FFF00FF0FFF000000000000000000FF00FF00FFFFFF00F0FF0F00FFFFFF000FFFF0000F00F0000000000
00000000000000000000000000000000000000000DDDDDDD60000000766666660000000000000000000000000000000000000000DDDDDDDD00000000D6666666
0000000000000000000000000000000000000000DDDDDDDD00000000D66666660000000000000000000000000000000000000000D0000000D000000000000000
Expand Down
84 changes: 71 additions & 13 deletions src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod brush_size;
pub mod key_combo;
mod notification;
pub mod serialize;
mod sound_editor;
mod undo_redo;

use crate::app::ElmApp;
Expand All @@ -19,6 +20,7 @@ use crate::Resources;
use crate::{Event, Key, KeyState, KeyboardEvent};
use itertools::Itertools;
use serialize::serialize;
use sound_editor::{Sound, SoundEditor};

use self::brush_size::{BrushSize, BrushSizeSelector};
use self::key_combo::KeyCombos;
Expand All @@ -34,6 +36,7 @@ pub(crate) struct Editor {
selected_sprite_page: usize,
sprite_button_state: button::State,
map_button_state: button::State,
sound_button_state: button::State,
tab_buttons: [button::State; 4],
color_selector_state: Vec<button::State>,
flag_buttons: Vec<button::State>,
Expand All @@ -52,18 +55,21 @@ pub(crate) struct Editor {
brush_size: BrushSize,
brush_size_state: brush_size::State,
editor_sprites: SpriteSheet,
sound_editor: SoundEditor,
}

#[derive(Debug, PartialEq, Clone, Copy)]
enum Tab {
pub(crate) enum Tab {
// ^ pub(crate) required because of Msg::ChangedTab
// We may want to fix that later (that is, we want this type to actually be private).
SpriteEditor,
MapEditor,
SoundEditor,
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum Msg {
SpriteTabClicked,
MapButtonClicked,
ChangedTab { new_tab: Tab },
ColorSelected(usize),
ColorHovered(usize),
SpritePageSelected(usize),
Expand Down Expand Up @@ -210,7 +216,8 @@ impl ElmApp for Editor {
cursor: cursor::State::new(),
sprite_button_state: button::State::new(),
map_button_state: button::State::new(),
tab: Tab::SpriteEditor,
sound_button_state: button::State::new(),
tab: Tab::SoundEditor,
selected_color: 0,
selected_sprite,
selected_sprite_page: 0,
Expand Down Expand Up @@ -246,6 +253,7 @@ impl ElmApp for Editor {
editor_sprites: load_editor_sprite_sheet()
// TODO: Change this to actually crash if it failed.
.unwrap_or_else(|_| SpriteSheet::new()),
sound_editor: SoundEditor::new(),
}
}

Expand All @@ -271,13 +279,9 @@ impl ElmApp for Editor {
_ => {}
}
}
Msg::SpriteTabClicked => {
self.tab = Tab::SpriteEditor;
println!("Sprite button clicked");
}
Msg::MapButtonClicked => {
self.tab = Tab::MapEditor;
println!("Map button clicked");
&Msg::ChangedTab { new_tab } => {
self.tab = new_tab;
println!("Changed tab: {:?}", new_tab);
}
Msg::ColorSelected(selected_color) => {
self.selected_color = *selected_color as u8;
Expand Down Expand Up @@ -355,6 +359,7 @@ impl ElmApp for Editor {
.push(top_bar(
&mut self.sprite_button_state,
&mut self.map_button_state,
&mut self.sound_button_state,
self.tab,
))
.push(match self.tab {
Expand All @@ -379,6 +384,9 @@ impl ElmApp for Editor {
self.show_sprites_in_map,
))
.into(),
Tab::SoundEditor => Tree::new()
.push(sound_editor::view(&mut self.sound_editor, &Sound::new()))
.into(),
})
.push(tools_row(
76,
Expand Down Expand Up @@ -414,6 +422,7 @@ impl ElmApp for Editor {
fn top_bar<'a>(
sprite_button_state: &'a mut button::State,
map_button_state: &'a mut button::State,
sound_button_state: &'a mut button::State,
tab: Tab,
) -> Element<'a, Msg> {
Tree::new()
Expand All @@ -422,6 +431,7 @@ fn top_bar<'a>(
}))
.push(sprite_editor_button(sprite_button_state, tab))
.push(map_editor_button(map_button_state, tab))
.push(sound_editor_button(sound_button_state, tab))
.into()
}

Expand Down Expand Up @@ -534,13 +544,46 @@ fn map_view<'a, 'b>(
fn sprite_editor_button(state: &mut button::State, tab: Tab) -> Element<'_, Msg> {
let selected = tab == Tab::SpriteEditor;

editor_button(state, 63, 110, 0, Msg::SpriteTabClicked, selected)
editor_button(
state,
Icons::SpriteEditor.to_raw(),
102,
0,
Msg::ChangedTab {
new_tab: Tab::SpriteEditor,
},
selected,
)
}

fn map_editor_button(state: &mut button::State, tab: Tab) -> Element<'_, Msg> {
let selected = tab == Tab::MapEditor;

editor_button(state, 62, 118, 0, Msg::MapButtonClicked, selected)
editor_button(
state,
Icons::MapEditor.to_raw(),
110,
0,
Msg::ChangedTab {
new_tab: Tab::MapEditor,
},
selected,
)
}

fn sound_editor_button(state: &mut button::State, tab: Tab) -> Element<'_, Msg> {
let selected = tab == Tab::SoundEditor;

editor_button(
state,
Icons::SoundEditor.to_raw(),
118,
0,
Msg::ChangedTab {
new_tab: Tab::SoundEditor,
},
selected,
)
}

fn editor_button(
Expand Down Expand Up @@ -959,3 +1002,18 @@ impl ShiftDirection {
}
}
}

#[derive(Clone, Copy)]
#[repr(usize)]
enum Icons {
MapEditor = 62,
SpriteEditor = 63,
SoundEditor = 60,
// MusicEditor = 61,
}

impl Icons {
fn to_raw(&self) -> usize {
*self as usize
}
}
128 changes: 128 additions & 0 deletions src/editor/sound_editor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use super::Msg;
use crate::ui::button::{self, Button};
use crate::ui::text::Text;
use crate::ui::{DrawFn, Element, Tree};

#[derive(Clone, Copy)]
enum BasicNote {
C = 0,
CSharp,
D,
DSharp,
E,
F,
FSharp,
G,
GSharp,
A,
ASharp,
B,
}

const NOTES: &'static [BasicNote] = {
use BasicNote::*;
&[C, CSharp, D, DSharp, E, F, FSharp, G, GSharp, A, ASharp, B]
};

#[derive(Clone, Copy)]
struct Note {
note: BasicNote,
octave: u8, // Actually goes from 0 to 5
}

impl Note {
fn y_offset(&self) -> i32 {
2 * (self.octave as i32 * 12 + self.note as i32)
}

fn available_notes() -> Vec<Self> {
(0..5)
.flat_map(|octave| NOTES.iter().copied().map(move |note| Self { note, octave }))
.collect()
}
}

// Range is actually 0..=7
// TODO: Pack later maybe?
type Volume = u8;

pub(crate) struct Sound {
notes: [(Note, Volume); SoundEditor::NOTES],
}

impl Sound {
pub fn new() -> Self {
let notes = [(
Note {
note: BasicNote::D,
octave: 3,
},
5,
); SoundEditor::NOTES];

Self { notes }
}
}

#[derive(Debug)]
pub(crate) struct SoundEditor {
buttons: Vec<Vec<button::State>>,
}

impl SoundEditor {
const NOTES: usize = 32;

pub(crate) fn new() -> Self {
Self {
buttons: vec![vec![button::State::new(); Note::available_notes().len()]; Self::NOTES],
}
}
}

pub(crate) fn view<'a>(sound_editor: &'a mut SoundEditor, sound: &Sound) -> Element<'a, Msg> {
let note_bars: Vec<_> = sound
.notes
.iter()
.zip(sound_editor.buttons.iter_mut())
.enumerate()
.map(|(index, ((note, _volume), button))| note_bar(button, index, note))
.collect();

Tree::with_children(note_bars)
.push(Text::new("THIS IS THE SOUND EDITOR", 20, 30, 7))
.into()
}

fn note_bar<'a, Msg: std::fmt::Debug + Copy + 'a>(
button_states: &'a mut [button::State],
index: usize,
note: &Note,
//on_click: &mut impl FnMut() -> Msg,
) -> Element<'a, Msg> {
let y_offset = note.y_offset();

let padding = 1;
let width = 2;
let x = index as i32 * 4 + padding;
let y = 80 - y_offset;

let mut buttons = vec![];
for (note, button_state) in Note::available_notes().into_iter().zip(button_states) {
buttons.push(
Button::new(
x,
y + note.y_offset(),
2,
2,
None, // Some(on_click()),
button_state,
DrawFn::new(|draw| {
draw.pset(0, 0, 7);
}),
)
.into(),
);
}

Tree::with_children(buttons).into()
}
24 changes: 16 additions & 8 deletions src/pico8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ impl Pico8 {
self.resources.sprite_flags.fget_n(sprite, flag)
}

// Pico8's fset(n, v)
// TODO: Reconsider naming.
pub fn fset_all(&mut self, sprite: usize, value: u8) -> u8 {
self.resources.sprite_flags.fset_all(sprite, value)
}

pub fn fset(&mut self, sprite: usize, flag: usize, value: bool) -> u8 {
self.resources.sprite_flags.fset(sprite, flag, value)
}
Expand Down Expand Up @@ -153,14 +159,6 @@ impl Pico8 {
self.draw_data.print(text, x, y, color);
}

// audio
pub fn sfx(&mut self, _sound_id: u8) {
todo!()
}
pub fn music(&mut self, _music_id: u8) {
todo!()
}

pub fn append_camera(&mut self, x: i32, y: i32) {
self.draw_data.append_camera(x, y);
}
Expand All @@ -170,6 +168,16 @@ impl Pico8 {
}
}

/// Audio
impl Pico8 {
pub fn sfx(&mut self, _sound_id: u8) {
todo!()
}
pub fn music(&mut self, _music_id: u8) {
todo!()
}
}

// Utility pub(crate) methods
impl Pico8 {
// TODO: Remove this `allow` when we use it in the editor.
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl Flags {
}

// Pico8's fset(n, v)
pub fn fset_all(&mut self, sprite: usize, flags: u8) -> u8 {
pub(crate) fn fset_all(&mut self, sprite: usize, flags: u8) -> u8 {
self.set(sprite, flags);

flags
Expand Down