天天看點

使用C語言擴充Python(四)

上一篇裡的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,如需轉載請自行聯系原作者