use crate::{
error::{GpResult, ToPrimitiveGp},
io::primitive::*,
model::song::*,
};
pub const CHANNEL_DEFAULT_NAMES: [&str; 128] = [
"Piano",
"Bright Piano",
"Electric Grand",
"Honky Tonk Piano",
"Electric Piano 1",
"Electric Piano 2",
"Harpsichord",
"Clavinet",
"Celesta",
"Glockenspiel",
"Music Box",
"Vibraphone",
"Marimba",
"Xylophone",
"Tubular Bell",
"Dulcimer",
"Hammond Organ",
"Perc Organ",
"Rock Organ",
"Church Organ",
"Reed Organ",
"Accordion",
"Harmonica",
"Tango Accordion",
"Nylon Str Guitar",
"Steel String Guitar",
"Jazz Electric Gtr",
"Clean Guitar",
"Muted Guitar",
"Overdrive Guitar",
"Distortion Guitar",
"Guitar Harmonics",
"Acoustic Bass",
"Fingered Bass",
"Picked Bass",
"Fretless Bass",
"Slap Bass 1",
"Slap Bass 2",
"Syn Bass 1",
"Syn Bass 2",
"Violin",
"Viola",
"Cello",
"Contrabass",
"Tremolo Strings",
"Pizzicato Strings",
"Orchestral Harp",
"Timpani",
"Ensemble Strings",
"Slow Strings",
"Synth Strings 1",
"Synth Strings 2",
"Choir Aahs",
"Voice Oohs",
"Syn Choir",
"Orchestra Hit",
"Trumpet",
"Trombone",
"Tuba",
"Muted Trumpet",
"French Horn",
"Brass Ensemble",
"Syn Brass 1",
"Syn Brass 2",
"Soprano Sax",
"Alto Sax",
"Tenor Sax",
"Baritone Sax",
"Oboe",
"English Horn",
"Bassoon",
"Clarinet",
"Piccolo",
"Flute",
"Recorder",
"Pan Flute",
"Bottle Blow",
"Shakuhachi",
"Whistle",
"Ocarina",
"Syn Square Wave",
"Syn Square Wave",
"Syn Calliope",
"Syn Chiff",
"Syn Charang",
"Syn Voice",
"Syn Fifths Saw",
"Syn Brass and Lead",
"Fantasia",
"Warm Pad",
"Polysynth",
"Space Vox",
"Bowed Glass",
"Metal Pad",
"Halo Pad",
"Sweep Pad",
"Ice Rain",
"Soundtrack",
"Crystal",
"Atmosphere",
"Brightness",
"Goblins",
"Echo Drops",
"Sci Fu",
"Sitar",
"Banjo",
"Shamisen",
"Koto",
"Kalimba",
"Bag Pipe",
"Fiddle",
"Shanai",
"Tinkle Bell",
"Agogo",
"Steel Drums",
"Woodblock",
"Taiko Drum",
"Melodic Tom",
"Syn Drum",
"Reverse Cymbal",
"Guitar Fret Noise",
"Breath Noise",
"Seashore",
"Bird",
"Telephone",
"Helicopter",
"Applause",
"Gunshot",
];
pub const DEFAULT_PERCUSSION_CHANNEL: u8 = 9;
#[derive(Debug, Copy, Clone)]
pub struct MidiChannel {
pub channel: u8,
pub effect_channel: u8,
pub instrument: i32,
pub volume: i8,
pub balance: i8,
pub chorus: i8,
pub reverb: i8,
pub phaser: i8,
pub tremolo: i8,
pub bank: u8,
}
impl Default for MidiChannel {
fn default() -> Self {
MidiChannel {
channel: 0,
effect_channel: 1,
instrument: 25,
volume: 104,
balance: 64,
chorus: 0,
reverb: 0,
phaser: 0,
tremolo: 0,
bank: 0,
}
}
}
impl MidiChannel {
pub(crate) fn is_percussion_channel(&self) -> bool {
(self.channel % 16) == DEFAULT_PERCUSSION_CHANNEL
}
pub(crate) fn set_instrument(&mut self, instrument: i32) {
if instrument == -1 && self.is_percussion_channel() {
self.instrument = 0;
} else {
self.instrument = instrument;
}
}
pub(crate) fn _get_instrument(self) -> i32 {
self.instrument
}
}
pub trait SongMidiOps {
fn read_midi_channels(&mut self, data: &[u8], seek: &mut usize) -> GpResult<()>;
fn read_midi_channel(
&self,
data: &[u8],
seek: &mut usize,
channel: u8,
) -> GpResult<MidiChannel>;
fn read_channel(&mut self, data: &[u8], seek: &mut usize) -> GpResult<usize>;
fn write_midi_channels(&self, data: &mut Vec<u8>);
}
impl SongMidiOps for Song {
fn read_midi_channels(&mut self, data: &[u8], seek: &mut usize) -> GpResult<()> {
for i in 0u8..64u8 {
self.channels.push(self.read_midi_channel(data, seek, i)?);
}
Ok(())
}
fn read_midi_channel(
&self,
data: &[u8],
seek: &mut usize,
channel: u8,
) -> GpResult<MidiChannel> {
let instrument = read_int(data, seek)?;
let mut c = MidiChannel {
channel,
effect_channel: channel,
..Default::default()
};
c.volume = read_signed_byte(data, seek)?;
c.balance = read_signed_byte(data, seek)?;
c.chorus = read_signed_byte(data, seek)?;
c.reverb = read_signed_byte(data, seek)?;
c.phaser = read_signed_byte(data, seek)?;
c.tremolo = read_signed_byte(data, seek)?;
c.set_instrument(instrument);
*seek += 2; Ok(c)
}
fn read_channel(&mut self, data: &[u8], seek: &mut usize) -> GpResult<usize> {
let index = read_int(data, seek)? - 1;
let effect_channel = read_int(data, seek)? - 1;
let channels_len = self.channels.len().to_i32_gp("channels length")?;
if 0 <= index && index < channels_len {
let idx = index.to_usize_gp("channel index")?;
if self.channels[idx].instrument < 0 {
self.channels[idx].instrument = 0;
}
if !self.channels[idx].is_percussion_channel() {
self.channels[idx].effect_channel = effect_channel.to_u8_gp("effect channel")?;
}
}
Ok(index.to_usize_gp("channel index")?)
}
fn write_midi_channels(&self, data: &mut Vec<u8>) {
for i in 0..self.channels.len() {
if self.channels[i].is_percussion_channel() && self.channels[i].instrument == 0 {
write_i32(data, -1);
} else {
write_i32(data, self.channels[i].instrument);
}
write_signed_byte(data, self.channels[i].volume);
write_signed_byte(data, self.channels[i].balance);
write_signed_byte(data, self.channels[i].chorus);
write_signed_byte(data, self.channels[i].reverb);
write_signed_byte(data, self.channels[i].phaser);
write_signed_byte(data, self.channels[i].tremolo);
write_placeholder_default(data, 2); }
}
}