使用C语言扩展Python(四)

上一篇里的LAME项目已经展示了python如何与C语言交互,但程序仍不够理想,在python这一端仅仅是传递源文件和目标文件的路径,再调用C模块的encode方法来进行编码,但问题在于你无法控制encode函数,比如你想编码的源文件如果不是原始数据,而是wav文件或者其他格式呢?对于这个问题,有两种方法可以选择,一种模仿前面的C模块,在你的Python代码中读取数据,并将数据块逐个传递给encode函数,另一种方法是你传进去一个对象,这个对象带有一个read方法,这样你就可以在C模块里直接调用它的read方法来读取其数据。
听起来好像第二种更加面向对象,但实际上第一种方法反而是更为合适的选择,因为它更为灵活,下面我们就在上一篇的基础上,利用第一种思路对其进行改造。在这种新方法中,我们需要多次调用C模块的函数,类似于将其视为类的方法。可C语言是不支持类的,因此需要将状态信息存储在某个地方。除此以外,我们需要将“类”暴露给外部的Python程序,使其能创建“类“的实例,并调用它的方法。在“类对象“的内部我们则将其写数据的文件信息储存在”对象“的状态中。听上去就是一种面向对象的方法,不是吗?
首先,遵循"测试先行"的原则,先来看我们改造后的Python这一端,你可以每次读取音频源文件的一个数据块,将其转递给Encoder对象的encode方法,这样无论你的源文件是何种格式,你都可以在Encoder中进行自由的控制,示例代码如下:
ExpandedBlockStart.gif 代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />--> 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扩展模块这一端,下面是完整的代码:

ExpandedBlockStart.gif 代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />--> #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__alreadycalled " );
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,
" encodernotopen " );
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,
" encodernotopen " );
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 PyMethodDefEncoder_methods[] = {
{
" encode " ,(PyCFunction)Encoder_encode,METH_VARARGS, " encodesandwritesdatatotheoutputfile. " },
{
" close " ,(PyCFunction)Encoder_close,METH_NOARGS, " closetheoutputfile. " },
{NULL,NULL,
0 ,NULL}
};

static PyTypeObjectclame_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
" Myfirstencoderobject. " , // 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 PyMethodDefclame_methods[] = {
{NULL,NULL,
0 ,NULL}
};

PyMODINIT_FUNCinitclame(){
PyObject
* m;
if (PyType_Ready( & clame_EncoderType) < 0 ){
return ;

m
= Py_InitModule3( " clame " ,clame_methods, " Mysecondlamemodule. " );
Py_INCREF(
& clame_EncoderType);
PyModule_AddObject(m,
" Encoder " ,(PyObject * ) & clame_EncoderType);
}

编译过程:

<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />--> gcc-shared-I / usr / include / python2 . 6 -I / usr / local / include / lameclame . c-lmp3lame-oclame . 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对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值