Skip to main content

hmi2mid/
lib.rs

1//! Convert HMI files to standard MIDIs.
2//!
3//! Based on the HMI2MID code found in Daggerfall Jukebox,
4//! (<https://www.dfworkshop.net/downloads/old-tools/daggerfall-jukebox/>),
5//! which is itself based on the WinRipper HMI to MID functions by Peter
6//! Palowski (<http://www.blorp.com/~peter/>)
7
8const Q_MAX      : usize = 128;
9/// Note: the original code used half of the buffer for reading in input bytes,
10/// and here we are providing the input as a complete byte vector up-front, so
11/// the buffer may not really need to be this large
12const BUF_SIZE   : usize = 2 * 1024 * 1024;
13const HMP_TRACK0 : [u8; 19] = [
14  b'M', b'T', b'r', b'k', 0, 0, 0, 11, 0, 0xFF, 0x51, 0x03, 0x18, 0x7F, 0xFF,
15  0, 0xFF, 0x2F, 0
16];
17
18pub struct Hmi2mid {
19  bw    : u32,
20  buf   : Vec <u8>,
21  q_rel : Vec <QRel>
22}
23
24#[derive(Clone, Default)]
25struct QRel {
26  pub tm : u32,
27  pub ch : u8,
28  pub n  : u8
29}
30
31impl Hmi2mid {
32  pub fn new() -> Self {
33    Hmi2mid {
34      bw:    0,
35      buf:   vec![0; BUF_SIZE],
36      q_rel: vec![QRel::default(); Q_MAX]
37    }
38  }
39  pub fn hmi_rip (&mut self, source : Vec <u8>) -> Result <Vec <u8>, ()> {
40    let mut out = Vec::new();
41    out.resize(14, 0);
42    let bbuf = &source;
43    let mut ptr = 0;
44    loop {
45      if ptr >= bbuf.len() {
46        eprintln!("ptr >= bbuf.len()");
47        return Err(())
48      }
49      if &bbuf[ptr..ptr+4] == &b"TRAC"[..] {
50        break
51      }
52      ptr += 1;
53    }
54    ptr -= 8;
55    let nft : u32 = u32::from_le_bytes([
56      bbuf[0xE4],
57      bbuf[0xE4+1],
58      bbuf[0xE4+2],
59      bbuf[0xE4+3]
60    ]);
61    let mut ntrax : u16 = 1;
62    out.extend_from_slice (&HMP_TRACK0);
63    for _ in 0..nft {
64      if &bbuf[ptr..ptr+13] != &b"HMI-MIDITRACK"[..] {
65        eprintln!(r#"&bbuf[ptr..ptr+13] != &b"HMI-MIDITRACK"[..]"#);
66        return Err(())
67      }
68      ntrax += 1;
69      ptr += bbuf[ptr + 0x57] as usize;
70      let dd = self.do_track(&bbuf[ptr..]);
71      if dd == std::usize::MAX {
72        eprintln!("dd == std::usize::MAX");
73        return Err(())
74      }
75      ptr += dd;
76      out.extend_from_slice(&b"MTrk"[..]);
77      out.extend_from_slice(&self.bw.to_be_bytes());
78      out.extend_from_slice(&self.buf[0..self.bw as usize]);
79    }
80    &out[0..4].copy_from_slice(&b"MThd"[..]);
81    &out[4..8].copy_from_slice(&0x06000000u32.to_le_bytes());
82    // midi header
83    out[8] = 0x00;
84    out[9] = 0x01;
85    &out[10..12].copy_from_slice(&ntrax.to_be_bytes());
86    out[12] = 0x00;
87    out[13] = 0xC0;
88
89    Ok (out)
90  }
91
92  fn do_track (&mut self, t : &[u8]) -> usize {
93    for n in 0..Q_MAX {
94      self.q_rel[n].tm = std::u32::MAX;
95    }
96    let mut pt   : usize = 0;
97    let mut ct   : u32   = 0;
98    let mut tw   : u32   = 0;
99    let mut run  : u8    = 0;
100    let mut rrun : u8    = 0;
101    self.bw = 0;
102    loop {
103      ct += self.read_delta (t, &mut pt);
104      self.do_queue (ct, &mut tw, &mut rrun);
105      let mut c : u8 = t[pt];
106      if c == 0xFF {
107        self.do_queue (std::u32::MAX-1, &mut tw, &mut rrun);
108        if t[pt+1] == 0x2F {
109          pt += 3;
110          self.buf[self.bw as usize] = 0;
111          self.bw += 1;
112          self.buf[self.bw as usize] = 0xFF;
113          self.bw += 1;
114          self.buf[self.bw as usize] = 0x2F;
115          self.bw += 1;
116          self.buf[self.bw as usize] = 0;
117          self.bw += 1;
118          break
119        }
120        return std::usize::MAX
121      } else if c == 0xF0 {
122        self.bw += Self::write_delta (
123          &mut self.buf[self.bw as usize..], ct - tw);
124        tw = ct;
125        while t[pt] != 0xF7 {
126          self.buf[self.bw as usize] = t[pt];
127          self.bw += 1;
128          pt += 1;
129        }
130      } else if c == 0xFE {
131        c = t[pt+1];
132        if c == 0x10 {
133          pt += t[pt+4] as usize + 9;
134        } else if c == 0x14 {
135          pt += 4;
136        } else if c == 0x15 {
137          pt += 8;
138        } else {
139          return std::usize::MAX
140        }
141      } else {
142        self.bw += Self::write_delta (
143          &mut self.buf[self.bw as usize..], ct - tw);
144        tw = ct;
145        if c & 0x80 != 0 {
146          pt += 1;
147          run = c;
148        } else {
149          c = run;
150        }
151        if c != rrun {
152          self.buf[self.bw as usize] = c;
153          self.bw += 1;
154          rrun = c;
155        }
156        self.buf[self.bw as usize] = t[pt];
157        self.bw += 1;
158        pt += 1;
159        let c1 : u8 = c & 0xF0;
160        if c1 != 0xC0 && c1 != 0xD0 {
161          self.buf[self.bw as usize] = t[pt];
162          self.bw += 1;
163          pt += 1;
164        }
165        if c1 == 0x90 {
166          let b  : u8  = t[pt-2];
167          let tt : u32 = ct + self.read_delta (t, &mut pt);
168          self.q_add (c & 0xF, b, tt);
169        }
170      }
171    }
172    pt
173  }
174
175  fn q_add (&mut self, ch : u8, nt : u8, t : u32) {
176    let mut n : usize = 0;
177    while self.q_rel[n].tm != std::u32::MAX {
178      n += 1;
179    }
180    self.q_rel[n] = QRel {
181      tm: t,
182      n: nt,
183      ch
184    };
185  }
186
187  fn write_delta (t : &mut [u8], dt : u32) -> u32 {
188    let mut tl  : isize = 3;
189    let mut pos : usize = 0;
190    if dt != 0 {
191      while dt >> (7*tl) == 0 {
192        tl -= 1;
193      }
194      loop {
195        t[pos] = (((dt >> (7*tl)) & 0x7F) | 0x80) as u8;
196        tl -= 1;
197        pos += 1;
198        if tl < 0 {
199          break
200        }
201      }
202      t[pos-1] &= 0x7F;
203    } else {
204      t[pos] = 0;
205      pos += 1;
206    }
207    pos as u32
208  }
209
210  fn read_delta (&self, t : &[u8], p : &mut usize) -> u32 {
211    let mut d : u32 = 0;
212    let mut b;
213    loop {
214      b = t[*p];
215      *p += 1;
216      d = (d << 7) | (b & 0x7F) as u32;
217      if b & 0x80 == 0 {
218        break
219      }
220    }
221    d
222  }
223
224  fn do_queue (&mut self, ct : u32, tw : &mut u32, run : &mut u8) {
225    let mut mt : u32;
226    let mut nn : usize = std::usize::MAX;
227    loop {
228      mt = std::u32::MAX;
229      for n in 0..Q_MAX {
230        if self.q_rel[n].tm < mt {
231          nn = n;
232          mt = self.q_rel[n].tm;
233        }
234      }
235      if mt > ct {
236        return
237      }
238      debug_assert!(nn != std::usize::MAX);
239      self.bw += Self::write_delta (
240        &mut self.buf[self.bw as usize..], mt - *tw);
241      *tw = mt;
242      let e : u8 = self.q_rel[nn].ch | 0x90;
243      if e != *run {
244        self.buf[self.bw as usize] = e;
245        self.bw += 1;
246        *run = e;
247      }
248      self.buf[self.bw as usize] = self.q_rel[nn].n;
249      self.bw += 1;
250      self.buf[self.bw as usize] = 0;
251      self.bw += 1;
252      self.q_rel[nn].tm = std::u32::MAX;
253    }
254  }
255}