[xiph-cvs] cvs commit: vorbis-python/test enc.py enc2.py
Andrew Catham Master of Python
andrew at xiph.org
Sun Jan 27 03:14:21 PST 2002
andrew 02/01/27 03:14:20
Modified: . ChangeLog README
src pyvorbiscodec.c pyvorbisinfo.c
test enc.py enc2.py
Log:
2001-01-27 Andrew H. Chatham <andrew.chatham at duke.edu>
* pyvorbiscodec.c (py_dsp_write_wav), (parse_wav_data): Added
* pyvorbiscodec.c (py_dsp_write): Write 0 if None
* pyvorbisinfo.c: Removed some unused variables
* pyvorbisinfo.c (py_info_new): Parse the quality option
* pyvorbisinfo.c (py_vorbis_info_blocksize): Added
* test/enc.py: Updated to do new encode process (with quality setting)
* test/enc2.py: Updated to do quality setting and use write_wav
Revision Changes Path
1.8 +11 -0 vorbis-python/ChangeLog
Index: ChangeLog
===================================================================
RCS file: /usr/local/cvsroot/vorbis-python/ChangeLog,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- ChangeLog 2002/01/22 01:58:17 1.7
+++ ChangeLog 2002/01/27 11:14:19 1.8
@@ -1,3 +1,14 @@
+2001-01-27 Andrew H. Chatham <andrew.chatham at duke.edu>
+ * pyvorbiscodec.c (py_dsp_write_wav), (parse_wav_data): Added
+ * pyvorbiscodec.c (py_dsp_write): Write 0 if None
+
+ * pyvorbisinfo.c: Removed some unused variables
+ * pyvorbisinfo.c (py_info_new): Parse the quality option
+ * pyvorbisinfo.c (py_vorbis_info_blocksize): Added
+
+ * test/enc.py: Updated to do new encode process (with quality setting)
+ * test/enc2.py: Updated to do quality setting and use write_wav
+
2001-01-21 Andrew H. Chatham <andrew.chatham at duke.edu>
* pyvorbisinfo.c: Comment objects now store pointers to the
comment structures, not the structure itself. Helps with dealloc.
<p><p>1.4 +7 -3 vorbis-python/README
Index: README
===================================================================
RCS file: /usr/local/cvsroot/vorbis-python/README,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- README 2002/01/22 01:58:17 1.3
+++ README 2002/01/27 11:14:19 1.4
@@ -38,15 +38,19 @@
To encode, you need to feed a VorbisDSPState object PCM data as one
array of floating point values ranging from -1.0 ... 1.0 per
channel. You can do this in pure Python, but it almost doubles the
-time it takes to encode (less if you optimize it, I suppose).
+time it takes to encode. The other option is to read data using the
+python "wave" module and write it to the VorbisDSPState directly using
+the x.write_wav(mywave.readframes(n)) call. This will only work with
+16-bit Wave files.
Perhaps you are wondering how much of a performance hit you'll be
taking using the Python bindings versus straight C. Well, I've tried
to make things as fast as possible, but of course nothing's
perfect. Decoding a file, top reports about the same CPU usage for my
ogg123.py and the C implementation of ogg123. For encoding, it's about
-twice as slow if you're using my test enc.py file. It can probably
-stand some speedup, though.
+twice as slow as oggenc if you're using my test enc.py file (parsing
+the wave file somewhat by hand). If you use the write_wav function,
+it's only about 3% slower than oggenc.
<p><p>1.5 +123 -8 vorbis-python/src/pyvorbiscodec.c
Index: pyvorbiscodec.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-python/src/pyvorbiscodec.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- pyvorbiscodec.c 2002/01/22 01:57:56 1.4
+++ pyvorbiscodec.c 2002/01/27 11:14:19 1.5
@@ -7,6 +7,8 @@
#include "pyvorbiscodec.h"
#include "pyvorbisinfo.h"
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
/**************************************************************
VorbisDSP Object
**************************************************************/
@@ -18,8 +20,14 @@
FDEF(vorbis_block_init) "Create a VorbisBlock object for use in encoding {more?!}";
FDEF(dsp_write) "Write audio data to the dsp device and have it analyzed. \n\
Each argument must be a string containing the audio data for a\n\
-single channel.";
+single channel.\n
+If None is passed as the only argument, this will signal that no more\n
+data needs to be written.";
+FDEF(dsp_write_wav) "Write audio data to the dsp device and have it analyzed.\n\
+The single argument is the output from the python wave module. Only supports\n\
+16-bit wave data (8-bit waves will produce garbage).";
FDEF(dsp_close) "Signal that all audio data has been written to the object.";
+FDEF(vorbis_bitrate_flushpacket) "";
static void py_dsp_dealloc(PyObject *);
static PyObject *py_dsp_getattr(PyObject *, char*);
@@ -64,8 +72,12 @@
METH_VARARGS, py_vorbis_block_init_doc},
{"write", py_dsp_write,
METH_VARARGS, py_dsp_write_doc},
+ {"write_wav", py_dsp_write_wav,
+ METH_VARARGS, py_dsp_write_wav_doc},
{"close", py_dsp_close,
METH_VARARGS, py_dsp_close_doc},
+ {"bitrate_flushpacket", py_vorbis_bitrate_flushpacket,
+ METH_VARARGS, py_vorbis_bitrate_flushpacket_doc},
{NULL, NULL}
};
@@ -95,6 +107,7 @@
return NULL;
ret = (py_dsp *) PyObject_NEW(py_dsp, &py_dsp_type);
+ ret->parent = NULL;
vi = &py_vi->vi;
vorbis_synthesis_init(&vd, vi);
return py_dsp_from_dsp(&vd, (PyObject *) py_vi);
@@ -103,6 +116,7 @@
static void
py_dsp_dealloc(PyObject *self)
{
+ vorbis_dsp_clear(PY_DSP(self));
Py_XDECREF(((py_dsp *)self)->parent);
PyMem_DEL(self);
}
@@ -147,9 +161,6 @@
PyObject *pyheader_code = NULL;
PyObject *ret = NULL;
- if (!PyArg_ParseTuple(args, ""))
- return NULL;
-
/* Takes a comment object as the argument.
I'll just give them an empty one if they don't provied one. */
if (!PyArg_ParseTuple(args, "|O!", &py_vcomment_type, &comm))
@@ -253,6 +264,11 @@
channels = dsp_self->vd.vi->channels;
+ if (PyTuple_Size(args) == 1 && PyTuple_GET_ITEM(args, 0) == Py_None) {
+ vorbis_analysis_wrote(&dsp_self->vd, 0);
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
if (PyTuple_Size(args) != channels) {
snprintf(err_msg, sizeof(err_msg),
"Expected %d strings as arguments; found %d arguments",
@@ -286,13 +302,68 @@
memcpy(analysis_buffer[k], buffs[k], len);
free(buffs);
-
vorbis_analysis_wrote(&dsp_self->vd, samples);
return PyInt_FromLong(samples); /* return the number of samples written */
}
+static void
+parse_wav_data(const char *byte_data, float **buff,
+ int channels, int samples)
+{
+ const float adjust = 1/32768.0;
+ int j, k;
+ for (j = 0; j < samples; j++) {
+ for (k = 0; k < channels; k++) {
+ float val = ((byte_data[j * 2 * channels + 2 * k + 1] << 8) |
+ (byte_data[j * 2 * channels + 2 * k] & 0xff)) * adjust;
+ buff[k][j] = val;
+ }
+ }
+}
+
static PyObject *
+py_dsp_write_wav(PyObject *self, PyObject *args)
+{
+ const char *byte_data;
+ int num_bytes, channels, k;
+ long samples;
+ const int samples_per_it = 1024;
+ py_dsp *dsp = (py_dsp *) self;
+ float **analysis_buffer;
+ int sample_width;
+
+ channels = dsp->vd.vi->channels;
+ sample_width = channels * 2;
+
+ if (!PyArg_ParseTuple(args, "s#", &byte_data, &num_bytes))
+ return NULL;
+
+ if (num_bytes % (channels * 2) != 0) {
+ PyErr_SetString(Py_VorbisError,
+ "Data is not a multiple of (2 * # of channels)");
+ return NULL;
+ }
+ samples = num_bytes / sample_width;
+
+ for (k = 0; k < samples / samples_per_it; k++) {
+ int to_write = MIN(samples - k * samples_per_it, samples_per_it);
+
+ analysis_buffer = vorbis_analysis_buffer(&dsp->vd,
+ to_write * sizeof(float));
+ /* Parse the wav data directly into the analysis buffer. */
+ parse_wav_data(byte_data, analysis_buffer, channels, to_write);
+
+ /* Skip any data we've already passed by incrementing the pointer */
+ byte_data += to_write * sample_width;
+
+ vorbis_analysis_wrote(&dsp->vd, to_write);
+ }
+
+ return PyInt_FromLong(samples);
+}
+
+static PyObject *
py_dsp_close(PyObject *self, PyObject *args)
{
py_dsp *dsp_self = (py_dsp *) self;
@@ -301,6 +372,27 @@
return Py_None;
}
+static PyObject *
+py_vorbis_bitrate_flushpacket(PyObject *self, PyObject *args)
+{
+ ogg_packet op;
+ int ret;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ret = vorbis_bitrate_flushpacket(PY_DSP(self), &op);
+ if (ret == 1)
+ return modinfo->ogg_packet_from_packet(&op);
+ else if (ret == 0) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ } else {
+ PyErr_SetString(Py_VorbisError, "Unknown return code from flushpacket");
+ return NULL;
+ }
+}
+
/*********************************************************
VorbisBlock
*********************************************************/
@@ -308,6 +400,7 @@
static PyObject *py_block_getattr(PyObject *, char*);
FDEF(vorbis_analysis) "Output an OggPage.";
+FDEF(vorbis_bitrate_addblock) "?";
char py_block_doc[] = "";
@@ -343,6 +436,8 @@
static PyMethodDef Block_methods[] = {
{"analysis", py_vorbis_analysis,
METH_VARARGS, py_vorbis_analysis_doc},
+ {"addblock", py_vorbis_bitrate_addblock,
+ METH_VARARGS, py_vorbis_bitrate_addblock_doc},
{NULL, NULL}
};
@@ -363,14 +458,34 @@
static PyObject*
py_vorbis_analysis(PyObject *self, PyObject *args)
{
+ int ret;
py_block *b_self = (py_block *) self;
- ogg_packet op;
if (!PyArg_ParseTuple(args, ""))
return NULL;
+
+ ret = vorbis_analysis(&b_self->vb, NULL);
+ if (ret < 0) {
+ PyErr_SetString(Py_VorbisError, "vorbis_analysis failure");
+ return NULL;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
- vorbis_analysis(&b_self->vb, &op); /* TODO error code */
- return modinfo->ogg_packet_from_packet(&op);
+static PyObject *
+py_vorbis_bitrate_addblock(PyObject *self, PyObject *args)
+{
+ int ret;
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ ret = vorbis_bitrate_addblock(PY_BLOCK(self));
+ if (ret < 0) {
+ PyErr_SetString(Py_VorbisError, "addblock failed");
+ return NULL;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
}
PyObject *
<p><p>1.5 +52 -31 vorbis-python/src/pyvorbisinfo.c
Index: pyvorbisinfo.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-python/src/pyvorbisinfo.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- pyvorbisinfo.c 2002/01/22 01:57:56 1.4
+++ pyvorbisinfo.c 2002/01/27 11:14:19 1.5
@@ -10,13 +10,14 @@
#include "vcedit.h"
/*
- *********************************************************
- VorbisInfo Object methods
- *********************************************************
+*********************************************************
+VorbisInfo Object methods
+*********************************************************
*/
FDEF(ov_info_clear) "Clears a VorbisInfo object";
FDEF(vorbis_analysis_init) "Create a DSP object to start audio analysis.";
+FDEF(vorbis_info_blocksize) "I have NO idea what this does.";
static void py_ov_info_dealloc(PyObject *);
static PyObject *py_ov_info_getattr(PyObject *, char *name);
@@ -26,6 +27,8 @@
METH_VARARGS, py_ov_info_clear_doc},
{"analysis_init", py_vorbis_analysis_init,
METH_VARARGS, py_vorbis_analysis_init_doc},
+ {"blocksize", py_vorbis_info_blocksize,
+ METH_VARARGS, py_vorbis_info_blocksize_doc},
{NULL, NULL}
};
@@ -74,12 +77,14 @@
}
static char *py_info_new_kw[] = {"channels", "rate", "max_bitrate",
- "nominal_bitrate", "min_bitrate", NULL};
+ "nominal_bitrate", "min_bitrate", "quality",
+ NULL};
PyObject *
py_info_new(PyObject *self, PyObject *args, PyObject *kwdict)
{
long channels, rate, max_bitrate, nominal_bitrate, min_bitrate;
+ double quality = -1.0;
vorbis_info vi;
int res;
@@ -89,15 +94,19 @@
nominal_bitrate = 128000;
min_bitrate = -1;
if (!PyArg_ParseTupleAndKeywords(args, kwdict,
- "|lllll", py_info_new_kw,
- &channels, &rate, &max_bitrate,
- &nominal_bitrate, &min_bitrate))
+ "|llllld", py_info_new_kw,
+ &channels, &rate, &max_bitrate,
+ &nominal_bitrate, &min_bitrate, &quality))
return NULL;
vorbis_info_init(&vi);
- res = vorbis_encode_init(&vi, channels, rate,
- max_bitrate, nominal_bitrate,
- min_bitrate);
+ if (quality > 0.0) {
+ res = vorbis_encode_init_vbr(&vi, channels, rate, quality);
+ } else {
+ res = vorbis_encode_init(&vi, channels, rate,
+ max_bitrate, nominal_bitrate,
+ min_bitrate);
+ }
if (res != 0) {
vorbis_info_clear(&vi);
@@ -131,6 +140,19 @@
return PyInt_FromLong(vi->x)
static PyObject *
+py_vorbis_info_blocksize(PyObject *self, PyObject *args)
+{
+ vorbis_info *vi = PY_VINFO(self);
+ int res, zo;
+
+ if (!PyArg_ParseTuple(args, "l", &zo))
+ return NULL;
+
+ res = vorbis_info_blocksize(vi, zo);
+ return PyInt_FromLong(res);
+}
+
+static PyObject *
py_ov_info_getattr(PyObject *self, char *name)
{
PyObject *res;
@@ -182,9 +204,9 @@
}
/*
- *********************************************************
- VorbisComment Object methods
- *********************************************************
+*********************************************************
+VorbisComment Object methods
+*********************************************************
*/
@@ -659,32 +681,32 @@
#endif
if (!item)
- goto error;
+ goto error;
if (make_caps_key(key, keylen)) { /* overwrites key */
- Py_DECREF(item);
- goto error;
+ Py_DECREF(item);
+ goto error;
}
/* GetItem borrows a reference */
if ((curlist = PyDict_GetItemString(retdict, key))) {
- /* A list already exists for that key */
- if (PyList_Append(curlist, item) < 0) {
- Py_DECREF(item);
- goto error;
- }
+ /* A list already exists for that key */
+ if (PyList_Append(curlist, item) < 0) {
+ Py_DECREF(item);
+ goto error;
+ }
} else {
- /* Add a new list in that position */
- curlist = PyList_New(1);
- PyList_SET_ITEM(curlist, 0, item);
- Py_INCREF(item);
-
- /* this does not steal a reference */
- PyDict_SetItemString(retdict, key, curlist);
- Py_DECREF(curlist);
+ /* Add a new list in that position */
+ curlist = PyList_New(1);
+ PyList_SET_ITEM(curlist, 0, item);
+ Py_INCREF(item);
+
+ /* this does not steal a reference */
+ PyDict_SetItemString(retdict, key, curlist);
+ Py_DECREF(curlist);
}
Py_DECREF(item);
}
@@ -759,7 +781,7 @@
if (vcedit_write(state, out_file) < 0) {
char buff[256];
snprintf(buff, sizeof(buff), "Could not write comments to file: %s",
- vcedit_error(state));
+ vcedit_error(state));
PyErr_SetString(Py_VorbisError, buff);
vcedit_clear(state);
@@ -787,7 +809,6 @@
py_comment_append_to(PyObject *self, PyObject *args)
{
vorbis_comment *vc = PY_VCOMMENT(self);
- PyObject *ret;
const char *filename;
if (!PyArg_ParseTuple(args, "s", &filename))
return NULL;
<p><p>1.2 +22 -15 vorbis-python/test/enc.py
Index: enc.py
===================================================================
RCS file: /usr/local/cvsroot/vorbis-python/test/enc.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- enc.py 2001/02/06 03:20:48 1.1
+++ enc.py 2002/01/27 11:14:20 1.2
@@ -82,7 +82,7 @@
def main():
- vi = ogg.vorbis.VorbisInfo(nominal_bitrate=150000)
+ vi = ogg.vorbis.VorbisInfo(quality=0.1)
vc = ogg.vorbis.VorbisComment()
vd = vi.analysis_init()
@@ -106,15 +106,18 @@
packets = 0
samples = 0
- while 1:
+ eos = 0
+ while not eos:
#returns a tuple of strings representing arrays of floats
channel_data = inwav.read_stereo(2048)
- if not channel_data: break # didn't read any data
- apply(vd.write, channel_data)
+ if not channel_data:
+ print "No data"
+ vd.write(None) # didn't read any data
+ else:
+ apply(vd.write, channel_data)
- samples = samples + len(channel_data[0]) / 2
+ samples = samples + len(channel_data[0]) / 4
- eos = 0
vb = vd.blockout()
while vb:
packets = packets + 1
@@ -125,15 +128,19 @@
if packets % 10 == 0:
print "%0.2f" % (100.0 * samples / inwav.samples)
- op = vb.analysis()
- os.packetin(op)
-
- while not eos:
- og = os.pageout()
- if not og:
- break
- og.writeout(fout)
- eos = og.eos()
+ vb.analysis()
+ vb.addblock()
+
+ op = vd.bitrate_flushpacket()
+ while op:
+ os.packetin(op)
+ while not eos:
+ og = os.pageout()
+ if not og:
+ break
+ og.writeout(fout)
+ eos = og.eos()
+ op = vd.bitrate_flushpacket()
vb = vd.blockout()
<p><p>1.2 +33 -13 vorbis-python/test/enc2.py
Index: enc2.py
===================================================================
RCS file: /usr/local/cvsroot/vorbis-python/test/enc2.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- enc2.py 2001/02/06 03:20:48 1.1
+++ enc2.py 2002/01/27 11:14:20 1.2
@@ -1,25 +1,45 @@
#!/usr/bin/env python
-import ogg.vorbis, audiofile
-vd = ogg.vorbis.VorbisInfo(nominal_bitrate=150000).analysis_init()
+'''An example of encoding using the Python wave module'''
+import ogg.vorbis, wave
+
+fout = open('out.ogg', 'w')
+inwav = wave.open('in.wav','r')
+channels = inwav.getnchannels()
+vd = ogg.vorbis.VorbisInfo(channels = channels,
+ rate = inwav.getframerate(),
+ quality = 0.2).analysis_init()
os = ogg.OggStreamState(5)
map(os.packetin, vd.headerout())
-fout = open('out.ogg', 'w')
-inwav = audiofile.WavReader('in.wav')
og = os.flush()
while og:
og.writeout(fout)
og = os.flush()
-while 1:
- channel_data = inwav.read_channels(1024)
- if not channel_data[0]: break
- apply(vd.write, channel_data)
+nsamples = 1024
+
+eos = 0
+total = 0
+while not eos:
+ data = inwav.readframes(nsamples)
+ total = total + nsamples
+ if not data:
+ vd.write(None)
+ break
+ vd.write_wav(data)
+ #print 100.0 * total / inwav.getnframes()
vb = vd.blockout()
while vb:
- os.packetin(vb.analysis())
- while 1:
- og = os.pageout()
- if not og: break
- og.writeout(fout)
+ vb.analysis()
+ vb.addblock()
+
+ op = vd.bitrate_flushpacket()
+ while op:
+ os.packetin(op)
+ while not eos:
+ og = os.pageout()
+ if not og: break
+ og.writeout(fout)
+ eos = og.eos()
+ op = vd.bitrate_flushpacket()
vb = vd.blockout()
<p><p><p>--- >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