[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