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


本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2010/05/23/1741860.html,如需转载请自行联系原作者

目录
相关文章
|
2月前
|
Unix 编译器 Shell
[oeasy]python0033_先有操作系统还是先有编程语言_c语言是怎么来的
本文回顾了计算机语言与操作系统的起源,探讨了早期 Unix 操作系统及其与 C 语言的相互促进发展。Unix 最初用汇编语言编写,运行在 PDP-7 上,后来 Thompson 和 Ritchie 开发了 C 语言及编译器,使 Unix 重写并成功编译。1974 年 Ritchie 发表论文,Unix 开始被学术界关注,并逐渐普及。伯克利分校也在此过程中发挥了重要作用,推动了 Unix 和 C 语言的广泛传播。
63 9
[oeasy]python0033_先有操作系统还是先有编程语言_c语言是怎么来的
|
9天前
|
缓存 监控 测试技术
Python中的装饰器:功能扩展与代码复用的利器###
本文深入探讨了Python中装饰器的概念、实现机制及其在实际开发中的应用价值。通过生动的实例和详尽的解释,文章展示了装饰器如何增强函数功能、提升代码可读性和维护性,并鼓励读者在项目中灵活运用这一强大的语言特性。 ###
|
2月前
|
Python
Python--turtle库科赫雪花的扩展
使用Python的turtle库创建科赫雪花,并加入随机阶数、尺寸、位置和颜色的功能,每次运行生成不同图像。
Python--turtle库科赫雪花的扩展
|
1月前
|
机器学习/深度学习 缓存 PyTorch
pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)
这篇文章是关于如何下载、安装和配置Miniconda,以及如何使用Miniconda创建和管理Python环境的详细指南。
390 0
pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)
|
1月前
|
机器学习/深度学习 物联网 数据处理
C语言用于物联网更合适还是python
选择 C 语言还是 Python 作为物联网(IoT)开发的语言取决于多个因素,包括项目的性质、资源限制、性能需求以及开发团队的技能水平。C 语言性能优越,适合资源受限的嵌入式设备,能直接访问硬件,广泛应用于底层开发,但开发复杂且容易出错。Python 则以其简单的语法和丰富的库支持快速开发和原型制作,适合跨平台应用和数据处理,但性能较低,资源占用较大。根据项目需求,性能和资源要求高时选择 C 语言,需快速开发和易于维护时选择 Python。
|
1月前
|
Python
Python扩展TimedRotatingFileHandler
【10月更文挑战第7天】 python log执行扩展压缩功能
50 0
|
1月前
|
编译器 C语言
初识C语言:扩展世界观,选择语句之行
初识C语言:扩展世界观,选择语句之行
|
2月前
|
存储 缓存 API
比较一下 Python、C、C 扩展、Cython 之间的差异
比较一下 Python、C、C 扩展、Cython 之间的差异
39 0
|
2月前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)
|
3月前
|
JSON C语言 数据格式
Python导出隐马尔科夫模型参数到JSON文件C语言读取
Python导出隐马尔科夫模型参数到JSON文件C语言读取
26 1