midi_extractor.c (6080B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2012 Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file plugins/midi_extractor.c 22 * @brief plugin to support MIDI files 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include "extractor.h" 27 #include <smf.h> 28 29 30 /** 31 * Types of events in MIDI. 32 */ 33 enum EventType 34 { 35 ET_SEQUENCE_NUMBER = 0, 36 ET_TEXT_EVENT = 1, 37 ET_COPYRIGHT_NOTICE = 2, 38 ET_TRACK_NAME = 3, 39 ET_INSTRUMENT_NAME = 4, 40 ET_LYRIC_TEXT = 5, 41 ET_MARKER_TEXT = 6, 42 ET_CUE_POINT = 7, 43 ET_CHANNEL_PREFIX_ASSIGNMENT = 0x20, 44 ET_END_OF_TRACK = 0x2F, 45 ET_TEMPO_SETTING = 0x51, 46 ET_SMPTE_OFFSET = 0x54, 47 ET_TIME_SIGNATURE = 0x58, 48 ET_KEY_SIGNATURE = 0x59, 49 ET_SEQUENCE_SPECIRFIC_EVENT = 0x7F 50 }; 51 52 53 /** 54 * Main entry method for the 'audio/midi' extraction plugin. 55 * 56 * @param ec extraction context provided to the plugin 57 */ 58 void 59 EXTRACTOR_midi_extract_method (struct EXTRACTOR_ExtractContext *ec); 60 61 void 62 EXTRACTOR_midi_extract_method (struct EXTRACTOR_ExtractContext *ec) 63 { 64 void *buf; 65 unsigned char *data; 66 uint64_t size; 67 uint64_t off; 68 ssize_t iret; 69 smf_t *m = NULL; 70 smf_event_t *event; 71 uint8_t len; 72 char value[256]; 73 74 if (4 >= (iret = ec->read (ec->cls, &buf, 1024))) 75 return; 76 data = buf; 77 if ( (data[0] != 0x4D) || (data[1] != 0x54) || 78 (data[2] != 0x68) || (data[3] != 0x64) ) 79 return; /* cannot be MIDI */ 80 size = ec->get_size (ec->cls); 81 if (size > 16 * 1024 * 1024) 82 return; /* too large */ 83 if (NULL == (data = malloc ((size_t) size))) 84 return; /* out of memory */ 85 memcpy (data, buf, iret); 86 off = iret; 87 while (off < size) 88 { 89 if (0 >= (iret = ec->read (ec->cls, &buf, 16 * 1024))) 90 { 91 free (data); 92 return; 93 } 94 memcpy (&data[off], buf, iret); 95 off += iret; 96 } 97 if (0 != ec->proc (ec->cls, 98 "midi", 99 EXTRACTOR_METATYPE_MIMETYPE, 100 EXTRACTOR_METAFORMAT_UTF8, 101 "text/plain", 102 "audio/midi", 103 strlen ("audio/midi") + 1)) 104 goto CLEANUP; 105 if (NULL == (m = smf_load_from_memory (data, size))) 106 goto CLEANUP; 107 while (NULL != (event = smf_get_next_event (m))) 108 { 109 if (! smf_event_is_metadata (event)) 110 break; 111 if (event->midi_buffer_length < 3) 112 continue; /* need at least FF type len */ 113 len = event->midi_buffer[2]; 114 if (event->midi_buffer_length < 3 + (int) len) 115 continue; /* declared length exceeds available buffer */ 116 if ( (len > 0) && 117 isspace (event->midi_buffer[2 + len])) 118 len--; 119 #if 0 120 fprintf (stderr, 121 "type: %d, len: %d value: %.*s\n", 122 event->midi_buffer[1], 123 event->midi_buffer[2], 124 (int) event->midi_buffer_length - 3, 125 (char *) &event->midi_buffer[3]); 126 #endif 127 if (1 != event->track_number) 128 continue; /* heuristic to not get instruments */ 129 if (0 == len) 130 continue; 131 /* MIDI meta events carry a *counted*, not 0-terminated string; 132 copy it out and terminate it so that we can honour the 133 "data_len == strlen (data) + 1" contract of METAFORMAT_UTF8. 134 'len' is a uint8_t, so 256 bytes always suffice. */ 135 memcpy (value, 136 &event->midi_buffer[3], 137 len); 138 value[len] = '\0'; 139 if (0 == strlen (value)) 140 continue; /* embedded 0 byte at the very start: nothing to report */ 141 switch (event->midi_buffer[1]) 142 { 143 case ET_TEXT_EVENT: 144 if (0 != ec->proc (ec->cls, 145 "midi", 146 EXTRACTOR_METATYPE_COMMENT, 147 EXTRACTOR_METAFORMAT_UTF8, 148 "text/plain", 149 value, 150 strlen (value) + 1)) 151 goto CLEANUP; 152 break; 153 case ET_COPYRIGHT_NOTICE: 154 if (0 != ec->proc (ec->cls, 155 "midi", 156 EXTRACTOR_METATYPE_COPYRIGHT, 157 EXTRACTOR_METAFORMAT_UTF8, 158 "text/plain", 159 value, 160 strlen (value) + 1)) 161 goto CLEANUP; 162 break; 163 case ET_TRACK_NAME: 164 if (0 != ec->proc (ec->cls, 165 "midi", 166 EXTRACTOR_METATYPE_TITLE, 167 EXTRACTOR_METAFORMAT_UTF8, 168 "text/plain", 169 value, 170 strlen (value) + 1)) 171 goto CLEANUP; 172 break; 173 case ET_INSTRUMENT_NAME: 174 if (0 != ec->proc (ec->cls, 175 "midi", 176 EXTRACTOR_METATYPE_SOURCE_DEVICE, 177 EXTRACTOR_METAFORMAT_UTF8, 178 "text/plain", 179 value, 180 strlen (value) + 1)) 181 goto CLEANUP; 182 break; 183 case ET_LYRIC_TEXT: 184 if (0 != ec->proc (ec->cls, 185 "midi", 186 EXTRACTOR_METATYPE_LYRICS, 187 EXTRACTOR_METAFORMAT_UTF8, 188 "text/plain", 189 value, 190 strlen (value) + 1)) 191 goto CLEANUP; 192 break; 193 default: 194 break; 195 } 196 } 197 198 CLEANUP: 199 if (NULL != m) 200 smf_delete (m); 201 free (data); 202 } 203 204 205 /* end of midi_extractor.c */