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
#[cfg(feature = "std")]
use std::io::Write;

use crate::{
    message::{ChannelVoiceMessage, Message, RealTimeMessage, SystemCommonMessage},
    status::{ChannelStatus, ChannelVoiceStatus, RequiredDataBytes, SystemCommonStatus},
};

/// An encoder state machine
pub struct Encoder<W> {
    write: W,

    running_status: Option<ChannelVoiceStatus>,
}

impl Encoder<()> {
    #[cfg(feature = "std")]
    pub fn from_writer(
        mut writer: impl Write,
    ) -> Encoder<impl FnMut(&[u8]) -> Result<(), std::io::Error>> {
        Encoder::new(move |buf| writer.write_all(buf))
    }
}

impl<W, E> Encoder<W>
where
    W: FnMut(&[u8]) -> Result<(), E>,
{
    pub fn new(write: W) -> Encoder<W> {
        Encoder {
            write,
            running_status: None,
        }
    }

    pub fn write_message(&mut self, message: Message) -> Result<(), E> {
        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.write)(&[status_byte]);
            }
            Message::SystemExclusive => return (self.write)(&[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.write)(&[data[0]]),
            (None, RequiredDataBytes::D2) => (self.write)(&[data[0], data[1]]),
            (Some(status_byte), RequiredDataBytes::D0) => (self.write)(&[status_byte]),
            (Some(status_byte), RequiredDataBytes::D1) => (self.write)(&[status_byte, data[0]]),
            (Some(status_byte), RequiredDataBytes::D2) => {
                (self.write)(&[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,
    }
}