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
154
155
156
157
158
159
160
161
162
163
164
165
166
use super::*;
#[derive(Clone, Debug)]
pub struct Sample {
/// Sample Name, null-terminated (but may also contain nulls)
pub name: Name,
/// DOS Filename, null-terminated
pub filename: DosFilename,
/// Global Volume
pub global_volume: u8,
/// Default Volume
pub default_volume: u8,
/// Default Panning
pub default_panning: u8,
/// Loop after the note has been released (Off ==) command, or directly after reaching the end
/// point if the sustain loop is off.
pub loop_: Option<SampleLoop>,
/// Loop after reching the end point while holding the note.
pub sustain_loop: Option<SampleLoop>,
/// C-5 playback frequency.
///
/// If set to the native sampling rate of the sound sample playing the sample at C-5 will play
/// it back unchanged.
pub samplerate_c5: u32,
/// Auto-Vibrato Rate (called Sweep in IT)
pub vibrato_speed: u8,
/// Auto-Vibrato Depth
pub vibrato_depth: u8,
/// Auto-Vibrato Sweep (called Rate in IT)
pub vibrato_rate: u8,
/// Auto-Vibrato Type
pub vibrato_type: u8,
/// Sample samples converted to a normalized `f32` representation (values from -1.0 to 1.0)
pub data: Option<Vec<f32>>,
}
pub(crate) struct SampleHeader {
pub(crate) name: Name,
pub(crate) filename: DosFilename,
pub(crate) global_volume: u8,
pub(crate) default_volume: u8,
pub(crate) default_panning: u8,
pub(crate) loop_: Option<SampleLoop>,
pub(crate) sustain_loop: Option<SampleLoop>,
pub(crate) samplerate_c5: u32,
pub(crate) vibrato_speed: u8,
pub(crate) vibrato_depth: u8,
pub(crate) vibrato_rate: u8,
pub(crate) vibrato_type: u8,
pub(crate) flags: SampleFlags,
pub(crate) data_offset: u32,
pub(crate) data_length: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct SampleLoop {
/// Start - offset into the sample in samples
pub start: u32,
/// End - offset into the sample in samples.
///
/// Must be always `>= start`
pub end: u32,
/// Bidirectional loop (also ping-pong loop)
///
/// * **false** after reading the sample at `end` offset the playback head jumps to the `start`
/// offset.
/// * **true** after reading the sample at `end` offset the playback reverses and continues with
/// the previous sample until it reaches the `start` offset, then it changes direction to
/// normal.
pub bidi: bool,
}
bitflags! {
pub(crate) struct SampleFlags: u16 {
// Originally `flags` field.
/// On = sample associated with header.
const DATA_PRESENT = 1 << 0;
/// On = 16 bit, Off = 8 bit.
const DATA_16BIT = 1 << 1;
/// On = stereo, Off = mono. Stereo samples not supported yet
const STEREO = 1 << 2;
/// On = compressed samples.
const COMPRESSED = 1 << 3;
/// On = Use loop
const LOOP = 1 << 4;
/// On = Use sustain loop
const SUSTAIN = 1 << 5;
/// On = Ping Pong loop, Off = Forwards loop
const BIDI_LOOP = 1 << 6;
/// On = Ping Pong Sustain loop, Off = Forwards Sustain loop
const BIDI_SUSTAIN = 1 << 7;
// Originally `cvt` field (shifted by additional 8 bits).
//
// From ITTECH.TXT:
// > Convert - bits other than bit 0 are used internally for the loading of alternative
// > formats.
/// Off: Samples are unsigned } IT 2.01 and below use unsigned samples
/// On: Samples are signed } IT 2.02 and above use signed samples
#[allow(clippy::identity_op)]
const DATA_SIGNED = 1 << (0 + 8);
// From OpenMPT:
// > ITTECH.TXT says these convert flags are "safe to ignore".
// > IT doesn't ignore them, though, so why should we? :)
/// Off: Intel lo-hi byte order for 16-bit samples
/// On: Motorola hi-lo byte order for 16-bit samples
const DATA_BIG_ENDIAN = 1 << (1 + 8);
/// Off: Samples are stored as PCM values
/// On: Samples are stored as Delta values
const DELTA = 1 << (2 + 8);
/// On: Samples are stored as byte delta values (for PTM loader)
const PTM8_TO_16 = 1 << (3 + 8);
// These seem to be missing from OpenMPT codebase, which hopefully means they're safe to
// ignore.
/// On: Samples are stored as TX-Wave 12-bit values
const TX_WAVE = 1 << (4 + 8);
/// On: Left/Right/All Stereo prompt
const STEREO_PROMPT = 1 << (5 + 8);
// These are OpenMPT extensions, ITTECH.TXT lists them as "Reserved"
/// FM instrument in MPTM
const OPL_INSTRUMENT = 1 << (6 + 8);
/// Keep MPTM sample on disk
const EXTERNAL_SAMPLE = 1 << (7 + 8);
// We're not sure what does this mean yet, but there is a frown next to it in the OpenMPT
// codebase so we assume we don't support it or something.
/// MODPlugin :(
const ADPCM_SAMPLE = 0xFF << 8;
}
}
impl SampleFlags {
pub(crate) fn from_parts(flags: u8, cvt: u8) -> SampleFlags {
let bits = u16::from(flags) | (u16::from(cvt) << 8);
SampleFlags::from_bits_truncate(bits)
}
}