/exult/branches/shared-ptr/audio/midi_drivers/XMidiEventList.cpp

# · C++ · 232 lines · 155 code · 37 blank · 40 comment · 29 complexity · af11326af37ac35b134ff4f1c3cef0a1 MD5 · raw file

  1. /*
  2. Copyright (C) 2003 The Pentagram Team
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #include <cstdlib>
  16. #include "pent_include.h"
  17. #include "XMidiEvent.h"
  18. #include "XMidiEventList.h"
  19. #ifdef PENTAGRAM_IN_EXULT
  20. #include "databuf.h"
  21. #else
  22. #include "ODataSource.h"
  23. #endif
  24. #ifndef UNDER_EMBEDDED_CE
  25. using std::atof;
  26. using std::atoi;
  27. using std::memcmp;
  28. using std::memcpy;
  29. using std::memset;
  30. using std::size_t;
  31. #endif
  32. using std::string;
  33. using std::endl;
  34. //#include "gamma.h"
  35. //
  36. // XMidiEventList stuff
  37. //
  38. int XMidiEventList::write (ODataSource *dest)
  39. {
  40. int len = 0;
  41. if (!events)
  42. {
  43. perr << "No midi data loaded." << endl;
  44. return 0;
  45. }
  46. // This is so if using buffer ODataSource, the caller can know how big to make the buffer
  47. if (!dest)
  48. {
  49. // Header is 14 bytes long and add the rest as well
  50. len = convertListToMTrk (NULL);
  51. return 14 + len;
  52. }
  53. dest->write1 ('M');
  54. dest->write1 ('T');
  55. dest->write1 ('h');
  56. dest->write1 ('d');
  57. dest->write4high (6);
  58. dest->write2high (0);
  59. dest->write2high (1);
  60. dest->write2high (60); // The PPQN
  61. len = convertListToMTrk (dest);
  62. return len + 14;
  63. }
  64. //
  65. // PutVLQ
  66. //
  67. // Write a Conventional Variable Length Quantity
  68. //
  69. int XMidiEventList::putVLQ(ODataSource *dest, uint32 value)
  70. {
  71. int buffer;
  72. int i = 1;
  73. buffer = value & 0x7F;
  74. while (value >>= 7)
  75. {
  76. buffer <<= 8;
  77. buffer |= ((value & 0x7F) | 0x80);
  78. i++;
  79. }
  80. if (!dest) return i;
  81. for (int j = 0; j < i; j++)
  82. {
  83. dest->write1(buffer & 0xFF);
  84. buffer >>= 8;
  85. }
  86. return i;
  87. }
  88. // Converts and event list to a MTrk
  89. // Returns bytes of the array
  90. // buf can be NULL
  91. uint32 XMidiEventList::convertListToMTrk (ODataSource *dest)
  92. {
  93. int time = 0;
  94. int lasttime = 0;
  95. XMidiEvent *event;
  96. uint32 delta;
  97. unsigned char last_status = 0;
  98. uint32 i = 8;
  99. uint32 j;
  100. uint32 size_pos=0;
  101. if (dest)
  102. {
  103. dest->write1('M');
  104. dest->write1('T');
  105. dest->write1('r');
  106. dest->write1('k');
  107. size_pos = dest->getPos();
  108. dest->skip(4);
  109. }
  110. for (event = events; event; event=event->next)
  111. {
  112. // We don't write the end of stream marker here, we'll do it later
  113. if (event->status == 0xFF && event->data[0] == 0x2f) {
  114. lasttime = event->time;
  115. continue;
  116. }
  117. delta = (event->time - time);
  118. time = event->time;
  119. i += putVLQ (dest, delta);
  120. if ((event->status != last_status) || (event->status >= 0xF0))
  121. {
  122. if (dest) dest->write1(event->status);
  123. i++;
  124. }
  125. last_status = event->status;
  126. switch (event->status >> 4)
  127. {
  128. // 2 bytes data
  129. // Note off, Note on, Aftertouch, Controller and Pitch Wheel
  130. case 0x8: case 0x9: case 0xA: case 0xB: case 0xE:
  131. if (dest)
  132. {
  133. dest->write1(event->data[0]);
  134. dest->write1(event->data[1]);
  135. }
  136. i += 2;
  137. break;
  138. // 1 bytes data
  139. // Program Change and Channel Pressure
  140. case 0xC: case 0xD:
  141. if (dest) dest->write1(event->data[0]);
  142. i++;
  143. break;
  144. // Variable length
  145. // SysEx
  146. case 0xF:
  147. if (event->status == 0xFF)
  148. {
  149. if (dest) dest->write1(event->data[0]);
  150. i++;
  151. }
  152. i += putVLQ (dest, event->ex.sysex_data.len);
  153. if (event->ex.sysex_data.len)
  154. {
  155. for (j = 0; j < event->ex.sysex_data.len; j++)
  156. {
  157. if (dest) dest->write1(event->ex.sysex_data.buffer[j]);
  158. i++;
  159. }
  160. }
  161. break;
  162. // Never occur
  163. default:
  164. perr << "Not supposed to see this" << endl;
  165. break;
  166. }
  167. }
  168. // Write out end of stream marker
  169. if (lasttime > time) i += putVLQ (dest, lasttime-time);
  170. else i += putVLQ (dest, 0);
  171. if (dest) {
  172. dest->write1(0xFF);
  173. dest->write1(0x2F);
  174. }
  175. i += 2+putVLQ (dest, 0);
  176. if (dest)
  177. {
  178. int cur_pos = dest->getPos();
  179. dest->seek (size_pos);
  180. dest->write4high (i-8);
  181. dest->seek (cur_pos);
  182. }
  183. return i;
  184. }
  185. void XMidiEventList::decerementCounter()
  186. {
  187. if (--counter < 0) {
  188. events->FreeThis();
  189. events = 0;
  190. XMidiEvent::Free(this);
  191. }
  192. }