libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

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 */