use crate::{
io::WriteSimple,
message::{ChannelVoiceMessage, Message, RealTimeMessage, SystemCommonMessage},
status::{ChannelStatus, ChannelVoiceStatus, RequiredDataBytes, SystemCommonStatus},
};
pub struct Encoder<W> {
writer: W,
running_status: Option<ChannelVoiceStatus>,
}
impl<W> Encoder<W>
where
W: WriteSimple,
{
pub fn new(writer: W) -> Encoder<W> {
Encoder {
writer,
running_status: None,
}
}
pub fn write_message(&mut self, message: Message) -> Result<(), W::Error> {
let (maybe_status, data_size, data) = match message {
Message::ChannelVoice { channel, message } => {
let (status, data) = deconstruct_channel_voice_message(channel, message);
let new_running_status = Some(status);
let maybe_status = if new_running_status == self.running_status {
None
} else {
Some(status.into())
};
self.running_status = new_running_status;
(maybe_status, status.status.required_data_bytes(), data)
}
Message::SystemCommon(message) => {
let (status, data) = deconstruct_system_common_message(message);
(Some(status.into()), status.required_data_bytes(), data)
}
Message::RealTime(message) => {
let status_byte = encode_real_time(message);
return self.writer.write_all_simple(&[status_byte]);
}
Message::SystemExclusive => return self.writer.write_all_simple(&[0b1111_0000]),
};
let data = [data[0] & DATA_MASK, data[1] & DATA_MASK];
match (maybe_status, data_size) {
(None, RequiredDataBytes::D0) => Ok(()),
(None, RequiredDataBytes::D1) => self.writer.write_all_simple(&[data[0]]),
(None, RequiredDataBytes::D2) => self.writer.write_all_simple(&[data[0], data[1]]),
(Some(status_byte), RequiredDataBytes::D0) => {
self.writer.write_all_simple(&[status_byte])
}
(Some(status_byte), RequiredDataBytes::D1) => {
self.writer.write_all_simple(&[status_byte, data[0]])
}
(Some(status_byte), RequiredDataBytes::D2) => {
self.writer
.write_all_simple(&[status_byte, data[0], data[1]])
}
}
}
}
const DATA_MASK: u8 = 0b0111_1111;
fn deconstruct_channel_voice_message(
channel: u8,
message: ChannelVoiceMessage,
) -> (ChannelVoiceStatus, [u8; 2]) {
let (status, data) = match message {
ChannelVoiceMessage::NoteOff { note, velocity } => {
(ChannelStatus::NoteOff, [note, velocity])
}
ChannelVoiceMessage::NoteOn { note, velocity } => (ChannelStatus::NoteOn, [note, velocity]),
ChannelVoiceMessage::PolyphonicPressure { note, pressure } => {
(ChannelStatus::PolyphonicPressure, [note, pressure])
}
ChannelVoiceMessage::ControlChange { control, value } => {
(ChannelStatus::ControlChange, [control, value])
}
ChannelVoiceMessage::ProgramChange { program } => {
(ChannelStatus::ProgramChange, [program, 0])
}
ChannelVoiceMessage::ChannelPressure { pressure } => {
(ChannelStatus::ChannelPressure, [pressure, 0])
}
ChannelVoiceMessage::PitchBend { pitch_bend } => (
ChannelStatus::PitchBend,
[pitch_bend as u8, (pitch_bend >> 7) as u8],
),
};
let status = ChannelVoiceStatus { status, channel };
(status, data)
}
fn deconstruct_system_common_message(
message: SystemCommonMessage,
) -> (SystemCommonStatus, [u8; 2]) {
match message {
SystemCommonMessage::MTCQuarterFrame { data } => {
(SystemCommonStatus::MTCQuarterFrame, [data, 0])
}
SystemCommonMessage::SongPositionPointer { low, high } => {
(SystemCommonStatus::SongPositionPointer, [low, high])
}
SystemCommonMessage::SongSelect { song } => (SystemCommonStatus::SongSelect, [song, 0]),
SystemCommonMessage::Undefined1 => (SystemCommonStatus::Undefined1, [0, 0]),
SystemCommonMessage::Undefined2 => (SystemCommonStatus::Undefined2, [0, 0]),
SystemCommonMessage::TuneRequest => (SystemCommonStatus::TuneRequest, [0, 0]),
SystemCommonMessage::EOX => (SystemCommonStatus::EOX, [0, 0]),
}
}
fn encode_real_time(real_time: RealTimeMessage) -> u8 {
match real_time {
RealTimeMessage::TimingClock => 0b1111_1000,
RealTimeMessage::Undefined1 => 0b1111_1001,
RealTimeMessage::Start => 0b1111_1010,
RealTimeMessage::Continue => 0b1111_1011,
RealTimeMessage::Stop => 0b1111_1100,
RealTimeMessage::Undefined2 => 0b1111_1101,
RealTimeMessage::ActiveSensing => 0b1111_1110,
RealTimeMessage::SystemReset => 0b1111_1111,
}
}