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

代码
<!--<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()
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扩展模块这一端,下面是完整的代码:

代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />-->#include<Python.h>
#include<lame.h>
typedefstruct{
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);
}
#include<lame.h>
typedefstruct{
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对象。
本文介绍了一种使用Python与C语言交互的新方法,通过创建一个Encoder对象,可以在Python端自由控制编码过程,无论输入文件为何种格式。
198

被折叠的 条评论
为什么被折叠?



