speed up midifile.c (#367)

* don't realloc for one event at time

* use memio

* fix gcc warning
This commit is contained in:
Roman Fomin 2021-12-06 14:19:29 +07:00 committed by GitHub
parent d5bb524f77
commit 97779b39fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 298 additions and 183 deletions

View File

@ -49,6 +49,7 @@ set(WOOF_SOURCES
m_misc2.c m_misc2.h m_misc2.c m_misc2.h
m_random.c m_random.h m_random.c m_random.h
m_swap.h m_swap.h
memio.c memio.h
midifile.c midifile.h midifile.c midifile.h
mmus2mid.c mmus2mid.h mmus2mid.c mmus2mid.h
net_client.c net_client.h net_client.c net_client.h

View File

@ -1625,32 +1625,9 @@ static boolean IsMid(byte *mem, int len)
return len > 4 && !memcmp(mem, "MThd", 4); return len > 4 && !memcmp(mem, "MThd", 4);
} }
static boolean ConvertMus(byte *musdata, int len, char *filename)
{
MIDI mididata;
UBYTE *mid;
int midlen;
int result;
// [FG] remove dependency on memio
memset(&mididata, 0, sizeof(MIDI));
result = mmus2mid(musdata, &mididata, 89, 0);
if (result == 0)
{
MIDIToMidi(&mididata, &mid, &midlen);
M_WriteFile(filename, mid, midlen);
free(mid);
}
return result;
}
static void *I_OPL_RegisterSong(void *data, int len) static void *I_OPL_RegisterSong(void *data, int len)
{ {
midi_file_t *result; midi_file_t *result;
char *filename;
if (!music_initialized) if (!music_initialized)
{ {
@ -1660,32 +1637,38 @@ static void *I_OPL_RegisterSong(void *data, int len)
// MUS files begin with "MUS" // MUS files begin with "MUS"
// Reject anything which doesnt have this signature // Reject anything which doesnt have this signature
filename = M_TempFile("doom.mid");
// [crispy] remove MID file size limit // [crispy] remove MID file size limit
if (IsMid(data, len) /* && len < MAXMIDLENGTH */) if (IsMid(data, len) /* && len < MAXMIDLENGTH */)
{ {
M_WriteFile(filename, data, len); result = MIDI_LoadFile(data, len);
} }
else else
{ {
// Assume a MUS file and try to convert // Assume a MUS file and try to convert
MIDI mididata;
UBYTE *mid;
int midlen;
int err;
ConvertMus(data, len, filename); memset(&mididata, 0, sizeof(MIDI));
err = mmus2mid(data, &mididata, 89, 0);
if (err == 0)
{
MIDIToMidi(&mididata, &mid, &midlen);
result = MIDI_LoadFile(mid, midlen);
}
else
{
result = NULL;
}
} }
result = MIDI_LoadFile(filename);
if (result == NULL) if (result == NULL)
{ {
fprintf(stderr, "I_OPL_RegisterSong: Failed to load MID.\n"); fprintf(stderr, "I_OPL_RegisterSong: Failed to load MID.\n");
} }
// remove file now
remove(filename);
(free)(filename);
return result; return result;
} }

View File

@ -463,15 +463,12 @@ void I_WIN_RegisterSong(void *data, int size)
{ {
int i; int i;
midi_file_t *file; midi_file_t *file;
char *filename;
MIDIPROPTIMEDIV timediv; MIDIPROPTIMEDIV timediv;
MIDIPROPTEMPO tempo; MIDIPROPTEMPO tempo;
MMRESULT mmr; MMRESULT mmr;
filename = M_TempFile("doom.mid"); file = MIDI_LoadFile(data, size);
M_WriteFile(filename, data, size);
file = MIDI_LoadFile(filename);
if (file == NULL) if (file == NULL)
{ {
@ -515,8 +512,6 @@ void I_WIN_RegisterSong(void *data, int size)
FillBuffer(); FillBuffer();
StreamOut(); StreamOut();
(free)(filename);
} }
void I_WIN_UnRegisterSong(void) void I_WIN_UnRegisterSong(void)

197
Source/memio.c Normal file
View File

@ -0,0 +1,197 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// Emulates the IO functions in C stdio.h reading and writing to
// memory.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memio.h"
#include "z_zone.h"
typedef enum {
MODE_READ,
MODE_WRITE,
} memfile_mode_t;
struct _MEMFILE {
unsigned char *buf;
size_t buflen;
size_t alloced;
unsigned int position;
memfile_mode_t mode;
};
// Open a memory area for reading
MEMFILE *mem_fopen_read(void *buf, size_t buflen)
{
MEMFILE *file;
file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0);
file->buf = (unsigned char *) buf;
file->buflen = buflen;
file->position = 0;
file->mode = MODE_READ;
return file;
}
// Read bytes
size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream)
{
size_t items;
if (stream->mode != MODE_READ)
{
printf("not a read stream\n");
return -1;
}
// Trying to read more bytes than we have left?
items = nmemb;
if (items * size > stream->buflen - stream->position)
{
items = (stream->buflen - stream->position) / size;
}
// Copy bytes to buffer
memcpy(buf, stream->buf + stream->position, items * size);
// Update position
stream->position += items * size;
return items;
}
// Open a memory area for writing
MEMFILE *mem_fopen_write(void)
{
MEMFILE *file;
file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0);
file->alloced = 1024;
file->buf = Z_Malloc(file->alloced, PU_STATIC, 0);
file->buflen = 0;
file->position = 0;
file->mode = MODE_WRITE;
return file;
}
// Write bytes to stream
size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream)
{
size_t bytes;
if (stream->mode != MODE_WRITE)
{
return -1;
}
// More bytes than can fit in the buffer?
// If so, reallocate bigger.
bytes = size * nmemb;
while (bytes > stream->alloced - stream->position)
{
unsigned char *newbuf;
newbuf = Z_Malloc(stream->alloced * 2, PU_STATIC, 0);
memcpy(newbuf, stream->buf, stream->alloced);
Z_Free(stream->buf);
stream->buf = newbuf;
stream->alloced *= 2;
}
// Copy into buffer
memcpy(stream->buf + stream->position, ptr, bytes);
stream->position += bytes;
if (stream->position > stream->buflen)
stream->buflen = stream->position;
return nmemb;
}
void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen)
{
*buf = stream->buf;
*buflen = stream->buflen;
}
void mem_fclose(MEMFILE *stream)
{
if (stream->mode == MODE_WRITE)
{
Z_Free(stream->buf);
}
Z_Free(stream);
}
long mem_ftell(MEMFILE *stream)
{
return stream->position;
}
int mem_fseek(MEMFILE *stream, signed long position, mem_rel_t whence)
{
unsigned int newpos;
switch (whence)
{
case MEM_SEEK_SET:
newpos = (int) position;
break;
case MEM_SEEK_CUR:
newpos = (int) (stream->position + position);
break;
case MEM_SEEK_END:
newpos = (int) (stream->buflen + position);
break;
default:
return -1;
}
if (newpos < stream->buflen)
{
stream->position = newpos;
return 0;
}
else
{
printf("Error seeking to %u\n", newpos);
return -1;
}
}

38
Source/memio.h Normal file
View File

@ -0,0 +1,38 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#ifndef MEMIO_H
#define MEMIO_H
typedef struct _MEMFILE MEMFILE;
typedef enum
{
MEM_SEEK_SET,
MEM_SEEK_CUR,
MEM_SEEK_END,
} mem_rel_t;
MEMFILE *mem_fopen_read(void *buf, size_t buflen);
size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream);
MEMFILE *mem_fopen_write(void);
size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream);
void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen);
void mem_fclose(MEMFILE *stream);
long mem_ftell(MEMFILE *stream);
int mem_fseek(MEMFILE *stream, signed long offset, mem_rel_t whence);
#endif /* #ifndef MEMIO_H */

View File

@ -23,6 +23,7 @@
#include "doomtype.h" #include "doomtype.h"
#include "m_swap.h" #include "m_swap.h"
#include "i_system.h" #include "i_system.h"
#include "memio.h"
#include "midifile.h" #include "midifile.h"
#ifdef _WIN32 #ifdef _WIN32
@ -67,6 +68,7 @@ typedef struct
midi_event_t *events; midi_event_t *events;
int num_events; int num_events;
int num_events_mem;
} midi_track_t; } midi_track_t;
struct midi_track_iter_s struct midi_track_iter_s
@ -111,28 +113,26 @@ static boolean CheckChunkHeader(chunk_header_t *chunk,
// Read a single byte. Returns false on error. // Read a single byte. Returns false on error.
static boolean ReadByte(byte *result, FILE *stream) static boolean ReadByte(byte *result, MEMFILE *stream)
{ {
int c; byte c;
c = fgetc(stream); if (mem_fread(&c, sizeof(byte), 1, stream) == 1)
if (c == EOF)
{ {
fprintf(stderr, "ReadByte: Unexpected end of file\n"); *result = c;
return false;
return true;
} }
else else
{ {
*result = (byte) c; fprintf(stderr, "ReadByte: Unexpected end of file\n");
return false;
return true;
} }
} }
// Read a variable-length value. // Read a variable-length value.
static boolean ReadVariableLength(unsigned int *result, FILE *stream) static boolean ReadVariableLength(unsigned int *result, MEMFILE *stream)
{ {
int i; int i;
byte b = 0; byte b = 0;
@ -168,7 +168,7 @@ static boolean ReadVariableLength(unsigned int *result, FILE *stream)
// Read a byte sequence into the data buffer. // Read a byte sequence into the data buffer.
static void *ReadByteSequence(unsigned int num_bytes, FILE *stream) static void *ReadByteSequence(unsigned int num_bytes, MEMFILE *stream)
{ {
unsigned int i; unsigned int i;
byte *result; byte *result;
@ -206,7 +206,7 @@ static void *ReadByteSequence(unsigned int num_bytes, FILE *stream)
static boolean ReadChannelEvent(midi_event_t *event, static boolean ReadChannelEvent(midi_event_t *event,
byte event_type, boolean two_param, byte event_type, boolean two_param,
FILE *stream) MEMFILE *stream)
{ {
byte b = 0; byte b = 0;
@ -246,7 +246,7 @@ static boolean ReadChannelEvent(midi_event_t *event,
// Read sysex event: // Read sysex event:
static boolean ReadSysExEvent(midi_event_t *event, int event_type, static boolean ReadSysExEvent(midi_event_t *event, int event_type,
FILE *stream) MEMFILE *stream)
{ {
event->event_type = event_type; event->event_type = event_type;
@ -272,7 +272,7 @@ static boolean ReadSysExEvent(midi_event_t *event, int event_type,
// Read meta event: // Read meta event:
static boolean ReadMetaEvent(midi_event_t *event, FILE *stream) static boolean ReadMetaEvent(midi_event_t *event, MEMFILE *stream)
{ {
byte b = 0; byte b = 0;
@ -311,7 +311,7 @@ static boolean ReadMetaEvent(midi_event_t *event, FILE *stream)
} }
static boolean ReadEvent(midi_event_t *event, unsigned int *last_event_type, static boolean ReadEvent(midi_event_t *event, unsigned int *last_event_type,
FILE *stream) MEMFILE *stream)
{ {
byte event_type = 0; byte event_type = 0;
@ -336,7 +336,7 @@ static boolean ReadEvent(midi_event_t *event, unsigned int *last_event_type,
{ {
event_type = *last_event_type; event_type = *last_event_type;
if (fseek(stream, -1, SEEK_CUR) < 0) if (mem_fseek(stream, -1, MEM_SEEK_CUR) < 0)
{ {
fprintf(stderr, "ReadEvent: Unable to seek in stream\n"); fprintf(stderr, "ReadEvent: Unable to seek in stream\n");
return false; return false;
@ -415,12 +415,12 @@ static void FreeEvent(midi_event_t *event)
// Read and check the track chunk header // Read and check the track chunk header
static boolean ReadTrackHeader(midi_track_t *track, FILE *stream) static boolean ReadTrackHeader(midi_track_t *track, MEMFILE *stream)
{ {
size_t records_read; size_t records_read;
chunk_header_t chunk_header; chunk_header_t chunk_header;
records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, stream); records_read = mem_fread(&chunk_header, sizeof(chunk_header_t), 1, stream);
if (records_read < 1) if (records_read < 1)
{ {
@ -437,9 +437,9 @@ static boolean ReadTrackHeader(midi_track_t *track, FILE *stream)
return true; return true;
} }
static boolean ReadTrack(midi_track_t *track, FILE *stream) static boolean ReadTrack(midi_track_t *track, MEMFILE *stream)
{ {
midi_event_t *new_events; midi_event_t *new_events = NULL;
midi_event_t *event; midi_event_t *event;
unsigned int last_event_type; unsigned int last_event_type;
@ -461,8 +461,18 @@ static boolean ReadTrack(midi_track_t *track, FILE *stream)
{ {
// Resize the track slightly larger to hold another event: // Resize the track slightly larger to hold another event:
new_events = realloc(track->events, // new_events = realloc(track->events,
sizeof(midi_event_t) * (track->num_events + 1)); // sizeof(midi_event_t) * (track->num_events + 1));
// Depending on the state of the heap and the malloc implementation,
// realloc() one more event at a time can be VERY slow.
if (track->num_events == track->num_events_mem)
{
track->num_events_mem += 100;
new_events = realloc(track->events,
sizeof (midi_event_t) * track->num_events_mem);
}
if (new_events == NULL) if (new_events == NULL)
{ {
@ -507,7 +517,7 @@ static void FreeTrack(midi_track_t *track)
free(track->events); free(track->events);
} }
static boolean ReadAllTracks(midi_file_t *file, FILE *stream) static boolean ReadAllTracks(midi_file_t *file, MEMFILE *stream)
{ {
unsigned int i; unsigned int i;
@ -537,12 +547,12 @@ static boolean ReadAllTracks(midi_file_t *file, FILE *stream)
// Read and check the header chunk. // Read and check the header chunk.
static boolean ReadFileHeader(midi_file_t *file, FILE *stream) static boolean ReadFileHeader(midi_file_t *file, MEMFILE *stream)
{ {
size_t records_read; size_t records_read;
unsigned int format_type; unsigned int format_type;
records_read = fread(&file->header, sizeof(midi_header_t), 1, stream); records_read = mem_fread(&file->header, sizeof(midi_header_t), 1, stream);
if (records_read < 1) if (records_read < 1)
{ {
@ -589,10 +599,10 @@ void MIDI_FreeFile(midi_file_t *file)
free(file); free(file);
} }
midi_file_t *MIDI_LoadFile(char *filename) midi_file_t *MIDI_LoadFile(void *buf, size_t buflen)
{ {
midi_file_t *file; midi_file_t *file;
FILE *stream; MEMFILE *stream;
file = malloc(sizeof(midi_file_t)); file = malloc(sizeof(midi_file_t));
@ -608,11 +618,11 @@ midi_file_t *MIDI_LoadFile(char *filename)
// Open file // Open file
stream = fopen(filename, "rb"); stream = mem_fopen_read(buf, buflen);
if (stream == NULL) if (stream == NULL)
{ {
fprintf(stderr, "MIDI_LoadFile: Failed to open '%s'\n", filename); fprintf(stderr, "MIDI_LoadFile: Failed to open\n");
MIDI_FreeFile(file); MIDI_FreeFile(file);
return NULL; return NULL;
} }
@ -621,7 +631,7 @@ midi_file_t *MIDI_LoadFile(char *filename)
if (!ReadFileHeader(file, stream)) if (!ReadFileHeader(file, stream))
{ {
fclose(stream); mem_fclose(stream);
MIDI_FreeFile(file); MIDI_FreeFile(file);
return NULL; return NULL;
} }
@ -630,12 +640,12 @@ midi_file_t *MIDI_LoadFile(char *filename)
if (!ReadAllTracks(file, stream)) if (!ReadAllTracks(file, stream))
{ {
fclose(stream); mem_fclose(stream);
MIDI_FreeFile(file); MIDI_FreeFile(file);
return NULL; return NULL;
} }
fclose(stream); mem_fclose(stream);
return file; return file;
} }
@ -738,112 +748,3 @@ void MIDI_RestartIterator(midi_track_iter_t *iter)
{ {
iter->position = 0; iter->position = 0;
} }
#ifdef TEST
static char *MIDI_EventTypeToString(midi_event_type_t event_type)
{
switch (event_type)
{
case MIDI_EVENT_NOTE_OFF:
return "MIDI_EVENT_NOTE_OFF";
case MIDI_EVENT_NOTE_ON:
return "MIDI_EVENT_NOTE_ON";
case MIDI_EVENT_AFTERTOUCH:
return "MIDI_EVENT_AFTERTOUCH";
case MIDI_EVENT_CONTROLLER:
return "MIDI_EVENT_CONTROLLER";
case MIDI_EVENT_PROGRAM_CHANGE:
return "MIDI_EVENT_PROGRAM_CHANGE";
case MIDI_EVENT_CHAN_AFTERTOUCH:
return "MIDI_EVENT_CHAN_AFTERTOUCH";
case MIDI_EVENT_PITCH_BEND:
return "MIDI_EVENT_PITCH_BEND";
case MIDI_EVENT_SYSEX:
return "MIDI_EVENT_SYSEX";
case MIDI_EVENT_SYSEX_SPLIT:
return "MIDI_EVENT_SYSEX_SPLIT";
case MIDI_EVENT_META:
return "MIDI_EVENT_META";
default:
return "(unknown)";
}
}
void PrintTrack(midi_track_t *track)
{
midi_event_t *event;
unsigned int i;
for (i=0; i<track->num_events; ++i)
{
event = &track->events[i];
if (event->delta_time > 0)
{
printf("Delay: %u ticks\n", event->delta_time);
}
printf("Event type: %s (%i)\n",
MIDI_EventTypeToString(event->event_type),
event->event_type);
switch(event->event_type)
{
case MIDI_EVENT_NOTE_OFF:
case MIDI_EVENT_NOTE_ON:
case MIDI_EVENT_AFTERTOUCH:
case MIDI_EVENT_CONTROLLER:
case MIDI_EVENT_PROGRAM_CHANGE:
case MIDI_EVENT_CHAN_AFTERTOUCH:
case MIDI_EVENT_PITCH_BEND:
printf("\tChannel: %u\n", event->data.channel.channel);
printf("\tParameter 1: %u\n", event->data.channel.param1);
printf("\tParameter 2: %u\n", event->data.channel.param2);
break;
case MIDI_EVENT_SYSEX:
case MIDI_EVENT_SYSEX_SPLIT:
printf("\tLength: %u\n", event->data.sysex.length);
break;
case MIDI_EVENT_META:
printf("\tMeta type: %u\n", event->data.meta.type);
printf("\tLength: %u\n", event->data.meta.length);
break;
}
}
}
int main(int argc, char *argv[])
{
midi_file_t *file;
unsigned int i;
if (argc < 2)
{
printf("Usage: %s <filename>\n", argv[0]);
exit(1);
}
file = MIDI_LoadFile(argv[1]);
if (file == NULL)
{
fprintf(stderr, "Failed to open %s\n", argv[1]);
exit(1);
}
for (i=0; i<file->num_tracks; ++i)
{
printf("\n== Track %u ==\n\n", i);
PrintTrack(&file->tracks[i]);
}
return 0;
}
#endif

View File

@ -131,7 +131,7 @@ typedef struct
// Load a MIDI file. // Load a MIDI file.
midi_file_t *MIDI_LoadFile(char *filename); midi_file_t *MIDI_LoadFile(void *buf, size_t buflen);
// Free a MIDI file. // Free a MIDI file.