/* This file is part of libextractor. Copyright (C) 2012 Christian Grothoff libextractor is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. libextractor is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libextractor; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file plugins/midi_extractor.c * @brief plugin to support MIDI files * @author Christian Grothoff */ #include "platform.h" #include "extractor.h" #include /** * Types of events in MIDI. */ enum EventType { ET_SEQUENCE_NUMBER = 0, ET_TEXT_EVENT = 1, ET_COPYRIGHT_NOTICE = 2, ET_TRACK_NAME = 3, ET_INSTRUMENT_NAME = 4, ET_LYRIC_TEXT = 5, ET_MARKER_TEXT = 6, ET_CUE_POINT = 7, ET_CHANNEL_PREFIX_ASSIGNMENT = 0x20, ET_END_OF_TRACK = 0x2F, ET_TEMPO_SETTING = 0x51, ET_SMPTE_OFFSET = 0x54, ET_TIME_SIGNATURE = 0x58, ET_KEY_SIGNATURE = 0x59, ET_SEQUENCE_SPECIRFIC_EVENT = 0x7F }; /** * Main entry method for the 'audio/midi' extraction plugin. * * @param ec extraction context provided to the plugin */ void EXTRACTOR_midi_extract_method (struct EXTRACTOR_ExtractContext *ec) { void *buf; unsigned char *data; uint64_t size; uint64_t off; ssize_t iret; smf_t *m = NULL; smf_event_t *event; uint8_t len; if (4 >= (iret = ec->read (ec->cls, &buf, 1024))) return; data = buf; if ( (data[0] != 0x4D) || (data[1] != 0x54) || (data[2] != 0x68) || (data[3] != 0x64) ) return; /* cannot be MIDI */ size = ec->get_size (ec->cls); if (size > 16 * 1024 * 1024) return; /* too large */ if (NULL == (data = malloc ((size_t) size))) return; /* out of memory */ memcpy (data, buf, iret); off = iret; while (off < size) { if (0 >= (iret = ec->read (ec->cls, &buf, 16 * 1024))) { free (data); return; } memcpy (&data[off], buf, iret); off += iret; } if (0 != ec->proc (ec->cls, "midi", EXTRACTOR_METATYPE_MIMETYPE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", "audio/midi", strlen ("audio/midi") + 1)) goto CLEANUP; if (NULL == (m = smf_load_from_memory (data, size))) goto CLEANUP; while (NULL != (event = smf_get_next_event (m))) { if (! smf_event_is_metadata (event)) break; len = event->midi_buffer[2]; if ( (len > 0) && isspace (event->midi_buffer[2 + len])) len--; #if 0 fprintf (stderr, "type: %d, len: %d value: %.*s\n", event->midi_buffer[1], event->midi_buffer[2], (int) event->midi_buffer_length - 3, (char *) &event->midi_buffer[3]); #endif if (1 != event->track_number) continue; /* heuristic to not get instruments */ if (0 == len) continue; switch (event->midi_buffer[1]) { case ET_TEXT_EVENT: if (0 != ec->proc (ec->cls, "midi", EXTRACTOR_METATYPE_COMMENT, EXTRACTOR_METAFORMAT_UTF8, "text/plain", (void*) &event->midi_buffer[3], len)) goto CLEANUP; break; case ET_COPYRIGHT_NOTICE: if (0 != ec->proc (ec->cls, "midi", EXTRACTOR_METATYPE_COPYRIGHT, EXTRACTOR_METAFORMAT_UTF8, "text/plain", (void*) &event->midi_buffer[3], len)) goto CLEANUP; break; case ET_TRACK_NAME: if (0 != ec->proc (ec->cls, "midi", EXTRACTOR_METATYPE_TITLE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", (void*) &event->midi_buffer[3], len)) goto CLEANUP; break; case ET_INSTRUMENT_NAME: if (0 != ec->proc (ec->cls, "midi", EXTRACTOR_METATYPE_SOURCE_DEVICE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", (void*) &event->midi_buffer[3], len)) goto CLEANUP; break; case ET_LYRIC_TEXT: if (0 != ec->proc (ec->cls, "midi", EXTRACTOR_METATYPE_LYRICS, EXTRACTOR_METAFORMAT_UTF8, "text/plain", (void*) &event->midi_buffer[3], len)) goto CLEANUP; break; default: break; } } CLEANUP: if (NULL != m) smf_delete (m); free (data); } /* end of midi_extractor.c */