使用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 " );
        
return   - 1 ;
    }
    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)) {
        
return  NULL;
    }
    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) {
    
int  mp3_length;
    
char *  mp3_buffer;
    
int  mp3_bytes;
    
if  ( ! (self -> outfp  &&  self -> gfp)) {
        PyErr_SetString(PyExc_Exception, 
" encoder not open " );
        
return  NULL;
    }
    mp3_length 
=   7200 ;
    mp3_buffer 
=  ( char * )malloc(mp3_length);
    mp3_bytes 
=  lame_encode_flush(self -> gfp, mp3_buffer,  sizeof (mp3_buffer));
    
if  (mp3_bytes  >   0 ) {
        fwrite(mp3_buffer, 
1 , mp3_bytes, self -> outfp);        
    }
    free(mp3_buffer);
    lame_close(self
-> gfp);
    self
-> gfp  =  NULL;
    fclose(self
-> outfp);
    self
-> outfp  =  NULL;
    Py_RETURN_NONE;
}

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[]  =  {    
    {NULL, NULL, 
0 , NULL}
};

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对象。 

 

 

你可能感兴趣的:(c,python,buffer,扩展,语言,methods)