⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smf.c

📁 VLC Player Source Code
💻 C
📖 第 1 页 / 共 2 页
字号:
/***************************************************************************** * smf.c : Standard MIDI File (.mid) demux module for vlc ***************************************************************************** * Copyright © 2007 Rémi Denis-Courmont * $Id$ * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/#ifdef HAVE_CONFIG_H# include "config.h"#endif#include <vlc_common.h>#include <vlc_plugin.h>#include <vlc_demux.h>#include <vlc_aout.h>#include <vlc_codecs.h>#include <vlc_charset.h>#include <limits.h>#include <assert.h>#define TEMPO_MIN  20#define TEMPO_MAX 250 /* Beats per minute */static int  Open  (vlc_object_t *);static void Close (vlc_object_t *);vlc_module_begin ();    set_description (N_("SMF demuxer"));    set_category (CAT_INPUT);    set_subcategory (SUBCAT_INPUT_DEMUX);    set_capability ("demux", 20);    set_callbacks (Open, Close);vlc_module_end ();static int Demux   (demux_t *);static int Control (demux_t *, int i_query, va_list args);typedef struct smf_track_t{    int64_t  offset; /* Read offset in the file (stream_Tell) */    int64_t  end;    /* End offset in the file */    uint64_t next;   /* Time of next message (in term of pulses) */    uint8_t  running_event; /* Running (previous) event */} mtrk_t;static int ReadDeltaTime (stream_t *s, mtrk_t *track);struct demux_sys_t{    es_out_id_t *es;    date_t       pts;    uint64_t     pulse; /* Pulses counter */    unsigned     ppqn;   /* Pulses Per Quarter Note */    /* by the way, "quarter note" is "noire" in French */    unsigned     trackc; /* Number of tracks */    mtrk_t       trackv[0]; /* Track states */};/***************************************************************************** * Open: check file and initializes structures *****************************************************************************/static int Open (vlc_object_t * p_this){    demux_t       *p_demux = (demux_t *)p_this;    stream_t      *stream = p_demux->s;    demux_sys_t   *p_sys;    const uint8_t *peek;    unsigned       tracks, ppqn;    bool     multitrack;    /* (Try to) parse the SMF header */    /* Header chunk always has 6 bytes payload */    if (stream_Peek (stream, &peek, 14) < 14)        return VLC_EGENERIC;    /* Skip RIFF MIDI header if present */    if (!memcmp (peek, "RIFF", 4) && !memcmp (peek + 8, "RMID", 4))    {        uint32_t riff_len = GetDWLE (peek + 4);        msg_Dbg (p_this, "detected RIFF MIDI file (%u bytes)",                 (unsigned)riff_len);        if ((stream_Read (stream, NULL, 12) < 12))            return VLC_EGENERIC;        /* Look for the RIFF data chunk */        for (;;)        {            char chnk_hdr[8];            uint32_t chnk_len;            if ((riff_len < 8)             || (stream_Read (stream, chnk_hdr, 8) < 8))                return VLC_EGENERIC;            riff_len -= 8;            chnk_len = GetDWLE (chnk_hdr + 4);            if (riff_len < chnk_len)                return VLC_EGENERIC;            riff_len -= chnk_len;            if (!memcmp (chnk_hdr, "data", 4))                break; /* found! */            if (stream_Read (stream, NULL, chnk_len) < (ssize_t)chnk_len)                return VLC_EGENERIC;        }        /* Read real SMF header. Assume RIFF data chunk length is proper. */        if (stream_Peek (stream, &peek, 14) < 14)            return VLC_EGENERIC;    }    if (memcmp (peek, "MThd\x00\x00\x00\x06", 8))        return VLC_EGENERIC;    peek += 8;    /* First word: SMF type */    switch (GetWBE (peek))    {        case 0:            multitrack = false;            break;        case 1:            multitrack = true;            break;        default:            /* We don't implement SMF2 (as do many) */            msg_Err (p_this, "unsupported SMF file type %u", GetWBE (peek));            return VLC_EGENERIC;    }    peek += 2;    /* Second word: number of tracks */    tracks = GetWBE (peek);    peek += 2;    if (!multitrack && (tracks != 1))    {        msg_Err (p_this, "invalid SMF type 0 file");        return VLC_EGENERIC;    }    msg_Dbg (p_this, "detected Standard MIDI File (type %u) with %u track(s)",             multitrack, tracks);    /* Third/last word: timing */    ppqn = GetWBE (peek);    if (ppqn & 0x8000)    {        /* FIXME */        msg_Err (p_this, "SMPTE timestamps not implemented");        return VLC_EGENERIC;    }    else    {        msg_Dbg (p_this, " %u pulses per quarter note", ppqn);    }    p_sys = malloc (sizeof (*p_sys) + (sizeof (mtrk_t) * tracks));    if (p_sys == NULL)        return VLC_ENOMEM;    /* We've had a valid SMF header - now skip it*/    if (stream_Read (stream, NULL, 14) < 14)        goto error;    p_demux->pf_demux   = Demux;    p_demux->pf_control = Control;    p_demux->p_sys      = p_sys;    /* Default SMF tempo is 120BPM, i.e. half a second per quarter note */    date_Init (&p_sys->pts, ppqn * 2, 1);    date_Set (&p_sys->pts, 1);    p_sys->pulse        = 0;    p_sys->ppqn         = ppqn;    p_sys->trackc       = tracks;    /* Prefetch track offsets */    for (unsigned i = 0; i < tracks; i++)    {        uint8_t head[8];        if (i > 0)        {            /* Seeking screws streaming up, but there is no way around this,             * as SMF1 tracks are performed simultaneously.             * Not a big deal as SMF1 are usually only a few kbytes anyway. */            if (stream_Seek (stream,  p_sys->trackv[i-1].end))            {                msg_Err (p_this, "cannot build SMF index (corrupted file?)");                goto error;            }        }        for (;;)        {            stream_Read (stream, head, 8);            if (memcmp (head, "MTrk", 4) == 0)                break;            msg_Dbg (p_this, "skipping unknown SMF chunk");            stream_Read (stream, NULL, GetDWBE (head + 4));        }        p_sys->trackv[i].offset = stream_Tell (stream);        p_sys->trackv[i].end = p_sys->trackv[i].offset + GetDWBE (head + 4);        p_sys->trackv[i].next = 0;        ReadDeltaTime (stream, p_sys->trackv + i);        p_sys->trackv[i].running_event = 0xF6;        /* Why 0xF6 (Tuning Calibration)?         * Because it has zero bytes of data, so the parser will detect the         * error if the first event uses running status. */    }    es_format_t  fmt;    es_format_Init (&fmt, AUDIO_ES, VLC_FOURCC('M', 'I', 'D', 'I'));    fmt.audio.i_channels = 2;    fmt.audio.i_original_channels = fmt.audio.i_physical_channels =        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;    fmt.audio.i_rate = 44100; /* dummy value */    p_sys->es = es_out_Add (p_demux->out, &fmt);    return VLC_SUCCESS;error:    free (p_sys);    return VLC_EGENERIC;}/** * Releases allocate resources. */static void Close (vlc_object_t * p_this){    demux_t *p_demux = (demux_t *)p_this;    demux_sys_t *p_sys = p_demux->p_sys;    free (p_sys);}/** * Reads MIDI variable length (7, 14, 21 or 28 bits) integer. * @return read value, or -1 on EOF/error. */static int32_t ReadVarInt (stream_t *s){    uint32_t val = 0;    uint8_t byte;    for (unsigned i = 0; i < 4; i++)    {        if (stream_Read (s, &byte, 1) < 1)            return -1;        val = (val << 7) | (byte & 0x7f);        if ((byte & 0x80) == 0)            return val;    }    return -1;}/** * Reads (delta) time from the next event of a given track. * @param s stream to read data from (must be positioned at the right offset) */static int ReadDeltaTime (stream_t *s, mtrk_t *track){    int32_t delta_time;    assert (stream_Tell (s) == track->offset);    if (track->offset >= track->end)    {        /* This track is done */        track->next = UINT64_MAX;        return 0;    }    delta_time = ReadVarInt (s);    if (delta_time < 0)        return -1;    track->next += delta_time;    track->offset = stream_Tell (s);    return 0;}/** * Non-MIDI Meta events handler */staticint HandleMeta (demux_t *p_demux, mtrk_t *tr){    stream_t *s = p_demux->s;    demux_sys_t *p_sys = p_demux->p_sys;    uint8_t *payload;    uint8_t type;    int32_t length;    int ret = 0;    if (stream_Read (s, &type, 1) != 1)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -