-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpattern.rs
More file actions
153 lines (125 loc) · 4.49 KB
/
pattern.rs
File metadata and controls
153 lines (125 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use crate::file::err;
use crate::project::event_command::NoteCommand;
use crate::project::note_event::{Note, NoteEvent, VolumeEffect};
use crate::project::pattern::{InPatternPosition, Pattern};
use std::io::Read;
/// reader should be buffered in some way and not do a syscall on every read call.
///
/// This function does a lot of read calls
pub fn parse_pattern<R: Read>(reader: &mut R) -> Result<Pattern, err::LoadErr> {
const PATTERN_HEADER_SIZE: usize = 8;
let (length, num_rows) = {
let mut header = [0; PATTERN_HEADER_SIZE];
reader.read_exact(&mut header)?;
(
u64::from(u16::from_le_bytes([header[0], header[1]])) + PATTERN_HEADER_SIZE as u64,
u16::from_le_bytes([header[2], header[3]]),
)
};
// a guarantee given by the impulse tracker "specs"
if length >= 64_000 {
return Err(err::LoadErr::Invalid);
}
if !(32..=200).contains(&num_rows) {
return Err(err::LoadErr::Invalid);
}
let mut pattern = Pattern::new(num_rows);
let mut row_num: u16 = 0;
let mut last_mask = [0; 64];
let mut last_event = [NoteEvent::default(); 64];
let mut scratch = [0; 1];
while row_num < num_rows {
let channel_variable = scratch[0];
if channel_variable == 0 {
row_num += 1;
continue;
}
let channel = (channel_variable - 1) & 63; // 64 channels, 0 based
let channel_id = usize::from(channel);
let maskvar = if (channel_variable & 0b10000000) != 0 {
reader.read_exact(&mut scratch)?;
let val = scratch[0];
last_mask[channel_id] = val;
val
} else {
last_mask[channel_id]
};
let mut event = NoteEvent::default();
// Note
if (maskvar & 0b00000001) != 0 {
reader.read_exact(&mut scratch)?;
// let note = match Note::new(scratch[0]) {
// Ok(n) => n,
// Err(_) => {
// // defect_handler(LoadDefect::OutOfBoundsValue);
// Note::default()
// }
// };
let note = Note::new(scratch[0]).unwrap_or_default();
event.note = note;
last_event[channel_id].note = note;
}
// Instrument / Sample
if (maskvar & 0b00000010) != 0 {
reader.read_exact(&mut scratch)?;
let instrument = scratch[0];
event.sample_instr = instrument;
last_event[channel_id].sample_instr = instrument;
}
// Volume
if (maskvar & 0b00000100) != 0 {
reader.read_exact(&mut scratch)?;
// let vol_pan = match vol_pan_raw.try_into() {
// Ok(v) => v,
// Err(_) => {
// // defect_handler(LoadDefect::OutOfBoundsValue);
// VolumeEffect::default()
// }
// };
let vol_pan = VolumeEffect::try_from(scratch[0]).unwrap_or_default();
last_event[channel_id].vol = vol_pan;
event.vol = vol_pan;
}
// Effect
if (maskvar & 0b00001000) != 0 {
reader.read_exact(&mut scratch)?;
let command = scratch[0];
reader.read_exact(&mut scratch)?;
let cmd_val = scratch[0];
// let cmd = match NoteCommand::try_from((command, cmd_val)) {
// Ok(cmd) => cmd,
// Err(_) => {
// // defect_handler(LoadDefect::OutOfBoundsValue);
// NoteCommand::default()
// }
// };
let cmd = NoteCommand::try_from((command, cmd_val)).unwrap_or_default();
last_event[channel_id].command = cmd;
event.command = cmd;
}
// Same note
if (maskvar & 0b00010000) != 0 {
event.note = last_event[channel_id].note;
}
// Same Instr / Sample
if (maskvar & 0b00100000) != 0 {
event.sample_instr = last_event[channel_id].sample_instr;
}
// Same volume
if (maskvar & 0b01000000) != 0 {
event.vol = last_event[channel_id].vol;
}
// Same Command
if (maskvar & 0b10000000) != 0 {
event.command = last_event[channel_id].command;
}
pattern.set_event(
InPatternPosition {
row: row_num,
channel,
},
event,
);
}
Ok(pattern)
}