使用C语言扩展Python(四)

本文介绍了一种使用Python与C语言交互的新方法,通过创建一个Encoder对象,可以在Python端自由控制编码过程,无论输入文件为何种格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇里的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/ />-->importclame

INBUFSIZE
=4096

if__name__=='__main__':
encoder
=clame.Encoder('test.mp3')
input
=file('test.raw','rb')
data
=input.read(INBUFSIZE)

whiledata!='':
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;

staticPyObject*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;
}

staticvoidEncoder_dealloc(clame_EncoderObject*self){
if(self->gfp){
lame_close(self
->gfp);
}
if(self->outfp){
fclose(self
->outfp);
}
self
->ob_type->tp_free(self);
}

staticintEncoder_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);
return0;
}

staticPyObject*Encoder_encode(clame_EncoderObject*self,PyObject*args){
char*in_buffer;
intin_length;
intmp3_length;
char*mp3_buffer;
intmp3_bytes;
if(!(self->outfp||self->gfp)){
PyErr_SetString(PyExc_Exception,
"encodernotopen");
returnNULL;
}
if(!PyArg_ParseTuple(args,"s#",&in_buffer,&in_length)){
returnNULL;
}
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;
}

staticPyObject*Encoder_close(clame_EncoderObject*self){
intmp3_length;
char*mp3_buffer;
intmp3_bytes;
if(!(self->outfp&&self->gfp)){
PyErr_SetString(PyExc_Exception,
"encodernotopen");
returnNULL;
}
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;
}

staticPyMethodDefEncoder_methods[]={
{
"encode",(PyCFunction)Encoder_encode,METH_VARARGS,"encodesandwritesdatatotheoutputfile."},
{
"close",(PyCFunction)Encoder_close,METH_NOARGS,"closetheoutputfile."},
{NULL,NULL,
0,NULL}
};

staticPyTypeObjectclame_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
};

staticPyMethodDefclame_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、付费专栏及课程。

余额充值