上一篇裡的LAME項目已經展示了python如何與C語言互動,但程式仍不夠理想,在python這一端僅僅是傳遞源檔案和目标檔案的路徑,再調用C子產品的encode方法來進行編碼,但問題在于你無法控制encode函數,比如你想編碼的源檔案如果不是原始資料,而是wav檔案或者其他格式呢?對于這個問題,有兩種方法可以選擇,一種模仿前面的C子產品,在你的Python代碼中讀取資料,并将資料塊逐個傳遞給encode函數,另一種方法是你傳進去一個對象,這個對象帶有一個read方法,這樣你就可以在C子產品裡直接調用它的read方法來讀取其資料。
聽起來好像第二種更加面向對象,但實際上第一種方法反而是更為合适的選擇,因為它更為靈活,下面我們就在上一篇的基礎上,利用第一種思路對其進行改造。在這種新方法中,我們需要多次調用C子產品的函數,類似于将其視為類的方法。可C語言是不支援類的,是以需要将狀态資訊存儲在某個地方。除此以外,我們需要将“類”暴露給外部的Python程式,使其能建立“類“的執行個體,并調用它的方法。在“類對象“的内部我們則将其寫資料的檔案資訊儲存在”對象“的狀态中。聽上去就是一種面向對象的方法,不是嗎?
首先,遵循"測試先行"的原則,先來看我們改造後的Python這一端,你可以每次讀取音頻源檔案的一個資料塊,将其轉遞給Encoder對象的encode方法,這樣無論你的源檔案是何種格式,你都可以在Encoder中進行自由的控制,示例代碼如下:
複制代碼
代碼
import clame
INBUFSIZE = 4096
if __name__ == '__main__':
encoder = clame.Encoder('test.mp3')
input = file('test.raw', 'rb')
data = input.read(INBUFSIZE)
while data != '':
encoder.encode(data)
data = input.read(INBUFSIZE)
input.close()
encoder.close()
再來看C擴充子產品這一端,下面是完整的代碼:
#include <Python.h>
#include <lame.h>
typedef struct {
PyObject_HEAD
FILE* outfp;
lame_global_flags* gfp;
}clame_EncoderObject;
static PyObject* Encoder_new(PyTypeObject* type, PyObject* args, PyObject* kw) {
clame_EncoderObject* self = (clame_EncoderObject* )type->tp_alloc(type, 0);
self->outfp = NULL;
self->gfp = NULL;
return (PyObject*)self;
}
static void Encoder_dealloc(clame_EncoderObject* self) {
if (self->gfp) {
lame_close(self->gfp);
}
if (self->outfp) {
fclose(self->outfp);
self->ob_type->tp_free(self);
static int Encoder_init(clame_EncoderObject* self, PyObject* args, PyObject* kw) {
char* outPath;
if (!PyArg_ParseTuple(args, "s", &outPath)) {
return -1;
if (self->outfp || self->gfp) {
PyErr_SetString(PyExc_Exception, "__init__ already called");
self->outfp = fopen(outPath, "wb");
self->gfp = lame_init();
lame_init_params(self->gfp);
return 0;
static PyObject* Encoder_encode(clame_EncoderObject* self, PyObject* args) {
char* in_buffer;
int in_length;
int mp3_length;
char* mp3_buffer;
int mp3_bytes;
if (!(self->outfp || self->gfp)) {
PyErr_SetString(PyExc_Exception, "encoder not open");
return NULL;
if (!PyArg_ParseTuple(args, "s#", &in_buffer, &in_length)) {
in_length /= 2;
mp3_length = (int)(1.25 * in_length) + 7200;
mp3_buffer = (char*)malloc(mp3_length);
if (in_length > 0) {
mp3_bytes = lame_encode_buffer_interleaved(self->gfp, (short*)in_buffer, in_length/2, mp3_buffer, mp3_length);
if (mp3_bytes > 0) {
fwrite(mp3_buffer, 1, mp3_bytes, self->outfp);
}
free(mp3_buffer);
Py_RETURN_NONE;
static PyObject* Encoder_close(clame_EncoderObject* self) {
if (!(self->outfp && self->gfp)) {
mp3_length = 7200;
mp3_bytes = lame_encode_flush(self->gfp, mp3_buffer, sizeof(mp3_buffer));
if (mp3_bytes > 0) {
fwrite(mp3_buffer, 1, mp3_bytes, self->outfp);
lame_close(self->gfp);
fclose(self->outfp);
static PyMethodDef Encoder_methods[] = {
{"encode", (PyCFunction)Encoder_encode, METH_VARARGS, "encodes and writes data to the output file."},
{"close", (PyCFunction)Encoder_close, METH_NOARGS, "close the output file."},
{NULL, NULL, 0, NULL}
};
static PyTypeObject clame_EncoderType = {
PyObject_HEAD_INIT(NULL)
0, // ob_size
"clame.Encoder", // tp_name
sizeof(clame_EncoderObject), // tp_basicsize
0, // tp_itemsize
(destructor)Encoder_dealloc, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_compare
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
"My first encoder object.", // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
Encoder_methods, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
(initproc)Encoder_init, // tp_init
0, // tp_alloc
Encoder_new, // tp_new
0, // tp_free
static PyMethodDef clame_methods[] = {
PyMODINIT_FUNC initclame() {
PyObject* m;
if (PyType_Ready(&clame_EncoderType) < 0) {
return;
m = Py_InitModule3("clame", clame_methods, "My second lame module.");
Py_INCREF(&clame_EncoderType);
PyModule_AddObject(m, "Encoder", (PyObject*) &clame_EncoderType);
編譯過程:
gcc -shared -I /usr/include/python2.6 -I /usr/local/include/lame clame.c -lmp3lame -o clame.so
首先定義了clame_EncoderObject結構體,這個結構體就是用來存儲狀态資訊的,字段outfp用來存儲輸出檔案,gfp則儲存lame的狀态,可以用來檢查是否已經是重複調用已經調用過的函數了。
為了建立這個結構體的一個新執行個體,我們需要定義Encoder_new函數,你可以把這個函數視為Python裡的__new__方法,當Python解釋器需要建立你定義的類型的新執行個體時就會去調用這個方法。在這個方法裡沒作什麼操作,僅僅是做初始化工作,把outfp和gfp都設定為NULL,此外,與Encoder_new函數對應,還需要定義Encoder_dealloc方法來對執行個體進行析構,你可以把這個函數視為Python的__del__方法,clame_EncoderType結構體則是真正定義了我們的Encoder對象,它的各個字段指定了_new,_close,_encode,_dealloc等方法。在initclame方法中,PyModuleObject則實際指定了在Python程式中使用的Encoder對象。
本文轉自Phinecos(洞庭散人)部落格園部落格,原文連結:http://www.cnblogs.com/phinecos/archive/2010/05/23/1741860.html,如需轉載請自行聯系原作者