上一篇里的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/ />-->
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()
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扩展模块这一端,下面是完整的代码:
代码
<!--<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);
}
#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对象。