├── .gitignore ├── COPYING ├── Makefile.am ├── NEWS ├── README ├── TODO ├── autogen.sh ├── configure.ac ├── doxygen.cfg ├── man ├── Makefile.am ├── smfsh-ref.sgml └── smfsh.1 ├── smf.pc.in └── src ├── Makefile.am ├── smf.c ├── smf.h ├── smf_decode.c ├── smf_load.c ├── smf_private.h ├── smf_save.c ├── smf_tempo.c └── smfsh.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | *.o 4 | *.pc 5 | .deps 6 | .libs 7 | Makefile 8 | Makefile.in 9 | aclocal.m4 10 | api 11 | autom4te.cache 12 | config.* 13 | configure 14 | depcomp 15 | install-sh 16 | libtool 17 | ltmain.sh 18 | missing 19 | smfsh 20 | stamp-h1 21 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007, 2008 Edward Tomasz Napierała 2 | 2013 John Stumpo 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src man 2 | 3 | EXTRA_DIST = smf.pc.in 4 | 5 | pkgconfigdir = $(libdir)/pkgconfig 6 | pkgconfig_DATA = smf.pc 7 | 8 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Changes between 1.2 and 1.3: 2 | 3 | - Make libsmf more tolerant for broken MIDI files - try to load as much 4 | as possible instead of not loading the track at all. 5 | 6 | - Add user pointers to smf_event_t and smf_track_t. 7 | 8 | - Make it easier to use libsmf under Microsoft Windows. 9 | 10 | Changes between 1.1 and 1.2: 11 | 12 | - Relaxed some overly restrictive tests that could trip asserts without 13 | good reason. 14 | 15 | - Fixed a bug that broke adding SMF events in the middle of the track 16 | in some cases. 17 | 18 | - Fixed a bug that broke removing tracks in some cases. 19 | 20 | If you encountered failed assertion during saving, it could be one 21 | of these bugs. 22 | 23 | Changes between 1.0 and 1.1: 24 | 25 | - libsmf released separately from jack-smf-utils. 26 | 27 | - Fixed build on SunOS. 28 | 29 | - Add "text" command to smfsh and corresponding smf_event_new_textual() API function. 30 | 31 | - smf_track_remove_event renamed to smf_event_remove_from_track, for consistency. 32 | That won't happen anymore, I promise. ;-) 33 | 34 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is the continuation of libsmf, a BSD-licensed C library for reading 2 | and writing Standard MIDI Files (*.mid). It transparently handles 3 | conversions between time and pulses, tempo maps, and more. Full API 4 | documentation and an example program (which is also rather useful in 5 | its own right) are also provided. libsmf was originally written and 6 | maintained by Edward Tomasz Napierała at http://libsmf.sourceforge.net/. 7 | 8 | You will need a C compilation environment and the GLib development files 9 | to compile this. It builds in the usual autotools manner: 10 | 11 | $ ./autogen.sh # if it's a fresh git clone 12 | $ ./configure [configure options] 13 | $ make 14 | # make install 15 | 16 | Release directory: 17 | http://ftp.stump.io/software/libsmf/ 18 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Things to do: 2 | 3 | - Implement parsing of sysexes chopped into several "packets". 4 | 5 | - Add support for type 2 SMF files. 6 | 7 | - Add support for SMPTE time. 8 | 9 | - Rework removal of tempo changes. Right now there is one scenario that is broken - when 10 | you have two tempo-related events at the end of the song (i.e. there are no events following), 11 | removing one of these tempo-related events will remove both tempo changes. 12 | 13 | - Add a way to force some particular tempo during playback and ignore the following tempo changes. 14 | 15 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | ${AUTORECONF:-autoreconf} -fiv 3 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.69]) 5 | AC_INIT([libsmf],[1.3+],[stump@stump.io]) 6 | AM_INIT_AUTOMAKE([-Wall foreign]) 7 | AC_CONFIG_SRCDIR([src/smf.c]) 8 | AC_CONFIG_HEADER([config.h]) 9 | 10 | # Checks for programs. 11 | AC_PROG_CC 12 | CFLAGS="$CFLAGS -W -Wall -Wno-sign-compare" # that last one's a project for another day 13 | LT_INIT 14 | 15 | # Checks for libraries. 16 | AC_CHECK_LIB([m], [pow]) 17 | AC_ARG_WITH([readline], 18 | [AS_HELP_STRING([--with-readline], 19 | [support fancy command line editing @<:@default=check@:>@])], 20 | [], 21 | [with_readline=check]) 22 | 23 | AS_IF([test "x$with_readline" != xno], 24 | [AC_CHECK_LIB([readline], [main], 25 | [AC_SUBST([READLINE_LIBS], ["-lreadline -lncurses"]) 26 | AC_DEFINE([HAVE_LIBREADLINE], [1], [Define if you have libreadline])], 27 | [if test "x$with_readline" != xcheck; then 28 | AC_MSG_FAILURE( 29 | [--with-readline was given, but test for readline failed]) 30 | fi 31 | ], -lncurses)]) 32 | 33 | 34 | # Checks for header files. 35 | AC_CHECK_HEADERS([arpa/inet.h stdint.h stdlib.h string.h]) 36 | 37 | # Checks for typedefs, structures, and compiler characteristics. 38 | AC_TYPE_INT8_T 39 | AC_TYPE_UINT16_T 40 | AC_TYPE_UINT32_T 41 | 42 | # Checks for library functions. 43 | AC_FUNC_MALLOC 44 | AC_FUNC_REALLOC 45 | AC_FUNC_STRTOD 46 | AC_CHECK_FUNCS([memset pow strdup strerror strtol strchr]) 47 | 48 | PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.2) 49 | AC_SUBST(GLIB_CFLAGS) 50 | AC_SUBST(GLIB_LIBS) 51 | 52 | case "$host" in 53 | *-cygwin|*-mingw*) WS2_32_IF_NEEDED=-lws2_32 ;; 54 | esac 55 | AC_SUBST([WS2_32_IF_NEEDED]) 56 | 57 | AC_CONFIG_FILES([Makefile smf.pc src/Makefile man/Makefile]) 58 | AC_OUTPUT 59 | -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = smfsh.1 2 | 3 | EXTRA_DIST = $(man_MANS) 4 | 5 | -------------------------------------------------------------------------------- /man/smfsh-ref.sgml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | smfsh 6 | 7 | 1 8 | smfsh 1.3 9 | 10 | 11 | 12 | smfsh 13 | 14 | 15 | SMF shell 16 | 17 | 18 | 19 | 20 | smfsh 21 | 22 | file 23 | 24 | 25 | 26 | OPTIONS 27 | 28 | 29 | -V 30 | 31 | 32 | Print libsmf version and exit. 33 | 34 | 35 | 36 | 37 | file 38 | 39 | 40 | Load the file. 41 | 42 | 43 | 44 | 45 | 46 | 47 | DESCRIPTION 48 | 49 | smfsh, an "SMF shell", is an interactive, command-driven frontend 50 | to libsmf, useful for modifying MIDI files by hand. It started its life as a debugging aid 51 | for libsmf. 52 | 53 | 54 | There are four groups of commands: SMF level, track level, event level, and others. 55 | 56 | 57 | 58 | 59 | SMF LEVEL COMMANDS 60 | 61 | SMF level commands display or change properties of smf (i.e. the MIDI file) as a whole. 62 | 63 | 64 | 65 | save file 66 | 67 | 68 | Save a file. If the file 69 | name is not given, the last file name is used. 70 | 71 | 72 | Note that this will not ask for confirmation. 73 | 74 | 75 | 76 | 77 | 78 | load file 79 | 80 | 81 | Load a file. If the file 82 | name is not given, the last file name is used. 83 | 84 | 85 | Instead of using "load", you can pass the file name on the command line. 86 | 87 | 88 | 89 | 90 | ppqn new-ppqn 91 | 92 | 93 | Show (if used without parameter) or change PPQN (Pulses Per Quarter Note, aka Division). 94 | 95 | 96 | 97 | 98 | format new-format 99 | 100 | 101 | Show (if used without parameter) or change format. 102 | 103 | 104 | 105 | 106 | tempo 107 | 108 | 109 | Show tempo map. Tempo map is computed automatically from the SMF contents - to modify 110 | a tempo, just add a Tempo Change event using "add" command. 111 | 112 | 113 | 114 | 115 | length 116 | 117 | 118 | Show total length of the song. Just like the tempo map, file length is computed 119 | from the SMF contents. 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | TRACK LEVEL COMMANDS 128 | 129 | Track level commands display or change properties of tracks. SMF may contain one or more tracks. 130 | 131 | 132 | 133 | tracks 134 | 135 | 136 | Show the list of tracks in the SMF. 137 | 138 | 139 | 140 | 141 | track track-number 142 | 143 | 144 | Show details of a track (if called without parameter), or select track 145 | with a given number - for example in order to display ("events") the events it contains. 146 | 147 | 148 | 149 | 150 | trackadd 151 | 152 | 153 | Create new track. 154 | 155 | 156 | 157 | 158 | trackrm track-number 159 | 160 | 161 | Remove the track. If the track number is not given, this will remove the track currently selected. 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | EVENT LEVEL COMMANDS 170 | 171 | Event level commands display or change properties of events. Events are grouped into tracks. 172 | Track may contain any number of events, including zero. 173 | 174 | 175 | 176 | events 177 | 178 | 179 | Show the list of events in the current track. 180 | 181 | 182 | 183 | 184 | event event-number 185 | 186 | 187 | Show information about a current event (if used without parameter) 188 | or select the event. 189 | 190 | 191 | 192 | 193 | add time-in-seconds midi-message-as-hex 194 | 195 | 196 | Add new event to the currently selected track. 197 | 198 | 199 | 200 | 201 | rm event-number 202 | 203 | 204 | Remove the event. If the event number is not given, this will remove the event currently selected. 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | OTHER COMMANDS 213 | 214 | There are three commands in the "other" category - "help", "version", 215 | and "exit". Their meaning should be self expanatory. 216 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /man/smfsh.1: -------------------------------------------------------------------------------- 1 | .\" This manpage has been automatically generated by docbook2man 2 | .\" from a DocBook document. This tool can be found at: 3 | .\" 4 | .\" Please send any bug reports, improvements, comments, patches, 5 | .\" etc. to Steve Cheng . 6 | .TH "SMFSH" "1" "21 March 2010" "smfsh 1.3" "" 7 | 8 | .SH NAME 9 | smfsh \- SMF shell 10 | .SH SYNOPSIS 11 | 12 | \fBsmfsh\fR [ \fB-V\fR ] [ \fB\fIfile\fB\fR ] 13 | 14 | .SH "OPTIONS" 15 | .TP 16 | \fB-V\fR 17 | Print libsmf version and exit. 18 | .TP 19 | \fB\fIfile\fB\fR 20 | Load the file. 21 | .SH "DESCRIPTION" 22 | .PP 23 | \fBsmfsh\fR, an "SMF shell", is an interactive, command-driven frontend 24 | to libsmf, useful for modifying MIDI files by hand. It started its life as a debugging aid 25 | for libsmf. 26 | .PP 27 | There are four groups of commands: SMF level, track level, event level, and others. 28 | .SH "SMF LEVEL COMMANDS" 29 | .PP 30 | SMF level commands display or change properties of smf (i.e. the MIDI file) as a whole. 31 | .TP 32 | \fBsave \fIfile\fB\fR 33 | Save a file. If the \fIfile\fR 34 | name is not given, the last file name is used. 35 | 36 | Note that this will not ask for confirmation. 37 | .TP 38 | \fBload \fIfile\fB\fR 39 | Load a file. If the \fIfile\fR 40 | name is not given, the last file name is used. 41 | 42 | Instead of using "load", you can pass the file name on the command line. 43 | .TP 44 | \fBppqn \fInew-ppqn\fB\fR 45 | Show (if used without parameter) or change PPQN (Pulses Per Quarter Note, aka Division). 46 | .TP 47 | \fBformat \fInew-format\fB\fR 48 | Show (if used without parameter) or change format. 49 | .TP 50 | \fBtempo\fR 51 | Show tempo map. Tempo map is computed automatically from the SMF contents - to modify 52 | a tempo, just add a Tempo Change event using "add" command. 53 | .TP 54 | \fBlength\fR 55 | Show total length of the song. Just like the tempo map, file length is computed 56 | from the SMF contents. 57 | .SH "TRACK LEVEL COMMANDS" 58 | .PP 59 | Track level commands display or change properties of tracks. SMF may contain one or more tracks. 60 | .TP 61 | \fBtracks\fR 62 | Show the list of tracks in the SMF. 63 | .TP 64 | \fBtrack \fItrack-number\fB\fR 65 | Show details of a track (if called without parameter), or select track 66 | with a given number - for example in order to display ("events") the events it contains. 67 | .TP 68 | \fBtrackadd\fR 69 | Create new track. 70 | .TP 71 | \fBtrackrm \fItrack-number\fB\fR 72 | Remove the track. If the track number is not given, this will remove the track currently selected. 73 | .SH "EVENT LEVEL COMMANDS" 74 | .PP 75 | Event level commands display or change properties of events. Events are grouped into tracks. 76 | Track may contain any number of events, including zero. 77 | .TP 78 | \fBevents\fR 79 | Show the list of events in the current track. 80 | .TP 81 | \fBevent \fIevent-number\fB\fR 82 | Show information about a current event (if used without parameter) 83 | or select the event. 84 | .TP 85 | \fBadd \fItime-in-seconds\fB \fImidi-message-as-hex\fB\fR 86 | Add new event to the currently selected track. 87 | .TP 88 | \fBrm \fIevent-number\fB\fR 89 | Remove the event. If the event number is not given, this will remove the event currently selected. 90 | .SH "OTHER COMMANDS" 91 | .PP 92 | There are three commands in the "other" category - "help", "version", 93 | and "exit". Their meaning should be self expanatory. 94 | -------------------------------------------------------------------------------- /smf.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: SMF 7 | Description: Standard MIDI File library 8 | Requires: glib-2.0 9 | Version: @VERSION@ 10 | Libs: -L${libdir} -lsmf -lm 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | include_HEADERS = smf.h 2 | 3 | lib_LTLIBRARIES = libsmf.la 4 | libsmf_la_SOURCES = smf.h smf_private.h smf.c smf_decode.c smf_load.c smf_save.c smf_tempo.c 5 | libsmf_la_CFLAGS = $(GLIB_CFLAGS) -DG_LOG_DOMAIN=\"libsmf\" 6 | libsmf_la_LIBADD = $(GLIB_LIBS) $(WS2_32_IF_NEEDED) 7 | libsmf_la_LDFLAGS = -no-undefined 8 | 9 | bin_PROGRAMS = smfsh 10 | smfsh_SOURCES = smfsh.c 11 | smfsh_CFLAGS = $(GLIB_CFLAGS) -DG_LOG_DOMAIN=\"smfsh\" 12 | smfsh_LDADD = libsmf.la $(GLIB_LIBS) $(READLINE_LIBS) -lm 13 | 14 | -------------------------------------------------------------------------------- /src/smf.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007, 2008 Edward Tomasz Napierała 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | /** 29 | * \file 30 | * 31 | * Various functions. 32 | * 33 | */ 34 | 35 | /* Reference: http://www.borg.com/~jglatt/tech/midifile.htm */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #ifdef __MINGW32__ 43 | #include 44 | #else /* ! __MINGW32__ */ 45 | #include 46 | #endif /* ! __MINGW32__ */ 47 | #include "smf.h" 48 | #include "smf_private.h" 49 | 50 | /** 51 | * Allocates new smf_t structure. 52 | * \return pointer to smf_t or NULL. 53 | */ 54 | smf_t * 55 | smf_new(void) 56 | { 57 | int cantfail; 58 | 59 | smf_t *smf = malloc(sizeof(smf_t)); 60 | if (smf == NULL) { 61 | g_critical("Cannot allocate smf_t structure: %s", strerror(errno)); 62 | return (NULL); 63 | } 64 | 65 | memset(smf, 0, sizeof(smf_t)); 66 | 67 | smf->tracks_array = g_ptr_array_new(); 68 | assert(smf->tracks_array); 69 | 70 | smf->tempo_array = g_ptr_array_new(); 71 | assert(smf->tempo_array); 72 | 73 | cantfail = smf_set_ppqn(smf, 120); 74 | assert(!cantfail); 75 | 76 | cantfail = smf_set_format(smf, 0); 77 | assert(!cantfail); 78 | 79 | smf_init_tempo(smf); 80 | 81 | return (smf); 82 | } 83 | 84 | /** 85 | * Frees smf and all it's descendant structures. 86 | */ 87 | void 88 | smf_delete(smf_t *smf) 89 | { 90 | /* Remove all the tracks, from last to first. */ 91 | while (smf->tracks_array->len > 0) 92 | smf_track_delete(g_ptr_array_index(smf->tracks_array, smf->tracks_array->len - 1)); 93 | 94 | smf_fini_tempo(smf); 95 | 96 | assert(smf->tracks_array->len == 0); 97 | assert(smf->number_of_tracks == 0); 98 | g_ptr_array_free(smf->tracks_array, TRUE); 99 | g_ptr_array_free(smf->tempo_array, TRUE); 100 | 101 | memset(smf, 0, sizeof(smf_t)); 102 | free(smf); 103 | } 104 | 105 | /** 106 | * Allocates new smf_track_t structure. 107 | * \return pointer to smf_track_t or NULL. 108 | */ 109 | smf_track_t * 110 | smf_track_new(void) 111 | { 112 | smf_track_t *track = malloc(sizeof(smf_track_t)); 113 | if (track == NULL) { 114 | g_critical("Cannot allocate smf_track_t structure: %s", strerror(errno)); 115 | return (NULL); 116 | } 117 | 118 | memset(track, 0, sizeof(smf_track_t)); 119 | track->next_event_number = -1; 120 | 121 | track->events_array = g_ptr_array_new(); 122 | assert(track->events_array); 123 | 124 | return (track); 125 | } 126 | 127 | /** 128 | * Detaches track from its smf and frees it. 129 | */ 130 | void 131 | smf_track_delete(smf_track_t *track) 132 | { 133 | assert(track); 134 | assert(track->events_array); 135 | 136 | /* Remove all the events, from last to first. */ 137 | while (track->events_array->len > 0) 138 | smf_event_delete(g_ptr_array_index(track->events_array, track->events_array->len - 1)); 139 | 140 | if (track->smf) 141 | smf_track_remove_from_smf(track); 142 | 143 | assert(track->events_array->len == 0); 144 | assert(track->number_of_events == 0); 145 | g_ptr_array_free(track->events_array, TRUE); 146 | 147 | memset(track, 0, sizeof(smf_track_t)); 148 | free(track); 149 | } 150 | 151 | 152 | /** 153 | * Appends smf_track_t to smf. 154 | */ 155 | void 156 | smf_add_track(smf_t *smf, smf_track_t *track) 157 | { 158 | int cantfail; 159 | 160 | assert(track->smf == NULL); 161 | 162 | track->smf = smf; 163 | g_ptr_array_add(smf->tracks_array, track); 164 | 165 | smf->number_of_tracks++; 166 | track->track_number = smf->number_of_tracks; 167 | 168 | if (smf->number_of_tracks > 1) { 169 | cantfail = smf_set_format(smf, 1); 170 | assert(!cantfail); 171 | } 172 | } 173 | 174 | /** 175 | * Detaches track from the smf. 176 | */ 177 | void 178 | smf_track_remove_from_smf(smf_track_t *track) 179 | { 180 | int i, j; 181 | smf_track_t *tmp; 182 | smf_event_t *ev; 183 | 184 | assert(track->smf != NULL); 185 | 186 | track->smf->number_of_tracks--; 187 | 188 | assert(track->smf->tracks_array); 189 | g_ptr_array_remove(track->smf->tracks_array, track); 190 | 191 | /* Renumber the rest of the tracks, so they are consecutively numbered. */ 192 | for (i = track->track_number; i <= track->smf->number_of_tracks; i++) { 193 | tmp = smf_get_track_by_number(track->smf, i); 194 | tmp->track_number = i; 195 | 196 | /* 197 | * Events have track numbers too. I guess this wasn't a wise 198 | * decision. ;-/ 199 | */ 200 | for (j = 1; j <= tmp->number_of_events; j++) { 201 | ev = smf_track_get_event_by_number(tmp, j); 202 | ev->track_number = i; 203 | } 204 | } 205 | 206 | track->track_number = -1; 207 | track->smf = NULL; 208 | } 209 | 210 | /** 211 | * Allocates new smf_event_t structure. The caller is responsible for allocating 212 | * event->midi_buffer, filling it with MIDI data and setting event->midi_buffer_length properly. 213 | * Note that event->midi_buffer will be freed by smf_event_delete. 214 | * \return pointer to smf_event_t or NULL. 215 | */ 216 | smf_event_t * 217 | smf_event_new(void) 218 | { 219 | smf_event_t *event = malloc(sizeof(smf_event_t)); 220 | if (event == NULL) { 221 | g_critical("Cannot allocate smf_event_t structure: %s", strerror(errno)); 222 | return (NULL); 223 | } 224 | 225 | memset(event, 0, sizeof(smf_event_t)); 226 | 227 | event->delta_time_pulses = -1; 228 | event->time_pulses = -1; 229 | event->time_seconds = -1.0; 230 | event->track_number = -1; 231 | 232 | return (event); 233 | } 234 | 235 | /** 236 | * Allocates an smf_event_t structure and fills it with "len" bytes copied 237 | * from "midi_data". 238 | * \param midi_data Pointer to MIDI data. It sill be copied to the newly allocated event->midi_buffer. 239 | * \param len Length of the buffer. It must be proper MIDI event length, e.g. 3 for Note On event. 240 | * \return Event containing MIDI data or NULL. 241 | */ 242 | smf_event_t * 243 | smf_event_new_from_pointer(void *midi_data, int len) 244 | { 245 | smf_event_t *event; 246 | 247 | event = smf_event_new(); 248 | if (event == NULL) 249 | return (NULL); 250 | 251 | event->midi_buffer_length = len; 252 | event->midi_buffer = malloc(event->midi_buffer_length); 253 | if (event->midi_buffer == NULL) { 254 | g_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno)); 255 | smf_event_delete(event); 256 | 257 | return (NULL); 258 | } 259 | 260 | memcpy(event->midi_buffer, midi_data, len); 261 | 262 | return (event); 263 | } 264 | 265 | /** 266 | * Allocates an smf_event_t structure and fills it with at most three bytes of data. 267 | * For example, if you need to create Note On event, do something like this: 268 | * 269 | * smf_event_new_from_bytes(0x90, 0x3C, 0x7f); 270 | * 271 | * To create event for MIDI message that is shorter than three bytes, do something 272 | * like this: 273 | * 274 | * smf_event_new_from_bytes(0xC0, 0x42, -1); 275 | * 276 | * \param first_byte First byte of MIDI message. Must be valid status byte. 277 | * \param second_byte Second byte of MIDI message or -1, if message is one byte long. 278 | * \param third_byte Third byte of MIDI message or -1, if message is two bytes long. 279 | * \return Event containing MIDI data or NULL. 280 | */ 281 | smf_event_t * 282 | smf_event_new_from_bytes(int first_byte, int second_byte, int third_byte) 283 | { 284 | int len; 285 | 286 | smf_event_t *event; 287 | 288 | event = smf_event_new(); 289 | if (event == NULL) 290 | return (NULL); 291 | 292 | if (first_byte < 0) { 293 | g_critical("First byte of MIDI message cannot be < 0"); 294 | smf_event_delete(event); 295 | 296 | return (NULL); 297 | } 298 | 299 | if (first_byte > 255) { 300 | g_critical("smf_event_new_from_bytes: first byte is %d, which is larger than 255.", first_byte); 301 | return (NULL); 302 | } 303 | 304 | if (!is_status_byte(first_byte)) { 305 | g_critical("smf_event_new_from_bytes: first byte is not a valid status byte."); 306 | return (NULL); 307 | } 308 | 309 | 310 | if (second_byte < 0) 311 | len = 1; 312 | else if (third_byte < 0) 313 | len = 2; 314 | else 315 | len = 3; 316 | 317 | if (len > 1) { 318 | if (second_byte > 255) { 319 | g_critical("smf_event_new_from_bytes: second byte is %d, which is larger than 255.", second_byte); 320 | return (NULL); 321 | } 322 | 323 | if (is_status_byte(second_byte)) { 324 | g_critical("smf_event_new_from_bytes: second byte cannot be a status byte."); 325 | return (NULL); 326 | } 327 | } 328 | 329 | if (len > 2) { 330 | if (third_byte > 255) { 331 | g_critical("smf_event_new_from_bytes: third byte is %d, which is larger than 255.", third_byte); 332 | return (NULL); 333 | } 334 | 335 | if (is_status_byte(third_byte)) { 336 | g_critical("smf_event_new_from_bytes: third byte cannot be a status byte."); 337 | return (NULL); 338 | } 339 | } 340 | 341 | event->midi_buffer_length = len; 342 | event->midi_buffer = malloc(event->midi_buffer_length); 343 | if (event->midi_buffer == NULL) { 344 | g_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno)); 345 | smf_event_delete(event); 346 | 347 | return (NULL); 348 | } 349 | 350 | event->midi_buffer[0] = first_byte; 351 | if (len > 1) 352 | event->midi_buffer[1] = second_byte; 353 | if (len > 2) 354 | event->midi_buffer[2] = third_byte; 355 | 356 | return (event); 357 | } 358 | 359 | /** 360 | * Detaches event from its track and frees it. 361 | */ 362 | void 363 | smf_event_delete(smf_event_t *event) 364 | { 365 | if (event->track != NULL) 366 | smf_event_remove_from_track(event); 367 | 368 | if (event->midi_buffer != NULL) { 369 | memset(event->midi_buffer, 0, event->midi_buffer_length); 370 | free(event->midi_buffer); 371 | } 372 | 373 | memset(event, 0, sizeof(smf_event_t)); 374 | free(event); 375 | } 376 | 377 | /** 378 | * Used for sorting track->events_array. 379 | */ 380 | static gint 381 | events_array_compare_function(gconstpointer aa, gconstpointer bb) 382 | { 383 | smf_event_t *a, *b; 384 | 385 | /* "The comparison function for g_ptr_array_sort() doesn't take the pointers 386 | from the array as arguments, it takes pointers to the pointers in the array." */ 387 | a = (smf_event_t *)*(gpointer *)aa; 388 | b = (smf_event_t *)*(gpointer *)bb; 389 | 390 | if (a->time_pulses < b->time_pulses) 391 | return (-1); 392 | 393 | if (a->time_pulses > b->time_pulses) 394 | return (1); 395 | 396 | /* 397 | * We need to preserve original order, otherwise things will break 398 | * when there are several events with the same ->time_pulses. 399 | * XXX: This is an ugly hack; we should remove sorting altogether. 400 | */ 401 | 402 | if (a->event_number < b->event_number) 403 | return (-1); 404 | 405 | if (a->event_number > b->event_number) 406 | return (1); 407 | 408 | return (0); 409 | } 410 | 411 | /* 412 | * An assumption here is that if there is an EOT event, it will be at the end of the track. 413 | */ 414 | static void 415 | remove_eot_if_before_pulses(smf_track_t *track, int pulses) 416 | { 417 | smf_event_t *event; 418 | 419 | event = smf_track_get_last_event(track); 420 | 421 | if (event == NULL) 422 | return; 423 | 424 | if (!smf_event_is_eot(event)) 425 | return; 426 | 427 | if (event->time_pulses > pulses) 428 | return; 429 | 430 | smf_event_remove_from_track(event); 431 | } 432 | 433 | /** 434 | * Adds the event to the track and computes ->delta_pulses. Note that it is faster 435 | * to append events to the end of the track than to insert them in the middle. 436 | * Usually you want to use smf_track_add_event_seconds or smf_track_add_event_pulses 437 | * instead of this one. Event needs to have ->time_pulses and ->time_seconds already set. 438 | * If you try to add event after an EOT, EOT event will be automatically deleted. 439 | */ 440 | void 441 | smf_track_add_event(smf_track_t *track, smf_event_t *event) 442 | { 443 | int i, last_pulses = 0; 444 | 445 | assert(track->smf != NULL); 446 | assert(event->track == NULL); 447 | assert(event->delta_time_pulses == -1); 448 | assert(event->time_pulses >= 0); 449 | assert(event->time_seconds >= 0.0); 450 | 451 | remove_eot_if_before_pulses(track, event->time_pulses); 452 | 453 | event->track = track; 454 | event->track_number = track->track_number; 455 | 456 | if (track->number_of_events == 0) { 457 | assert(track->next_event_number == -1); 458 | track->next_event_number = 1; 459 | } 460 | 461 | if (track->number_of_events > 0) 462 | last_pulses = smf_track_get_last_event(track)->time_pulses; 463 | 464 | track->number_of_events++; 465 | 466 | /* Are we just appending element at the end of the track? */ 467 | if (last_pulses <= event->time_pulses) { 468 | event->delta_time_pulses = event->time_pulses - last_pulses; 469 | assert(event->delta_time_pulses >= 0); 470 | g_ptr_array_add(track->events_array, event); 471 | event->event_number = track->number_of_events; 472 | 473 | /* We need to insert in the middle of the track. XXX: This is slow. */ 474 | } else { 475 | /* Append, then sort according to ->time_pulses. */ 476 | g_ptr_array_add(track->events_array, event); 477 | g_ptr_array_sort(track->events_array, events_array_compare_function); 478 | 479 | /* Renumber entries and fix their ->delta_pulses. */ 480 | for (i = 1; i <= track->number_of_events; i++) { 481 | smf_event_t *tmp = smf_track_get_event_by_number(track, i); 482 | tmp->event_number = i; 483 | 484 | if (tmp->delta_time_pulses != -1) 485 | continue; 486 | 487 | if (i == 1) { 488 | tmp->delta_time_pulses = tmp->time_pulses; 489 | } else { 490 | tmp->delta_time_pulses = tmp->time_pulses - 491 | smf_track_get_event_by_number(track, i - 1)->time_pulses; 492 | assert(tmp->delta_time_pulses >= 0); 493 | } 494 | } 495 | 496 | /* Adjust ->delta_time_pulses of the next event. */ 497 | if (event->event_number < track->number_of_events) { 498 | smf_event_t *next_event = smf_track_get_event_by_number(track, event->event_number + 1); 499 | assert(next_event); 500 | assert(next_event->time_pulses >= event->time_pulses); 501 | next_event->delta_time_pulses -= event->delta_time_pulses; 502 | assert(next_event->delta_time_pulses >= 0); 503 | } 504 | } 505 | 506 | if (smf_event_is_tempo_change_or_time_signature(event)) { 507 | if (smf_event_is_last(event)) 508 | maybe_add_to_tempo_map(event); 509 | else 510 | smf_create_tempo_map_and_compute_seconds(event->track->smf); 511 | } 512 | } 513 | 514 | /** 515 | * Add End Of Track metaevent. Using it is optional, libsmf will automatically 516 | * add EOT to the tracks during smf_save, with delta_pulses 0. If you try to add EOT 517 | * in the middle of the track, it will fail and nonzero value will be returned. 518 | * If you try to add EOT after another EOT event, it will be added, but the existing 519 | * EOT event will be removed. 520 | * 521 | * \return 0 if everything went ok, nonzero otherwise. 522 | */ 523 | int 524 | smf_track_add_eot_delta_pulses(smf_track_t *track, int delta) 525 | { 526 | smf_event_t *event; 527 | 528 | event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00); 529 | if (event == NULL) 530 | return (-1); 531 | 532 | smf_track_add_event_delta_pulses(track, event, delta); 533 | 534 | return (0); 535 | } 536 | 537 | int 538 | smf_track_add_eot_pulses(smf_track_t *track, int pulses) 539 | { 540 | smf_event_t *event, *last_event; 541 | 542 | last_event = smf_track_get_last_event(track); 543 | if (last_event != NULL) { 544 | if (last_event->time_pulses > pulses) 545 | return (-2); 546 | } 547 | 548 | event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00); 549 | if (event == NULL) 550 | return (-3); 551 | 552 | smf_track_add_event_pulses(track, event, pulses); 553 | 554 | return (0); 555 | } 556 | 557 | int 558 | smf_track_add_eot_seconds(smf_track_t *track, double seconds) 559 | { 560 | smf_event_t *event, *last_event; 561 | 562 | last_event = smf_track_get_last_event(track); 563 | if (last_event != NULL) { 564 | if (last_event->time_seconds > seconds) 565 | return (-2); 566 | } 567 | 568 | event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00); 569 | if (event == NULL) 570 | return (-1); 571 | 572 | smf_track_add_event_seconds(track, event, seconds); 573 | 574 | return (0); 575 | } 576 | 577 | /** 578 | * Detaches event from its track. 579 | */ 580 | void 581 | smf_event_remove_from_track(smf_event_t *event) 582 | { 583 | int i, was_last; 584 | smf_event_t *tmp; 585 | smf_track_t *track; 586 | 587 | assert(event->track != NULL); 588 | assert(event->track->smf != NULL); 589 | 590 | track = event->track; 591 | was_last = smf_event_is_last(event); 592 | 593 | /* Adjust ->delta_time_pulses of the next event. */ 594 | if (event->event_number < track->number_of_events) { 595 | tmp = smf_track_get_event_by_number(track, event->event_number + 1); 596 | assert(tmp); 597 | tmp->delta_time_pulses += event->delta_time_pulses; 598 | } 599 | 600 | track->number_of_events--; 601 | g_ptr_array_remove(track->events_array, event); 602 | 603 | if (track->number_of_events == 0) 604 | track->next_event_number = -1; 605 | 606 | /* Renumber the rest of the events, so they are consecutively numbered. */ 607 | for (i = event->event_number; i <= track->number_of_events; i++) { 608 | tmp = smf_track_get_event_by_number(track, i); 609 | tmp->event_number = i; 610 | } 611 | 612 | if (smf_event_is_tempo_change_or_time_signature(event)) { 613 | /* XXX: This will cause problems, when there is more than one Tempo Change event at a given time. */ 614 | if (was_last) 615 | remove_last_tempo_with_pulses(event->track->smf, event->time_pulses); 616 | else 617 | smf_create_tempo_map_and_compute_seconds(track->smf); 618 | } 619 | 620 | event->track = NULL; 621 | event->event_number = -1; 622 | event->delta_time_pulses = -1; 623 | event->time_pulses = -1; 624 | event->time_seconds = -1.0; 625 | } 626 | 627 | /** 628 | * \return Nonzero if event is Tempo Change or Time Signature metaevent. 629 | */ 630 | int 631 | smf_event_is_tempo_change_or_time_signature(const smf_event_t *event) 632 | { 633 | if (!smf_event_is_metadata(event)) 634 | return (0); 635 | 636 | assert(event->midi_buffer_length >= 2); 637 | 638 | if (event->midi_buffer[1] == 0x51 || event->midi_buffer[1] == 0x58) 639 | return (1); 640 | 641 | return (0); 642 | } 643 | 644 | /** 645 | * Sets "Format" field of MThd header to the specified value. Note that you 646 | * don't really need to use this, as libsmf will automatically change format 647 | * from 0 to 1 when you add the second track. 648 | * \param smf SMF. 649 | * \param format 0 for one track per file, 1 for several tracks per file. 650 | * \return 0 if everything went ok, nonzero otherwise. 651 | */ 652 | int 653 | smf_set_format(smf_t *smf, int format) 654 | { 655 | assert(format == 0 || format == 1); 656 | 657 | if (smf->number_of_tracks > 1 && format == 0) { 658 | g_critical("There is more than one track, cannot set format to 0."); 659 | return (-1); 660 | } 661 | 662 | smf->format = format; 663 | 664 | return (0); 665 | } 666 | 667 | /** 668 | * Sets the PPQN ("Division") field of MThd header. This is mandatory, you 669 | * should call it right after smf_new. Note that changing PPQN will change time_seconds 670 | * of all the events. 671 | * \param smf SMF. 672 | * \param ppqn New PPQN. 673 | * \return 0 if everything went ok, nonzero otherwise. 674 | */ 675 | int 676 | smf_set_ppqn(smf_t *smf, int ppqn) 677 | { 678 | assert(ppqn > 0); 679 | 680 | smf->ppqn = ppqn; 681 | 682 | return (0); 683 | } 684 | 685 | /** 686 | * Returns next event from the track given and advances next event counter. 687 | * Do not depend on End Of Track event being the last event on the track - it 688 | * is possible that the track will not end with EOT if you haven't added it 689 | * yet. EOTs are added automatically during smf_save(). 690 | * 691 | * \return Event or NULL, if there are no more events left in this track. 692 | */ 693 | smf_event_t * 694 | smf_track_get_next_event(smf_track_t *track) 695 | { 696 | smf_event_t *event, *next_event; 697 | 698 | /* End of track? */ 699 | if (track->next_event_number == -1) 700 | return (NULL); 701 | 702 | assert(track->next_event_number >= 1); 703 | assert(track->number_of_events > 0); 704 | 705 | event = smf_track_get_event_by_number(track, track->next_event_number); 706 | 707 | assert(event != NULL); 708 | 709 | /* Is this the last event in the track? */ 710 | if (track->next_event_number < track->number_of_events) { 711 | next_event = smf_track_get_event_by_number(track, track->next_event_number + 1); 712 | assert(next_event); 713 | 714 | track->time_of_next_event = next_event->time_pulses; 715 | track->next_event_number++; 716 | } else { 717 | track->next_event_number = -1; 718 | } 719 | 720 | return (event); 721 | } 722 | 723 | /** 724 | * Returns next event from the track given. Does not change next event counter, 725 | * so repeatedly calling this routine will return the same event. 726 | * \return Event or NULL, if there are no more events left in this track. 727 | */ 728 | static smf_event_t * 729 | smf_peek_next_event_from_track(smf_track_t *track) 730 | { 731 | smf_event_t *event; 732 | 733 | /* End of track? */ 734 | if (track->next_event_number == -1) 735 | return (NULL); 736 | 737 | assert(track->next_event_number >= 1); 738 | assert(track->events_array->len != 0); 739 | 740 | event = smf_track_get_event_by_number(track, track->next_event_number); 741 | 742 | return (event); 743 | } 744 | 745 | /** 746 | * \return Track with a given number or NULL, if there is no such track. 747 | * Tracks are numbered consecutively starting from one. 748 | */ 749 | smf_track_t * 750 | smf_get_track_by_number(const smf_t *smf, int track_number) 751 | { 752 | smf_track_t *track; 753 | 754 | assert(track_number >= 1); 755 | 756 | if (track_number > smf->number_of_tracks) 757 | return (NULL); 758 | 759 | track = (smf_track_t *)g_ptr_array_index(smf->tracks_array, track_number - 1); 760 | 761 | assert(track); 762 | 763 | return (track); 764 | } 765 | 766 | /** 767 | * \return Event with a given number or NULL, if there is no such event. 768 | * Events are numbered consecutively starting from one. 769 | */ 770 | smf_event_t * 771 | smf_track_get_event_by_number(const smf_track_t *track, int event_number) 772 | { 773 | smf_event_t *event; 774 | 775 | assert(event_number >= 1); 776 | 777 | if (event_number > track->number_of_events) 778 | return (NULL); 779 | 780 | event = g_ptr_array_index(track->events_array, event_number - 1); 781 | 782 | assert(event); 783 | 784 | return (event); 785 | } 786 | 787 | /** 788 | * \return Last event on the track or NULL, if track is empty. 789 | */ 790 | smf_event_t * 791 | smf_track_get_last_event(const smf_track_t *track) 792 | { 793 | smf_event_t *event; 794 | 795 | if (track->number_of_events == 0) 796 | return (NULL); 797 | 798 | event = smf_track_get_event_by_number(track, track->number_of_events); 799 | 800 | return (event); 801 | } 802 | 803 | /** 804 | * Searches for track that contains next event, in time order. In other words, 805 | * returns the track that contains event that should be played next. 806 | * \return Track with next event or NULL, if there are no events left. 807 | */ 808 | smf_track_t * 809 | smf_find_track_with_next_event(smf_t *smf) 810 | { 811 | int i, min_time = 0; 812 | smf_track_t *track = NULL, *min_time_track = NULL; 813 | 814 | /* Find track with event that should be played next. */ 815 | for (i = 1; i <= smf->number_of_tracks; i++) { 816 | track = smf_get_track_by_number(smf, i); 817 | 818 | assert(track); 819 | 820 | /* No more events in this track? */ 821 | if (track->next_event_number == -1) 822 | continue; 823 | 824 | if (track->time_of_next_event < min_time || min_time_track == NULL) { 825 | min_time = track->time_of_next_event; 826 | min_time_track = track; 827 | } 828 | } 829 | 830 | return (min_time_track); 831 | } 832 | 833 | /** 834 | * \return Next event, in time order, or NULL, if there are none left. 835 | */ 836 | smf_event_t * 837 | smf_get_next_event(smf_t *smf) 838 | { 839 | smf_event_t *event; 840 | smf_track_t *track = smf_find_track_with_next_event(smf); 841 | 842 | if (track == NULL) { 843 | #if 0 844 | g_debug("End of the song."); 845 | #endif 846 | 847 | return (NULL); 848 | } 849 | 850 | event = smf_track_get_next_event(track); 851 | 852 | assert(event != NULL); 853 | 854 | event->track->smf->last_seek_position = -1.0; 855 | 856 | return (event); 857 | } 858 | 859 | /** 860 | * Advance the "next event counter". This is functionally the same as calling 861 | * smf_get_next_event and ignoring the return value. 862 | */ 863 | void 864 | smf_skip_next_event(smf_t *smf) 865 | { 866 | void *notused; 867 | 868 | notused = smf_get_next_event(smf); 869 | (void) notused; 870 | } 871 | 872 | /** 873 | * \return Next event, in time order, or NULL, if there are none left. Does 874 | * not advance position in song. 875 | */ 876 | smf_event_t * 877 | smf_peek_next_event(smf_t *smf) 878 | { 879 | smf_event_t *event; 880 | smf_track_t *track = smf_find_track_with_next_event(smf); 881 | 882 | if (track == NULL) { 883 | #if 0 884 | g_debug("End of the song."); 885 | #endif 886 | 887 | return (NULL); 888 | } 889 | 890 | event = smf_peek_next_event_from_track(track); 891 | 892 | assert(event != NULL); 893 | 894 | return (event); 895 | } 896 | 897 | /** 898 | * Rewinds the SMF. What that means is, after calling this routine, smf_get_next_event 899 | * will return first event in the song. 900 | */ 901 | void 902 | smf_rewind(smf_t *smf) 903 | { 904 | int i; 905 | smf_track_t *track = NULL; 906 | smf_event_t *event; 907 | 908 | assert(smf); 909 | 910 | smf->last_seek_position = 0.0; 911 | 912 | for (i = 1; i <= smf->number_of_tracks; i++) { 913 | track = smf_get_track_by_number(smf, i); 914 | 915 | assert(track != NULL); 916 | 917 | if (track->number_of_events > 0) { 918 | track->next_event_number = 1; 919 | event = smf_peek_next_event_from_track(track); 920 | assert(event); 921 | track->time_of_next_event = event->time_pulses; 922 | } else { 923 | track->next_event_number = -1; 924 | track->time_of_next_event = 0; 925 | #if 0 926 | g_warning("Warning: empty track."); 927 | #endif 928 | } 929 | } 930 | } 931 | 932 | /** 933 | * Seeks the SMF to the given event. After calling this routine, smf_get_next_event 934 | * will return the event that was the second argument of this call. 935 | */ 936 | int 937 | smf_seek_to_event(smf_t *smf, const smf_event_t *target) 938 | { 939 | smf_event_t *event; 940 | 941 | smf_rewind(smf); 942 | 943 | #if 0 944 | g_debug("Seeking to event %d, track %d.", target->event_number, target->track->track_number); 945 | #endif 946 | 947 | for (;;) { 948 | event = smf_peek_next_event(smf); 949 | 950 | /* There can't be NULL here, unless "target" is not in this smf. */ 951 | assert(event); 952 | 953 | if (event != target) 954 | smf_skip_next_event(smf); 955 | else 956 | break; 957 | } 958 | 959 | smf->last_seek_position = event->time_seconds; 960 | 961 | return (0); 962 | } 963 | 964 | /** 965 | * Seeks the SMF to the given position. For example, after seeking to 1.0 seconds, 966 | * smf_get_next_event will return first event that happens after the first second of song. 967 | * \return 0 if everything went ok, nonzero otherwise. 968 | */ 969 | int 970 | smf_seek_to_seconds(smf_t *smf, double seconds) 971 | { 972 | smf_event_t *event; 973 | 974 | assert(seconds >= 0.0); 975 | 976 | if (seconds == smf->last_seek_position) { 977 | #if 0 978 | g_debug("Avoiding seek to %f seconds.", seconds); 979 | #endif 980 | return (0); 981 | } 982 | 983 | smf_rewind(smf); 984 | 985 | #if 0 986 | g_debug("Seeking to %f seconds.", seconds); 987 | #endif 988 | 989 | for (;;) { 990 | event = smf_peek_next_event(smf); 991 | 992 | if (event == NULL) { 993 | g_critical("Trying to seek past the end of song."); 994 | return (-1); 995 | } 996 | 997 | if (event->time_seconds < seconds) 998 | smf_skip_next_event(smf); 999 | else 1000 | break; 1001 | } 1002 | 1003 | smf->last_seek_position = seconds; 1004 | 1005 | return (0); 1006 | } 1007 | 1008 | /** 1009 | * Seeks the SMF to the given position. For example, after seeking to 10 pulses, 1010 | * smf_get_next_event will return first event that happens after the first ten pulses. 1011 | * \return 0 if everything went ok, nonzero otherwise. 1012 | */ 1013 | int 1014 | smf_seek_to_pulses(smf_t *smf, int pulses) 1015 | { 1016 | smf_event_t *event; 1017 | 1018 | assert(pulses >= 0); 1019 | 1020 | smf_rewind(smf); 1021 | 1022 | #if 0 1023 | g_debug("Seeking to %d pulses.", pulses); 1024 | #endif 1025 | 1026 | for (;;) { 1027 | event = smf_peek_next_event(smf); 1028 | 1029 | if (event == NULL) { 1030 | g_critical("Trying to seek past the end of song."); 1031 | return (-1); 1032 | } 1033 | 1034 | if (event->time_pulses < pulses) 1035 | smf_skip_next_event(smf); 1036 | else 1037 | break; 1038 | } 1039 | 1040 | smf->last_seek_position = event->time_seconds; 1041 | 1042 | return (0); 1043 | } 1044 | 1045 | /** 1046 | * \return Length of SMF, in pulses. 1047 | */ 1048 | int 1049 | smf_get_length_pulses(const smf_t *smf) 1050 | { 1051 | int pulses = 0, i; 1052 | 1053 | for (i = 1; i <= smf->number_of_tracks; i++) { 1054 | smf_track_t *track; 1055 | smf_event_t *event; 1056 | 1057 | track = smf_get_track_by_number(smf, i); 1058 | assert(track); 1059 | 1060 | event = smf_track_get_last_event(track); 1061 | /* Empty track? */ 1062 | if (event == NULL) 1063 | continue; 1064 | 1065 | if (event->time_pulses > pulses) 1066 | pulses = event->time_pulses; 1067 | } 1068 | 1069 | return (pulses); 1070 | } 1071 | 1072 | /** 1073 | * \return Length of SMF, in seconds. 1074 | */ 1075 | double 1076 | smf_get_length_seconds(const smf_t *smf) 1077 | { 1078 | int i; 1079 | double seconds = 0.0; 1080 | 1081 | for (i = 1; i <= smf->number_of_tracks; i++) { 1082 | smf_track_t *track; 1083 | smf_event_t *event; 1084 | 1085 | track = smf_get_track_by_number(smf, i); 1086 | assert(track); 1087 | 1088 | event = smf_track_get_last_event(track); 1089 | /* Empty track? */ 1090 | if (event == NULL) 1091 | continue; 1092 | 1093 | if (event->time_seconds > seconds) 1094 | seconds = event->time_seconds; 1095 | } 1096 | 1097 | return (seconds); 1098 | } 1099 | 1100 | /** 1101 | * \return Nonzero, if there are no events in the SMF after this one. 1102 | * Note that may be more than one "last event", if they occur at the same time. 1103 | */ 1104 | int 1105 | smf_event_is_last(const smf_event_t *event) 1106 | { 1107 | if (smf_get_length_pulses(event->track->smf) <= event->time_pulses) 1108 | return (1); 1109 | 1110 | return (0); 1111 | } 1112 | 1113 | /** 1114 | * \return Version of libsmf. 1115 | */ 1116 | const char * 1117 | smf_get_version(void) 1118 | { 1119 | return (SMF_VERSION); 1120 | } 1121 | 1122 | -------------------------------------------------------------------------------- /src/smf.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007, 2008 Edward Tomasz Napierała 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | /** 29 | * \file 30 | * 31 | * Public interface declaration for libsmf, Standard MIDI File format library. 32 | */ 33 | 34 | /** 35 | * 36 | * \mainpage libsmf - general usage instructions 37 | * 38 | * An smf_t structure represents a "song". Every valid smf contains one or more tracks. 39 | * Tracks contain zero or more events. Libsmf doesn't care about actual MIDI data, as long 40 | * as it is valid from the MIDI specification point of view - it may be realtime message, 41 | * SysEx, whatever. 42 | * 43 | * The only field in smf_t, smf_track_t, smf_event_t and smf_tempo_t structures your 44 | * code may modify is event->midi_buffer and event->midi_buffer_length. Do not modify 45 | * other fields, _ever_. You may read them, though. Do not declare static instances 46 | * of these types, i.e. never do something like this: "smf_t smf;". Always use 47 | * "smf_t *smf = smf_new();". The same applies to smf_track_t and smf_event_t. 48 | * 49 | * Say you want to load a Standard MIDI File (.mid) file and play it back somehow. This is (roughly) 50 | * how you do this: 51 | * 52 | * \code 53 | * smf_t *smf; 54 | * smf_event_t *event; 55 | * 56 | * smf = smf_load(file_name); 57 | * if (smf == NULL) { 58 | * Whoops, something went wrong. 59 | * return; 60 | * } 61 | * 62 | * while ((event = smf_get_next_event(smf)) != NULL) { 63 | * if (smf_event_is_metadata(event)) 64 | * continue; 65 | * 66 | * wait until event->time_seconds. 67 | * feed_to_midi_output(event->midi_buffer, event->midi_buffer_length); 68 | * } 69 | * 70 | * smf_delete(smf); 71 | * 72 | * \endcode 73 | * 74 | * Saving works like this: 75 | * 76 | * \code 77 | * 78 | * smf_t *smf; 79 | * smf_track_t *track; 80 | * smf_event_t *event; 81 | * 82 | * smf = smf_new(); 83 | * if (smf == NULL) { 84 | * Whoops. 85 | * return; 86 | * } 87 | * 88 | * for (int i = 1; i <= number of tracks; i++) { 89 | * track = smf_track_new(); 90 | * if (track == NULL) { 91 | * Whoops. 92 | * return; 93 | * } 94 | * 95 | * smf_add_track(smf, track); 96 | * 97 | * for (int j = 1; j <= number of events you want to put into this track; j++) { 98 | * event = smf_event_new_from_pointer(your MIDI message, message length); 99 | * if (event == NULL) { 100 | * Whoops. 101 | * return; 102 | * } 103 | * 104 | * smf_track_add_event_seconds(track, event, seconds since start of the song); 105 | * } 106 | * } 107 | * 108 | * ret = smf_save(smf, file_name); 109 | * if (ret) { 110 | * Whoops, saving failed for some reason. 111 | * return; 112 | * } 113 | * 114 | * smf_delete(smf); 115 | * 116 | * \endcode 117 | * 118 | * There are two basic ways of getting MIDI data out of smf - sequential or by track/event number. You may 119 | * mix them if you need to. First one is used in the example above - seek to the point from which you want 120 | * the playback to start (using smf_seek_to_seconds(), smf_seek_to_pulses() or smf_seek_to_event()) and then 121 | * do smf_get_next_event() in loop, until it returns NULL. Calling smf_load() causes the smf to be rewound 122 | * to the start of the song. 123 | * 124 | * Getting events by number works like this: 125 | * 126 | * \code 127 | * 128 | * smf_track_t *track = smf_get_track_by_number(smf, track_number); 129 | * smf_event_t *event = smf_track_get_event_by_number(track, event_number); 130 | * 131 | * \endcode 132 | * 133 | * To create new event, use smf_event_new(), smf_event_new_from_pointer() or smf_event_new_from_bytes(). 134 | * First one creates an empty event - you need to manually allocate (using malloc(3)) buffer for 135 | * MIDI data, write MIDI data into it, put the address of that buffer into event->midi_buffer, 136 | * and the length of MIDI data into event->midi_buffer_length. Note that deleting the event 137 | * (using smf_event_delete()) will free the buffer. 138 | * 139 | * Second form does most of this for you: it takes an address of the buffer containing MIDI data, 140 | * allocates storage and copies MIDI data into it. 141 | * 142 | * Third form is useful for manually creating short events, up to three bytes in length, for 143 | * example Note On or Note Off events. It simply takes three bytes and creates MIDI event containing 144 | * them. If you need to create MIDI message that takes only two bytes, pass -1 as the third byte. 145 | * For one byte message (System Realtime), pass -1 as second and third byte. 146 | * 147 | * To free an event, use smf_event_delete(). 148 | * 149 | * To add event to the track, use smf_track_add_event_delta_pulses(), smf_track_add_event_pulses(), 150 | * or smf_track_add_event_seconds(). The difference between them is in the way you specify the time of 151 | * the event - with the first one, you specify it as an interval, in pulses, from the previous event 152 | * in this track; with the second one, you specify it as pulses from the start of the song, and with the 153 | * last one, you specify it as seconds from the start of the song. Obviously, the first version can 154 | * only append events at the end of the track. 155 | * 156 | * To remove an event from the track it's attached to, use smf_event_remove_from_track(). You may 157 | * want to free the event (using smf_event_delete()) afterwards. 158 | * 159 | * To create new track, use smf_track_new(). To add track to the smf, use smf_add_track(). 160 | * To remove track from its smf, use smf_track_remove_from_smf(). To free the track structure, 161 | * use smf_track_delete(). 162 | * 163 | * Note that libsmf keeps things consistent. If you free (using smf_track_delete()) a track that 164 | * is attached to an smf and contains events, libsmf will detach the events, free them, detach 165 | * the track, free it etc. 166 | * 167 | * Tracks and events are numbered consecutively, starting from one. If you remove a track or event, 168 | * the rest of tracks/events will get renumbered. To get the number of a given event in its track, use event->event_number. 169 | * To get the number of track in its smf, use track->track_number. To get the number of events in the track, 170 | * use track->number_of_events. To get the number of tracks in the smf, use smf->number_of_tracks. 171 | * 172 | * In SMF File Format, each track has to end with End Of Track metaevent. If you load SMF file using smf_load(), 173 | * that will be the case. If you want to create or edit an SMF, you don't need to worry about EOT events; 174 | * libsmf automatically takes care of them for you. If you try to save an SMF with tracks that do not end 175 | * with EOTs, smf_save() will append them. If you try to add event that happens after EOT metaevent, libsmf 176 | * will remove the EOT. If you want to add EOT manually, you can, of course, using smf_track_add_eot_seconds() 177 | * or smf_track_add_eot_pulses(). 178 | * 179 | * Each event carries three time values - event->time_seconds, which is seconds since the start of the song, 180 | * event->time_pulses, which is PPQN clocks since the start of the song, and event->delta_pulses, which is PPQN clocks 181 | * since the previous event in that track. These values are invalid if the event is not attached to the track. 182 | * If event is attached, all three values are valid. Time of the event is specified when adding the event 183 | * (using smf_track_add_event_seconds(), smf_track_add_event_pulses() or smf_track_add_event_delta_pulses()); the remaining 184 | * two values are computed from that. 185 | * 186 | * Tempo related stuff happens automatically - when you add a metaevent that 187 | * is Tempo Change or Time Signature, libsmf adds that event to the tempo map. If you remove 188 | * Tempo Change event that is in the middle of the song, the rest of the events will have their 189 | * event->time_seconds recomputed from event->time_pulses before smf_event_remove_from_track() function returns. 190 | * Adding Tempo Change in the middle of the song works in a similar way. 191 | * 192 | * MIDI data (event->midi_buffer) is always kept in normalized form - it always begins with status byte 193 | * (no running status), there are no System Realtime events embedded in them etc. Events like SysExes 194 | * are in "on the wire" form, without embedded length that is used in SMF file format. Obviously 195 | * libsmf "normalizes" MIDI data during loading and "denormalizes" (adding length to SysExes, escaping 196 | * System Common and System Realtime messages etc) during writing. 197 | * 198 | * Note that you always have to first add the track to smf, and then add events to the track. 199 | * Doing it the other way around will trip asserts. Also, try to add events at the end of the track and remove 200 | * them from the end of the track, that's much more efficient. 201 | * 202 | * All the libsmf functions have prefix "smf_". First argument for routines whose names start with 203 | * "smf_event_" is "smf_event_t *", for routines whose names start with "smf_track_" - "smf_track_t *", 204 | * and for plain "smf_" - "smf_t *". The only exception are smf_whatever_new routines. 205 | * Library does not use any global variables and is thread-safe, 206 | * as long as you don't try to work on the same SMF (smf_t and its descendant tracks and events) from several 207 | * threads at once without protecting it with mutex. Library depends on glib and nothing else. License is 208 | * BSD, two clause, which basically means you can use it freely in your software, both Open Source (including 209 | * GPL) and closed source. 210 | * 211 | */ 212 | 213 | #ifndef SMF_H 214 | #define SMF_H 215 | 216 | #ifdef __cplusplus 217 | extern "C" { 218 | #endif 219 | 220 | #include 221 | #include 222 | 223 | #if defined(__GNUC__) && __GNUC__ >= 4 224 | #define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) 225 | #else 226 | #define WARN_UNUSED_RESULT 227 | #endif 228 | 229 | /** Represents a "song", that is, collection of one or more tracks. */ 230 | struct smf_struct { 231 | int format; 232 | 233 | /** These fields are extracted from "division" field of MThd header. Valid is _either_ ppqn or frames_per_second/resolution. */ 234 | int ppqn; 235 | int frames_per_second; 236 | int resolution; 237 | int number_of_tracks; 238 | 239 | /** These are private fields using only by loading and saving routines. */ 240 | FILE *stream; 241 | void *file_buffer; 242 | int file_buffer_length; 243 | int next_chunk_offset; 244 | int expected_number_of_tracks; 245 | 246 | /** Private, used by smf.c. */ 247 | GPtrArray *tracks_array; 248 | double last_seek_position; 249 | 250 | /** Private, used by smf_tempo.c. */ 251 | /** Array of pointers to smf_tempo_struct. */ 252 | GPtrArray *tempo_array; 253 | }; 254 | 255 | typedef struct smf_struct smf_t; 256 | 257 | /** Describes a single tempo or time signature change. */ 258 | struct smf_tempo_struct { 259 | int time_pulses; 260 | double time_seconds; 261 | int microseconds_per_quarter_note; 262 | int numerator; 263 | int denominator; 264 | int clocks_per_click; 265 | int notes_per_note; 266 | }; 267 | 268 | typedef struct smf_tempo_struct smf_tempo_t; 269 | 270 | /** Represents a single track. */ 271 | struct smf_track_struct { 272 | smf_t *smf; 273 | 274 | int track_number; 275 | int number_of_events; 276 | 277 | /** These are private fields using only by loading and saving routines. */ 278 | void *file_buffer; 279 | int file_buffer_length; 280 | int last_status; /* Used for "running status". */ 281 | 282 | /** Private, used by smf.c. */ 283 | /** Offset into buffer, used in parse_next_event(). */ 284 | int next_event_offset; 285 | int next_event_number; 286 | 287 | /** Absolute time of next event on events_queue. */ 288 | int time_of_next_event; 289 | GPtrArray *events_array; 290 | 291 | /** API consumer is free to use this for whatever purpose. NULL in freshly allocated track. 292 | Note that tracks might be deallocated not only explicitly, by calling smf_track_delete(), 293 | but also implicitly, e.g. when calling smf_delete() with tracks still added to 294 | the smf; there is no mechanism for libsmf to notify you about removal of the track. */ 295 | void *user_pointer; 296 | }; 297 | 298 | typedef struct smf_track_struct smf_track_t; 299 | 300 | /** Represents a single MIDI event or metaevent. */ 301 | struct smf_event_struct { 302 | /** Pointer to the track, or NULL if event is not attached. */ 303 | smf_track_t *track; 304 | 305 | /** Number of this event in the track. Events are numbered consecutively, starting from one. */ 306 | int event_number; 307 | 308 | /** Note that the time fields are invalid, if event is not attached to a track. */ 309 | /** Time, in pulses, since the previous event on this track. */ 310 | int delta_time_pulses; 311 | 312 | /** Time, in pulses, since the start of the song. */ 313 | int time_pulses; 314 | 315 | /** Time, in seconds, since the start of the song. */ 316 | double time_seconds; 317 | 318 | /** Tracks are numbered consecutively, starting from 1. */ 319 | int track_number; 320 | 321 | /** Pointer to the buffer containing MIDI message. This is freed by smf_event_delete. */ 322 | unsigned char *midi_buffer; 323 | 324 | /** Length of the MIDI message in the buffer, in bytes. */ 325 | int midi_buffer_length; 326 | 327 | /** API consumer is free to use this for whatever purpose. NULL in freshly allocated event. 328 | Note that events might be deallocated not only explicitly, by calling smf_event_delete(), 329 | but also implicitly, e.g. when calling smf_track_delete() with events still added to 330 | the track; there is no mechanism for libsmf to notify you about removal of the event. */ 331 | void *user_pointer; 332 | }; 333 | 334 | typedef struct smf_event_struct smf_event_t; 335 | 336 | /* Routines for manipulating smf_t. */ 337 | smf_t *smf_new(void) WARN_UNUSED_RESULT; 338 | void smf_delete(smf_t *smf); 339 | 340 | int smf_set_format(smf_t *smf, int format) WARN_UNUSED_RESULT; 341 | int smf_set_ppqn(smf_t *smf, int format) WARN_UNUSED_RESULT; 342 | 343 | char *smf_decode(const smf_t *smf) WARN_UNUSED_RESULT; 344 | 345 | smf_track_t *smf_get_track_by_number(const smf_t *smf, int track_number) WARN_UNUSED_RESULT; 346 | 347 | smf_event_t *smf_peek_next_event(smf_t *smf) WARN_UNUSED_RESULT; 348 | smf_event_t *smf_get_next_event(smf_t *smf) WARN_UNUSED_RESULT; 349 | void smf_skip_next_event(smf_t *smf); 350 | 351 | void smf_rewind(smf_t *smf); 352 | int smf_seek_to_seconds(smf_t *smf, double seconds) WARN_UNUSED_RESULT; 353 | int smf_seek_to_pulses(smf_t *smf, int pulses) WARN_UNUSED_RESULT; 354 | int smf_seek_to_event(smf_t *smf, const smf_event_t *event) WARN_UNUSED_RESULT; 355 | 356 | int smf_get_length_pulses(const smf_t *smf) WARN_UNUSED_RESULT; 357 | double smf_get_length_seconds(const smf_t *smf) WARN_UNUSED_RESULT; 358 | int smf_event_is_last(const smf_event_t *event) WARN_UNUSED_RESULT; 359 | 360 | void smf_add_track(smf_t *smf, smf_track_t *track); 361 | void smf_track_remove_from_smf(smf_track_t *track); 362 | 363 | /* Routines for manipulating smf_track_t. */ 364 | smf_track_t *smf_track_new(void) WARN_UNUSED_RESULT; 365 | void smf_track_delete(smf_track_t *track); 366 | 367 | smf_event_t *smf_track_get_next_event(smf_track_t *track) WARN_UNUSED_RESULT; 368 | smf_event_t *smf_track_get_event_by_number(const smf_track_t *track, int event_number) WARN_UNUSED_RESULT; 369 | smf_event_t *smf_track_get_last_event(const smf_track_t *track) WARN_UNUSED_RESULT; 370 | 371 | void smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, int pulses); 372 | void smf_track_add_event_pulses(smf_track_t *track, smf_event_t *event, int pulses); 373 | void smf_track_add_event_seconds(smf_track_t *track, smf_event_t *event, double seconds); 374 | int smf_track_add_eot_delta_pulses(smf_track_t *track, int delta) WARN_UNUSED_RESULT; 375 | int smf_track_add_eot_pulses(smf_track_t *track, int pulses) WARN_UNUSED_RESULT; 376 | int smf_track_add_eot_seconds(smf_track_t *track, double seconds) WARN_UNUSED_RESULT; 377 | void smf_event_remove_from_track(smf_event_t *event); 378 | 379 | /* Routines for manipulating smf_event_t. */ 380 | smf_event_t *smf_event_new(void) WARN_UNUSED_RESULT; 381 | smf_event_t *smf_event_new_from_pointer(void *midi_data, int len) WARN_UNUSED_RESULT; 382 | smf_event_t *smf_event_new_from_bytes(int first_byte, int second_byte, int third_byte) WARN_UNUSED_RESULT; 383 | smf_event_t *smf_event_new_textual(int type, const char *text) WARN_UNUSED_RESULT; 384 | void smf_event_delete(smf_event_t *event); 385 | 386 | int smf_event_is_valid(const smf_event_t *event) WARN_UNUSED_RESULT; 387 | int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT; 388 | int smf_event_is_system_realtime(const smf_event_t *event) WARN_UNUSED_RESULT; 389 | int smf_event_is_system_common(const smf_event_t *event) WARN_UNUSED_RESULT; 390 | int smf_event_is_sysex(const smf_event_t *event) WARN_UNUSED_RESULT; 391 | int smf_event_is_eot(const smf_event_t *event) WARN_UNUSED_RESULT; 392 | int smf_event_is_textual(const smf_event_t *event) WARN_UNUSED_RESULT; 393 | char *smf_event_decode(const smf_event_t *event) WARN_UNUSED_RESULT; 394 | char *smf_event_extract_text(const smf_event_t *event) WARN_UNUSED_RESULT; 395 | 396 | /* Routines for loading SMF files. */ 397 | smf_t *smf_load(const char *file_name) WARN_UNUSED_RESULT; 398 | smf_t *smf_load_from_memory(const void *buffer, const int buffer_length) WARN_UNUSED_RESULT; 399 | 400 | /* Routine for writing SMF files. */ 401 | int smf_save(smf_t *smf, const char *file_name) WARN_UNUSED_RESULT; 402 | 403 | /* Routines for manipulating smf_tempo_t. */ 404 | smf_tempo_t *smf_get_tempo_by_pulses(const smf_t *smf, int pulses) WARN_UNUSED_RESULT; 405 | smf_tempo_t *smf_get_tempo_by_seconds(const smf_t *smf, double seconds) WARN_UNUSED_RESULT; 406 | smf_tempo_t *smf_get_tempo_by_number(const smf_t *smf, int number) WARN_UNUSED_RESULT; 407 | smf_tempo_t *smf_get_last_tempo(const smf_t *smf) WARN_UNUSED_RESULT; 408 | 409 | const char *smf_get_version(void) WARN_UNUSED_RESULT; 410 | 411 | #ifdef __cplusplus 412 | } 413 | #endif 414 | 415 | #endif /* SMF_H */ 416 | 417 | -------------------------------------------------------------------------------- /src/smf_decode.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007, 2008 Edward Tomasz Napierała 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | /** 29 | * \file 30 | * 31 | * Event decoding routines. 32 | * 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #ifdef __MINGW32__ 41 | #include 42 | #else /* ! __MINGW32__ */ 43 | #include 44 | #endif /* ! __MINGW32__ */ 45 | #include 46 | #include "smf.h" 47 | #include "smf_private.h" 48 | 49 | #define BUFFER_SIZE 1024 50 | 51 | /** 52 | * \return Nonzero if event is metaevent. You should never send metaevents; 53 | * they are not really MIDI messages. They carry information like track title, 54 | * time signature etc. 55 | */ 56 | int 57 | smf_event_is_metadata(const smf_event_t *event) 58 | { 59 | assert(event->midi_buffer); 60 | assert(event->midi_buffer_length > 0); 61 | 62 | if (event->midi_buffer[0] == 0xFF) 63 | return (1); 64 | 65 | return (0); 66 | } 67 | 68 | /** 69 | * \return Nonzero if event is System Realtime. 70 | */ 71 | int 72 | smf_event_is_system_realtime(const smf_event_t *event) 73 | { 74 | assert(event->midi_buffer); 75 | assert(event->midi_buffer_length > 0); 76 | 77 | if (smf_event_is_metadata(event)) 78 | return (0); 79 | 80 | if (event->midi_buffer[0] >= 0xF8) 81 | return (1); 82 | 83 | return (0); 84 | } 85 | 86 | /** 87 | * \return Nonzero if event is System Common. 88 | */ 89 | int 90 | smf_event_is_system_common(const smf_event_t *event) 91 | { 92 | assert(event->midi_buffer); 93 | assert(event->midi_buffer_length > 0); 94 | 95 | if (event->midi_buffer[0] >= 0xF0 && event->midi_buffer[0] <= 0xF7) 96 | return (1); 97 | 98 | return (0); 99 | } 100 | /** 101 | * \return Nonzero if event is SysEx message. 102 | */ 103 | int 104 | smf_event_is_sysex(const smf_event_t *event) 105 | { 106 | assert(event->midi_buffer); 107 | assert(event->midi_buffer_length > 0); 108 | 109 | if (event->midi_buffer[0] == 0xF0) 110 | return (1); 111 | 112 | return (0); 113 | } 114 | 115 | static char * 116 | smf_event_decode_textual(const smf_event_t *event, const char *name) 117 | { 118 | int off = 0; 119 | char *buf, *extracted; 120 | 121 | buf = malloc(BUFFER_SIZE); 122 | if (buf == NULL) { 123 | g_critical("smf_event_decode_textual: malloc failed."); 124 | return (NULL); 125 | } 126 | 127 | extracted = smf_event_extract_text(event); 128 | if (extracted == NULL) { 129 | free(buf); 130 | return (NULL); 131 | } 132 | 133 | snprintf(buf + off, BUFFER_SIZE - off, "%s: %s", name, extracted); 134 | free(extracted); 135 | 136 | return (buf); 137 | } 138 | 139 | static char * 140 | smf_event_decode_metadata(const smf_event_t *event) 141 | { 142 | int off = 0, mspqn, flats, isminor; 143 | char *buf; 144 | 145 | static const char *const major_keys[] = {"Fb", "Cb", "Gb", "Db", "Ab", 146 | "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#"}; 147 | 148 | static const char *const minor_keys[] = {"Dbm", "Abm", "Ebm", "Bbm", "Fm", 149 | "Cm", "Gm", "Dm", "Am", "Em", "Bm", "F#m", "C#m", "G#m", "D#m", "A#m", "E#m"}; 150 | 151 | assert(smf_event_is_metadata(event)); 152 | 153 | switch (event->midi_buffer[1]) { 154 | case 0x01: 155 | return (smf_event_decode_textual(event, "Text")); 156 | 157 | case 0x02: 158 | return (smf_event_decode_textual(event, "Copyright")); 159 | 160 | case 0x03: 161 | return (smf_event_decode_textual(event, "Sequence/Track Name")); 162 | 163 | case 0x04: 164 | return (smf_event_decode_textual(event, "Instrument")); 165 | 166 | case 0x05: 167 | return (smf_event_decode_textual(event, "Lyric")); 168 | 169 | case 0x06: 170 | return (smf_event_decode_textual(event, "Marker")); 171 | 172 | case 0x07: 173 | return (smf_event_decode_textual(event, "Cue Point")); 174 | 175 | case 0x08: 176 | return (smf_event_decode_textual(event, "Program Name")); 177 | 178 | case 0x09: 179 | return (smf_event_decode_textual(event, "Device (Port) Name")); 180 | 181 | default: 182 | break; 183 | } 184 | 185 | buf = malloc(BUFFER_SIZE); 186 | if (buf == NULL) { 187 | g_critical("smf_event_decode_metadata: malloc failed."); 188 | return (NULL); 189 | } 190 | 191 | switch (event->midi_buffer[1]) { 192 | case 0x00: 193 | off += snprintf(buf + off, BUFFER_SIZE - off, "Sequence number"); 194 | break; 195 | 196 | /* http://music.columbia.edu/pipermail/music-dsp/2004-August/061196.html */ 197 | case 0x20: 198 | if (event->midi_buffer_length < 4) { 199 | g_critical("smf_event_decode_metadata: truncated MIDI message."); 200 | goto error; 201 | } 202 | 203 | off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Prefix: %d", event->midi_buffer[3]); 204 | break; 205 | 206 | case 0x21: 207 | if (event->midi_buffer_length < 4) { 208 | g_critical("smf_event_decode_metadata: truncated MIDI message."); 209 | goto error; 210 | } 211 | 212 | off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Port: %d", event->midi_buffer[3]); 213 | break; 214 | 215 | case 0x2F: 216 | off += snprintf(buf + off, BUFFER_SIZE - off, "End Of Track"); 217 | break; 218 | 219 | case 0x51: 220 | if (event->midi_buffer_length < 6) { 221 | g_critical("smf_event_decode_metadata: truncated MIDI message."); 222 | goto error; 223 | } 224 | 225 | mspqn = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5]; 226 | 227 | off += snprintf(buf + off, BUFFER_SIZE - off, "Tempo: %d microseconds per quarter note, %.2f BPM", 228 | mspqn, 60000000.0 / (double)mspqn); 229 | break; 230 | 231 | case 0x54: 232 | off += snprintf(buf + off, BUFFER_SIZE - off, "SMPTE Offset"); 233 | break; 234 | 235 | case 0x58: 236 | if (event->midi_buffer_length < 7) { 237 | g_critical("smf_event_decode_metadata: truncated MIDI message."); 238 | goto error; 239 | } 240 | 241 | off += snprintf(buf + off, BUFFER_SIZE - off, 242 | "Time Signature: %d/%d, %d clocks per click, %d notated 32nd notes per quarter note", 243 | event->midi_buffer[3], (int)pow(2, event->midi_buffer[4]), event->midi_buffer[5], 244 | event->midi_buffer[6]); 245 | break; 246 | 247 | case 0x59: 248 | if (event->midi_buffer_length < 5) { 249 | g_critical("smf_event_decode_metadata: truncated MIDI message."); 250 | goto error; 251 | } 252 | 253 | flats = event->midi_buffer[3]; 254 | isminor = event->midi_buffer[4]; 255 | 256 | if (isminor != 0 && isminor != 1) { 257 | g_critical("smf_event_decode_metadata: last byte of the Key Signature event has invalid value %d.", isminor); 258 | goto error; 259 | } 260 | 261 | off += snprintf(buf + off, BUFFER_SIZE - off, "Key Signature: "); 262 | 263 | if (flats > 8 && flats < 248) { 264 | off += snprintf(buf + off, BUFFER_SIZE - off, "%d %s, %s key", abs((int8_t)flats), 265 | flats > 127 ? "flats" : "sharps", isminor ? "minor" : "major"); 266 | } else { 267 | int i = (flats - 248) & 255; 268 | 269 | assert(i >= 0 && i < sizeof(minor_keys) / sizeof(*minor_keys)); 270 | assert(i >= 0 && i < sizeof(major_keys) / sizeof(*major_keys)); 271 | 272 | if (isminor) 273 | off += snprintf(buf + off, BUFFER_SIZE - off, "%s", minor_keys[i]); 274 | else 275 | off += snprintf(buf + off, BUFFER_SIZE - off, "%s", major_keys[i]); 276 | } 277 | 278 | break; 279 | 280 | case 0x7F: 281 | off += snprintf(buf + off, BUFFER_SIZE - off, "Proprietary (aka Sequencer) Event, length %d", 282 | event->midi_buffer_length); 283 | break; 284 | 285 | default: 286 | goto error; 287 | } 288 | 289 | return (buf); 290 | 291 | error: 292 | free(buf); 293 | 294 | return (NULL); 295 | } 296 | 297 | static char * 298 | smf_event_decode_system_realtime(const smf_event_t *event) 299 | { 300 | int off = 0; 301 | char *buf; 302 | 303 | assert(smf_event_is_system_realtime(event)); 304 | 305 | if (event->midi_buffer_length != 1) { 306 | g_critical("smf_event_decode_system_realtime: event length is not 1."); 307 | return (NULL); 308 | } 309 | 310 | buf = malloc(BUFFER_SIZE); 311 | if (buf == NULL) { 312 | g_critical("smf_event_decode_system_realtime: malloc failed."); 313 | return (NULL); 314 | } 315 | 316 | switch (event->midi_buffer[0]) { 317 | case 0xF8: 318 | off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Clock (realtime)"); 319 | break; 320 | 321 | case 0xF9: 322 | off += snprintf(buf + off, BUFFER_SIZE - off, "Tick (realtime)"); 323 | break; 324 | 325 | case 0xFA: 326 | off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Start (realtime)"); 327 | break; 328 | 329 | case 0xFB: 330 | off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Continue (realtime)"); 331 | break; 332 | 333 | case 0xFC: 334 | off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Stop (realtime)"); 335 | break; 336 | 337 | case 0xFE: 338 | off += snprintf(buf + off, BUFFER_SIZE - off, "Active Sense (realtime)"); 339 | break; 340 | 341 | default: 342 | free(buf); 343 | return (NULL); 344 | } 345 | 346 | return (buf); 347 | } 348 | 349 | static char * 350 | smf_event_decode_sysex(const smf_event_t *event) 351 | { 352 | int off = 0; 353 | char *buf, manufacturer, subid, subid2; 354 | 355 | assert(smf_event_is_sysex(event)); 356 | 357 | if (event->midi_buffer_length < 5) { 358 | g_critical("smf_event_decode_sysex: truncated MIDI message."); 359 | return (NULL); 360 | } 361 | 362 | buf = malloc(BUFFER_SIZE); 363 | if (buf == NULL) { 364 | g_critical("smf_event_decode_sysex: malloc failed."); 365 | return (NULL); 366 | } 367 | 368 | manufacturer = event->midi_buffer[1]; 369 | 370 | if (manufacturer == 0x7F) { 371 | off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, realtime, channel %d", event->midi_buffer[2]); 372 | } else if (manufacturer == 0x7E) { 373 | off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, non-realtime, channel %d", event->midi_buffer[2]); 374 | } else { 375 | off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, manufacturer 0x%x", manufacturer); 376 | 377 | return (buf); 378 | } 379 | 380 | subid = event->midi_buffer[3]; 381 | subid2 = event->midi_buffer[4]; 382 | 383 | if (subid == 0x01) 384 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Header"); 385 | 386 | else if (subid == 0x02) 387 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Data Packet"); 388 | 389 | else if (subid == 0x03) 390 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Request"); 391 | 392 | else if (subid == 0x04 && subid2 == 0x01) 393 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Master Volume"); 394 | 395 | else if (subid == 0x05 && subid2 == 0x01) 396 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Loop Point Retransmit"); 397 | 398 | else if (subid == 0x05 && subid2 == 0x02) 399 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Loop Point Request"); 400 | 401 | else if (subid == 0x06 && subid2 == 0x01) 402 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Identity Request"); 403 | 404 | else if (subid == 0x06 && subid2 == 0x02) 405 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Identity Reply"); 406 | 407 | else if (subid == 0x08 && subid2 == 0x00) 408 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump Request"); 409 | 410 | else if (subid == 0x08 && subid2 == 0x01) 411 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump"); 412 | 413 | else if (subid == 0x08 && subid2 == 0x02) 414 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Single Note Tuning Change"); 415 | 416 | else if (subid == 0x08 && subid2 == 0x03) 417 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump Request (Bank)"); 418 | 419 | else if (subid == 0x08 && subid2 == 0x04) 420 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Key Based Tuning Dump"); 421 | 422 | else if (subid == 0x08 && subid2 == 0x05) 423 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Scale/Octave Tuning Dump, 1 byte format"); 424 | 425 | else if (subid == 0x08 && subid2 == 0x06) 426 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Scale/Octave Tuning Dump, 2 byte format"); 427 | 428 | else if (subid == 0x08 && subid2 == 0x07) 429 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Single Note Tuning Change (Bank)"); 430 | 431 | else if (subid == 0x09) 432 | off += snprintf(buf + off, BUFFER_SIZE - off, ", General MIDI %s", subid2 == 0 ? "disable" : "enable"); 433 | 434 | else if (subid == 0x7C) 435 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Wait"); 436 | 437 | else if (subid == 0x7D) 438 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Cancel"); 439 | 440 | else if (subid == 0x7E) 441 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump NAK"); 442 | 443 | else if (subid == 0x7F) 444 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump ACK"); 445 | 446 | else 447 | off += snprintf(buf + off, BUFFER_SIZE - off, ", Unknown"); 448 | 449 | return (buf); 450 | } 451 | 452 | static char * 453 | smf_event_decode_system_common(const smf_event_t *event) 454 | { 455 | int off = 0; 456 | char *buf; 457 | 458 | assert(smf_event_is_system_common(event)); 459 | 460 | if (smf_event_is_sysex(event)) 461 | return (smf_event_decode_sysex(event)); 462 | 463 | buf = malloc(BUFFER_SIZE); 464 | if (buf == NULL) { 465 | g_critical("smf_event_decode_system_realtime: malloc failed."); 466 | return (NULL); 467 | } 468 | 469 | switch (event->midi_buffer[0]) { 470 | case 0xF1: 471 | off += snprintf(buf + off, BUFFER_SIZE - off, "MTC Quarter Frame"); 472 | break; 473 | 474 | case 0xF2: 475 | off += snprintf(buf + off, BUFFER_SIZE - off, "Song Position Pointer"); 476 | break; 477 | 478 | case 0xF3: 479 | off += snprintf(buf + off, BUFFER_SIZE - off, "Song Select"); 480 | break; 481 | 482 | case 0xF6: 483 | off += snprintf(buf + off, BUFFER_SIZE - off, "Tune Request"); 484 | break; 485 | 486 | default: 487 | free(buf); 488 | return (NULL); 489 | } 490 | 491 | return (buf); 492 | } 493 | 494 | static void 495 | note_from_int(char *buf, int note_number) 496 | { 497 | int note, octave; 498 | char *names[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; 499 | 500 | octave = note_number / 12 - 1; 501 | note = note_number % 12; 502 | 503 | sprintf(buf, "%s%d", names[note], octave); 504 | } 505 | 506 | /** 507 | * \return Textual representation of the event given, or NULL, if event is unknown. 508 | * Returned string looks like this: 509 | * 510 | * Note On, channel 1, note F#3, velocity 0 511 | * 512 | * You should free the returned string afterwards, using free(3). 513 | */ 514 | char * 515 | smf_event_decode(const smf_event_t *event) 516 | { 517 | int off = 0, channel; 518 | char *buf, note[5]; 519 | 520 | if (smf_event_is_metadata(event)) 521 | return (smf_event_decode_metadata(event)); 522 | 523 | if (smf_event_is_system_realtime(event)) 524 | return (smf_event_decode_system_realtime(event)); 525 | 526 | if (smf_event_is_system_common(event)) 527 | return (smf_event_decode_system_common(event)); 528 | 529 | if (!smf_event_length_is_valid(event)) { 530 | g_critical("smf_event_decode: incorrect MIDI message length."); 531 | return (NULL); 532 | } 533 | 534 | buf = malloc(BUFFER_SIZE); 535 | if (buf == NULL) { 536 | g_critical("smf_event_decode: malloc failed."); 537 | return (NULL); 538 | } 539 | 540 | /* + 1, because user-visible channels used to be in range <1-16>. */ 541 | channel = (event->midi_buffer[0] & 0x0F) + 1; 542 | 543 | switch (event->midi_buffer[0] & 0xF0) { 544 | case 0x80: 545 | note_from_int(note, event->midi_buffer[1]); 546 | off += snprintf(buf + off, BUFFER_SIZE - off, "Note Off, channel %d, note %s, velocity %d", 547 | channel, note, event->midi_buffer[2]); 548 | break; 549 | 550 | case 0x90: 551 | note_from_int(note, event->midi_buffer[1]); 552 | off += snprintf(buf + off, BUFFER_SIZE - off, "Note On, channel %d, note %s, velocity %d", 553 | channel, note, event->midi_buffer[2]); 554 | break; 555 | 556 | case 0xA0: 557 | note_from_int(note, event->midi_buffer[1]); 558 | off += snprintf(buf + off, BUFFER_SIZE - off, "Aftertouch, channel %d, note %s, pressure %d", 559 | channel, note, event->midi_buffer[2]); 560 | break; 561 | 562 | case 0xB0: 563 | off += snprintf(buf + off, BUFFER_SIZE - off, "Controller, channel %d, controller %d, value %d", 564 | channel, event->midi_buffer[1], event->midi_buffer[2]); 565 | break; 566 | 567 | case 0xC0: 568 | off += snprintf(buf + off, BUFFER_SIZE - off, "Program Change, channel %d, controller %d", 569 | channel, event->midi_buffer[1]); 570 | break; 571 | 572 | case 0xD0: 573 | off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Pressure, channel %d, pressure %d", 574 | channel, event->midi_buffer[1]); 575 | break; 576 | 577 | case 0xE0: 578 | off += snprintf(buf + off, BUFFER_SIZE - off, "Pitch Wheel, channel %d, value %d", 579 | channel, ((int)event->midi_buffer[2] << 7) | (int)event->midi_buffer[2]); 580 | break; 581 | 582 | default: 583 | free(buf); 584 | return (NULL); 585 | } 586 | 587 | return (buf); 588 | } 589 | 590 | /** 591 | * \return Textual representation of the data extracted from MThd header, or NULL, if something goes wrong. 592 | * Returned string looks like this: 593 | * 594 | * format: 1 (several simultaneous tracks); number of tracks: 4; division: 192 PPQN. 595 | * 596 | * You should free the returned string afterwards, using free(3). 597 | */ 598 | char * 599 | smf_decode(const smf_t *smf) 600 | { 601 | int off = 0; 602 | char *buf; 603 | 604 | buf = malloc(BUFFER_SIZE); 605 | if (buf == NULL) { 606 | g_critical("smf_event_decode: malloc failed."); 607 | return (NULL); 608 | } 609 | 610 | off += snprintf(buf + off, BUFFER_SIZE - off, "format: %d ", smf->format); 611 | 612 | switch (smf->format) { 613 | case 0: 614 | off += snprintf(buf + off, BUFFER_SIZE - off, "(single track)"); 615 | break; 616 | 617 | case 1: 618 | off += snprintf(buf + off, BUFFER_SIZE - off, "(several simultaneous tracks)"); 619 | break; 620 | 621 | case 2: 622 | off += snprintf(buf + off, BUFFER_SIZE - off, "(several independent tracks)"); 623 | break; 624 | 625 | default: 626 | off += snprintf(buf + off, BUFFER_SIZE - off, "(INVALID FORMAT)"); 627 | break; 628 | } 629 | 630 | off += snprintf(buf + off, BUFFER_SIZE - off, "; number of tracks: %d", smf->number_of_tracks); 631 | 632 | if (smf->ppqn != 0) 633 | off += snprintf(buf + off, BUFFER_SIZE - off, "; division: %d PPQN", smf->ppqn); 634 | else 635 | off += snprintf(buf + off, BUFFER_SIZE - off, "; division: %d FPS, %d resolution", smf->frames_per_second, smf->resolution); 636 | 637 | return (buf); 638 | } 639 | 640 | -------------------------------------------------------------------------------- /src/smf_load.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007, 2008 Edward Tomasz Napierała 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | /** 29 | * \file 30 | * 31 | * Standard MIDI File format loader. 32 | * 33 | */ 34 | 35 | /* Reference: http://www.borg.com/~jglatt/tech/midifile.htm */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #ifdef __MINGW32__ 44 | #include 45 | #else /* ! __MINGW32__ */ 46 | #include 47 | #endif /* ! __MINGW32__ */ 48 | #include "smf.h" 49 | #include "smf_private.h" 50 | 51 | /** 52 | * Returns pointer to the next SMF chunk in smf->buffer, based on length of the previous one. 53 | * Returns NULL in case of error. 54 | */ 55 | static struct chunk_header_struct * 56 | next_chunk(smf_t *smf) 57 | { 58 | struct chunk_header_struct *chunk; 59 | void *next_chunk_ptr; 60 | 61 | assert(smf->file_buffer != NULL); 62 | assert(smf->file_buffer_length > 0); 63 | assert(smf->next_chunk_offset >= 0); 64 | 65 | if (smf->next_chunk_offset + sizeof(struct chunk_header_struct) >= smf->file_buffer_length) { 66 | g_critical("SMF warning: no more chunks left."); 67 | return (NULL); 68 | } 69 | 70 | next_chunk_ptr = (unsigned char *)smf->file_buffer + smf->next_chunk_offset; 71 | 72 | chunk = (struct chunk_header_struct *)next_chunk_ptr; 73 | 74 | if (!isalpha(chunk->id[0]) || !isalpha(chunk->id[1]) || !isalpha(chunk->id[2]) || !isalpha(chunk->id[3])) { 75 | g_critical("SMF error: chunk signature contains at least one non-alphanumeric byte."); 76 | return (NULL); 77 | } 78 | 79 | /* 80 | * XXX: On SPARC, after compiling with "-fast" option there will be SIGBUS here. 81 | * Please compile with -xmemalign=8i". 82 | */ 83 | smf->next_chunk_offset += sizeof(struct chunk_header_struct) + ntohl(chunk->length); 84 | 85 | if (smf->next_chunk_offset > smf->file_buffer_length) { 86 | g_critical("SMF warning: malformed chunk; truncated file?"); 87 | smf->next_chunk_offset = smf->file_buffer_length; 88 | } 89 | 90 | return (chunk); 91 | } 92 | 93 | /** 94 | * Returns 1, iff signature of the "chunk" is the same as string passed as "signature". 95 | */ 96 | static int 97 | chunk_signature_matches(const struct chunk_header_struct *chunk, const char *signature) 98 | { 99 | if (!memcmp(chunk->id, signature, 4)) 100 | return (1); 101 | 102 | return (0); 103 | } 104 | 105 | /** 106 | * Verifies if MThd header looks OK. Returns 0 iff it does. 107 | */ 108 | static int 109 | parse_mthd_header(smf_t *smf) 110 | { 111 | int len; 112 | struct chunk_header_struct *mthd, *tmp_mthd; 113 | 114 | /* Make sure compiler didn't do anything stupid. */ 115 | assert(sizeof(struct chunk_header_struct) == 8); 116 | 117 | /* 118 | * We could just do "mthd = smf->file_buffer;" here, but this way we wouldn't 119 | * get useful error messages. 120 | */ 121 | if (smf->file_buffer_length < 6) { 122 | g_critical("SMF error: file is too short, it cannot be a MIDI file."); 123 | 124 | return (-1); 125 | } 126 | 127 | tmp_mthd = smf->file_buffer; 128 | 129 | if (!chunk_signature_matches(tmp_mthd, "MThd")) { 130 | g_critical("SMF error: MThd signature not found, is that a MIDI file?"); 131 | 132 | return (-2); 133 | } 134 | 135 | /* Ok, now use next_chunk(). */ 136 | mthd = next_chunk(smf); 137 | if (mthd == NULL) 138 | return (-3); 139 | 140 | assert(mthd == tmp_mthd); 141 | 142 | len = ntohl(mthd->length); 143 | if (len != 6) { 144 | g_critical("SMF error: MThd chunk length %d, must be 6.", len); 145 | 146 | return (-4); 147 | } 148 | 149 | return (0); 150 | } 151 | 152 | /** 153 | * Parses MThd chunk, filling "smf" structure with values extracted from it. Returns 0 iff everything went OK. 154 | */ 155 | static int 156 | parse_mthd_chunk(smf_t *smf) 157 | { 158 | signed char first_byte_of_division, second_byte_of_division; 159 | 160 | struct mthd_chunk_struct *mthd; 161 | 162 | assert(sizeof(struct mthd_chunk_struct) == 14); 163 | 164 | if (parse_mthd_header(smf)) 165 | return (1); 166 | 167 | mthd = (struct mthd_chunk_struct *)smf->file_buffer; 168 | 169 | smf->format = ntohs(mthd->format); 170 | if (smf->format < 0 || smf->format > 2) { 171 | g_critical("SMF error: bad MThd format field value: %d, valid values are 0-2, inclusive.", smf->format); 172 | return (-1); 173 | } 174 | 175 | if (smf->format == 2) { 176 | g_critical("SMF file uses format #2, no support for that yet."); 177 | return (-2); 178 | } 179 | 180 | smf->expected_number_of_tracks = ntohs(mthd->number_of_tracks); 181 | if (smf->expected_number_of_tracks <= 0) { 182 | g_critical("SMF error: bad number of tracks: %d, must be greater than zero.", smf->expected_number_of_tracks); 183 | return (-3); 184 | } 185 | 186 | /* XXX: endianess? */ 187 | first_byte_of_division = *((signed char *)&(mthd->division)); 188 | second_byte_of_division = *((signed char *)&(mthd->division) + 1); 189 | 190 | if (first_byte_of_division >= 0) { 191 | smf->ppqn = ntohs(mthd->division); 192 | smf->frames_per_second = 0; 193 | smf->resolution = 0; 194 | } else { 195 | smf->ppqn = 0; 196 | smf->frames_per_second = - first_byte_of_division; 197 | smf->resolution = second_byte_of_division; 198 | } 199 | 200 | if (smf->ppqn == 0) { 201 | g_critical("SMF file uses FPS timing instead of PPQN, no support for that yet."); 202 | return (-4); 203 | } 204 | 205 | return (0); 206 | } 207 | 208 | /** 209 | * Interprets Variable Length Quantity pointed at by "buf" and puts its value into "value" and number 210 | * of bytes consumed into "len", making sure it does not read past "buf" + "buffer_length". 211 | * Explanation of Variable Length Quantities is here: http://www.borg.com/~jglatt/tech/midifile/vari.htm 212 | * Returns 0 iff everything went OK, different value in case of error. 213 | */ 214 | static int 215 | extract_vlq(const unsigned char *buf, const int buffer_length, int *value, int *len) 216 | { 217 | int val = 0; 218 | const unsigned char *c = buf; 219 | 220 | assert(buffer_length > 0); 221 | 222 | for (;;) { 223 | if (c >= buf + buffer_length) { 224 | g_critical("End of buffer in extract_vlq()."); 225 | return (-1); 226 | } 227 | 228 | val = (val << 7) + (*c & 0x7F); 229 | 230 | if (*c & 0x80) 231 | c++; 232 | else 233 | break; 234 | }; 235 | 236 | *value = val; 237 | *len = c - buf + 1; 238 | 239 | if (*len > 4) { 240 | g_critical("SMF error: Variable Length Quantities longer than four bytes are not supported yet."); 241 | return (-2); 242 | } 243 | 244 | return (0); 245 | } 246 | 247 | /** 248 | * Returns 1 if the given byte is a valid status byte, 0 otherwise. 249 | */ 250 | int 251 | is_status_byte(const unsigned char status) 252 | { 253 | return (status & 0x80); 254 | } 255 | 256 | static int 257 | is_sysex_byte(const unsigned char status) 258 | { 259 | if (status == 0xF0) 260 | return (1); 261 | 262 | return (0); 263 | } 264 | 265 | static int 266 | is_escape_byte(const unsigned char status) 267 | { 268 | if (status == 0xF7) 269 | return (1); 270 | 271 | return (0); 272 | } 273 | 274 | /** 275 | * Just like expected_message_length(), but only for System Exclusive messages. 276 | * Note that value returned by this thing here is the length of SysEx "on the wire", 277 | * not the number of bytes that this sysex takes in the file - in SMF format sysex 278 | * contains VLQ telling how many bytes it takes, "on the wire" format does not have 279 | * this. 280 | */ 281 | static int 282 | expected_sysex_length(const unsigned char status, const unsigned char *second_byte, const int buffer_length, int *consumed_bytes) 283 | { 284 | int sysex_length, len; 285 | 286 | assert(status == 0xF0); 287 | 288 | if (buffer_length < 3) { 289 | g_critical("SMF error: end of buffer in expected_sysex_length()."); 290 | return (-1); 291 | } 292 | 293 | if (extract_vlq(second_byte, buffer_length, &sysex_length, &len)) 294 | return (-1); 295 | 296 | if (consumed_bytes != NULL) 297 | *consumed_bytes = len; 298 | 299 | /* +1, because the length does not include status byte. */ 300 | return (sysex_length + 1); 301 | } 302 | 303 | static int 304 | expected_escaped_length(const unsigned char status, const unsigned char *second_byte, const int buffer_length, int *consumed_bytes) 305 | { 306 | /* -1, because we do not want to account for 0x7F status. */ 307 | return (expected_sysex_length(status, second_byte, buffer_length, consumed_bytes) - 1); 308 | } 309 | 310 | /** 311 | * Returns expected length of the midi message (including the status byte), in bytes, for the given status byte. 312 | * The "second_byte" points to the expected second byte of the MIDI message. "buffer_length" is the buffer 313 | * length limit, counting from "second_byte". Returns value < 0 iff there was an error. 314 | */ 315 | static int 316 | expected_message_length(unsigned char status, const unsigned char *second_byte, const int buffer_length) 317 | { 318 | /* Make sure this really is a valid status byte. */ 319 | assert(is_status_byte(status)); 320 | 321 | /* We cannot use this routine for sysexes. */ 322 | assert(!is_sysex_byte(status)); 323 | 324 | /* We cannot use this routine for escaped events. */ 325 | assert(!is_escape_byte(status)); 326 | 327 | /* Buffer length may be zero, for e.g. realtime messages. */ 328 | assert(buffer_length >= 0); 329 | 330 | /* Is this a metamessage? */ 331 | if (status == 0xFF) { 332 | if (buffer_length < 2) { 333 | g_critical("SMF error: end of buffer in expected_message_length()."); 334 | return (-1); 335 | } 336 | 337 | /* 338 | * Format of this kind of messages is like this: 0xFF 0xwhatever 0xlength and then "length" bytes. 339 | * Second byte points to this: ^^^^^^^^^^ 340 | */ 341 | return (*(second_byte + 1) + 3); 342 | } 343 | 344 | if ((status & 0xF0) == 0xF0) { 345 | switch (status) { 346 | case 0xF2: /* Song Position Pointer. */ 347 | return (3); 348 | 349 | case 0xF1: /* MTC Quarter Frame. */ 350 | case 0xF3: /* Song Select. */ 351 | return (2); 352 | 353 | case 0xF6: /* Tune Request. */ 354 | case 0xF8: /* MIDI Clock. */ 355 | case 0xF9: /* Tick. */ 356 | case 0xFA: /* MIDI Start. */ 357 | case 0xFB: /* MIDI Continue. */ 358 | case 0xFC: /* MIDI Stop. */ 359 | case 0xFE: /* Active Sense. */ 360 | return (1); 361 | 362 | default: 363 | g_critical("SMF error: unknown 0xFx-type status byte '0x%x'.", status); 364 | return (-2); 365 | } 366 | } 367 | 368 | /* Filter out the channel. */ 369 | status &= 0xF0; 370 | 371 | switch (status) { 372 | case 0x80: /* Note Off. */ 373 | case 0x90: /* Note On. */ 374 | case 0xA0: /* AfterTouch. */ 375 | case 0xB0: /* Control Change. */ 376 | case 0xE0: /* Pitch Wheel. */ 377 | return (3); 378 | 379 | case 0xC0: /* Program Change. */ 380 | case 0xD0: /* Channel Pressure. */ 381 | return (2); 382 | 383 | default: 384 | g_critical("SMF error: unknown status byte '0x%x'.", status); 385 | return (-3); 386 | } 387 | } 388 | 389 | static int 390 | extract_sysex_event(const unsigned char *buf, const int buffer_length, smf_event_t *event, int *len, int last_status) 391 | { 392 | int status, message_length, vlq_length; 393 | const unsigned char *c = buf; 394 | (void) last_status; 395 | 396 | status = *buf; 397 | 398 | assert(is_sysex_byte(status)); 399 | 400 | c++; 401 | 402 | message_length = expected_sysex_length(status, c, buffer_length - 1, &vlq_length); 403 | 404 | if (message_length < 0) 405 | return (-3); 406 | 407 | c += vlq_length; 408 | 409 | if (vlq_length + message_length >= buffer_length) { 410 | g_critical("End of buffer in extract_sysex_event()."); 411 | return (-5); 412 | } 413 | 414 | event->midi_buffer_length = message_length; 415 | event->midi_buffer = malloc(event->midi_buffer_length); 416 | if (event->midi_buffer == NULL) { 417 | g_critical("Cannot allocate memory in extract_sysex_event(): %s", strerror(errno)); 418 | return (-4); 419 | } 420 | 421 | event->midi_buffer[0] = status; 422 | memcpy(event->midi_buffer + 1, c, message_length - 1); 423 | 424 | *len = vlq_length + message_length; 425 | 426 | return (0); 427 | } 428 | 429 | static int 430 | extract_escaped_event(const unsigned char *buf, const int buffer_length, smf_event_t *event, int *len, int last_status) 431 | { 432 | int status, message_length, vlq_length; 433 | const unsigned char *c = buf; 434 | (void) last_status; 435 | 436 | status = *buf; 437 | 438 | assert(is_escape_byte(status)); 439 | 440 | c++; 441 | 442 | message_length = expected_escaped_length(status, c, buffer_length - 1, &vlq_length); 443 | 444 | if (message_length < 0) 445 | return (-3); 446 | 447 | c += vlq_length; 448 | 449 | if (vlq_length + message_length >= buffer_length) { 450 | g_critical("End of buffer in extract_escaped_event()."); 451 | return (-5); 452 | } 453 | 454 | event->midi_buffer_length = message_length; 455 | event->midi_buffer = malloc(event->midi_buffer_length); 456 | if (event->midi_buffer == NULL) { 457 | g_critical("Cannot allocate memory in extract_escaped_event(): %s", strerror(errno)); 458 | return (-4); 459 | } 460 | 461 | memcpy(event->midi_buffer, c, message_length); 462 | 463 | if (smf_event_is_valid(event)) { 464 | g_critical("Escaped event is invalid."); 465 | return (-1); 466 | } 467 | 468 | if (smf_event_is_system_realtime(event) || smf_event_is_system_common(event)) { 469 | g_warning("Escaped event is not System Realtime nor System Common."); 470 | } 471 | 472 | *len = vlq_length + message_length; 473 | 474 | return (0); 475 | } 476 | 477 | 478 | /** 479 | * Puts MIDI data extracted from from "buf" into "event" and number of consumed bytes into "len". 480 | * In case valid status is not found, it uses "last_status" (so called "running status"). 481 | * Returns 0 iff everything went OK, value < 0 in case of error. 482 | */ 483 | static int 484 | extract_midi_event(const unsigned char *buf, const int buffer_length, smf_event_t *event, int *len, int last_status) 485 | { 486 | int status, message_length; 487 | const unsigned char *c = buf; 488 | 489 | assert(buffer_length > 0); 490 | 491 | /* Is the first byte the status byte? */ 492 | if (is_status_byte(*c)) { 493 | status = *c; 494 | c++; 495 | 496 | } else { 497 | /* No, we use running status then. */ 498 | status = last_status; 499 | } 500 | 501 | if (!is_status_byte(status)) { 502 | g_critical("SMF error: bad status byte (MSB is zero)."); 503 | return (-1); 504 | } 505 | 506 | if (is_sysex_byte(status)) 507 | return (extract_sysex_event(buf, buffer_length, event, len, last_status)); 508 | 509 | if (is_escape_byte(status)) 510 | return (extract_escaped_event(buf, buffer_length, event, len, last_status)); 511 | 512 | /* At this point, "c" points to first byte following the status byte. */ 513 | message_length = expected_message_length(status, c, buffer_length - (c - buf)); 514 | 515 | if (message_length < 0) 516 | return (-3); 517 | 518 | if (message_length - 1 > buffer_length - (c - buf)) { 519 | g_critical("End of buffer in extract_midi_event()."); 520 | return (-5); 521 | } 522 | 523 | event->midi_buffer_length = message_length; 524 | event->midi_buffer = malloc(event->midi_buffer_length); 525 | if (event->midi_buffer == NULL) { 526 | g_critical("Cannot allocate memory in extract_midi_event(): %s", strerror(errno)); 527 | return (-4); 528 | } 529 | 530 | event->midi_buffer[0] = status; 531 | memcpy(event->midi_buffer + 1, c, message_length - 1); 532 | 533 | *len = c + message_length - 1 - buf; 534 | 535 | return (0); 536 | } 537 | 538 | /** 539 | * Locates, basing on track->next_event_offset, the next event data in track->buffer, 540 | * interprets it, allocates smf_event_t and fills it properly. Returns smf_event_t 541 | * or NULL, if there was an error. Allocating event means adding it to the track; 542 | * see smf_event_new(). 543 | */ 544 | static smf_event_t * 545 | parse_next_event(smf_track_t *track) 546 | { 547 | int time = 0, len, buffer_length; 548 | unsigned char *c, *start; 549 | 550 | smf_event_t *event = smf_event_new(); 551 | if (event == NULL) 552 | goto error; 553 | 554 | c = start = (unsigned char *)track->file_buffer + track->next_event_offset; 555 | 556 | assert(track->file_buffer != NULL); 557 | assert(track->file_buffer_length > 0); 558 | assert(track->next_event_offset > 0); 559 | 560 | buffer_length = track->file_buffer_length - track->next_event_offset; 561 | assert(buffer_length > 0); 562 | 563 | /* First, extract time offset from previous event. */ 564 | if (extract_vlq(c, buffer_length, &time, &len)) 565 | goto error; 566 | 567 | c += len; 568 | buffer_length -= len; 569 | 570 | if (buffer_length <= 0) 571 | goto error; 572 | 573 | /* Now, extract the actual event. */ 574 | if (extract_midi_event(c, buffer_length, event, &len, track->last_status)) 575 | goto error; 576 | 577 | c += len; 578 | buffer_length -= len; 579 | track->last_status = event->midi_buffer[0]; 580 | track->next_event_offset += c - start; 581 | 582 | smf_track_add_event_delta_pulses(track, event, time); 583 | 584 | return (event); 585 | 586 | error: 587 | if (event != NULL) 588 | smf_event_delete(event); 589 | 590 | return (NULL); 591 | } 592 | 593 | /** 594 | * Takes "len" characters starting in "buf", making sure it does not access past the length of the buffer, 595 | * and makes ordinary, zero-terminated string from it. May return NULL if there was any problem. 596 | */ 597 | static char * 598 | make_string(const unsigned char *buf, const int buffer_length, int len) 599 | { 600 | char *str; 601 | 602 | assert(buffer_length > 0); 603 | assert(len > 0); 604 | 605 | if (len > buffer_length) { 606 | g_critical("End of buffer in make_string()."); 607 | 608 | len = buffer_length; 609 | } 610 | 611 | str = malloc(len + 1); 612 | if (str == NULL) { 613 | g_critical("Cannot allocate memory in make_string()."); 614 | return (NULL); 615 | } 616 | 617 | memcpy(str, buf, len); 618 | str[len] = '\0'; 619 | 620 | return (str); 621 | } 622 | 623 | /** 624 | * \return 1, if passed a metaevent containing text, that is, Text, Copyright, 625 | * Sequence/Track Name, Instrument, Lyric, Marker, Cue Point, Program Name, 626 | * or Device Name; 0 otherwise. 627 | */ 628 | int 629 | smf_event_is_textual(const smf_event_t *event) 630 | { 631 | if (!smf_event_is_metadata(event)) 632 | return (0); 633 | 634 | if (event->midi_buffer_length < 4) 635 | return (0); 636 | 637 | if (event->midi_buffer[3] < 1 && event->midi_buffer[3] > 9) 638 | return (0); 639 | 640 | return (1); 641 | } 642 | 643 | /** 644 | * Extracts text from "textual metaevents", such as Text or Lyric. 645 | * 646 | * \return Zero-terminated string extracted from "text events" or NULL, if there was any problem. 647 | */ 648 | char * 649 | smf_event_extract_text(const smf_event_t *event) 650 | { 651 | int string_length = -1, length_length = -1; 652 | 653 | if (!smf_event_is_textual(event)) 654 | return (NULL); 655 | 656 | if (event->midi_buffer_length < 3) { 657 | g_critical("smf_event_extract_text: truncated MIDI message."); 658 | return (NULL); 659 | } 660 | 661 | extract_vlq((void *)&(event->midi_buffer[2]), event->midi_buffer_length - 2, &string_length, &length_length); 662 | 663 | if (string_length <= 0) { 664 | g_critical("smf_event_extract_text: truncated MIDI message."); 665 | return (NULL); 666 | } 667 | 668 | return (make_string((void *)(&event->midi_buffer[2] + length_length), event->midi_buffer_length - 2 - length_length, string_length)); 669 | } 670 | 671 | /** 672 | * Verify if the next chunk really is MTrk chunk, and if so, initialize some track variables and return 0. 673 | * Return different value otherwise. 674 | */ 675 | static int 676 | parse_mtrk_header(smf_track_t *track) 677 | { 678 | struct chunk_header_struct *mtrk; 679 | 680 | /* Make sure compiler didn't do anything stupid. */ 681 | assert(sizeof(struct chunk_header_struct) == 8); 682 | assert(track->smf != NULL); 683 | 684 | mtrk = next_chunk(track->smf); 685 | 686 | if (mtrk == NULL) 687 | return (-1); 688 | 689 | if (!chunk_signature_matches(mtrk, "MTrk")) { 690 | g_warning("SMF warning: Expected MTrk signature, got %c%c%c%c instead; ignoring this chunk.", 691 | mtrk->id[0], mtrk->id[1], mtrk->id[2], mtrk->id[3]); 692 | 693 | return (-2); 694 | } 695 | 696 | track->file_buffer = mtrk; 697 | track->file_buffer_length = sizeof(struct chunk_header_struct) + ntohl(mtrk->length); 698 | track->next_event_offset = sizeof(struct chunk_header_struct); 699 | 700 | return (0); 701 | } 702 | 703 | /** 704 | * Return 1 if event is end-of-the-track, 0 otherwise. 705 | */ 706 | static int 707 | event_is_end_of_track(const smf_event_t *event) 708 | { 709 | if (event->midi_buffer[0] == 0xFF && event->midi_buffer[1] == 0x2F) 710 | return (1); 711 | 712 | return (0); 713 | } 714 | 715 | /** 716 | * \return Nonzero, if event is as long as it should be, from the MIDI specification point of view. 717 | * Does not work for SysExes - it doesn't recognize internal structure of SysEx. 718 | */ 719 | int 720 | smf_event_length_is_valid(const smf_event_t *event) 721 | { 722 | assert(event); 723 | assert(event->midi_buffer); 724 | 725 | if (event->midi_buffer_length < 1) 726 | return (0); 727 | 728 | /* We cannot use expected_message_length on sysexes. */ 729 | if (smf_event_is_sysex(event)) 730 | return (1); 731 | 732 | if (event->midi_buffer_length != expected_message_length(event->midi_buffer[0], 733 | &(event->midi_buffer[1]), event->midi_buffer_length - 1)) { 734 | 735 | return (0); 736 | } 737 | 738 | return (1); 739 | } 740 | 741 | /** 742 | * \return Nonzero, if MIDI data in the event is valid, 0 otherwise. For example, 743 | * it checks if event length is correct. 744 | */ 745 | /* XXX: this routine requires some more work to detect more errors. */ 746 | int 747 | smf_event_is_valid(const smf_event_t *event) 748 | { 749 | assert(event); 750 | assert(event->midi_buffer); 751 | assert(event->midi_buffer_length >= 1); 752 | 753 | if (!is_status_byte(event->midi_buffer[0])) { 754 | g_critical("First byte of MIDI message is not a valid status byte."); 755 | 756 | return (0); 757 | } 758 | 759 | if (!smf_event_length_is_valid(event)) 760 | return (0); 761 | 762 | return (1); 763 | } 764 | 765 | /** 766 | * Parse events and put it on the track. 767 | */ 768 | static int 769 | parse_mtrk_chunk(smf_track_t *track) 770 | { 771 | smf_event_t *event; 772 | 773 | if (parse_mtrk_header(track)) 774 | return (-1); 775 | 776 | for (;;) { 777 | event = parse_next_event(track); 778 | 779 | /* Couldn't parse an event? */ 780 | if (event == NULL) { 781 | g_critical("Unable to parse MIDI event; truncating track."); 782 | if (smf_track_add_eot_delta_pulses(track, 0) != 0) { 783 | g_critical("smf_track_add_eot_delta_pulses failed."); 784 | return (-2); 785 | } 786 | break; 787 | } 788 | 789 | assert(smf_event_is_valid(event)); 790 | 791 | if (event_is_end_of_track(event)) 792 | break; 793 | } 794 | 795 | track->file_buffer = NULL; 796 | track->file_buffer_length = 0; 797 | track->next_event_offset = -1; 798 | 799 | return (0); 800 | } 801 | 802 | /** 803 | * Allocate buffer of proper size and read file contents into it. Close file afterwards. 804 | */ 805 | static int 806 | load_file_into_buffer(void **file_buffer, int *file_buffer_length, const char *file_name) 807 | { 808 | FILE *stream = fopen(file_name, "rb"); 809 | 810 | if (stream == NULL) { 811 | g_critical("Cannot open input file: %s", strerror(errno)); 812 | 813 | return (-1); 814 | } 815 | 816 | if (fseek(stream, 0, SEEK_END)) { 817 | g_critical("fseek(3) failed: %s", strerror(errno)); 818 | 819 | return (-2); 820 | } 821 | 822 | *file_buffer_length = ftell(stream); 823 | if (*file_buffer_length == -1) { 824 | g_critical("ftell(3) failed: %s", strerror(errno)); 825 | 826 | return (-3); 827 | } 828 | 829 | if (fseek(stream, 0, SEEK_SET)) { 830 | g_critical("fseek(3) failed: %s", strerror(errno)); 831 | 832 | return (-4); 833 | } 834 | 835 | *file_buffer = malloc(*file_buffer_length); 836 | if (*file_buffer == NULL) { 837 | g_critical("malloc(3) failed: %s", strerror(errno)); 838 | 839 | return (-5); 840 | } 841 | 842 | if (fread(*file_buffer, 1, *file_buffer_length, stream) != *file_buffer_length) { 843 | g_critical("fread(3) failed: %s", strerror(errno)); 844 | 845 | return (-6); 846 | } 847 | 848 | if (fclose(stream)) { 849 | g_critical("fclose(3) failed: %s", strerror(errno)); 850 | 851 | return (-7); 852 | } 853 | 854 | return (0); 855 | } 856 | 857 | /** 858 | * Creates new SMF and fills it with data loaded from the given buffer. 859 | * \return SMF or NULL, if loading failed. 860 | */ 861 | smf_t * 862 | smf_load_from_memory(const void *buffer, const int buffer_length) 863 | { 864 | int i; 865 | 866 | smf_t *smf = smf_new(); 867 | 868 | smf->file_buffer = (void *)buffer; 869 | smf->file_buffer_length = buffer_length; 870 | smf->next_chunk_offset = 0; 871 | 872 | if (parse_mthd_chunk(smf)) 873 | return (NULL); 874 | 875 | for (i = 1; i <= smf->expected_number_of_tracks; i++) { 876 | smf_track_t *track = smf_track_new(); 877 | if (track == NULL) 878 | return (NULL); 879 | 880 | smf_add_track(smf, track); 881 | 882 | /* Skip unparseable chunks. */ 883 | if (parse_mtrk_chunk(track)) { 884 | g_warning("SMF warning: Cannot load track."); 885 | smf_track_delete(track); 886 | } 887 | 888 | track->file_buffer = NULL; 889 | track->file_buffer_length = 0; 890 | track->next_event_offset = -1; 891 | } 892 | 893 | if (smf->expected_number_of_tracks != smf->number_of_tracks) { 894 | g_warning("SMF warning: MThd header declared %d tracks, but only %d found; continuing anyway.", 895 | smf->expected_number_of_tracks, smf->number_of_tracks); 896 | 897 | smf->expected_number_of_tracks = smf->number_of_tracks; 898 | } 899 | 900 | smf->file_buffer = NULL; 901 | smf->file_buffer_length = 0; 902 | smf->next_chunk_offset = -1; 903 | 904 | return (smf); 905 | } 906 | 907 | /** 908 | * Loads SMF file. 909 | * 910 | * \param file_name Path to the file. 911 | * \return SMF or NULL, if loading failed. 912 | */ 913 | smf_t * 914 | smf_load(const char *file_name) 915 | { 916 | int file_buffer_length; 917 | void *file_buffer; 918 | smf_t *smf; 919 | 920 | if (load_file_into_buffer(&file_buffer, &file_buffer_length, file_name)) 921 | return (NULL); 922 | 923 | smf = smf_load_from_memory(file_buffer, file_buffer_length); 924 | 925 | memset(file_buffer, 0, file_buffer_length); 926 | free(file_buffer); 927 | 928 | if (smf == NULL) 929 | return (NULL); 930 | 931 | smf_rewind(smf); 932 | 933 | return (smf); 934 | } 935 | 936 | -------------------------------------------------------------------------------- /src/smf_private.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007, 2008 Edward Tomasz Napierała 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | #ifndef SMF_PRIVATE_H 29 | #define SMF_PRIVATE_H 30 | 31 | #include 32 | #include 33 | 34 | #include "config.h" 35 | 36 | #define SMF_VERSION PACKAGE_VERSION 37 | 38 | /** 39 | * \file 40 | * 41 | * Private header. Applications using libsmf should use smf.h. 42 | * 43 | */ 44 | 45 | #if defined(__GNUC__) 46 | #define ATTRIBUTE_PACKED __attribute__((__packed__)) 47 | #else 48 | #define ATTRIBUTE_PACKED 49 | #pragma pack(1) 50 | #endif 51 | 52 | /** SMF chunk header, used only by smf_load.c and smf_save.c. */ 53 | struct chunk_header_struct { 54 | char id[4]; 55 | uint32_t length; 56 | } ATTRIBUTE_PACKED; 57 | 58 | /** SMF chunk, used only by smf_load.c and smf_save.c. */ 59 | struct mthd_chunk_struct { 60 | struct chunk_header_struct mthd_header; 61 | uint16_t format; 62 | uint16_t number_of_tracks; 63 | uint16_t division; 64 | } ATTRIBUTE_PACKED; 65 | 66 | #if (!defined __GNUC__) 67 | #pragma pack() 68 | #endif 69 | 70 | void smf_track_add_event(smf_track_t *track, smf_event_t *event); 71 | void smf_init_tempo(smf_t *smf); 72 | void smf_fini_tempo(smf_t *smf); 73 | void smf_create_tempo_map_and_compute_seconds(smf_t *smf); 74 | void maybe_add_to_tempo_map(smf_event_t *event); 75 | void remove_last_tempo_with_pulses(smf_t *smf, int pulses); 76 | int smf_event_is_tempo_change_or_time_signature(const smf_event_t *event) WARN_UNUSED_RESULT; 77 | int smf_event_length_is_valid(const smf_event_t *event) WARN_UNUSED_RESULT; 78 | int is_status_byte(const unsigned char status) WARN_UNUSED_RESULT; 79 | 80 | #endif /* SMF_PRIVATE_H */ 81 | 82 | -------------------------------------------------------------------------------- /src/smf_save.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007, 2008 Edward Tomasz Napierała 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | /** 29 | * \file 30 | * 31 | * Standard MIDI File writer. 32 | * 33 | */ 34 | 35 | /* Reference: http://www.borg.com/~jglatt/tech/midifile.htm */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #ifdef __MINGW32__ 43 | #include 44 | #else /* ! __MINGW32__ */ 45 | #include 46 | #endif /* ! __MINGW32__ */ 47 | #include "smf.h" 48 | #include "smf_private.h" 49 | 50 | #define MAX_VLQ_LENGTH 128 51 | 52 | /** 53 | * Extends (reallocates) smf->file_buffer and returns pointer to the newly added space, 54 | * that is, pointer to the first byte after the previous buffer end. Returns NULL in case 55 | * of error. 56 | */ 57 | static void * 58 | smf_extend(smf_t *smf, const int length) 59 | { 60 | int i, previous_file_buffer_length = smf->file_buffer_length; 61 | char *previous_file_buffer = smf->file_buffer; 62 | 63 | /* XXX: Not terribly efficient. */ 64 | smf->file_buffer_length += length; 65 | smf->file_buffer = realloc(smf->file_buffer, smf->file_buffer_length); 66 | if (smf->file_buffer == NULL) { 67 | g_critical("realloc(3) failed: %s", strerror(errno)); 68 | smf->file_buffer_length = 0; 69 | return (NULL); 70 | } 71 | 72 | /* Fix up pointers. XXX: omgwtf. */ 73 | for (i = 1; i <= smf->number_of_tracks; i++) { 74 | smf_track_t *track; 75 | track = smf_get_track_by_number(smf, i); 76 | if (track->file_buffer != NULL) 77 | track->file_buffer = (char *)track->file_buffer + ((char *)smf->file_buffer - previous_file_buffer); 78 | } 79 | 80 | return ((char *)smf->file_buffer + previous_file_buffer_length); 81 | } 82 | 83 | /** 84 | * Appends "buffer_length" bytes pointed to by "buffer" to the smf, reallocating storage as needed. Returns 0 85 | * if everything went ok, different value if there was any problem. 86 | */ 87 | static int 88 | smf_append(smf_t *smf, const void *buffer, const int buffer_length) 89 | { 90 | void *dest; 91 | 92 | dest = smf_extend(smf, buffer_length); 93 | if (dest == NULL) { 94 | g_critical("Cannot extend track buffer."); 95 | return (-1); 96 | } 97 | 98 | memcpy(dest, buffer, buffer_length); 99 | 100 | return (0); 101 | } 102 | 103 | /** 104 | * Appends MThd header to the track. Returns 0 if everything went ok, different value if not. 105 | */ 106 | static int 107 | write_mthd_header(smf_t *smf) 108 | { 109 | struct mthd_chunk_struct mthd_chunk; 110 | 111 | memcpy(mthd_chunk.mthd_header.id, "MThd", 4); 112 | mthd_chunk.mthd_header.length = htonl(6); 113 | mthd_chunk.format = htons(smf->format); 114 | mthd_chunk.number_of_tracks = htons(smf->number_of_tracks); 115 | mthd_chunk.division = htons(smf->ppqn); 116 | 117 | return (smf_append(smf, &mthd_chunk, sizeof(mthd_chunk))); 118 | } 119 | 120 | /** 121 | * Extends (reallocates) track->file_buffer and returns pointer to the newly added space, 122 | * that is, pointer to the first byte after the previous buffer end. Returns NULL in case 123 | * of error. 124 | */ 125 | static void * 126 | track_extend(smf_track_t *track, const int length) 127 | { 128 | void *buf; 129 | 130 | assert(track->smf); 131 | 132 | buf = smf_extend(track->smf, length); 133 | if (buf == NULL) 134 | return (NULL); 135 | 136 | track->file_buffer_length += length; 137 | if (track->file_buffer == NULL) 138 | track->file_buffer = buf; 139 | 140 | return (buf); 141 | } 142 | 143 | /** 144 | * Appends "buffer_length" bytes pointed to by "buffer" to the track, reallocating storage as needed. Returns 0 145 | * if everything went ok, different value if there was any problem. 146 | */ 147 | static int 148 | track_append(smf_track_t *track, const void *buffer, const int buffer_length) 149 | { 150 | void *dest; 151 | 152 | dest = track_extend(track, buffer_length); 153 | if (dest == NULL) { 154 | g_critical("Cannot extend track buffer."); 155 | return (-1); 156 | } 157 | 158 | memcpy(dest, buffer, buffer_length); 159 | 160 | return (0); 161 | } 162 | 163 | static int 164 | format_vlq(unsigned char *buf, int length, unsigned long value) 165 | { 166 | int i; 167 | unsigned long buffer; 168 | 169 | /* Taken from http://www.borg.com/~jglatt/tech/midifile/vari.htm */ 170 | buffer = value & 0x7F; 171 | 172 | while ((value >>= 7)) { 173 | buffer <<= 8; 174 | buffer |= ((value & 0x7F) | 0x80); 175 | } 176 | 177 | for (i = 0;; i++) { 178 | buf[i] = buffer; 179 | 180 | if (buffer & 0x80) 181 | buffer >>= 8; 182 | else 183 | break; 184 | } 185 | 186 | assert(i <= length); 187 | 188 | /* + 1, because "i" is an offset, not a count. */ 189 | return (i + 1); 190 | } 191 | 192 | smf_event_t * 193 | smf_event_new_textual(int type, const char *text) 194 | { 195 | int vlq_length, text_length, copied_length; 196 | smf_event_t *event; 197 | 198 | assert(type >= 1 && type <= 9); 199 | 200 | text_length = strlen(text); 201 | 202 | event = smf_event_new(); 203 | if (event == NULL) 204 | return (NULL); 205 | 206 | /* "2 +" is for leading 0xFF 0xtype. */ 207 | event->midi_buffer_length = 2 + text_length + MAX_VLQ_LENGTH; 208 | event->midi_buffer = malloc(event->midi_buffer_length); 209 | if (event->midi_buffer == NULL) { 210 | g_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno)); 211 | smf_event_delete(event); 212 | 213 | return (NULL); 214 | } 215 | 216 | event->midi_buffer[0] = 0xFF; 217 | event->midi_buffer[1] = type; 218 | 219 | vlq_length = format_vlq(event->midi_buffer + 2, MAX_VLQ_LENGTH - 2, text_length); 220 | copied_length = snprintf((char *)event->midi_buffer + vlq_length + 2, event->midi_buffer_length - vlq_length - 2, "%s", text); 221 | 222 | assert(copied_length == text_length); 223 | 224 | event->midi_buffer_length = 2 + vlq_length + text_length; 225 | 226 | return event; 227 | } 228 | 229 | /** 230 | * Appends value, expressed as Variable Length Quantity, to event->track. 231 | */ 232 | static int 233 | write_vlq(smf_event_t *event, unsigned long value) 234 | { 235 | unsigned char buf[MAX_VLQ_LENGTH]; 236 | int vlq_length; 237 | 238 | vlq_length = format_vlq(buf, MAX_VLQ_LENGTH, value); 239 | 240 | return (track_append(event->track, buf, vlq_length)); 241 | } 242 | 243 | /** 244 | * Appends event time as Variable Length Quantity. Returns 0 if everything went ok, 245 | * different value in case of error. 246 | */ 247 | static int 248 | write_event_time(smf_event_t *event) 249 | { 250 | assert(event->delta_time_pulses >= 0); 251 | 252 | return (write_vlq(event, event->delta_time_pulses)); 253 | } 254 | 255 | static int 256 | write_sysex_contents(smf_event_t *event) 257 | { 258 | int ret; 259 | unsigned char sysex_status = 0xF0; 260 | 261 | assert(smf_event_is_sysex(event)); 262 | 263 | ret = track_append(event->track, &sysex_status, 1); 264 | if (ret) 265 | return (ret); 266 | 267 | /* -1, because length does not include status byte. */ 268 | ret = write_vlq(event, event->midi_buffer_length - 1); 269 | if (ret) 270 | return (ret); 271 | 272 | ret = track_append(event->track, event->midi_buffer + 1, event->midi_buffer_length - 1); 273 | if (ret) 274 | return (ret); 275 | 276 | return (0); 277 | } 278 | 279 | /** 280 | * Appends contents of event->midi_buffer wrapped into 0xF7 MIDI event. 281 | */ 282 | static int 283 | write_escaped_event_contents(smf_event_t *event) 284 | { 285 | int ret; 286 | unsigned char escape_status = 0xF7; 287 | 288 | if (smf_event_is_sysex(event)) 289 | return (write_sysex_contents(event)); 290 | 291 | ret = track_append(event->track, &escape_status, 1); 292 | if (ret) 293 | return (ret); 294 | 295 | ret = write_vlq(event, event->midi_buffer_length); 296 | if (ret) 297 | return (ret); 298 | 299 | ret = track_append(event->track, event->midi_buffer, event->midi_buffer_length); 300 | if (ret) 301 | return (ret); 302 | 303 | return (0); 304 | } 305 | 306 | /** 307 | * Appends contents of event->midi_buffer. Returns 0 if everything went 0, 308 | * different value in case of error. 309 | */ 310 | static int 311 | write_event_contents(smf_event_t *event) 312 | { 313 | if (smf_event_is_system_realtime(event) || smf_event_is_system_common(event)) 314 | return (write_escaped_event_contents(event)); 315 | 316 | return (track_append(event->track, event->midi_buffer, event->midi_buffer_length)); 317 | } 318 | 319 | /** 320 | * Writes out an event. 321 | */ 322 | static int 323 | write_event(smf_event_t *event) 324 | { 325 | int ret; 326 | 327 | ret = write_event_time(event); 328 | if (ret) 329 | return (ret); 330 | 331 | ret = write_event_contents(event); 332 | if (ret) 333 | return (ret); 334 | 335 | return (0); 336 | } 337 | 338 | /** 339 | * Writes out MTrk header, except of MTrk chunk length, which is written by write_mtrk_length(). 340 | */ 341 | static int 342 | write_mtrk_header(smf_track_t *track) 343 | { 344 | struct chunk_header_struct mtrk_header; 345 | 346 | memcpy(mtrk_header.id, "MTrk", 4); 347 | 348 | return (track_append(track, &mtrk_header, sizeof(mtrk_header))); 349 | } 350 | 351 | /** 352 | * Updates MTrk chunk length of a given track. 353 | */ 354 | static int 355 | write_mtrk_length(smf_track_t *track) 356 | { 357 | struct chunk_header_struct *mtrk_header; 358 | 359 | assert(track->file_buffer != NULL); 360 | assert(track->file_buffer_length >= 6); 361 | 362 | mtrk_header = (struct chunk_header_struct *)track->file_buffer; 363 | mtrk_header->length = htonl(track->file_buffer_length - sizeof(struct chunk_header_struct)); 364 | 365 | return (0); 366 | } 367 | 368 | /** 369 | * Writes out the track. 370 | */ 371 | static int 372 | write_track(smf_track_t *track) 373 | { 374 | int ret; 375 | smf_event_t *event; 376 | 377 | ret = write_mtrk_header(track); 378 | if (ret) 379 | return (ret); 380 | 381 | while ((event = smf_track_get_next_event(track)) != NULL) { 382 | ret = write_event(event); 383 | if (ret) 384 | return (ret); 385 | } 386 | 387 | ret = write_mtrk_length(track); 388 | if (ret) 389 | return (ret); 390 | 391 | return (0); 392 | } 393 | 394 | /** 395 | * Takes smf->file_buffer and saves it to the file. 396 | */ 397 | static int 398 | write_file(smf_t *smf, const char *file_name) 399 | { 400 | FILE *stream; 401 | 402 | stream = fopen(file_name, "wb+"); 403 | if (stream == NULL) { 404 | g_critical("Cannot open input file: %s", strerror(errno)); 405 | 406 | return (-1); 407 | } 408 | 409 | if (fwrite(smf->file_buffer, 1, smf->file_buffer_length, stream) != smf->file_buffer_length) { 410 | g_critical("fwrite(3) failed: %s", strerror(errno)); 411 | 412 | return (-2); 413 | } 414 | 415 | if (fclose(stream)) { 416 | g_critical("fclose(3) failed: %s", strerror(errno)); 417 | 418 | return (-3); 419 | } 420 | 421 | return (0); 422 | } 423 | 424 | static void 425 | free_buffer(smf_t *smf) 426 | { 427 | int i; 428 | smf_track_t *track; 429 | 430 | /* Clear the pointers. */ 431 | memset(smf->file_buffer, 0, smf->file_buffer_length); 432 | free(smf->file_buffer); 433 | smf->file_buffer = NULL; 434 | smf->file_buffer_length = 0; 435 | 436 | for (i = 1; i <= smf->number_of_tracks; i++) { 437 | track = smf_get_track_by_number(smf, i); 438 | assert(track); 439 | track->file_buffer = NULL; 440 | track->file_buffer_length = 0; 441 | } 442 | } 443 | 444 | #ifndef NDEBUG 445 | 446 | /** 447 | * \return Nonzero, if all pointers supposed to be NULL are NULL. Triggers assertion if not. 448 | */ 449 | static int 450 | pointers_are_clear(smf_t *smf) 451 | { 452 | int i; 453 | 454 | smf_track_t *track; 455 | assert(smf->file_buffer == NULL); 456 | assert(smf->file_buffer_length == 0); 457 | 458 | for (i = 1; i <= smf->number_of_tracks; i++) { 459 | track = smf_get_track_by_number(smf, i); 460 | 461 | assert(track != NULL); 462 | assert(track->file_buffer == NULL); 463 | assert(track->file_buffer_length == 0); 464 | } 465 | 466 | return (1); 467 | } 468 | 469 | #endif /* !NDEBUG */ 470 | 471 | /** 472 | * \return Nonzero, if event is End Of Track metaevent. 473 | */ 474 | int 475 | smf_event_is_eot(const smf_event_t *event) 476 | { 477 | if (event->midi_buffer_length != 3) 478 | return (0); 479 | 480 | if (event->midi_buffer[0] != 0xFF || event->midi_buffer[1] != 0x2F || event->midi_buffer[2] != 0x00) 481 | return (0); 482 | 483 | return (1); 484 | } 485 | 486 | /** 487 | * Check if SMF is valid and add missing EOT events. 488 | * 489 | * \return 0, if SMF is valid. 490 | */ 491 | static int 492 | smf_validate(smf_t *smf) 493 | { 494 | int trackno, eventno, eot_found; 495 | smf_track_t *track; 496 | smf_event_t *event; 497 | 498 | if (smf->format < 0 || smf->format > 2) { 499 | g_critical("SMF error: smf->format is less than zero of greater than two."); 500 | return (-1); 501 | } 502 | 503 | if (smf->number_of_tracks < 1) { 504 | g_critical("SMF error: number of tracks is less than one."); 505 | return (-2); 506 | } 507 | 508 | if (smf->format == 0 && smf->number_of_tracks > 1) { 509 | g_critical("SMF error: format is 0, but number of tracks is more than one."); 510 | return (-3); 511 | } 512 | 513 | if (smf->ppqn <= 0) { 514 | g_critical("SMF error: PPQN has to be > 0."); 515 | return (-4); 516 | } 517 | 518 | for (trackno = 1; trackno <= smf->number_of_tracks; trackno++) { 519 | track = smf_get_track_by_number(smf, trackno); 520 | assert(track); 521 | 522 | eot_found = 0; 523 | 524 | for (eventno = 1; eventno <= track->number_of_events; eventno++) { 525 | event = smf_track_get_event_by_number(track, eventno); 526 | assert(event); 527 | 528 | if (!smf_event_is_valid(event)) { 529 | g_critical("Event #%d on track #%d is invalid.", eventno, trackno); 530 | return (-5); 531 | } 532 | 533 | if (smf_event_is_eot(event)) { 534 | if (eot_found) { 535 | g_critical("Duplicate End Of Track event on track #%d.", trackno); 536 | return (-6); 537 | } 538 | 539 | eot_found = 1; 540 | } 541 | } 542 | 543 | if (!eot_found) { 544 | if (smf_track_add_eot_delta_pulses(track, 0)) { 545 | g_critical("smf_track_add_eot_delta_pulses failed."); 546 | return (-6); 547 | } 548 | } 549 | 550 | } 551 | 552 | return (0); 553 | } 554 | 555 | #ifndef NDEBUG 556 | 557 | static void 558 | assert_smf_event_is_identical(const smf_event_t *a, const smf_event_t *b) 559 | { 560 | assert(a->event_number == b->event_number); 561 | assert(a->delta_time_pulses == b->delta_time_pulses); 562 | assert(abs(a->time_pulses - b->time_pulses) <= 2); 563 | assert(fabs(a->time_seconds - b->time_seconds) <= 0.01); 564 | assert(a->track_number == b->track_number); 565 | assert(a->midi_buffer_length == b->midi_buffer_length); 566 | assert(memcmp(a->midi_buffer, b->midi_buffer, a->midi_buffer_length) == 0); 567 | } 568 | 569 | static void 570 | assert_smf_track_is_identical(const smf_track_t *a, const smf_track_t *b) 571 | { 572 | int i; 573 | 574 | assert(a->track_number == b->track_number); 575 | assert(a->number_of_events == b->number_of_events); 576 | 577 | for (i = 1; i <= a->number_of_events; i++) 578 | assert_smf_event_is_identical(smf_track_get_event_by_number(a, i), smf_track_get_event_by_number(b, i)); 579 | } 580 | 581 | static void 582 | assert_smf_is_identical(const smf_t *a, const smf_t *b) 583 | { 584 | int i; 585 | 586 | assert(a->format == b->format); 587 | assert(a->ppqn == b->ppqn); 588 | assert(a->frames_per_second == b->frames_per_second); 589 | assert(a->resolution == b->resolution); 590 | assert(a->number_of_tracks == b->number_of_tracks); 591 | 592 | for (i = 1; i <= a->number_of_tracks; i++) 593 | assert_smf_track_is_identical(smf_get_track_by_number(a, i), smf_get_track_by_number(b, i)); 594 | 595 | /* We do not need to compare tempos explicitly, as tempo is always computed from track contents. */ 596 | } 597 | 598 | static void 599 | assert_smf_saved_correctly(const smf_t *smf, const char *file_name) 600 | { 601 | smf_t *saved; 602 | 603 | saved = smf_load(file_name); 604 | assert(saved != NULL); 605 | 606 | assert_smf_is_identical(smf, saved); 607 | 608 | smf_delete(saved); 609 | } 610 | 611 | #endif /* !NDEBUG */ 612 | 613 | /** 614 | * Writes the contents of SMF to the file given. 615 | * \param smf SMF. 616 | * \param file_name Path to the file. 617 | * \return 0, if saving was successfull. 618 | */ 619 | int 620 | smf_save(smf_t *smf, const char *file_name) 621 | { 622 | int i, error; 623 | smf_track_t *track; 624 | 625 | smf_rewind(smf); 626 | 627 | assert(pointers_are_clear(smf)); 628 | 629 | if (smf_validate(smf)) 630 | return (-1); 631 | 632 | if (write_mthd_header(smf)) 633 | return (-2); 634 | 635 | for (i = 1; i <= smf->number_of_tracks; i++) { 636 | track = smf_get_track_by_number(smf, i); 637 | 638 | assert(track != NULL); 639 | 640 | error = write_track(track); 641 | if (error) { 642 | free_buffer(smf); 643 | return (error); 644 | } 645 | } 646 | 647 | error = write_file(smf, file_name); 648 | 649 | free_buffer(smf); 650 | 651 | if (error) 652 | return (error); 653 | 654 | #ifndef NDEBUG 655 | assert_smf_saved_correctly(smf, file_name); 656 | #endif 657 | 658 | return (0); 659 | } 660 | 661 | -------------------------------------------------------------------------------- /src/smf_tempo.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007, 2008 Edward Tomasz Napierała 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | /** 29 | * \file 30 | * 31 | * Tempo map related part. 32 | * 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "smf.h" 40 | #include "smf_private.h" 41 | 42 | static double seconds_from_pulses(const smf_t *smf, int pulses); 43 | 44 | /** 45 | * If there is tempo starting at "pulses" already, return it. Otherwise, 46 | * allocate new one, fill it with values from previous one (or default ones, 47 | * if there is no previous one) and attach it to "smf". 48 | */ 49 | static smf_tempo_t * 50 | new_tempo(smf_t *smf, int pulses) 51 | { 52 | smf_tempo_t *tempo, *previous_tempo = NULL; 53 | 54 | if (smf->tempo_array->len > 0) { 55 | previous_tempo = smf_get_last_tempo(smf); 56 | 57 | /* If previous tempo starts at the same time as new one, reuse it, updating in place. */ 58 | if (previous_tempo->time_pulses == pulses) 59 | return (previous_tempo); 60 | } 61 | 62 | tempo = malloc(sizeof(smf_tempo_t)); 63 | if (tempo == NULL) { 64 | g_critical("Cannot allocate smf_tempo_t."); 65 | return (NULL); 66 | } 67 | 68 | tempo->time_pulses = pulses; 69 | 70 | if (previous_tempo != NULL) { 71 | tempo->microseconds_per_quarter_note = previous_tempo->microseconds_per_quarter_note; 72 | tempo->numerator = previous_tempo->numerator; 73 | tempo->denominator = previous_tempo->denominator; 74 | tempo->clocks_per_click = previous_tempo->clocks_per_click; 75 | tempo->notes_per_note = previous_tempo->notes_per_note; 76 | } else { 77 | tempo->microseconds_per_quarter_note = 500000; /* Initial tempo is 120 BPM. */ 78 | tempo->numerator = 4; 79 | tempo->denominator = 4; 80 | tempo->clocks_per_click = -1; 81 | tempo->notes_per_note = -1; 82 | } 83 | 84 | g_ptr_array_add(smf->tempo_array, tempo); 85 | 86 | if (pulses == 0) 87 | tempo->time_seconds = 0.0; 88 | else 89 | tempo->time_seconds = seconds_from_pulses(smf, pulses); 90 | 91 | return (tempo); 92 | } 93 | 94 | static int 95 | add_tempo(smf_t *smf, int pulses, int tempo) 96 | { 97 | smf_tempo_t *smf_tempo = new_tempo(smf, pulses); 98 | if (smf_tempo == NULL) 99 | return (-1); 100 | 101 | smf_tempo->microseconds_per_quarter_note = tempo; 102 | 103 | return (0); 104 | } 105 | 106 | static int 107 | add_time_signature(smf_t *smf, int pulses, int numerator, int denominator, int clocks_per_click, int notes_per_note) 108 | { 109 | smf_tempo_t *smf_tempo = new_tempo(smf, pulses); 110 | if (smf_tempo == NULL) 111 | return (-1); 112 | 113 | smf_tempo->numerator = numerator; 114 | smf_tempo->denominator = denominator; 115 | smf_tempo->clocks_per_click = clocks_per_click; 116 | smf_tempo->notes_per_note = notes_per_note; 117 | 118 | return (0); 119 | } 120 | 121 | /** 122 | * \internal 123 | */ 124 | void 125 | maybe_add_to_tempo_map(smf_event_t *event) 126 | { 127 | if (!smf_event_is_metadata(event)) 128 | return; 129 | 130 | assert(event->track != NULL); 131 | assert(event->track->smf != NULL); 132 | assert(event->midi_buffer_length >= 1); 133 | 134 | /* Tempo Change? */ 135 | if (event->midi_buffer[1] == 0x51) { 136 | int new_tempo = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5]; 137 | if (new_tempo <= 0) { 138 | g_critical("Ignoring invalid tempo change."); 139 | return; 140 | } 141 | 142 | add_tempo(event->track->smf, event->time_pulses, new_tempo); 143 | } 144 | 145 | /* Time Signature? */ 146 | if (event->midi_buffer[1] == 0x58) { 147 | int numerator, denominator, clocks_per_click, notes_per_note; 148 | 149 | if (event->midi_buffer_length < 7) { 150 | g_critical("Time Signature event seems truncated."); 151 | return; 152 | } 153 | 154 | numerator = event->midi_buffer[3]; 155 | denominator = (int)pow(2, event->midi_buffer[4]); 156 | clocks_per_click = event->midi_buffer[5]; 157 | notes_per_note = event->midi_buffer[6]; 158 | 159 | add_time_signature(event->track->smf, event->time_pulses, numerator, denominator, clocks_per_click, notes_per_note); 160 | } 161 | 162 | return; 163 | } 164 | 165 | /** 166 | * \internal 167 | * 168 | * This is an internal function, called from smf_track_remove_event when tempo-related 169 | * event being removed does not require recreation of tempo map, i.e. there are no events 170 | * after that one. 171 | */ 172 | void 173 | remove_last_tempo_with_pulses(smf_t *smf, int pulses) 174 | { 175 | smf_tempo_t *tempo; 176 | 177 | /* XXX: This is a partial workaround for the following problem: we have two tempo-related 178 | events, A and B, that occur at the same time. We remove B, then try to remove 179 | A. However, both tempo changes got coalesced in new_tempo(), so it is impossible 180 | to remove B. */ 181 | if (smf->tempo_array->len == 0) 182 | return; 183 | 184 | tempo = smf_get_last_tempo(smf); 185 | 186 | /* Workaround part two. */ 187 | if (tempo->time_pulses != pulses) 188 | return; 189 | 190 | memset(tempo, 0, sizeof(smf_tempo_t)); 191 | free(tempo); 192 | 193 | g_ptr_array_remove_index(smf->tempo_array, smf->tempo_array->len - 1); 194 | } 195 | 196 | static double 197 | seconds_from_pulses(const smf_t *smf, int pulses) 198 | { 199 | double seconds; 200 | smf_tempo_t *tempo; 201 | 202 | tempo = smf_get_tempo_by_pulses(smf, pulses); 203 | assert(tempo); 204 | assert(tempo->time_pulses <= pulses); 205 | 206 | seconds = tempo->time_seconds + (double)(pulses - tempo->time_pulses) * 207 | (tempo->microseconds_per_quarter_note / ((double)smf->ppqn * 1000000.0)); 208 | 209 | return (seconds); 210 | } 211 | 212 | static int 213 | pulses_from_seconds(const smf_t *smf, double seconds) 214 | { 215 | int pulses = 0; 216 | smf_tempo_t *tempo; 217 | 218 | tempo = smf_get_tempo_by_seconds(smf, seconds); 219 | assert(tempo); 220 | assert(tempo->time_seconds <= seconds); 221 | 222 | pulses = tempo->time_pulses + (seconds - tempo->time_seconds) * 223 | ((double)smf->ppqn * 1000000.0 / tempo->microseconds_per_quarter_note); 224 | 225 | return (pulses); 226 | } 227 | 228 | /** 229 | * \internal 230 | * 231 | * Computes value of event->time_seconds for all events in smf. 232 | * Warning: rewinds the smf. 233 | */ 234 | void 235 | smf_create_tempo_map_and_compute_seconds(smf_t *smf) 236 | { 237 | smf_event_t *event; 238 | 239 | smf_rewind(smf); 240 | smf_init_tempo(smf); 241 | 242 | for (;;) { 243 | event = smf_get_next_event(smf); 244 | 245 | if (event == NULL) 246 | return; 247 | 248 | maybe_add_to_tempo_map(event); 249 | 250 | event->time_seconds = seconds_from_pulses(smf, event->time_pulses); 251 | } 252 | 253 | /* Not reached. */ 254 | } 255 | 256 | smf_tempo_t * 257 | smf_get_tempo_by_number(const smf_t *smf, int number) 258 | { 259 | assert(number >= 0); 260 | 261 | if (number >= smf->tempo_array->len) 262 | return (NULL); 263 | 264 | return (g_ptr_array_index(smf->tempo_array, number)); 265 | } 266 | 267 | /** 268 | * Return last tempo (i.e. tempo with greatest time_pulses) that happens before "pulses". 269 | */ 270 | smf_tempo_t * 271 | smf_get_tempo_by_pulses(const smf_t *smf, int pulses) 272 | { 273 | int i; 274 | smf_tempo_t *tempo; 275 | 276 | assert(pulses >= 0); 277 | 278 | if (pulses == 0) 279 | return (smf_get_tempo_by_number(smf, 0)); 280 | 281 | assert(smf->tempo_array != NULL); 282 | 283 | for (i = smf->tempo_array->len - 1; i >= 0; i--) { 284 | tempo = smf_get_tempo_by_number(smf, i); 285 | 286 | assert(tempo); 287 | if (tempo->time_pulses < pulses) 288 | return (tempo); 289 | } 290 | 291 | return (NULL); 292 | } 293 | 294 | /** 295 | * Return last tempo (i.e. tempo with greatest time_seconds) that happens before "seconds". 296 | */ 297 | smf_tempo_t * 298 | smf_get_tempo_by_seconds(const smf_t *smf, double seconds) 299 | { 300 | int i; 301 | smf_tempo_t *tempo; 302 | 303 | assert(seconds >= 0.0); 304 | 305 | if (seconds == 0.0) 306 | return (smf_get_tempo_by_number(smf, 0)); 307 | 308 | assert(smf->tempo_array != NULL); 309 | 310 | for (i = smf->tempo_array->len - 1; i >= 0; i--) { 311 | tempo = smf_get_tempo_by_number(smf, i); 312 | 313 | assert(tempo); 314 | if (tempo->time_seconds < seconds) 315 | return (tempo); 316 | } 317 | 318 | return (NULL); 319 | } 320 | 321 | 322 | /** 323 | * Return last tempo. 324 | */ 325 | smf_tempo_t * 326 | smf_get_last_tempo(const smf_t *smf) 327 | { 328 | smf_tempo_t *tempo; 329 | 330 | tempo = smf_get_tempo_by_number(smf, smf->tempo_array->len - 1); 331 | assert(tempo); 332 | 333 | return (tempo); 334 | } 335 | 336 | /** 337 | * \internal 338 | * 339 | * Remove all smf_tempo_t structures from SMF. 340 | */ 341 | void 342 | smf_fini_tempo(smf_t *smf) 343 | { 344 | smf_tempo_t *tempo; 345 | 346 | while (smf->tempo_array->len > 0) { 347 | tempo = g_ptr_array_index(smf->tempo_array, smf->tempo_array->len - 1); 348 | assert(tempo); 349 | 350 | memset(tempo, 0, sizeof(smf_tempo_t)); 351 | free(tempo); 352 | 353 | g_ptr_array_remove_index(smf->tempo_array, smf->tempo_array->len - 1); 354 | } 355 | 356 | assert(smf->tempo_array->len == 0); 357 | } 358 | 359 | /** 360 | * \internal 361 | * 362 | * Remove any existing tempos and add default one. 363 | * 364 | * \bug This will abort (by calling g_error) if new_tempo() (memory allocation there) fails. 365 | */ 366 | void 367 | smf_init_tempo(smf_t *smf) 368 | { 369 | smf_tempo_t *tempo; 370 | 371 | smf_fini_tempo(smf); 372 | 373 | tempo = new_tempo(smf, 0); 374 | if (tempo == NULL) 375 | g_error("tempo_init failed, sorry."); 376 | } 377 | 378 | /** 379 | * Returns ->time_pulses of last event on the given track, or 0, if track is empty. 380 | */ 381 | static int 382 | last_event_pulses(const smf_track_t *track) 383 | { 384 | /* Get time of last event on this track. */ 385 | if (track->number_of_events > 0) { 386 | smf_event_t *previous_event = smf_track_get_last_event(track); 387 | assert(previous_event); 388 | assert(previous_event->time_pulses >= 0); 389 | 390 | return (previous_event->time_pulses); 391 | } 392 | 393 | return (0); 394 | } 395 | 396 | /** 397 | * Adds event to the track at the time "pulses" clocks from the previous event in this track. 398 | * The remaining two time fields will be computed automatically based on the third argument 399 | * and current tempo map. Note that ->delta_pulses is computed by smf.c:smf_track_add_event, 400 | * not here. 401 | */ 402 | void 403 | smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, int delta) 404 | { 405 | assert(delta >= 0); 406 | assert(event->time_pulses == -1); 407 | assert(event->time_seconds == -1.0); 408 | assert(track->smf != NULL); 409 | 410 | smf_track_add_event_pulses(track, event, last_event_pulses(track) + delta); 411 | } 412 | 413 | /** 414 | * Adds event to the track at the time "pulses" clocks from the start of song. 415 | * The remaining two time fields will be computed automatically based on the third argument 416 | * and current tempo map. 417 | */ 418 | void 419 | smf_track_add_event_pulses(smf_track_t *track, smf_event_t *event, int pulses) 420 | { 421 | assert(pulses >= 0); 422 | assert(event->time_pulses == -1); 423 | assert(event->time_seconds == -1.0); 424 | assert(track->smf != NULL); 425 | 426 | event->time_pulses = pulses; 427 | event->time_seconds = seconds_from_pulses(track->smf, pulses); 428 | smf_track_add_event(track, event); 429 | } 430 | 431 | /** 432 | * Adds event to the track at the time "seconds" seconds from the start of song. 433 | * The remaining two time fields will be computed automatically based on the third argument 434 | * and current tempo map. 435 | */ 436 | void 437 | smf_track_add_event_seconds(smf_track_t *track, smf_event_t *event, double seconds) 438 | { 439 | assert(seconds >= 0.0); 440 | assert(event->time_pulses == -1); 441 | assert(event->time_seconds == -1.0); 442 | assert(track->smf != NULL); 443 | 444 | event->time_seconds = seconds; 445 | event->time_pulses = pulses_from_seconds(track->smf, seconds); 446 | smf_track_add_event(track, event); 447 | } 448 | 449 | -------------------------------------------------------------------------------- /src/smfsh.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007, 2008 Edward Tomasz Napierała 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 15 | * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | /** 29 | * \file 30 | * 31 | * "SMF shell", command line utility. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #ifdef __MINGW32__ 38 | #define EX_OK 0 39 | #define EX_USAGE 64 40 | #else /* ! __MINGW32__ */ 41 | #include 42 | #endif /* ! __MINGW32__ */ 43 | #include 44 | #include 45 | #include 46 | #include "smf.h" 47 | #include "config.h" 48 | 49 | #ifdef HAVE_LIBREADLINE 50 | #include 51 | #include 52 | #endif 53 | 54 | smf_track_t *selected_track = NULL; 55 | smf_event_t *selected_event = NULL; 56 | smf_t *smf = NULL; 57 | char *last_file_name = NULL; 58 | 59 | #define COMMAND_LENGTH 10 60 | 61 | static void 62 | log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer notused) 63 | { 64 | (void) notused; 65 | if (strcmp(log_domain, "smfsh") == 0) 66 | fprintf(stderr, "%s\n", message); 67 | else 68 | fprintf(stderr, "%s: %s\n", log_domain, message); 69 | } 70 | 71 | static int cmd_track(char *arg); 72 | 73 | static int 74 | cmd_load(char *file_name) 75 | { 76 | char *decoded; 77 | 78 | if (file_name == NULL) { 79 | if (last_file_name == NULL) { 80 | g_critical("Please specify file name."); 81 | return (-1); 82 | } 83 | 84 | file_name = strdup(last_file_name); 85 | } else { 86 | file_name = strdup(file_name); 87 | } 88 | 89 | selected_track = NULL; 90 | selected_event = NULL; 91 | 92 | if (smf != NULL) { 93 | smf_delete(smf); 94 | smf = NULL; 95 | } 96 | 97 | if (last_file_name != NULL) 98 | free(last_file_name); 99 | last_file_name = strdup(file_name); 100 | 101 | smf = smf_load(file_name); 102 | if (smf == NULL) { 103 | g_critical("Couldn't load '%s'.", file_name); 104 | 105 | smf = smf_new(); 106 | if (smf == NULL) { 107 | g_critical("Cannot initialize smf_t."); 108 | return (-1); 109 | } 110 | 111 | return (-2); 112 | } 113 | 114 | g_message("File '%s' loaded.", file_name); 115 | decoded = smf_decode(smf); 116 | g_message("%s.", decoded); 117 | free(decoded); 118 | 119 | cmd_track("1"); 120 | 121 | free(file_name); 122 | 123 | return (0); 124 | } 125 | 126 | static int 127 | cmd_save(char *file_name) 128 | { 129 | int ret; 130 | 131 | if (file_name == NULL) { 132 | if (last_file_name == NULL) { 133 | g_critical("Please specify file name."); 134 | return (-1); 135 | } 136 | 137 | file_name = strdup(last_file_name); 138 | } else { 139 | file_name = strdup(file_name); 140 | } 141 | 142 | if (last_file_name != NULL) 143 | free(last_file_name); 144 | last_file_name = strdup(file_name); 145 | 146 | ret = smf_save(smf, file_name); 147 | if (ret) { 148 | g_critical("Couldn't save '%s'", file_name); 149 | return (-1); 150 | } 151 | 152 | g_message("File '%s' saved.", file_name); 153 | 154 | free(file_name); 155 | 156 | return (0); 157 | } 158 | 159 | static int 160 | cmd_ppqn(char *new_ppqn) 161 | { 162 | int tmp; 163 | char *end; 164 | 165 | if (new_ppqn == NULL) { 166 | g_message("Pulses Per Quarter Note (aka Division) is %d.", smf->ppqn); 167 | } else { 168 | tmp = strtol(new_ppqn, &end, 10); 169 | if (end - new_ppqn != strlen(new_ppqn)) { 170 | g_critical("Invalid PPQN, garbage characters after the number."); 171 | return (-1); 172 | } 173 | 174 | if (tmp <= 0) { 175 | g_critical("Invalid PPQN, valid values are greater than zero."); 176 | return (-2); 177 | } 178 | 179 | if (smf_set_ppqn(smf, tmp)) { 180 | g_message("smf_set_ppqn failed."); 181 | return (-3); 182 | } 183 | 184 | g_message("Pulses Per Quarter Note changed to %d.", smf->ppqn); 185 | } 186 | 187 | return (0); 188 | } 189 | 190 | static int 191 | cmd_format(char *new_format) 192 | { 193 | int tmp; 194 | char *end; 195 | 196 | if (new_format == NULL) { 197 | g_message("Format is %d.", smf->format); 198 | } else { 199 | tmp = strtol(new_format, &end, 10); 200 | if (end - new_format != strlen(new_format)) { 201 | g_critical("Invalid format value, garbage characters after the number."); 202 | return (-1); 203 | } 204 | 205 | if (tmp < 0 || tmp > 2) { 206 | g_critical("Invalid format value, valid values are in range 0 - 2, inclusive."); 207 | return (-2); 208 | } 209 | 210 | if (smf_set_format(smf, tmp)) { 211 | g_critical("smf_set_format failed."); 212 | return (-3); 213 | } 214 | 215 | g_message("Forma changed to %d.", smf->format); 216 | } 217 | 218 | return (0); 219 | } 220 | 221 | static int 222 | cmd_tracks(char *notused) 223 | { 224 | (void) notused; 225 | if (smf->number_of_tracks > 0) 226 | g_message("There are %d tracks, numbered from 1 to %d.", smf->number_of_tracks, smf->number_of_tracks); 227 | else 228 | g_message("There are no tracks."); 229 | 230 | return (0); 231 | } 232 | 233 | static int 234 | parse_track_number(const char *arg) 235 | { 236 | int num; 237 | char *end; 238 | 239 | if (arg == NULL) { 240 | if (selected_track == NULL) { 241 | g_message("No track currently selected and no track number given."); 242 | return (-1); 243 | } else { 244 | return (selected_track->track_number); 245 | } 246 | } 247 | 248 | num = strtol(arg, &end, 10); 249 | if (end - arg != strlen(arg)) { 250 | g_critical("Invalid track number, garbage characters after the number."); 251 | return (-1); 252 | } 253 | 254 | if (num < 1 || num > smf->number_of_tracks) { 255 | if (smf->number_of_tracks > 0) { 256 | g_critical("Invalid track number specified; valid choices are 1 - %d.", smf->number_of_tracks); 257 | } else { 258 | g_critical("There are no tracks."); 259 | } 260 | 261 | return (-1); 262 | } 263 | 264 | return (num); 265 | } 266 | 267 | static int 268 | cmd_track(char *arg) 269 | { 270 | int num; 271 | 272 | if (arg == NULL) { 273 | if (selected_track == NULL) 274 | g_message("No track currently selected."); 275 | else 276 | g_message("Currently selected is track number %d, containing %d events.", 277 | selected_track->track_number, selected_track->number_of_events); 278 | } else { 279 | if (smf->number_of_tracks == 0) { 280 | g_message("There are no tracks."); 281 | return (-1); 282 | } 283 | 284 | num = parse_track_number(arg); 285 | if (num < 0) 286 | return (-1); 287 | 288 | selected_track = smf_get_track_by_number(smf, num); 289 | if (selected_track == NULL) { 290 | g_critical("smf_get_track_by_number() failed, track not selected."); 291 | return (-3); 292 | } 293 | 294 | selected_event = NULL; 295 | 296 | g_message("Track number %d selected; it contains %d events.", 297 | selected_track->track_number, selected_track->number_of_events); 298 | } 299 | 300 | return (0); 301 | } 302 | 303 | static int 304 | cmd_trackadd(char *notused) 305 | { 306 | (void) notused; 307 | selected_track = smf_track_new(); 308 | if (selected_track == NULL) { 309 | g_critical("smf_track_new() failed, track not created."); 310 | return (-1); 311 | } 312 | 313 | smf_add_track(smf, selected_track); 314 | 315 | selected_event = NULL; 316 | 317 | g_message("Created new track; track number %d selected.", selected_track->track_number); 318 | 319 | return (0); 320 | } 321 | 322 | static int 323 | cmd_trackrm(char *arg) 324 | { 325 | int num = parse_track_number(arg); 326 | 327 | if (num < 0) 328 | return (-1); 329 | 330 | if (selected_track != NULL && num == selected_track->track_number) { 331 | selected_track = NULL; 332 | selected_event = NULL; 333 | } 334 | 335 | smf_track_delete(smf_get_track_by_number(smf, num)); 336 | 337 | g_message("Track %d removed.", num); 338 | 339 | return (0); 340 | } 341 | 342 | #define BUFFER_SIZE 1024 343 | 344 | static int 345 | show_event(smf_event_t *event) 346 | { 347 | int off = 0, i; 348 | char *decoded, *type; 349 | 350 | if (smf_event_is_metadata(event)) 351 | type = "Metadata"; 352 | else 353 | type = "Event"; 354 | 355 | decoded = smf_event_decode(event); 356 | 357 | if (decoded == NULL) { 358 | decoded = malloc(BUFFER_SIZE); 359 | if (decoded == NULL) { 360 | g_critical("show_event: malloc failed."); 361 | return (-1); 362 | } 363 | 364 | off += snprintf(decoded + off, BUFFER_SIZE - off, "Unknown event:"); 365 | 366 | for (i = 0; i < event->midi_buffer_length && i < 5; i++) 367 | off += snprintf(decoded + off, BUFFER_SIZE - off, " 0x%x", event->midi_buffer[i]); 368 | } 369 | 370 | g_message("%d: %s: %s, %f seconds, %d pulses, %d delta pulses", event->event_number, type, decoded, 371 | event->time_seconds, event->time_pulses, event->delta_time_pulses); 372 | 373 | free(decoded); 374 | 375 | return (0); 376 | } 377 | 378 | static int 379 | cmd_events(char *notused) 380 | { 381 | smf_event_t *event; 382 | (void) notused; 383 | 384 | if (selected_track == NULL) { 385 | g_critical("No track selected - please use 'track ' command first."); 386 | return (-1); 387 | } 388 | 389 | if (selected_track->number_of_events == 0) { 390 | g_message("Selected track is empty."); 391 | return (0); 392 | } 393 | 394 | g_message("List of events in track %d follows:", selected_track->track_number); 395 | 396 | smf_rewind(smf); 397 | 398 | while ((event = smf_track_get_next_event(selected_track)) != NULL) 399 | show_event(event); 400 | 401 | smf_rewind(smf); 402 | 403 | return (0); 404 | } 405 | 406 | static int 407 | parse_event_number(const char *arg) 408 | { 409 | int num; 410 | char *end; 411 | 412 | if (selected_track == NULL) { 413 | g_critical("You need to select track first (using 'track ')."); 414 | return (-1); 415 | } 416 | 417 | if (arg == NULL) { 418 | if (selected_event == NULL) { 419 | g_message("No event currently selected and no event number given."); 420 | return (-1); 421 | } else { 422 | return (selected_event->event_number); 423 | } 424 | } 425 | 426 | num = strtol(arg, &end, 10); 427 | if (end - arg != strlen(arg)) { 428 | g_critical("Invalid event number, garbage characters after the number."); 429 | return (-1); 430 | } 431 | 432 | if (num < 1 || num > selected_track->number_of_events) { 433 | if (selected_track->number_of_events > 0) 434 | g_critical("Invalid event number specified; valid choices are 1 - %d.", selected_track->number_of_events); 435 | else 436 | g_critical("There are no events in currently selected track."); 437 | 438 | return (-1); 439 | } 440 | 441 | return (num); 442 | } 443 | 444 | static int 445 | cmd_event(char *arg) 446 | { 447 | int num; 448 | 449 | if (arg == NULL) { 450 | if (selected_event == NULL) { 451 | g_message("No event currently selected."); 452 | } else { 453 | g_message("Currently selected is event %d, track %d.", selected_event->event_number, selected_track->track_number); 454 | show_event(selected_event); 455 | } 456 | } else { 457 | num = parse_event_number(arg); 458 | if (num < 0) 459 | return (-1); 460 | 461 | selected_event = smf_track_get_event_by_number(selected_track, num); 462 | if (selected_event == NULL) { 463 | g_critical("smf_get_event_by_number() failed, event not selected."); 464 | return (-2); 465 | } 466 | 467 | g_message("Event number %d selected.", selected_event->event_number); 468 | show_event(selected_event); 469 | } 470 | 471 | return (0); 472 | } 473 | 474 | static int 475 | decode_hex(char *str, unsigned char **buffer, int *length) 476 | { 477 | int i, value, midi_buffer_length; 478 | char buf[3]; 479 | unsigned char *midi_buffer = NULL; 480 | char *end = NULL; 481 | 482 | if ((strlen(str) % 2) != 0) { 483 | g_critical("Hex value should have even number of characters, you know."); 484 | goto error; 485 | } 486 | 487 | midi_buffer_length = strlen(str) / 2; 488 | midi_buffer = malloc(midi_buffer_length); 489 | if (midi_buffer == NULL) { 490 | g_critical("malloc() failed."); 491 | goto error; 492 | } 493 | 494 | for (i = 0; i < midi_buffer_length; i++) { 495 | buf[0] = str[i * 2]; 496 | buf[1] = str[i * 2 + 1]; 497 | buf[2] = '\0'; 498 | value = strtoll(buf, &end, 16); 499 | 500 | if (end - buf != 2) { 501 | g_critical("Garbage characters detected after hex."); 502 | goto error; 503 | } 504 | 505 | midi_buffer[i] = value; 506 | } 507 | 508 | *buffer = midi_buffer; 509 | *length = midi_buffer_length; 510 | 511 | return (0); 512 | 513 | error: 514 | if (midi_buffer != NULL) 515 | free(midi_buffer); 516 | 517 | return (-1); 518 | } 519 | 520 | static void 521 | eventadd_usage(void) 522 | { 523 | g_message("Usage: add - for example, 'add 1 903C7F' will add"); 524 | g_message("Note On event, note C4, velocity 127, channel 1, one second from the start of song, channel 1."); 525 | } 526 | 527 | static int 528 | cmd_eventadd(char *str) 529 | { 530 | int midi_buffer_length; 531 | double seconds; 532 | unsigned char *midi_buffer; 533 | char *time, *endtime; 534 | 535 | if (selected_track == NULL) { 536 | g_critical("Please select a track first, using 'track ' command."); 537 | return (-1); 538 | } 539 | 540 | if (str == NULL) { 541 | eventadd_usage(); 542 | return (-2); 543 | } 544 | 545 | /* Extract the time. Don't use strsep(3), it doesn't work on SunOS. */ 546 | time = str; 547 | str = strchr(str, ' '); 548 | if (str != NULL) { 549 | *str = '\0'; 550 | str++; 551 | } 552 | 553 | seconds = strtod(time, &endtime); 554 | if (endtime - time != strlen(time)) { 555 | g_critical("Time is supposed to be a number, without trailing characters."); 556 | return (-3); 557 | } 558 | 559 | /* Called with one parameter? */ 560 | if (str == NULL) { 561 | eventadd_usage(); 562 | return (-4); 563 | } 564 | 565 | if (decode_hex(str, &midi_buffer, &midi_buffer_length)) { 566 | eventadd_usage(); 567 | return (-5); 568 | } 569 | 570 | selected_event = smf_event_new(); 571 | if (selected_event == NULL) { 572 | g_critical("smf_event_new() failed, event not created."); 573 | return (-6); 574 | } 575 | 576 | selected_event->midi_buffer = midi_buffer; 577 | selected_event->midi_buffer_length = midi_buffer_length; 578 | 579 | if (smf_event_is_valid(selected_event) == 0) { 580 | g_critical("Event is invalid from the MIDI specification point of view, not created."); 581 | smf_event_delete(selected_event); 582 | selected_event = NULL; 583 | return (-7); 584 | } 585 | 586 | smf_track_add_event_seconds(selected_track, selected_event, seconds); 587 | 588 | g_message("Event created."); 589 | 590 | return (0); 591 | } 592 | 593 | static int 594 | cmd_text(char *str) 595 | { 596 | double seconds, type; 597 | char *time, *typestr, *end; 598 | 599 | if (selected_track == NULL) { 600 | g_critical("Please select a track first, using 'track ' command."); 601 | return (-1); 602 | } 603 | 604 | if (str == NULL) { 605 | g_critical("Usage: text "); 606 | return (-2); 607 | } 608 | 609 | /* Extract the time. Don't use strsep(3), it doesn't work on SunOS. */ 610 | time = str; 611 | str = strchr(str, ' '); 612 | if (str != NULL) { 613 | *str = '\0'; 614 | str++; 615 | } 616 | 617 | seconds = strtod(time, &end); 618 | if (end - time != strlen(time)) { 619 | g_critical("Time is supposed to be a number, without trailing characters."); 620 | return (-3); 621 | } 622 | 623 | /* Called with one parameter? */ 624 | if (str == NULL) { 625 | g_critical("Usage: text "); 626 | return (-4); 627 | } 628 | 629 | /* Extract the event type. */ 630 | typestr = str; 631 | str = strchr(str, ' '); 632 | if (str != NULL) { 633 | *str = '\0'; 634 | str++; 635 | } 636 | 637 | type = strtod(typestr, &end); 638 | if (end - typestr != strlen(typestr)) { 639 | g_critical("Type is supposed to be a number, without trailing characters."); 640 | return (-4); 641 | } 642 | 643 | if (type < 1 || type > 9) { 644 | g_critical("Valid values for type are 1 - 9, inclusive."); 645 | return (-5); 646 | } 647 | 648 | /* Called with one parameter? */ 649 | if (str == NULL) { 650 | g_critical("Usage: text "); 651 | return (-4); 652 | } 653 | 654 | selected_event = smf_event_new_textual(type, str); 655 | if (selected_event == NULL) { 656 | g_critical("smf_event_new_textual() failed, event not created."); 657 | return (-6); 658 | } 659 | 660 | assert(smf_event_is_valid(selected_event)); 661 | 662 | smf_track_add_event_seconds(selected_track, selected_event, seconds); 663 | 664 | g_message("Event created."); 665 | 666 | return (0); 667 | } 668 | 669 | 670 | static int 671 | cmd_eventaddeot(char *time) 672 | { 673 | double seconds; 674 | char *end; 675 | 676 | if (selected_track == NULL) { 677 | g_critical("Please select a track first, using 'track ' command."); 678 | return (-1); 679 | } 680 | 681 | if (time == NULL) { 682 | g_critical("Please specify the time, in seconds."); 683 | return (-2); 684 | } 685 | 686 | seconds = strtod(time, &end); 687 | if (end - time != strlen(time)) { 688 | g_critical("Time is supposed to be a number, without trailing characters."); 689 | return (-3); 690 | } 691 | 692 | if (smf_track_add_eot_seconds(selected_track, seconds)) { 693 | g_critical("smf_track_add_eot() failed."); 694 | return (-4); 695 | } 696 | 697 | g_message("Event created."); 698 | 699 | return (0); 700 | } 701 | 702 | static int 703 | cmd_eventrm(char *number) 704 | { 705 | int num = parse_event_number(number); 706 | 707 | if (num < 0) 708 | return (-1); 709 | 710 | if (selected_event != NULL && num == selected_event->event_number) 711 | selected_event = NULL; 712 | 713 | smf_event_delete(smf_track_get_event_by_number(selected_track, num)); 714 | 715 | g_message("Event #%d removed.", num); 716 | 717 | return (0); 718 | } 719 | 720 | static int 721 | cmd_tempo(char *notused) 722 | { 723 | int i; 724 | smf_tempo_t *tempo; 725 | (void) notused; 726 | 727 | for (i = 0;; i++) { 728 | tempo = smf_get_tempo_by_number(smf, i); 729 | if (tempo == NULL) 730 | break; 731 | 732 | g_message("Tempo #%d: Starts at %d pulses, %f seconds, setting %d microseconds per quarter note, %.2f BPM.", 733 | i, tempo->time_pulses, tempo->time_seconds, tempo->microseconds_per_quarter_note, 734 | 60000000.0 / (double)tempo->microseconds_per_quarter_note); 735 | g_message("Time signature: %d/%d, %d clocks per click, %d 32nd notes per quarter note.", 736 | tempo->numerator, tempo->denominator, tempo->clocks_per_click, tempo->notes_per_note); 737 | } 738 | 739 | return (0); 740 | } 741 | 742 | static int 743 | cmd_length(char *notused) 744 | { 745 | (void) notused; 746 | g_message("Length: %d pulses, %f seconds.", smf_get_length_pulses(smf), smf_get_length_seconds(smf)); 747 | 748 | return (0); 749 | } 750 | 751 | static int 752 | cmd_version(char *notused) 753 | { 754 | (void) notused; 755 | g_message("libsmf version %s.", smf_get_version()); 756 | 757 | return (0); 758 | } 759 | 760 | static int 761 | cmd_exit(char *notused) 762 | { 763 | (void) notused; 764 | g_debug("Good bye."); 765 | exit(0); 766 | } 767 | 768 | static int cmd_help(char *notused); 769 | 770 | static struct command_struct { 771 | char *name; 772 | int (*function)(char *command); 773 | char *help; 774 | } commands[] = {{"help", cmd_help, "Show this help."}, 775 | {"?", cmd_help, NULL}, 776 | {"load", cmd_load, "Load named file."}, 777 | {"open", cmd_load, NULL}, 778 | {"save", cmd_save, "Save to named file."}, 779 | {"ppqn", cmd_ppqn, "Show ppqn (aka division), or set ppqn if used with parameter."}, 780 | {"format", cmd_format, "Show format, or set format if used with parameter."}, 781 | {"tracks", cmd_tracks, "Show number of tracks."}, 782 | {"track", cmd_track, "Show number of currently selected track, or select a track."}, 783 | {"trackadd", cmd_trackadd, "Add a track and select it."}, 784 | {"trackrm", cmd_trackrm, "Remove currently selected track."}, 785 | {"events", cmd_events, "Show events in the currently selected track."}, 786 | {"event", cmd_event, "Show number of currently selected event, or select an event."}, 787 | {"add", cmd_eventadd, "Add an event and select it."}, 788 | {"text", cmd_text, "Add textual event and select it."}, 789 | {"eventadd", cmd_eventadd, NULL}, 790 | {"eot", cmd_eventaddeot, "Add an End Of Track event."}, 791 | {"eventaddeot", cmd_eventaddeot, NULL}, 792 | {"eventrm", cmd_eventrm, NULL}, 793 | {"rm", cmd_eventrm, "Remove currently selected event."}, 794 | {"tempo", cmd_tempo, "Show tempo map."}, 795 | {"length", cmd_length, "Show length of the song."}, 796 | {"version", cmd_version, "Show libsmf version."}, 797 | {"exit", cmd_exit, "Exit to shell."}, 798 | {"quit", cmd_exit, NULL}, 799 | {"bye", cmd_exit, NULL}, 800 | {NULL, NULL, NULL}}; 801 | 802 | static int 803 | cmd_help(char *notused) 804 | { 805 | int i, padding_length; 806 | char padding[COMMAND_LENGTH + 1]; 807 | struct command_struct *tmp; 808 | (void) notused; 809 | 810 | g_message("Available commands:"); 811 | 812 | for (tmp = commands; tmp->name != NULL; tmp++) { 813 | /* Skip commands with no help string. */ 814 | if (tmp->help == NULL) 815 | continue; 816 | 817 | padding_length = COMMAND_LENGTH - strlen(tmp->name); 818 | assert(padding_length >= 0); 819 | for (i = 0; i < padding_length; i++) 820 | padding[i] = ' '; 821 | padding[i] = '\0'; 822 | 823 | g_message("%s:%s%s", tmp->name, padding, tmp->help); 824 | } 825 | 826 | return (0); 827 | } 828 | 829 | /** 830 | * Removes (in place) all whitespace characters before the first 831 | * non-whitespace and all trailing whitespace characters. Replaces 832 | * more than one consecutive whitespace characters with one. 833 | */ 834 | static void 835 | strip_unneeded_whitespace(char *str, int len) 836 | { 837 | char *src, *dest; 838 | int skip_white = 1; 839 | 840 | for (src = str, dest = str; src < dest + len; src++) { 841 | if (*src == '\n' || *src == '\0') { 842 | *dest = '\0'; 843 | break; 844 | } 845 | 846 | if (isspace(*src)) { 847 | if (skip_white) 848 | continue; 849 | 850 | skip_white = 1; 851 | } else { 852 | skip_white = 0; 853 | } 854 | 855 | *dest = *src; 856 | dest++; 857 | } 858 | 859 | /* Remove trailing whitespace. */ 860 | len = strlen(dest); 861 | if (isspace(dest[len - 1])) 862 | dest[len - 1] = '\0'; 863 | } 864 | 865 | static char * 866 | read_command(void) 867 | { 868 | char *buf; 869 | int len; 870 | 871 | #ifdef HAVE_LIBREADLINE 872 | buf = readline("smfsh> "); 873 | #else 874 | buf = malloc(1024); 875 | if (buf == NULL) { 876 | g_critical("Malloc failed."); 877 | return (NULL); 878 | } 879 | 880 | fprintf(stdout, "smfsh> "); 881 | fflush(stdout); 882 | 883 | buf = fgets(buf, 1024, stdin); 884 | #endif 885 | 886 | if (buf == NULL) { 887 | fprintf(stdout, "exit\n"); 888 | return (strdup("exit")); 889 | } 890 | 891 | strip_unneeded_whitespace(buf, 1024); 892 | 893 | len = strlen(buf); 894 | 895 | if (len == 0) 896 | return (read_command()); 897 | 898 | #ifdef HAVE_LIBREADLINE 899 | add_history(buf); 900 | #endif 901 | 902 | return (buf); 903 | } 904 | 905 | static int 906 | execute_command(char *line) 907 | { 908 | char *command, *args; 909 | struct command_struct *tmp; 910 | 911 | command = line; 912 | args = strchr(line, ' '); 913 | if (args != NULL) { 914 | *args = '\0'; 915 | args++; 916 | } 917 | 918 | for (tmp = commands; tmp->name != NULL; tmp++) { 919 | if (strcmp(tmp->name, command) == 0) 920 | return ((tmp->function)(args)); 921 | } 922 | 923 | g_warning("No such command: '%s'. Type 'help' to see available commands.", command); 924 | 925 | return (-1); 926 | } 927 | 928 | static void 929 | read_and_execute_command(void) 930 | { 931 | int ret; 932 | char *command_line, *command, *next_command; 933 | 934 | command = command_line = read_command(); 935 | 936 | do { 937 | next_command = strchr(command, ';'); 938 | if (next_command != NULL) { 939 | *next_command = '\0'; 940 | next_command++; 941 | } 942 | 943 | strip_unneeded_whitespace(command, 1024); 944 | if (strlen(command) > 0) { 945 | ret = execute_command(command); 946 | if (ret) 947 | g_warning("Command finished with error."); 948 | } 949 | 950 | command = next_command; 951 | 952 | } while (command); 953 | 954 | free(command_line); 955 | } 956 | 957 | #ifdef HAVE_LIBREADLINE 958 | 959 | static char * 960 | smfsh_command_generator(const char *text, int state) 961 | { 962 | static struct command_struct *command = commands; 963 | char *tmp; 964 | 965 | if (state == 0) 966 | command = commands; 967 | 968 | while (command->name != NULL) { 969 | tmp = command->name; 970 | command++; 971 | 972 | if (strncmp(tmp, text, strlen(text)) == 0) 973 | return (strdup(tmp)); 974 | } 975 | 976 | return (NULL); 977 | } 978 | 979 | static char ** 980 | smfsh_completion(const char *text, int start, int end) 981 | { 982 | int i; 983 | (void) end; 984 | 985 | /* Return NULL if "text" is not the first word in the input line. */ 986 | if (start != 0) { 987 | for (i = 0; i < start; i++) { 988 | if (!isspace(rl_line_buffer[i])) 989 | return (NULL); 990 | } 991 | } 992 | 993 | return (rl_completion_matches(text, smfsh_command_generator)); 994 | } 995 | 996 | #endif 997 | 998 | static void 999 | usage(void) 1000 | { 1001 | fprintf(stderr, "usage: smfsh [-V | file]\n"); 1002 | 1003 | exit(EX_USAGE); 1004 | } 1005 | 1006 | int 1007 | main(int argc, char *argv[]) 1008 | { 1009 | int ch; 1010 | 1011 | while ((ch = getopt(argc, argv, "V")) != -1) { 1012 | switch (ch) { 1013 | case 'V': 1014 | cmd_version(NULL); 1015 | exit(EX_OK); 1016 | 1017 | case '?': 1018 | default: 1019 | usage(); 1020 | } 1021 | } 1022 | 1023 | if (argc > 2) 1024 | usage(); 1025 | 1026 | g_log_set_default_handler(log_handler, NULL); 1027 | 1028 | smf = smf_new(); 1029 | if (smf == NULL) { 1030 | g_critical("Cannot initialize smf_t."); 1031 | return (-1); 1032 | } 1033 | 1034 | if (argc == 2) 1035 | cmd_load(argv[1]); 1036 | else 1037 | cmd_trackadd(NULL); 1038 | 1039 | #ifdef HAVE_LIBREADLINE 1040 | rl_readline_name = "smfsh"; 1041 | rl_attempted_completion_function = smfsh_completion; 1042 | #endif 1043 | 1044 | for (;;) 1045 | read_and_execute_command(); 1046 | 1047 | return (0); 1048 | } 1049 | 1050 | --------------------------------------------------------------------------------