[xiph-cvs] cvs commit: ogg-tools/oggmerge midi.c midi.h mng.h vorbis.c vorbis.h Makefile.am TODO configure.in oggmerge.c oggmerge.h

Jack Moffitt jack at xiph.org
Mon Sep 3 16:27:03 PDT 2001



jack        01/09/03 16:27:03

  Modified:    oggmerge Makefile.am TODO configure.in oggmerge.c oggmerge.h
  Added:       oggmerge midi.c midi.h mng.h vorbis.c vorbis.h
  Log:
  This is a working oggmerge that supports MIDI + Ogg Vorbis files.  Hooks
  for other media types are there (just need packetizer plugins written) and
  MNG should follow soon in a commit from Ralph.
  
  oggmerge only takes degenerate .ogg streams that contain vorbis files and
  type 0 midi files at this point.
  
  Updated build system and TODO for this new set of code.

Revision  Changes    Path
1.2       +6 -4      ogg-tools/oggmerge/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/ogg-tools/oggmerge/Makefile.am,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- Makefile.am	2001/08/25 22:22:30	1.1
+++ Makefile.am	2001/09/03 23:27:01	1.2
@@ -1,9 +1,11 @@
 ## process this file with automake to produce Makefile.in
 
-AUTOMAKE_OPTIONS = foreign
+AUTOMAKE_OPTIONS = dist-zip foreign
 
 bin_PROGRAMS = oggmerge
 
-oggmerge_SOURCES = oggmerge.c oggmerge.h mngread.c
-oggmerge_CFLAGS = @OGG_CFLAGS@
-oggmerge_LDADD = @OGG_LIBS@
+oggmerge_SOURCES = oggmerge.c vorbis.c midi.c\
+			oggmerge.h vorbis.h midi.h mng.h
+
+oggmerge_CFLAGS = @OGG_CFLAGS@ @VORBIS_CFLAGS@
+oggmerge_LDADD = @OGG_LIBS@ @VORBIS_LIBS@

1.2       +2 -2      ogg-tools/oggmerge/TODO

Index: TODO
===================================================================
RCS file: /usr/local/cvsroot/ogg-tools/oggmerge/TODO,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- TODO	2001/08/25 22:22:31	1.1
+++ TODO	2001/09/03 23:27:01	1.2
@@ -1,6 +1,6 @@
 TODO list for oggmerge
 
-* get simple mng->ogg working
-* framework redesign to allow mng+oggvorbis->ogg
+* write mng packetizer plugin
 * experiment with stream-description metadata
 * xml substream support
+* test

1.2       +3 -3      ogg-tools/oggmerge/configure.in

Index: configure.in
===================================================================
RCS file: /usr/local/cvsroot/ogg-tools/oggmerge/configure.in,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- configure.in	2001/08/25 22:22:30	1.1
+++ configure.in	2001/09/03 23:27:01	1.2
@@ -1,12 +1,12 @@
 dnl process this file with autoconf to generate the configure script
 
 AC_INIT(oggmerge.c)
-AM_INIT_AUTOMAKE(oggmerge,0.1)
-AM_CONFIG_HEADER(config.h)
+AM_INIT_AUTOMAKE(oggmerge,0.9)
 
 AC_PROG_CC
 
 AM_PATH_OGG()
+AM_PATH_VORBIS()
 
 dnl Check for libraries
 #AC_CHECK_LIB(png, png_check_sig)
@@ -15,6 +15,6 @@
 
 dnl Check for headers
 AC_HEADER_STDC()
-AC_CHECK_HEADERS(libmng.h)
+#AC_CHECK_HEADERS(libmng.h)
 
 AC_OUTPUT(Makefile)

1.2       +281 -66   ogg-tools/oggmerge/oggmerge.c

Index: oggmerge.c
===================================================================
RCS file: /usr/local/cvsroot/ogg-tools/oggmerge/oggmerge.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- oggmerge.c	2001/08/25 22:22:30	1.1
+++ oggmerge.c	2001/09/03 23:27:01	1.2
@@ -1,105 +1,320 @@
 /*
-	oggmerge -- utility for splicing together ogg bitstreams
+oggmerge -- utility for splicing together ogg bitstreams
                         from component media subtypes
 
         oggmerge.c
 
         Copyright 2000 Ralph Giles <Ralph_Giles at telus.net>
+	               Jack Moffitt <jack at xiph.org>
 
         Distributed under the GPL
         see http://www.gnu.org/copyleft/gpl.html for details
 */
+#define _GNU_SOURCE
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <getopt.h>
 
 #include <ogg/ogg.h>
 
 #include "oggmerge.h"
-#include "config.h"
+#include "vorbis.h"
+#include "midi.h"
 
-void usage(void)
+#define VERSIONINFO "oggmerge v0.9\n"
+
+param_t params;
+
+void _usage(void)
 {
         /* prefer stdout to stderr to coddle win32 */
         FILE *out = stdout;
 
         fprintf(out,
-		"oggmerge [-o <outfile>] <file1> [<file2> ...]\n"
+		VERSIONINFO \
+		"oggmerge [-o <outfile> | --output=<outfile>] <file1> <file2> [<file3> ...]\n"
                 "  Any of the file arguments can be '-' for stdin/out\n"
                 "  Other options:\n"
-		"         -h             this help\n"
-		"         --help         longer help\n"
-		"         -v             verbose status\n"
+		"         -h, --help     this help\n"
+		"         -v, --verbose  verbose status\n"
                 "         -q, --quiet    suppress status output\n"
                 "         --version      print version information\n"
-		"  caveat: much of this is unimplemented!\n"
         );
 }
+
+void _set_defaults(void)
+{
+	/* set defaults */
+	params.outfile = NULL;
+	params.outfile = NULL;
+	params.input = NULL;
+	params.quiet = 0;
+	params.verbose = 0;
+}
+
+struct option long_options[] = {
+	{"quiet", 0, NULL, 'q'},
+	{"verbose", 0, NULL, 'v'},
+	{"help", 0, NULL, 'h'},
+	{"version", 0, NULL, 'V'},
+	{"output", 1, NULL, 'o'},
+	{NULL, 0, NULL, 0}
+};
+
+void _parse_args(int argc, char **argv)
+{
+	int ret;
+	int option_index = 1;
+
+	while ((ret = getopt_long(argc, argv, "qvhVo:", long_options, &option_index)) != -1) {
+		switch (ret) {
+		case 0:
+			fprintf(stderr, "Internal error parsing command line options.\n");
+			exit(1);
+			break;
+		case 'q':
+			params.quiet = 1;
+			params.verbose = 0;
+			break;
+		case 'v':
+			params.quiet = 0;
+			params.verbose = 1;
+			break;
+		case 'h':
+			_usage();
+			exit(0);
+			break;
+		case 'V':
+			fprintf(stderr, VERSIONINFO);
+			exit(0);
+			break;
+		case 'o':
+			if (params.outfile != NULL) {
+				fprintf(stderr, "Only one output file allowed.\n");
+				exit(1);
+			}
+			params.outfile = (char *)strdup(optarg);
+			break;
+		default:
+			_usage();
+			exit(0);
+		}
+	}
+}
+
+int _get_type(char *filename)
+{
+	char *ext;
+
+	ext = strrchr(filename, '.');
+	if (ext == NULL) return TYPEUNKNOWN;
+
+	if (strcasecmp(ext, ".ogg") == 0)
+		return TYPEVORBIS;
+	else if (strcasecmp(ext, ".mid") == 0)
+		return TYPEMIDI;
+/*	else if (strcasecmp(ext, ".mng") == 0)
+	return TYPEMNG; */
+	return TYPEUNKNOWN;
+}
+
+void _add_file(filelist_t *file)
+{
+	filelist_t *temp;
+
+	if (params.input == NULL) {
+		params.input = file;
+	} else {
+		temp = params.input;
+		while (temp->next) temp = temp->next;
+		temp->next = file;
+	}
+}
 
-int main(int argc, char *argv[])
+int _unique_serialno(int serialno)
 {
-	char *infile,*outfile;
-	FILE *in,*out;
-	param_t *param;
-
-	if (argc != 2) {
-		usage();
-		exit(1);
-	}
-
-	/* initialize defaults */
-	param = malloc(sizeof(*param));
-	if (param == NULL) {
-		fprintf(stderr, "couldn't allocation memory for parameters\n");
-		exit(1);
-	}
-	param->in = param->out = NULL;
-	param->infile = param->outfile = NULL;
-	param->quiet = 0;
-	param->verbose = 0;
-	param->os = NULL;
-	param->oy = NULL;
-
-	infile = strdup(argv[1]);
-	in = fopen(infile, "rb");
-	if (in == NULL) {
-		fprintf(stderr, "Could not open '%s'\n", infile);
-		exit(1);
+	filelist_t *file;
+
+	file = params.input;
+	while (file) {
+		if (file->serialno == serialno) return 0;
+		file = file->next;
         }
+	return 1;
+}
 
-	if (strlen(infile)<5) {
-		outfile = "out.ogg";
+#define BUFSIZE 1024
+
+int main(int argc, char **argv)
+{
+	char **infiles;
+	int numfiles;
+	int i;
+	filelist_t *file, *winner;
+	unsigned long bytes;
+	int serialno;
+	ogg_page *page;
+	char buf[BUFSIZE];
+
+	srand(time(NULL));
+
+	_set_defaults();
+	_parse_args(argc, argv);
+	
+	if (optind >= argc) {
+		fprintf(stderr, "Error: No input files specified.  Use -h for help.\n");
+		return 1;
         } else {
-		outfile = strdup(infile);
-		outfile[strlen(infile)-3] = 'o';
-		outfile[strlen(infile)-2] = 'g';
-		outfile[strlen(infile)-1] = 'g';
-		outfile[strlen(infile)] = '\0';
-	}
-	out = fopen(outfile, "wb");
-	if (out == NULL) {
-		fprintf(stderr, "Could not open '%s'\n", outfile);
-                exit(1);    
-        }
-	fprintf(stderr, "converting %s to %s\n", argv[1], outfile);
-
-	/* fill in parameters for the conversion */
-	param->in = in;
-	param->out = out;
-	param->infile = infile;
-	param->outfile = outfile;
-
-	/* set up the ogg stream for output */
-	param->os = (ogg_stream_state*)malloc(sizeof(ogg_stream_state));
-	param->oy = (ogg_sync_state*)malloc(sizeof(ogg_sync_state));
-	ogg_stream_init(param->os,0x1f1f3e3e);
-	ogg_sync_init(param->oy);
+		infiles = argv + optind;
+		numfiles = argc - optind;
+	}
 
-	mngconvert(param);
+	/* setup the input files */
+	for (i = 0; i < numfiles; i++) {
+		file = (filelist_t *)malloc(sizeof(filelist_t));
+		if (file == NULL) {
+			fprintf(stderr, "Error: Memory error.\n");
+			return 1;
+		}
+
+		file->name = (char *)strdup(infiles[i]);
+		file->type = _get_type(file->name);
+		
+		if (file->type == TYPEUNKNOWN) {
+			fprintf(stderr, "Error: File %s has unknown type.\n", file->name);
+			return 1;
+		}
+
+		file->fp = NULL;
+		switch (file->type) {
+		case TYPEVORBIS:
+			file->state_init = vorbis_state_init;
+			file->data_in = vorbis_data_in;
+			file->page_out = vorbis_page_out;
+			break;
+		case TYPEMIDI:
+			file->state_init = midi_state_init;
+			file->data_in = midi_data_in;
+			file->page_out = midi_page_out;
+			break;
+		}
+
+		file->serialno = 0;
+		file->status = EMOREDATA;
+		file->page = NULL;
+		file->next = NULL;
+
+		_add_file(file);
+	}
+
+	/* open output file */
+	if (params.outfile == NULL) {
+		params.outfile = (char *)strdup("stdout");
+		params.out = stdout;
+	} else {
+		params.out = fopen(params.outfile, "w");
+		if (params.out == NULL) {
+			fprintf(stderr, "Error: Couldn't open output file %s.\n", params.outfile);
+			return 1;
+		}
+	}
 
-	fclose(param->out);
-	fclose(param->in);
+	/* open all files and prepare for processing */
+	file = params.input;
+	while (file) {
+		fprintf(stderr, "Opening %s for reading...\n", file->name);
+		file->fp = fopen(file->name, "r");
+		if (file->fp == NULL) {
+			fprintf(stderr, "Error: Couldn't open input file %s.\n", file->name);
+			return 1;
+		}
+		
+		do {
+			serialno = rand();
+		} while (!_unique_serialno(serialno));
+		file->serialno = serialno;
 
-	free(param);
+		file->state_init(&file->state, file->serialno);
+
+		file = file->next;
+	}
+
+	/* let her rip! */
+	while (1) {
+		/* Step 1: make sure an ogg page is available for each input 
+		** as long we havne't already processed the last one
+		*/
+		file = params.input;
+		while (file) {
+			if (file->page == NULL) {
+				while (!feof(file->fp) && file->status == EMOREDATA  && (file->page = file->page_out(&file->state)) == NULL) {
+					bytes = fread(buf, 1, BUFSIZE, file->fp);
+					file->status = file->data_in(&file->state, buf, bytes);
+					if (file->status < 0 && file->status != EMOREDATA) {
+						fprintf(stderr, "Error: Packetizer error on file %s.\n", file->name);
+						return 1;
+					}
+				}
+			}
+
+			file = file->next;
+		}
+
+		/* Step 2: Pick the page with the lowest timestamp and 
+		** stuff it into the ogg stream
+		*/
+		winner = params.input;
+		file = winner->next;
+		while (file) {
+			if (file->page != NULL) {
+				if (winner->page != NULL) {
+					if (file->page->timestamp < winner->page->timestamp)
+						winner = file;
+				} else {
+					winner = file;
+				}
+			}
+			file = file->next;
+		}
+
+		/* exit if there are no more pages */
+		if (winner->page == NULL) break;
+
+		/* Step 3: Write out the winning page */
+		page = winner->page->og;
+
+		bytes = fwrite(page->header, 1, page->header_len, params.out);
+		if (bytes != page->header_len) {
+			fprintf(stderr, "Error: Output error writing to %s.\n", params.outfile);
+			return 1;
+		}
+		bytes = fwrite(page->body, 1, page->body_len, params.out);
+		if (bytes != page->body_len) {
+			fprintf(stderr, "Error: Output error writing to %s.\n", params.outfile);
+			return 1;
+		}
+
+		/* Step 4: Cleanup! */
+		free(page->header);
+		free(page->body);
+		free(page);
+		free(winner->page);
+		winner->page = NULL;
+	}
+
+	file = params.input;
+	while (file) {
+		fclose(file->fp);
+		file = file->next;
+	}
+	fclose(params.out);
+
+	return 0;
 }
+
+
+

1.2       +53 -19    ogg-tools/oggmerge/oggmerge.h

Index: oggmerge.h
===================================================================
RCS file: /usr/local/cvsroot/ogg-tools/oggmerge/oggmerge.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- oggmerge.h	2001/08/25 22:22:31	1.1
+++ oggmerge.h	2001/09/03 23:27:01	1.2
@@ -1,29 +1,63 @@
-/*
-	oggmerge -- utility for splicing together ogg bitstreams
-			from component media subtypes
+/* oggmerge.h
+**
+*/
 
-	oggmerge.h
+#ifndef __OGGMERGE_H__
+#define __OGGMERGE_H__
 
-	Copyright 2000 Ralph Giles <Ralph_Giles at telus.net>
+#include <ogg/ogg.h>
 
-	Distributed under the GPL
-	see the file COPYING for details
-	or visit http://www.gnu.org/copyleft/gpl.html
-*/
+typedef struct {
+	ogg_page *og;
+	u_int64_t timestamp;
+} oggmerge_page_t;
 
-#ifndef OGGMERGE_H
-#define OGGMERGE_H
+typedef struct {
+	void *private;
+} oggmerge_state_t;
 
-#include <ogg/ogg.h>
+typedef struct filelist_tag {
+	char *name;
+	FILE *fp;
+
+	int type;
+
+	oggmerge_state_t state;
+	oggmerge_page_t *page;
+	int status;
+	unsigned serialno;
+
+	int (*state_init)(oggmerge_state_t *state, int serialno);
+	int (*data_in)(oggmerge_state_t *state, char *buffer, unsigned long size);
+	oggmerge_page_t *(*page_out)(oggmerge_state_t *state);
 
-/* state storage structure */
+	struct filelist_tag *next;
+} filelist_t;
+
 typedef struct {
-	char	*infile, *outfile;
-	FILE	*in,*out;
-	int	quiet,verbose;
-	ogg_stream_state *os;
-	ogg_sync_state *oy;
+	char *outfile;
+	FILE *out;
+	filelist_t *input;
+	int quiet;
+	int verbose;
 } param_t;
 
+/* errors */
+#define EMOREDATA -1
+#define EMALLOC -2
+#define EBADHEADER -3
+#define EBADEVENT -4
+#define EOTHER -5
+
+/* types */
+#define TYPEUNKNOWN 0
+#define TYPEVORBIS 1
+#define TYPEMIDI 2
+#define TYPEMNG 3
+
+#endif  /* __OGGMERGE_H__ */
+
+
+
+
 
-#endif /* OGGMERGE_H */

1.1                  ogg-tools/oggmerge/midi.c

Index: midi.c
===================================================================
/* midi.c
**
** oggmerge midi module
**
*/

#include <stdio.h>
#include <ogg/ogg.h>

#include "oggmerge.h"
#include "midi.h"

typedef struct databuf_tag {
        char *data;
        unsigned long size;
        unsigned long pos;

        struct databuf_tag *next;
} databuf_t;

typedef enum {
        e_header,
        e_newpacket,
        e_newevent,
        e_timestamp,
        e_status,
        e_onebytedata,
        e_twobytedata,
        e_sysexlengthdata,
        e_sysexdata,
        e_metatypedata,
        e_metalengthdata,
        e_metadata,
        e_endofevent,
        e_done
} process_state_t;

typedef struct midi_page_tag {
        oggmerge_page_t *page;
        struct midi_page_tag *next;
} midi_page_t;

typedef struct {
        databuf_t *data;
        process_state_t ps;
        ogg_stream_state os;
        ogg_packet *op;
        unsigned long ppos;
        midi_page_t *pages;
        int serialno;
        // timing information
        int ticks;
        long tempo;
        int frames;
        int smtpe;
        // interim holders for midi state values
        u_int64_t current;
        unsigned long timestamp;
        unsigned long length;
        unsigned char metatype;
} midi_state_t;

tatic int _process_data(midi_state_t *midistate);

int midi_state_init(oggmerge_state_t *state, int serialno)
{
        midi_state_t *midistate;

        if (state == NULL) return 0;

        midistate = (midi_state_t *)malloc(sizeof(midi_state_t));

        if (midistate != NULL) {
                ogg_stream_init(&midistate->os, serialno);
                midistate->ps = e_header;
                midistate->data = NULL;
                midistate->op = NULL;
                midistate->pages = NULL;
                midistate->serialno = serialno;
                midistate->ticks = 480;
                midistate->tempo = 500000; // 120 bpm == 500,000 microseconds per beat
                midistate->frames = 0;
                midistate->smtpe = 0;
                midistate->current = 0;
                state->private = (void *)midistate;
                return 1;
        }

        return 0;
}

/* midi_data_in
**
** all we do here is create a new databuf_t and append it to our 
** buffer list.
**
** we make a copy of the data.
*/
int midi_data_in(oggmerge_state_t *state, char *buffer, unsigned long size)
{
        midi_state_t *midistate;
        databuf_t *databuf, *temp;

        if (state == NULL || buffer == NULL || size <= 0) return;

        midistate = (midi_state_t *)state->private;

        databuf = (databuf_t *)malloc(sizeof(databuf_t));
        if (databuf == NULL) return;

        databuf->data = (char *)malloc(size);
        databuf->size = size;
        databuf->pos = 0;
        databuf->next = NULL;

        if (databuf->data == NULL) {
                free(databuf);
                return;
        }

        // copy data
        memcpy(databuf->data, buffer, size);

        // add to list
        if (midistate->data == NULL) {
                midistate->data = databuf;
        } else {
                temp = midistate->data;
                while (temp->next != NULL) temp = temp->next;
                temp->next = databuf;
        }

        return _process_data(midistate);
}

oggmerge_page_t *midi_page_out(oggmerge_state_t *state)
{
        midi_state_t *midistate;
        oggmerge_page_t *page;

        midistate = (midi_state_t *)state->private;

        if (midistate->pages == NULL) return NULL;

        page = midistate->pages->page;
        midistate->pages = midistate->pages->next;

        return page;
}

tatic int _little_endian(void)
{
        unsigned short pattern = 0xbabe;
        unsigned char *bytewise = (unsigned char *)&pattern;

        if (bytewise[0] == 0xba) return 0;
        return 1;
}

tatic unsigned long _swap_long(unsigned long data)
{
        unsigned long ret;
        unsigned char *dataptr = (unsigned char *)&data;
        unsigned char *retptr = (unsigned char *)&ret;

        retptr[0] = dataptr[3];
        retptr[1] = dataptr[2];
        retptr[2] = dataptr[1];
        retptr[3] = dataptr[0];

        return ret;
}

tatic unsigned short _swap_short(unsigned short data)
{
        unsigned short ret;
        unsigned char *dataptr = (unsigned char *)&data;
        unsigned char *retptr = (unsigned char *)&ret;

        retptr[0] = dataptr[1];
        retptr[1] = dataptr[0];

        return ret;
}

tatic u_int64_t _data_left(midi_state_t *midistate)
{
        u_int64_t len = 0;
        databuf_t *buf;

        if (midistate->data == NULL) return 0;

        buf = midistate->data;
        while (buf) {
                len += (buf->size - buf->pos);
                buf = buf->next;
        }

        return len;
}

/* _get_data
**
** this returns a buffer of the requested size
** or NULL if there's not enough data
** this advances the data pointer too
** caller is responsible for freeing the buffer
*/
static char *_get_data(midi_state_t *midistate, unsigned long size)
{
        char *buffer, *ptr;
        unsigned long copy;
        databuf_t *databuf;

        if (_data_left(midistate) < size) return NULL;

        buffer = (char *)malloc(size);
        if (buffer == NULL) return NULL;

        databuf = midistate->data;

        ptr = buffer;
        while (size > 0) {
                copy = databuf->size - databuf->pos >= size ? size : databuf->size - databuf->pos;
                memcpy(ptr, &databuf->data[databuf->pos], copy);
                ptr += copy;
                databuf->pos += copy;
                size -= copy;

                // might need to advance to the next block
                if (databuf->pos == databuf->size) {
                        midistate->data = databuf->next;
                        databuf = databuf->next;
                }
        }

        return buffer;
}

tatic int _get_byte(midi_state_t *midistate)
{
        int value;
        databuf_t *databuf;

        if (_data_left(midistate) < 1) return -1;

        databuf = midistate->data;
        value = databuf->data[databuf->pos++];

        // may need to advance block
        if (databuf->pos == databuf->size)
                midistate->data = databuf->next;

        return value;
}

tatic int64_t _get_long(midi_state_t *midistate)
{
        int64_t value;

        if (_data_left(midistate) < 4) return -1;
        
        value = ((_get_byte(midistate) & 0xFF) << 24) | ((_get_byte(midistate) & 0xFF) << 16) | 
                ((_get_byte(midistate) & 0xFF) << 8) | (_get_byte(midistate) & 0xFF);

//	if (_little_endian()) return _swap_long(value);
        return value;
}

tatic long _get_short(midi_state_t *midistate)
{
        long value;
        
        if (_data_left(midistate) < 2) return -1;

        value = ((_get_byte(midistate) & 0xFF) << 8) | (_get_byte(midistate) & 0xFF);

//	if (_little_endian()) return _swap_short(value);
        return value;
}

tatic ogg_page *_copy_ogg_page(ogg_page *og)
{
        ogg_page *page;

        page = (ogg_page *)malloc(sizeof(ogg_page));
        if (page == NULL) return NULL;

        page->header_len = og->header_len;
        page->body_len = og->body_len;
        page->header = (unsigned char *)malloc(page->header_len);
        if (page->header == NULL) {
                free(page);
                return NULL;
        }
        memcpy(page->header, og->header, page->header_len);
        page->body = (unsigned char *)malloc(page->body_len);
        if (page->body == NULL) {
                free(page->header);
                free(page);
                return NULL;
        }
        memcpy(page->body, og->body, page->body_len);

        return page;
}

tatic oggmerge_page_t *_make_oggmerge_page(ogg_page *og, u_int64_t timestamp)
{
        oggmerge_page_t *omp;

        omp = (oggmerge_page_t *)malloc(sizeof(oggmerge_page_t));
        if (omp == NULL) return NULL;

        omp->og = _copy_ogg_page(og);
        omp->timestamp = timestamp;

        return omp;
}

/* _make_timestamp
** 
** convert native midi stamps into absolute microseconds relative
** to the beginning of the stream
*/
static u_int64_t _make_timestamp(midi_state_t *midistate, ogg_int64_t granulepos)
{
        u_int64_t timestamp;

        if (midistate->smtpe) {
                timestamp = (double)granulepos * ((double)(midistate->frames * midistate->ticks) / (double)1000000);
        } else {
                // tempo is in us/quartnernote
                timestamp = (double)granulepos * (double)midistate->tempo / (double)midistate->ticks;
        }

        return timestamp;
}

tatic void _add_oggmerge_page(midi_state_t *midistate, oggmerge_page_t *omp)
{
        midi_page_t *mp, *temp;
        
        mp = (midi_page_t *)malloc(sizeof(midi_page_t));
        if (mp == NULL) return;

        mp->next = NULL;
        mp->page = omp;

        temp = midistate->pages;
        if (temp == NULL) {
                midistate->pages = mp;
        } else {
                while (temp->next != NULL) temp = temp->next;
                temp->next = mp;
        }
}

tatic void _flush_pages(midi_state_t *midistate)
{
        ogg_page *og;
        oggmerge_page_t *omp;

        og = (ogg_page *)malloc(sizeof(ogg_page));
        if (og == NULL) return;

        while (ogg_stream_flush(&midistate->os, og)) {
                omp = _make_oggmerge_page(og, _make_timestamp(midistate, ogg_page_granulepos(og)));
                _add_oggmerge_page(midistate, omp);
        }

        free(og);
}

tatic void _queue_pages(midi_state_t *midistate)
{
        ogg_page *og;
        oggmerge_page_t *omp;

        og = (ogg_page *)malloc(sizeof(ogg_page));
        if (og == NULL) return;

        while (ogg_stream_pageout(&midistate->os, og)) {
                omp = _make_oggmerge_page(og, _make_timestamp(midistate, ogg_page_granulepos(og)));
                _add_oggmerge_page(midistate, omp);
        }

        free(og);
}

tatic void _resize_packet(midi_state_t *midistate, unsigned long size)
{
        unsigned char *packet;

        // check if the packet has been created
        if (midistate->op->packet == NULL) {
                // create new packet data
                midistate->op->packet = (unsigned char *)malloc(size);
                if (midistate->op->packet == NULL) {
                        printf("ERROR!!!!\n");
                        return;
                }
                midistate->ppos = 0;
                midistate->op->bytes = size;
        } else {
                // create new buffer
                packet = (unsigned char *)malloc(size);
                if (packet == NULL) return;
                // copy old data to new buffer
                memcpy(packet, midistate->op->packet, midistate->ppos <= size ? midistate->ppos : size);
                // free old buffer
                free(midistate->op->packet);
                // set new buffer
                midistate->op->packet = packet;
                midistate->op->bytes = size;
        }
}

tatic void _packet_write_byte(midi_state_t *midistate, unsigned char byte)
{
        if (midistate->ppos == midistate->op->bytes) {
                _resize_packet(midistate, midistate->op->bytes + 25);
        }

        midistate->op->packet[midistate->ppos++] = byte;
}

tatic void _packet_write_data(midi_state_t *midistate, unsigned char *data, unsigned long len)
{
        if (midistate->ppos + len >= midistate->op->bytes) {
                _resize_packet(midistate, midistate->op->bytes + len + 25);
        }

        memcpy(&midistate->op->packet[midistate->ppos], data, len);
        midistate->ppos += len;
}

tatic int _process_data(midi_state_t *midistate)
{
        char *buf;
        unsigned short rawtime;
        unsigned char smtpe;
        int frames;
        int ticks;
        ogg_packet *op;
        unsigned char *packet;
        unsigned char version;
        int stamp, status, length, type;

        int out_of_data = 0;
        int all_done = 0;

        while (!out_of_data && midistate->ps != e_done) {
                switch (midistate->ps) {
                case e_header:
                        // make sure we have enough data to complete
                        // this state
                        if (_data_left(midistate) < 22) {
                                out_of_data = 1;
                                break;
                        }
                        
                        printf("_process_data(): parsing header\n");
                        // parse the header, make a packet, and stuff
                        // into the stream
                        buf = _get_data(midistate, 4);
                        if (buf == NULL || memcmp(buf, "MThd", 4) != 0) {
                                printf("_process_data(): MThd chunk not found\n");
                                if (buf != NULL) free(buf);
                                return EBADHEADER;
                        }
                        free(buf);

                        // check header chunk length
                        if (_get_long(midistate) != 6) return EBADHEADER;
                        // check that format == 0
                        if (_get_short(midistate) != 0) return EBADHEADER;
                        // check that tracks == 1
                        if (_get_short(midistate) != 1) return EBADHEADER;

                        rawtime = _get_short(midistate) & 0xFFFF;
                        smtpe = (rawtime & 0x8000) == 0x8000;
                        if (smtpe) {
                                frames = (rawtime & 0x7F80) >> 7;
                                ticks = (rawtime & 0x007F);
                                frames = -frames;
                        } else {
                                ticks = (rawtime & 0x7FFF);
                        }
                
                        if (smtpe)
                                printf("SMTPE timebase of %d frames and %d ticks per frame\n", frames, ticks);
                        else
                                printf("Timebase of %d ticks per quarternote\n", ticks);

                        // grab second chunk header
                        buf = _get_data(midistate, 4);
                        if (buf == NULL || memcmp(buf, "MTrk", 4) != 0) {
                                printf("_process_data(): MTrk chunk not found\n");
                                free(buf);
                                return EBADHEADER;
                        }
                        free(buf);

                        // waste chunk length
                        _get_long(midistate);

                        // create the packet
                        op = (ogg_packet *)malloc(sizeof(ogg_packet));
                        if (op == NULL) return EMALLOC;

                        op->b_o_s = 1;
                        op->e_o_s = 0;
                        op->granulepos = 0;
                        op->packetno = 0;
                        op->bytes = 12;
                        packet = (unsigned char *)malloc(12);
                        if (packet == NULL) {
                                free(op);
                                return EMALLOC;
                        }
                        memcpy(packet, "OggMIDI\0", 8);
                        version = 0;
                        packet[8] = version;
                        packet[9] = smtpe == 0 ? 0 : frames;
                        memcpy(packet+10, &ticks, 2);

                        op->packet = packet;

                        // submit the packet
                        ogg_stream_packetin(&midistate->os, op);

                        // flush the header pages
                        _flush_pages(midistate);

                        midistate->ps = e_newpacket;

                        // fall through
                        // at this point everything will be raw midi events
                case e_newpacket:
                        // make a new packet
                        midistate->op = (ogg_packet *)malloc(sizeof(ogg_packet));
                        if (midistate->op == NULL) return EMALLOC;
                        memset(midistate->op, 0, sizeof(ogg_packet));
                        midistate->ppos = 0;
                        midistate->op->granulepos = 0;
                        midistate->op->b_o_s = 0;
                        midistate->op->e_o_s = 0;
                        _resize_packet(midistate, 512);
                        midistate->ps = e_newevent;
                        // fall through
                case e_newevent:
                        midistate->timestamp = 0;
                        midistate->length = 0;
                        midistate->ps = e_timestamp;
                case e_timestamp:
                        // handle timestamp
                        stamp = _get_byte(midistate);
                        if (stamp < 0) {
                                out_of_data = 1;
                                continue;
                        }
                        _packet_write_byte(midistate, stamp & 0xFF);
                        // decode
                        midistate->timestamp <<= 7;
                        midistate->timestamp |= (stamp & 0x7F);
                        if ((stamp & 0x80) == 0x80)
                                continue;
                        else 
                                midistate->ps = e_status;
                        // fall through
                case e_status:
                        // handle status
                        status = _get_byte(midistate);
                        if (status < 0) {
                                out_of_data = 1;
                                continue;
                        }
                        _packet_write_byte(midistate, status & 0xFF);
                        switch (status & 0xF0) {
                        case 0x80:
                        case 0x90:
                        case 0xA0:
                        case 0xB0:
                        case 0xE0:
                                midistate->ps = e_twobytedata;
                                continue;
                        case 0xC0:
                        case 0xD0:
                                midistate->ps = e_onebytedata;
                                continue;
                        case 0xF0:
                                // test for sysex or meta
                                if (status == 0xFF) {
                                        midistate->ps = e_metatypedata;
                                        continue;
                                } else if (status == 0xF0) {
                                        midistate->ps = e_sysexlengthdata;
                                        continue;
                                } else {
                                        printf("_process_data(): 1bad event of %02x\n", status);
                                        return EBADEVENT;
                                }
                        default:
                                printf("_process_data(): 2bad event of %02x\n", status);
                                return EBADEVENT;
                        }
                case e_onebytedata:
                        if (_data_left(midistate) < 1) {
                                out_of_data = 1;
                                continue;
                        }
                        _packet_write_byte(midistate, _get_byte(midistate) & 0xFF);
                        midistate->ps = e_endofevent;
                        continue;
                case e_twobytedata:
                        if (_data_left(midistate) < 2) {
                                out_of_data = 1;
                                continue;
                        }
                        _packet_write_byte(midistate, _get_byte(midistate) & 0xFF);
                        _packet_write_byte(midistate, _get_byte(midistate) & 0xFF);
                        midistate->ps = e_endofevent;
                        continue;
                case e_sysexlengthdata:
                        // handle sysex length
                        length = _get_byte(midistate);
                        if (length < 0) {
                                out_of_data = 1;
                                continue;
                        }
                        _packet_write_byte(midistate, length & 0xFF);
                        // decode
                        midistate->length <<= 7;
                        midistate->length |= (length & 0x7F);
                        if ((length & 0x80) == 0x80)
                                continue;
                        else 
                                midistate->ps = e_sysexdata;
                        // fall through		
                case e_sysexdata:
                        // handle the rest of the sysex event
                        if (_data_left(midistate) < length) {
                                out_of_data = 1;
                                continue;
                        }
                        buf = _get_data(midistate, length);
                        if (buf == NULL) return EMALLOC;
                        _packet_write_data(midistate, buf, length);
                        free(buf);
                        midistate->length = 0;
                        midistate->ps = e_endofevent;
                        continue;
                case e_metatypedata:
                        // handle meta-events
                        type = _get_byte(midistate);
                        if (type < 1) {
                                out_of_data = 1;
                                continue;
                        }
                        _packet_write_byte(midistate, type & 0xFF);
                        midistate->metatype = type & 0xFF;
                        midistate->ps = e_metalengthdata;
                        // fall through
                case e_metalengthdata:
                        // handle meta length
                        length = _get_byte(midistate);
                        if (length < 0) {
                                out_of_data = 1;
                                continue;
                        }
                        _packet_write_byte(midistate, length & 0xFF);
                        // decode
                        midistate->length <<= 7;
                        midistate->length |= (length & 0x7F);
                        if ((length & 0x80) == 0x80)
                                continue;
                        else 
                                midistate->ps = e_metadata;
                        // fall through
                case e_metadata:
                        // handle data for meta event
                        if (midistate->metatype == 0x2F && length == 0) {
                                all_done = 1;
                        }
                        if (length == 0) {
                                midistate->ps = e_endofevent;
                                continue;
                        }
                        if (_data_left(midistate) < length) {
                                out_of_data = 1;
                                continue;
                        }
                        buf = _get_data(midistate, length);
                        if (buf == NULL) return EMALLOC;
                        _packet_write_data(midistate, buf, length);
                        // need to catch tempo changes here
                        if (midistate->metatype == 0x51) {
                                // 0x51 has 0x03 length
                                midistate->tempo = (buf[0] << 16) | (buf[1] << 8) | buf[2];
                        }
                        free(buf);
                        midistate->length = 0;
                        midistate->ps = e_endofevent;
                        continue;
                case e_endofevent:
                        midistate->current += midistate->timestamp;
                        if (midistate->ppos >= 512 || all_done) {
                                // close out this packet and start a new one
                                _resize_packet(midistate, midistate->ppos);
                                midistate->op->granulepos = midistate->current;
                                ogg_stream_packetin(&midistate->os, midistate->op);
                                midistate->op = NULL;
                                _queue_pages(midistate);
                                if (all_done) {
                                        midistate->ps = e_done;
                                        _flush_pages(midistate);
                                } else {
                                        midistate->ps = e_newpacket;
                                }
                                continue;
                        } else {
                                midistate->ps = e_newevent;
                                continue;
                        }
                }
        }

        if (all_done) return 0;
        return EMOREDATA;
}

1.1                  ogg-tools/oggmerge/midi.h

Index: midi.h
===================================================================
/* midi.h
**
** oggmerge midi file module
**
*/

#ifndef __MIDI_H__
#define __MIDI_H__

#include "oggmerge.h"

int midi_state_init(oggmerge_state_t *state, int serialno);
int midi_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
oggmerge_page_t *midi_page_out(oggmerge_state_t *state);

#endif  /* __MIDI_H__*/

1.1                  ogg-tools/oggmerge/mng.h

Index: mng.h
===================================================================
/* mng.h
**
** oggmerge mng file module
**
*/

#ifndef __MNG_H__
#define __MNG_H__

#include "oggmerge.h"

int mng_state_init(oggmerge_state_t *state, int serialno);
int mng_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
oggmerge_page_t *mng_page_out(oggmerge_state_t *state);

#endif  /* __MNG_H__*/

1.1                  ogg-tools/oggmerge/vorbis.c

Index: vorbis.c
===================================================================
/* vorbis.c
**
** vorbis dummy packetizing module
*/

#include <stdio.h>
#include <ogg/ogg.h>
#include <vorbis/codec.h>

#include "oggmerge.h"
#include "vorbis.h"

typedef struct vorbis_page_tag {
        oggmerge_page_t *page;
        struct vorbis_page_tag *next;
} vorbis_page_t;

typedef struct {
        ogg_sync_state oy;
        int serialno;
        int first_page;
        unsigned long samplerate;
        vorbis_page_t *pages;
} vorbis_state_t;

tatic void _add_vorbis_page(vorbis_state_t *vstate, ogg_page *og);

int vorbis_state_init(oggmerge_state_t *state, int serialno)
{
        vorbis_state_t *vstate;

        if (state == NULL) return 0;

        vstate = (vorbis_state_t *)malloc(sizeof(vorbis_state_t));
        if (vstate == NULL) return 0;

        ogg_sync_init(&vstate->oy);
        vstate->first_page = 1;
        vstate->samplerate = 0;
        
        state->private = (void *)vstate;

        return 1;
}

int vorbis_data_in(oggmerge_state_t *state, char *buffer, unsigned long size)
{
        int ret;
        char *buf;
        vorbis_state_t *vstate;
        ogg_page og;
        ogg_packet op;
        ogg_stream_state os;
        vorbis_info vi;
        vorbis_comment vc;

        vstate = (vorbis_state_t *)state->private;

        // stuff data in
        buf = ogg_sync_buffer(&vstate->oy, size);
        memcpy(buf, buffer, size);
        ogg_sync_wrote(&vstate->oy, size);

        // pull pages out
        while ((ret = ogg_sync_pageout(&vstate->oy, &og)) == 1) {
                if (vstate->first_page) {
                        vstate->first_page = 0;
                        ogg_stream_init(&os, ogg_page_serialno(&og));
                        vorbis_info_init(&vi);
                        vorbis_comment_init(&vc);
                        if (ogg_stream_pagein(&os, &og) < 0) {
                                vorbis_comment_clear(&vc);
                                vorbis_info_clear(&vi);
                                ogg_stream_clear(&os);
                                return EBADHEADER;
                        }
                        if (ogg_stream_packetout(&os, &op) != 1) {
                                vorbis_comment_clear(&vc);
                                vorbis_info_clear(&vi);
                                ogg_stream_clear(&os);
                                return EBADHEADER;
                        }
                        if (vorbis_synthesis_headerin(&vi, &vc, &op) < 0) {
                                vorbis_comment_clear(&vc);
                                vorbis_info_clear(&vi);
                                ogg_stream_clear(&os);
                                return EBADHEADER;
                        }
                        vstate->samplerate = vi.rate;
                        vorbis_comment_clear(&vc);
                        vorbis_info_clear(&vi);
                        ogg_stream_clear(&os);
                        
                }
                _add_vorbis_page(vstate, &og);
        }

        if (ret == 0) return EMOREDATA;
        return EOTHER;
}

oggmerge_page_t *vorbis_page_out(oggmerge_state_t *state)
{
        vorbis_state_t *vstate;
        oggmerge_page_t *page;

        vstate = (vorbis_state_t *)state->private;

        if (vstate->pages == NULL) return NULL;

        page = vstate->pages->page;
        vstate->pages = vstate->pages->next;

        return page;
}

tatic ogg_page *_copy_ogg_page(ogg_page *og)
{
        ogg_page *page;

        page = (ogg_page *)malloc(sizeof(ogg_page));
        if (page == NULL) return NULL;

        page->header_len = og->header_len;
        page->body_len = og->body_len;
        page->header = (unsigned char *)malloc(page->header_len);
        if (page->header == NULL) {
                free(page);
                return NULL;
        }
        memcpy(page->header, og->header, page->header_len);
        page->body = (unsigned char *)malloc(page->body_len);
        if (page->body == NULL) {
                free(page->header);
                free(page);
                return NULL;
        }
        memcpy(page->body, og->body, page->body_len);

        return page;
}

tatic u_int64_t _make_timestamp(vorbis_state_t *vstate, ogg_int64_t granulepos)
{
        u_int64_t stamp;
        
        if (vstate->samplerate == 0) return 0;
        
        printf("making timestamp from granulepos = %lld and samplerate = %d\n", granulepos, vstate->samplerate);
        stamp = (double)granulepos * (double)1000000 / (double)vstate->samplerate;
        
        return stamp;
}

tatic void _add_vorbis_page(vorbis_state_t *vstate, ogg_page *og)
{
        oggmerge_page_t *page;
        vorbis_page_t *vpage, *temp;

        // build oggmerge page
        page = (oggmerge_page_t *)malloc(sizeof(oggmerge_page_t));
        page->og = _copy_ogg_page(og);
        page->timestamp = _make_timestamp(vstate, ogg_page_granulepos(og));
        
        // build vorbis page
        vpage = (vorbis_page_t *)malloc(sizeof(vorbis_page_t));
        if (vpage == NULL) {
                free(page->og->header);
                free(page->og->body);
                free(page->og);
                free(page);
                return;
        }

        vpage->page = page;
        vpage->next = NULL;

        // add page to state
        temp = vstate->pages;
        if (temp == NULL) {
                vstate->pages = vpage;
        } else {
                while (temp->next) temp = temp->next;
                temp->next = vpage;
        }
}

1.1                  ogg-tools/oggmerge/vorbis.h

Index: vorbis.h
===================================================================
/* vorbis.h
**
** oggmerge vorbis file module
**
*/

#ifndef __VORBIS_H__
#define __VORBIS_H__

#include "oggmerge.h"

int vorbis_state_init(oggmerge_state_t *state, int serialno);
int vorbis_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
oggmerge_page_t *vorbis_page_out(oggmerge_state_t *state);

#endif  /* __VORBIS_H__*/

--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list