1#
2# Copyright (C) 2014 IRCAM
3#
4# author: Axel Roebel
5# date  : 6.5.2014
6#
7# All rights reserved.
8#
9# This file is part of pysndfile.
10#
11# pysndfile is free software: you can redistribute it and/or modify
12# it under the terms of the GNU Lesser General Public License as published by
13# the Free Software Foundation, either version 3 of the License, or
14# (at your option) any later version.
15#
16# pysndfile is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19# GNU Lesser General Public License for more details.
20#
21# You should have received a copy of the GNU Lesser General Public License
22# along with pysndfile.  If not, see <http://www.gnu.org/licenses/>.
23#
24
25# cython: embedsignature=True
26# cython: language_level=2
27
28import numpy as np
29import warnings
30import os
31
32cimport numpy as cnp
33from libcpp.string cimport string
34
35cdef extern from "Python.h":
36    ctypedef int Py_intptr_t
37
38_pysndfile_version=(1, 4, 3)
39def get_pysndfile_version():
40    """
41    return tuple describing the version of pysndfile
42    """
43    return _pysndfile_version
44
45
46_max_supported_string_length_tuple = (
47    ("wav", 2040),
48    ("wavex", 2040),
49    ("aiff", 8190),
50    ("caf", 16370),
51    )
52
53
54max_supported_string_length = dict(_max_supported_string_length_tuple)
55"""dict: the maximum length of each of the string types that can be read
56   from the various sound file formats in libsndfile is limited.
57   we ensure these limits during writing to be able to read the strings back
58"""
59
60cdef extern from "numpy/arrayobject.h":
61    void PyArray_ENABLEFLAGS(cnp.ndarray arr, int flags)
62
63
64cdef extern from "numpy/arrayobject.h":
65    ctypedef Py_intptr_t npy_intp
66    void *PyArray_DATA(cnp.ndarray arr)
67    int PyArray_NDIM(cnp.ndarray arr)
68    npy_intp* PyArray_DIMS(cnp.ndarray arr)
69
70IF UNAME_SYSNAME == "Windows":
71    cdef extern from *:
72        """
73        #define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1
74        """
75
76    from libc.stddef cimport wchar_t
77
78    cdef extern from "Windows.h":
79        ctypedef const wchar_t *LPCWSTR
80
81    cdef extern from "Python.h":
82        wchar_t* PyUnicode_AsWideCharString(object, Py_ssize_t *)
83        void PyMem_Free(void *p)
84
85cdef extern from "pysndfile.hh":
86    ctypedef struct SF_FORMAT_INFO:
87        int format
88        char *name
89        char *extension
90
91    ctypedef cnp.int64_t sf_count_t
92
93    struct SF_INFO:
94        sf_count_t frames
95        int channels
96        int samplerate
97        int format
98        int sections
99        int seekable
100
101    cdef struct SNDFILE :
102        pass
103
104    ctypedef struct SF_CUE_POINT:
105        int  indx
106        unsigned int position
107        int fcc_chunk
108        int chunk_start
109        int block_start
110        unsigned int sample_offset
111        char name[256]
112
113    ctypedef struct SF_CUES:
114        unsigned int cue_count
115        SF_CUE_POINT cue_points[100]
116
117    cdef int sf_command(SNDFILE *sndfile, int command, void *data, int datasize)
118    cdef int sf_format_check (const SF_INFO *info)
119    cdef char *sf_error_number(int errnum)
120
121    cdef int C_SF_FORMAT_WAV "SF_FORMAT_WAV"     # /* Microsoft WAV format (little endian default). */
122    cdef int C_SF_FORMAT_AIFF "SF_FORMAT_AIFF"   # /* Apple/SGI AIFF format (big endian). */
123    cdef int C_SF_FORMAT_AU "SF_FORMAT_AU"       # /* Sun/NeXT AU format (big endian). */
124    cdef int C_SF_FORMAT_RAW "SF_FORMAT_RAW"     # /* RAW PCM data. */
125    cdef int C_SF_FORMAT_PAF "SF_FORMAT_PAF"     # /* Ensoniq PARIS file format. */
126    cdef int C_SF_FORMAT_SVX "SF_FORMAT_SVX"     # /* Amiga IFF / SVX8 / SV16 format. */
127    cdef int C_SF_FORMAT_NIST "SF_FORMAT_NIST"   # /* Sphere NIST format. */
128    cdef int C_SF_FORMAT_VOC "SF_FORMAT_VOC"     # /* VOC files. */
129    cdef int C_SF_FORMAT_IRCAM "SF_FORMAT_IRCAM" # /* Berkeley/IRCAM/CARL */
130    cdef int C_SF_FORMAT_W64 "SF_FORMAT_W64"     # /* Sonic Foundry's 64 bit RIFF/WAV */
131    cdef int C_SF_FORMAT_MAT4 "SF_FORMAT_MAT4"   # /* Matlab (tm) V4.2 / GNU Octave 2.0 */
132    cdef int C_SF_FORMAT_MAT5 "SF_FORMAT_MAT5"   # /* Matlab (tm) V5.0 / GNU Octave 2.1 */
133    cdef int C_SF_FORMAT_PVF "SF_FORMAT_PVF"     # /* Portable Voice Format */
134    cdef int C_SF_FORMAT_XI "SF_FORMAT_XI"       # /* Fasttracker 2 Extended Instrument */
135    cdef int C_SF_FORMAT_HTK "SF_FORMAT_HTK"     # /* HMM Tool Kit format */
136    cdef int C_SF_FORMAT_SDS "SF_FORMAT_SDS"     # /* Midi Sample Dump Standard */
137    cdef int C_SF_FORMAT_AVR "SF_FORMAT_AVR"     # /* Audio Visual Research */
138    cdef int C_SF_FORMAT_WAVEX "SF_FORMAT_WAVEX" # /* MS WAVE with WAVEFORMATEX */
139    cdef int C_SF_FORMAT_SD2 "SF_FORMAT_SD2"     # /* Sound Designer 2 */
140    cdef int C_SF_FORMAT_FLAC "SF_FORMAT_FLAC"   # /* FLAC lossless file format */
141    cdef int C_SF_FORMAT_CAF "SF_FORMAT_CAF"     # /* Core Audio File format */
142    cdef int C_SF_FORMAT_WVE "SF_FORMAT_WVE"     # /* Psion WVE format */
143    cdef int C_SF_FORMAT_OGG "SF_FORMAT_OGG"     # /* Xiph OGG container */
144    cdef int C_SF_FORMAT_MPCK "SF_FORMAT_MPC2K"  # /* Akai MPC 2000 sampler */
145    cdef int C_SF_FORMAT_RF64 "SF_FORMAT_RF64"   # /* RF64 WAV file */
146
147    #/* Subtypes from here on. */
148    cdef int C_SF_FORMAT_PCM_S8 "SF_FORMAT_PCM_S8"    # /* Signed 8 bit data */
149    cdef int C_SF_FORMAT_PCM_16 "SF_FORMAT_PCM_16"    # /* Signed 16 bit data */
150    cdef int C_SF_FORMAT_PCM_24 "SF_FORMAT_PCM_24"    # /* Signed 24 bit data */
151    cdef int C_SF_FORMAT_PCM_32 "SF_FORMAT_PCM_32"    # /* Signed 32 bit data */
152
153    cdef int C_SF_FORMAT_PCM_U8 "SF_FORMAT_PCM_U8"    # /* Unsigned 8 bit data (WAV and RAW only) */
154
155    cdef int C_SF_FORMAT_FLOAT "SF_FORMAT_FLOAT"      # /* 32 bit float data */
156    cdef int C_SF_FORMAT_DOUBLE "SF_FORMAT_DOUBLE"    # /* 64 bit float data */
157
158    cdef int C_SF_FORMAT_ULAW "SF_FORMAT_ULAW"            # /* U-Law encoded. */
159    cdef int C_SF_FORMAT_ALAW "SF_FORMAT_ALAW"            # /* A-Law encoded. */
160    cdef int C_SF_FORMAT_IMA_ADPCM "SF_FORMAT_IMA_ADPCM"  # /* IMA ADPCM. */
161    cdef int C_SF_FORMAT_MS_ADPCM "SF_FORMAT_MS_ADPCM"    # /* Microsoft ADPCM. */
162
163    cdef int C_SF_FORMAT_GSM610 "SF_FORMAT_GSM610"    # /* GSM 6.10 encoding. */
164    cdef int C_SF_FORMAT_VOX_ADPCM "SF_FORMAT_VOX_ADPCM"  # /* OKI / Dialogix ADPCM */
165    cdef int C_SF_FORMAT_G721_32 "SF_FORMAT_G721_32"   # /* 32kbs G721 ADPCM encoding. */
166    cdef int C_SF_FORMAT_G723_24 "SF_FORMAT_G723_24"   # /* 24kbs G723 ADPCM encoding. */
167    cdef int C_SF_FORMAT_G723_40 "SF_FORMAT_G723_40"   # /* 40kbs G723 ADPCM encoding. */
168
169    cdef int C_SF_FORMAT_DWVW_12 "SF_FORMAT_DWVW_12"   # /* 12 bit Delta Width Variable Word encoding. */
170    cdef int C_SF_FORMAT_DWVW_16 "SF_FORMAT_DWVW_16"   # /* 16 bit Delta Width Variable Word encoding. */
171    cdef int C_SF_FORMAT_DWVW_24 "SF_FORMAT_DWVW_24"   # /* 24 bit Delta Width Variable Word encoding. */
172    cdef int C_SF_FORMAT_DWVW_N "SF_FORMAT_DWVW_N"    # /* N bit Delta Width Variable Word encoding. */
173
174    cdef int C_SF_FORMAT_DPCM_8 "SF_FORMAT_DPCM_8"    # /* 8 bit differential PCM (XI only) */
175    cdef int C_SF_FORMAT_DPCM_16 "SF_FORMAT_DPCM_16"   # /* 16 bit differential PCM (XI only) */
176
177    #    /* Endian-ness options. */
178    cdef int C_SF_ENDIAN_FILE "SF_ENDIAN_FILE"   # /* Default file endian-ness. */
179    cdef int C_SF_ENDIAN_LITTLE "SF_ENDIAN_LITTLE"  # /* Force little endian-ness. */
180    cdef int C_SF_ENDIAN_BIG "SF_ENDIAN_BIG"   # /* Force big endian-ness. */
181    cdef int C_SF_ENDIAN_CPU "SF_ENDIAN_CPU"   # /* Force CPU endian-ness. */
182
183    cdef int C_SF_FORMAT_SUBMASK "SF_FORMAT_SUBMASK"
184    cdef int C_SF_FORMAT_TYPEMASK "SF_FORMAT_TYPEMASK"
185    cdef int C_SF_FORMAT_ENDMASK "SF_FORMAT_ENDMASK"
186
187    # commands
188    cdef int C_SFC_GET_LIB_VERSION "SFC_GET_LIB_VERSION"
189    cdef int C_SFC_GET_LOG_INFO "SFC_GET_LOG_INFO"
190
191    cdef int C_SFC_GET_NORM_DOUBLE "SFC_GET_NORM_DOUBLE"
192    cdef int C_SFC_GET_NORM_FLOAT "SFC_GET_NORM_FLOAT"
193    cdef int C_SFC_SET_NORM_DOUBLE "SFC_SET_NORM_DOUBLE"
194    cdef int C_SFC_SET_NORM_FLOAT "SFC_SET_NORM_FLOAT"
195    cdef int C_SFC_SET_SCALE_FLOAT_INT_READ "SFC_SET_SCALE_FLOAT_INT_READ"
196    cdef int C_SFC_SET_SCALE_INT_FLOAT_WRITE "SFC_SET_SCALE_INT_FLOAT_WRITE"
197
198    cdef int C_SFC_GET_SIMPLE_FORMAT_COUNT "SFC_GET_SIMPLE_FORMAT_COUNT"
199    cdef int C_SFC_GET_SIMPLE_FORMAT "SFC_GET_SIMPLE_FORMAT"
200
201    cdef int C_SFC_GET_FORMAT_INFO "SFC_GET_FORMAT_INFO"
202
203    cdef int C_SFC_GET_FORMAT_MAJOR_COUNT "SFC_GET_FORMAT_MAJOR_COUNT"
204    cdef int C_SFC_GET_FORMAT_MAJOR "SFC_GET_FORMAT_MAJOR"
205    cdef int C_SFC_GET_FORMAT_SUBTYPE_COUNT "SFC_GET_FORMAT_SUBTYPE_COUNT"
206    cdef int C_SFC_GET_FORMAT_SUBTYPE "SFC_GET_FORMAT_SUBTYPE"
207
208    cdef int C_SFC_CALC_SIGNAL_MAX "SFC_CALC_SIGNAL_MAX"
209    cdef int C_SFC_CALC_NORM_SIGNAL_MAX "SFC_CALC_NORM_SIGNAL_MAX"
210    cdef int C_SFC_CALC_MAX_ALL_CHANNELS "SFC_CALC_MAX_ALL_CHANNELS"
211    cdef int C_SFC_CALC_NORM_MAX_ALL_CHANNELS "SFC_CALC_NORM_MAX_ALL_CHANNELS"
212    cdef int C_SFC_GET_SIGNAL_MAX "SFC_GET_SIGNAL_MAX"
213    cdef int C_SFC_GET_MAX_ALL_CHANNELS "SFC_GET_MAX_ALL_CHANNELS"
214
215    cdef int C_SFC_SET_ADD_PEAK_CHUNK "SFC_SET_ADD_PEAK_CHUNK"
216
217    cdef int C_SFC_UPDATE_HEADER_NOW "SFC_UPDATE_HEADER_NOW"
218    cdef int C_SFC_SET_UPDATE_HEADER_AUTO "SFC_SET_UPDATE_HEADER_AUTO"
219
220    cdef int C_SFC_FILE_TRUNCATE "SFC_FILE_TRUNCATE"
221
222    cdef int C_SFC_SET_RAW_START_OFFSET "SFC_SET_RAW_START_OFFSET"
223
224    cdef int C_SFC_SET_DITHER_ON_WRITE "SFC_SET_DITHER_ON_WRITE"
225    cdef int C_SFC_SET_DITHER_ON_READ "SFC_SET_DITHER_ON_READ"
226
227    cdef int C_SFC_GET_DITHER_INFO_COUNT "SFC_GET_DITHER_INFO_COUNT"
228    cdef int C_SFC_GET_DITHER_INFO "SFC_GET_DITHER_INFO"
229
230    cdef int C_SFC_GET_EMBED_FILE_INFO "SFC_GET_EMBED_FILE_INFO"
231
232    cdef int C_SFC_SET_CLIPPING "SFC_SET_CLIPPING"
233    cdef int C_SFC_GET_CLIPPING "SFC_GET_CLIPPING"
234
235    cdef int C_SFC_GET_CUE_COUNT "SFC_GET_CUE_COUNT"
236    cdef int C_SFC_GET_CUE "SFC_GET_CUE"
237
238    cdef int C_SFC_GET_INSTRUMENT "SFC_GET_INSTRUMENT"
239    cdef int C_SFC_SET_INSTRUMENT "SFC_SET_INSTRUMENT"
240
241    cdef int C_SFC_GET_LOOP_INFO "SFC_GET_LOOP_INFO"
242
243    cdef int C_SFC_GET_BROADCAST_INFO "SFC_GET_BROADCAST_INFO"
244    cdef int C_SFC_SET_BROADCAST_INFO "SFC_SET_BROADCAST_INFO"
245
246    cdef int C_SFC_WAVEX_SET_AMBISONIC "SFC_WAVEX_SET_AMBISONIC"
247    cdef int C_SFC_WAVEX_GET_AMBISONIC "SFC_WAVEX_GET_AMBISONIC"
248
249    cdef int C_SFC_RF64_AUTO_DOWNGRADE "SFC_RF64_AUTO_DOWNGRADE"
250
251    cdef int C_SFC_SET_VBR_ENCODING_QUALITY "SFC_SET_VBR_ENCODING_QUALITY"
252    cdef int C_SFC_SET_COMPRESSION_LEVEL "SFC_SET_COMPRESSION_LEVEL"
253
254    cdef int C_SF_STR_TITLE "SF_STR_TITLE"
255    cdef int C_SF_STR_COPYRIGHT "SF_STR_COPYRIGHT"
256    cdef int C_SF_STR_SOFTWARE "SF_STR_SOFTWARE"
257    cdef int C_SF_STR_ARTIST "SF_STR_ARTIST"
258    cdef int C_SF_STR_COMMENT "SF_STR_COMMENT"
259    cdef int C_SF_STR_DATE "SF_STR_DATE"
260
261    # these are the only values retrieved from the header file. So we cannot
262    # try to write/get strings that are not supported by the library we use.
263    cdef int C_SF_STR_FIRST "SF_STR_FIRST"
264    cdef int C_SF_STR_LAST  "SF_STR_LAST"
265
266    cdef int C_SF_FALSE "SF_FALSE"
267    cdef int C_SF_TRUE "SF_TRUE"
268
269    #        /* Modes for opening files. */
270    cdef int C_SFM_READ "SFM_READ"
271    cdef int C_SFM_WRITE "SFM_WRITE"
272    cdef int C_SFM_RDWR "SFM_RDWR"
273
274    cdef int C_SEEK_SET "SEEK_SET"
275    cdef int C_SEEK_CUR "SEEK_CUR"
276    cdef int C_SEEK_END "SEEK_END"
277
278    cdef int C_SF_ERR_NO_ERROR "SF_ERR_NO_ERROR"
279    cdef int C_SF_ERR_UNRECOGNISED_FORMAT "SF_ERR_UNRECOGNISED_FORMAT"
280    cdef int C_SF_ERR_SYSTEM "SF_ERR_SYSTEM"
281    cdef int C_SF_ERR_MALFORMED_FILE "SF_ERR_MALFORMED_FILE"
282    cdef int C_SF_ERR_UNSUPPORTED_ENCODING "SF_ERR_UNSUPPORTED_ENCODING"
283
284    cdef int C_SF_COUNT_MAX "SF_COUNT_MAX"
285
286IF UNAME_SYSNAME == "Windows":
287    include "sndfile_win32.pxi"
288ELSE:
289    include "sndfile_linux.pxi"
290
291# these two come with more recent versions of libsndfile
292# to not break compilation they are defined outside sndfile.h
293cdef int C_SF_STR_ALBUM = 0x07
294cdef int C_SF_STR_LICENSE = 0x08
295cdef int C_SF_STR_TRACKNUMBER = 0x09
296cdef int C_SF_STR_GENRE = 0x10
297
298
299SF_MAX_CHANNELS  = 1024
300"""int: maximum number if channels supported by libsndfile 1.0.28.
301"""
302
303SF_FORMAT_SUBMASK  = C_SF_FORMAT_SUBMASK
304"""int: format submask to retrieve encoding from format integer.
305"""
306
307SF_FORMAT_TYPEMASK = C_SF_FORMAT_TYPEMASK
308"""int: format typemask to retrieve major file format from format integer.
309"""
310
311SF_FORMAT_ENDMASK  = C_SF_FORMAT_ENDMASK
312"""int: endienness mask to retrieve endienness from format integer.
313"""
314
315_encoding_id_tuple = (
316    ('pcms8' , C_SF_FORMAT_PCM_S8),
317    ('pcm16' , C_SF_FORMAT_PCM_16),
318    ('pcm24' , C_SF_FORMAT_PCM_24),
319    ('pcm32' , C_SF_FORMAT_PCM_32),
320    ('pcmu8' , C_SF_FORMAT_PCM_U8),
321
322    ('float32' , C_SF_FORMAT_FLOAT),
323    ('float64' , C_SF_FORMAT_DOUBLE),
324
325    ('ulaw'      , C_SF_FORMAT_ULAW),
326    ('alaw'      , C_SF_FORMAT_ALAW),
327    ('ima_adpcm' , C_SF_FORMAT_IMA_ADPCM),
328    ('ms_adpcm'  , C_SF_FORMAT_MS_ADPCM),
329
330    ('gsm610'    , C_SF_FORMAT_GSM610),
331    ('vox_adpcm' , C_SF_FORMAT_VOX_ADPCM),
332
333    ('g721_32'   , C_SF_FORMAT_G721_32),
334    ('g723_24'   , C_SF_FORMAT_G723_24),
335    ('g723_40'   , C_SF_FORMAT_G723_40),
336
337    ('dww12' , C_SF_FORMAT_DWVW_12),
338    ('dww16' , C_SF_FORMAT_DWVW_16),
339    ('dww24' , C_SF_FORMAT_DWVW_24),
340    ('dwwN'  , C_SF_FORMAT_DWVW_N),
341
342    ('dpcm8' , C_SF_FORMAT_DPCM_8),
343    ('dpcm16', C_SF_FORMAT_DPCM_16)
344    )
345
346encoding_name_to_id = dict(_encoding_id_tuple)
347"""dict: mapping of pysndfile's encoding names to libsndfile's encoding ids.
348"""
349encoding_id_to_name = dict([(id, enc) for enc, id in _encoding_id_tuple])
350"""dict: mapping of libsndfile's encoding ids to pysndfile's encoding names.
351"""
352
353_fileformat_id_tuple = (
354    ('wav' , C_SF_FORMAT_WAV),
355    ('aiff' , C_SF_FORMAT_AIFF),
356    ('au'   , C_SF_FORMAT_AU),
357    ('raw'  , C_SF_FORMAT_RAW),
358    ('paf'  , C_SF_FORMAT_PAF),
359    ('svx'  , C_SF_FORMAT_SVX),
360    ('nist' , C_SF_FORMAT_NIST),
361    ('voc'  , C_SF_FORMAT_VOC),
362    ('ircam', C_SF_FORMAT_IRCAM),
363    ('wav64', C_SF_FORMAT_W64),
364    ('mat4' , C_SF_FORMAT_MAT4),
365    ('mat5' , C_SF_FORMAT_MAT5),
366    ('pvf'  , C_SF_FORMAT_PVF),
367    ('xi'   , C_SF_FORMAT_XI),
368    ('htk'  , C_SF_FORMAT_HTK),
369    ('sds'  , C_SF_FORMAT_SDS),
370    ('avr'  , C_SF_FORMAT_AVR),
371    ('wavex', C_SF_FORMAT_WAVEX),
372    ('sd2'  , C_SF_FORMAT_SD2),
373    ('flac' , C_SF_FORMAT_FLAC),
374    ('caf'  , C_SF_FORMAT_CAF),
375    ('wve'  , C_SF_FORMAT_WVE),
376    ('ogg'  , C_SF_FORMAT_OGG),
377    ('mpck'  , C_SF_FORMAT_MPCK),
378    ('rf64'  , C_SF_FORMAT_RF64),
379    )
380
381
382#: mapping of pysndfile's major fileformat names to libsndfile's major fileformat ids.
383fileformat_name_to_id = dict (_fileformat_id_tuple)
384
385#: mapping of libsndfile's major fileformat ids to pysndfile's major fileformat names.
386fileformat_id_to_name = dict ([(id, format) for format, id in _fileformat_id_tuple])
387
388
389_endian_to_id_tuple = (
390    ('file'   , C_SF_ENDIAN_FILE),
391    ('little' , C_SF_ENDIAN_LITTLE),
392    ('big'    , C_SF_ENDIAN_BIG),
393    ('cpu'    , C_SF_ENDIAN_CPU)
394    )
395
396#: dict mapping of pysndfile's endian names to libsndfile's endian ids.
397endian_name_to_id = dict(_endian_to_id_tuple)
398#: dict mapping of libsndfile's endian ids to pysndfile's endian names.
399endian_id_to_name = dict([(id, endname) for endname, id in _endian_to_id_tuple])
400
401_commands_to_id_tuple = (
402    ("SFC_GET_LIB_VERSION" , C_SFC_GET_LIB_VERSION),
403    ("SFC_GET_LOG_INFO" ,     C_SFC_GET_LOG_INFO),
404
405    ("SFC_GET_NORM_DOUBLE" , C_SFC_GET_NORM_DOUBLE),
406    ("SFC_GET_NORM_FLOAT" , C_SFC_GET_NORM_FLOAT),
407    ("SFC_SET_NORM_DOUBLE" , C_SFC_SET_NORM_DOUBLE),
408    ("SFC_SET_NORM_FLOAT" , C_SFC_SET_NORM_FLOAT),
409    ("SFC_SET_SCALE_FLOAT_INT_READ" , C_SFC_SET_SCALE_FLOAT_INT_READ),
410    ("SFC_SET_SCALE_INT_FLOAT_WRITE" , C_SFC_SET_SCALE_INT_FLOAT_WRITE),
411
412    ("SFC_GET_SIMPLE_FORMAT_COUNT" , C_SFC_GET_SIMPLE_FORMAT_COUNT),
413    ("SFC_GET_SIMPLE_FORMAT" , C_SFC_GET_SIMPLE_FORMAT),
414
415    ("SFC_GET_FORMAT_INFO" , C_SFC_GET_FORMAT_INFO),
416
417    ("SFC_GET_FORMAT_MAJOR_COUNT" , C_SFC_GET_FORMAT_MAJOR_COUNT),
418    ("SFC_GET_FORMAT_MAJOR" , C_SFC_GET_FORMAT_MAJOR),
419    ("SFC_GET_FORMAT_SUBTYPE_COUNT" , C_SFC_GET_FORMAT_SUBTYPE_COUNT),
420    ("SFC_GET_FORMAT_SUBTYPE" , C_SFC_GET_FORMAT_SUBTYPE),
421
422    ("SFC_CALC_SIGNAL_MAX" , C_SFC_CALC_SIGNAL_MAX),
423    ("SFC_CALC_NORM_SIGNAL_MAX" , C_SFC_CALC_NORM_SIGNAL_MAX),
424    ("SFC_CALC_MAX_ALL_CHANNELS" , C_SFC_CALC_MAX_ALL_CHANNELS),
425    ("SFC_CALC_NORM_MAX_ALL_CHANNELS" , C_SFC_CALC_NORM_MAX_ALL_CHANNELS),
426    ("SFC_GET_SIGNAL_MAX" , C_SFC_GET_SIGNAL_MAX),
427    ("SFC_GET_MAX_ALL_CHANNELS" , C_SFC_GET_MAX_ALL_CHANNELS),
428
429    ("SFC_SET_ADD_PEAK_CHUNK" , C_SFC_SET_ADD_PEAK_CHUNK),
430
431    ("SFC_UPDATE_HEADER_NOW" , C_SFC_UPDATE_HEADER_NOW),
432    ("SFC_SET_UPDATE_HEADER_AUTO" , C_SFC_SET_UPDATE_HEADER_AUTO),
433
434    ("SFC_FILE_TRUNCATE" , C_SFC_FILE_TRUNCATE),
435
436    ("SFC_SET_RAW_START_OFFSET" , C_SFC_SET_RAW_START_OFFSET),
437
438    ("SFC_SET_DITHER_ON_WRITE" , C_SFC_SET_DITHER_ON_WRITE),
439    ("SFC_SET_DITHER_ON_READ" , C_SFC_SET_DITHER_ON_READ),
440
441    ("SFC_GET_DITHER_INFO_COUNT" , C_SFC_GET_DITHER_INFO_COUNT),
442    ("SFC_GET_DITHER_INFO" , C_SFC_GET_DITHER_INFO),
443
444    ("SFC_GET_EMBED_FILE_INFO" , C_SFC_GET_EMBED_FILE_INFO),
445
446    ("SFC_SET_CLIPPING" , C_SFC_SET_CLIPPING),
447    ("SFC_GET_CLIPPING" , C_SFC_GET_CLIPPING),
448
449    ("SFC_GET_INSTRUMENT" , C_SFC_GET_INSTRUMENT),
450    ("SFC_SET_INSTRUMENT" , C_SFC_SET_INSTRUMENT),
451
452    ("SFC_GET_LOOP_INFO", C_SFC_GET_LOOP_INFO),
453
454    ("SFC_GET_BROADCAST_INFO", C_SFC_GET_BROADCAST_INFO),
455    ("SFC_SET_BROADCAST_INFO", C_SFC_SET_BROADCAST_INFO),
456
457    ("SFC_WAVEX_SET_AMBISONIC", C_SFC_WAVEX_SET_AMBISONIC),
458    ("SFC_WAVEX_GET_AMBISONIC", C_SFC_WAVEX_GET_AMBISONIC),
459    ("SFC_RF64_AUTO_DOWNGRADE", C_SFC_RF64_AUTO_DOWNGRADE),
460
461    ("SFC_SET_VBR_ENCODING_QUALITY", C_SFC_SET_VBR_ENCODING_QUALITY),
462    ("SFC_SET_COMPRESSION_LEVEL", C_SFC_SET_COMPRESSION_LEVEL),
463    )
464
465
466#:dict mapping of pysndfile's commandtype names to libsndfile's commandtype ids.
467commands_name_to_id = dict(_commands_to_id_tuple)
468#: dict mapping of libsndfile's commandtype ids to pysndfile's commandtype names.
469commands_id_to_name = dict([(id, com) for com, id in _commands_to_id_tuple])
470
471# define these by hand so we can use here all string types known for the
472# most recent libsndfile version. STrings will be filtered according to SF_STR_LAST
473
474_stringtype_to_id_tuple = (
475    ("SF_STR_TITLE", C_SF_STR_TITLE),
476    ("SF_STR_COPYRIGHT", C_SF_STR_COPYRIGHT),
477    ("SF_STR_SOFTWARE", C_SF_STR_SOFTWARE),
478    ("SF_STR_ARTIST", C_SF_STR_ARTIST),
479    ("SF_STR_COMMENT", C_SF_STR_COMMENT),
480    ("SF_STR_DATE", C_SF_STR_DATE),
481    ("SF_STR_ALBUM", C_SF_STR_ALBUM),
482    ("SF_STR_LICENSE", C_SF_STR_LICENSE),
483    ("SF_STR_TRACKNUMBER", C_SF_STR_TRACKNUMBER),
484    ("SF_STR_GENRE", C_SF_STR_GENRE),
485    )
486
487#: dict mapping of pysndfile's stringtype nams to libsndfile's stringtype ids.
488stringtype_name_to_id = dict(_stringtype_to_id_tuple[:C_SF_STR_LAST+1])
489
490#: dict mapping of libsndfile's stringtype ids to pysndfile's stringtype names.
491stringtype_id_to_name = dict([(id, com) for com, id in _stringtype_to_id_tuple[:C_SF_STR_LAST+1]])
492
493
494def get_sndfile_version():
495    """
496    return a tuple of ints representing the version of libsndfile that is used
497    """
498    cdef int status
499    cdef char buffer[256]
500
501    st = sf_command(NULL, C_SFC_GET_LIB_VERSION, buffer, 256)
502    version = buffer.decode("UTF-8")
503
504    # Get major, minor and micro from version
505    # Template: libsndfile-X.X.XpreX with preX being optional
506    version = version.split('-')[1]
507    prerelease = 0
508    major, minor, micro = [i for i in version.split('.')]
509    try:
510        micro = int(micro)
511    except ValueError,e:
512        #print "micro is " + str(micro)
513        micro, prerelease = micro.split('pre')
514
515    return int(major), int(minor), int(micro), prerelease
516
517
518def get_sndfile_encodings(major):
519    """
520    Return lists of available encoding for the given sndfile format.
521
522    *Parameters*
523
524    :param major: (str) sndfile format for that the list of available encodings should
525             be returned. format should be specified as a string, using
526             one of the strings returned by :py:func:`get_sndfile_formats`
527    """
528
529    # make major an id
530    if major in fileformat_id_to_name:
531        pass
532    elif major in fileformat_name_to_id:
533        major = fileformat_name_to_id[major]
534    else:
535        raise ValueError("PySndfile::File format {0} not known by PySndfile".format(str(major)))
536
537    if major not in get_sndfile_formats_from_libsndfile():
538        raise ValueError("PySndfile::File format {0}:{1:x} not supported by libsndfile".format(fileformat_id_to_name[major], major))
539
540    enc = []
541    for i in _get_sub_formats_for_major(major):
542        # Handle the case where libsndfile supports an encoding we don't
543        if i not in encoding_id_to_name:
544            warnings.warn("Encoding {0:x} supported by libsndfile but not by PySndfile"
545                          .format(i & C_SF_FORMAT_SUBMASK))
546        else:
547            enc.append(encoding_id_to_name[i & C_SF_FORMAT_SUBMASK])
548    return enc
549
550cdef _get_sub_formats_for_major(int major):
551    """
552    Retrieve list of subtype formats or encodings given the major format specified as int.
553
554    internal function
555
556    :param major: (int) major format specified as integer, the mapping from format strings to integers
557                   can be retrieved from :py:data:`fileformat_name_to_id`
558
559    :return: list of sub formats or encodings in form of integers, these integers can be converted to strings
560                  by means of :py:data:`encoding_id_to_name`
561    """
562    cdef int nsub
563    cdef int i
564    cdef SF_FORMAT_INFO info
565    cdef SF_INFO sfinfo
566
567    sf_command (NULL, C_SFC_GET_FORMAT_SUBTYPE_COUNT, &nsub, sizeof(int))
568
569    subs = []
570    # create a valid sfinfo struct
571    sfinfo.channels   = 1
572    sfinfo.samplerate = 44100
573    for i in range(nsub):
574        info.format = i
575        sf_command (NULL, C_SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info))
576        sfinfo.format = (major & C_SF_FORMAT_TYPEMASK) | info.format
577        if sf_format_check(&sfinfo):
578            subs.append(info.format)
579
580    return subs
581
582cdef get_sndfile_formats_from_libsndfile():
583    """
584    retrieve list of major format ids
585
586    :return: list of strings representing all major sound formats that can be handled by the libsndfile
587             library that is used by pysndfile.
588    """
589    cdef int nmajor
590    cdef int i
591    cdef SF_FORMAT_INFO info
592
593    sf_command (NULL, C_SFC_GET_FORMAT_MAJOR_COUNT, &nmajor, sizeof(int))
594
595    majors = []
596    for i in xrange(nmajor):
597        info.format = i
598        sf_command (NULL, C_SFC_GET_FORMAT_MAJOR, &info, sizeof (info))
599        majors.append(info.format)
600
601    return majors
602
603def get_sf_log():
604    """
605    retrieve internal log from libsndfile, notably useful in case of errors.
606
607    :return: string representing the internal error log managed by libsndfile
608    """
609    cdef char buf[2048]
610    sf_command (NULL, C_SFC_GET_LOG_INFO, &buf, sizeof (buf))
611    return str(buf)
612
613def get_sndfile_formats():
614    """
615    Return lists of available file formats supported by libsndfile and pysndfile.
616
617    :return: list of strings representing all major sound formats that can be handled by the libsndfile
618             library and the pysndfile interface.
619    """
620    fmt = []
621    for i in get_sndfile_formats_from_libsndfile():
622        # Handle the case where libsndfile supports a format we don't
623        if not i in fileformat_id_to_name:
624            warnings.warn("Format {0:x} supported by libsndfile but not "
625                          "yet supported by PySndfile".format(i & C_SF_FORMAT_TYPEMASK))
626        else:
627            fmt.append(fileformat_id_to_name[i & C_SF_FORMAT_TYPEMASK])
628    return fmt
629
630cdef class PySndfile:
631    """\
632    PySndfile is a python class for reading/writing audio files.
633
634    PySndfile is proxy for the SndfileHandle class in sndfile.hh
635    Once an instance is created, it can be used to read and/or write
636    data from/to numpy arrays, query the audio file meta-data, etc...
637
638    :param filename: <string or int> name of the file to open (string), or file descriptor (integer)
639    :param mode: <string> 'r' for read, 'w' for write, or 'rw' for read and write.
640    :param format: <int> Required when opening a new file for writing, or to read raw audio files (without header).
641                   See function :py:meth:`construct_format`.
642    :param channels: <int> number of channels.
643    :param samplerate: <int> sampling rate.
644
645    :return: valid PySndfile instance. An IOError exception is thrown if any error is
646        encountered in libsndfile. A ValueError exception is raised if the arguments are invalid.
647
648    *Notes*
649
650      * the files will be opened with auto clipping set to True
651        see the member set_autoclipping for more information.
652      * the soundfile will be closed when the class is destroyed
653      * format, channels and samplerate need to be given only
654        in the write modes and for raw files.
655    """
656
657    cdef SndfileHandle *thisPtr
658    cdef int fd
659    cdef string filename
660    def __cinit__(self, filename, mode='r', int format=0,
661                    int channels=0, int samplerate=0, *args, **kwrds):
662        cdef int sfmode
663        cdef const char*cfilename
664        cdef int fh
665
666        IF UNAME_SYSNAME == "Windows":
667           cdef Py_ssize_t length
668           cdef wchar_t *my_wchars
669
670        # -1 will indicate that the file has been open from filename, not from
671        # file descriptor
672        self.fd = -1
673        self.thisPtr = NULL
674
675        if channels > SF_MAX_CHANNELS:
676            raise ValueError( "PySndfile:: max number of channels exceeded {} > {}!".format(channels, SF_MAX_CHANNELS))
677
678        # Check the mode is one of the expected values
679        if mode == 'r':
680            sfmode = C_SFM_READ
681        elif mode == 'w':
682            sfmode = C_SFM_WRITE
683            if format is 0:
684                raise ValueError( "PySndfile::opening for writing requires a format argument !")
685        elif mode == 'rw':
686            sfmode  = C_SFM_RDWR
687            if format is 0:
688                raise ValueError( "PySndfile::opening for writing requires a format argument !")
689        else:
690            raise ValueError("PySndfile::mode {0} not recognized".format(str(mode)))
691
692        self.fd = -1
693        if isinstance(filename, int):
694            fh = filename
695            self.thisPtr = new SndfileHandle(fh, 0, sfmode, format, channels, samplerate)
696            self.filename = b""
697            self.fd = filename
698        else:
699            filename = os.path.expanduser(filename)
700
701            IF UNAME_SYSNAME == "Windows":
702                # Need to get the wchars before filename is converted to utf-8
703                my_wchars = PyUnicode_AsWideCharString(filename, &length)
704
705            if isinstance(filename, unicode):
706                filename = bytes(filename, "UTF-8")
707            self.filename = filename
708
709            IF UNAME_SYSNAME == "Windows":
710                if length > 0:
711                    self.thisPtr = new SndfileHandle(my_wchars, sfmode, format, channels, samplerate)
712                    PyMem_Free(my_wchars)
713                else:
714                    raise RuntimeError("PySndfile::error while converting {0} into wchars".format(filename))
715            ELSE:
716                self.thisPtr = new SndfileHandle(self.filename.c_str(), sfmode, format, channels, samplerate)
717
718
719        if self.thisPtr == NULL or self.thisPtr.rawHandle() == NULL:
720            raise IOError("PySndfile::error while opening {0}\n\t->{1}".format(self.filename,
721                                                                                   self.thisPtr.strError()))
722
723        self.set_auto_clipping(True)
724
725    # supoort use as context manager
726    def __enter__(self):
727        return self
728
729    def __exit__(self, *args):
730        self.close()
731
732    def get_name(self):
733        """
734        :return: <str> filename that was used to open the underlying sndfile
735        """
736        return self.filename
737
738    def __dealloc__(self):
739        self.close()
740
741    def close(self):
742        """
743        Closes file and deallocates internal structures
744        """
745        if self.thisPtr != NULL and self.thisPtr:
746            del self.thisPtr
747            self.thisPtr = NULL
748
749    def command(self, command, arg=0) :
750        """
751        interface for passing commands via sf_command to underlying soundfile
752        using sf_command(this_sndfile, command_id, NULL, arg)
753
754        :param command: <string or int>
755              libsndfile command macro to be used. They can be specified either as string using the command macros name, or the command id.
756
757              Supported commands are:
758
759|                 SFC_SET_NORM_FLOAT
760|                 SFC_SET_NORM_DOUBLE
761|                 SFC_GET_NORM_FLOAT
762|                 SFC_GET_NORM_DOUBLE
763|                 SFC_SET_SCALE_FLOAT_INT_READ
764|                 SFC_SET_SCALE_INT_FLOAT_WRITE
765|                 SFC_SET_ADD_PEAK_CHUNK
766|                 SFC_UPDATE_HEADER_NOW
767|                 SFC_SET_UPDATE_HEADER_AUTO
768|                 SFC_SET_CLIPPING (see :py:func:`pysndfile.PySndfile.set_auto_clipping`)
769|                 SFC_GET_CLIPPING (see :py:func:`pysndfile.PySndfile.set_auto_clipping`)
770|                 SFC_WAVEX_GET_AMBISONIC
771|                 SFC_WAVEX_SET_AMBISONIC
772|                 SFC_RAW_NEEDS_ENDSWAP
773
774        :param arg: <int> additional argument of the command
775
776        :return: <int> 1 for success or True, 0 for failure or False
777        """
778        if isinstance(command, str) :
779            return self.thisPtr.command(commands_name_to_id[command], NULL, arg)
780        # so we suppose it is an int
781        return self.thisPtr.command(command, NULL, arg)
782
783
784    def set_auto_clipping( self, arg = True) :
785        """
786        enable auto clipping when reading/writing samples from/to sndfile.
787
788        auto clipping is enabled by default.
789        auto clipping is required by libsndfile to properly handle scaling between sndfiles with pcm encoding and float representation of the samples in numpy.
790        When auto clipping is set to on reading pcm data into a float vector and writing it back with libsndfile will reproduce
791        the original samples. If auto clipping is off, samples will be changed slightly as soon as the amplitude is close to the
792        sample range because libsndfile applies slightly different scaling factors during read and write.
793
794        :param arg: <bool> indicator of the desired clipping state
795
796        :return: <int> 1 for success, 0 for failure
797        """
798        if self.thisPtr == NULL or not self.thisPtr:
799            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
800        return self.thisPtr.command(C_SFC_SET_CLIPPING, NULL, arg);
801
802    def writeSync(self):
803        """
804        call the operating system's function to force the writing of all
805        file cache buffers to disk the file.
806
807        No effect if file is open as read
808        """
809        if self.thisPtr == NULL or not self.thisPtr:
810            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
811        self.thisPtr.writeSync()
812
813
814    def __str__( self):
815        if self.thisPtr == NULL or not self.thisPtr:
816            return "invalid sndfile"
817        repstr = ["----------------------------------------"]
818        if not self.fd == -1:
819            repstr += ["File        : %d (opened by file descriptor)" % self.fd]
820        else:
821            repstr += ["File        : %s" % self.filename.decode("UTF-8")]
822        repstr  += ["Channels    : %d" % self.thisPtr.channels()]
823        repstr  += ["Sample rate : %d" % self.thisPtr.samplerate()]
824        repstr  += ["Frames      : %d" % self.thisPtr.frames()]
825        repstr  += ["Raw Format  : %#010x" % self.thisPtr.format()]
826        repstr  += ["File format : %s" % fileformat_id_to_name[self.thisPtr.format()& C_SF_FORMAT_TYPEMASK]]
827        repstr  += ["Encoding    : %s" % encoding_id_to_name[self.thisPtr.format()& C_SF_FORMAT_SUBMASK]]
828        #repstr  += ["Endianness  : %s" % ]
829        #repstr  += "Sections    : %d\n" % self._sfinfo.sections
830        repstr  += ["Seekable    : %s\n" % self.thisPtr.seekable()]
831        #repstr  += "Duration    : %s\n" % self._generate_duration_str()
832        return "\n".join(repstr)
833
834
835    def read_frames(self, sf_count_t nframes=-1, dtype=np.float64, force_2d = False, fill_value=None, min_read=0):
836        """
837        Read the given number of frames and put the data into a numpy array of
838        the requested dtype.
839
840        :param nframes: number of frames to read (default = -1 -> read all).
841        :type nframes: int
842        :param dtype: data type of the returned array containing read data (see note).
843        :type dtype: numpy.dtype
844        :param force_2d: always return 2D arrays even if file is mono
845        :type force_2d: bool
846        :param fill_value: value to use for filling frames in case nframes is larger than the file
847        :type fill_value: any tye that can be assigned to an array containing dtype
848        :param min_read: when fill_value is not None and EOFError will be thrown when the number
849                 of read sample frames is equal to or lower than this value
850        :return: np.array<dtype> with sound data
851
852        *Notes*
853
854          * One column per channel.
855
856        """
857        if self.thisPtr == NULL or not self.thisPtr:
858            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
859
860        if nframes < 0 :
861            whence = C_SEEK_CUR | C_SFM_READ
862            pos = self.thisPtr.seek(0, whence)
863            nframes = self.thisPtr.frames() - pos
864        if dtype == np.float64:
865            y = self.read_frames_double(nframes, fill_value=fill_value, min_read=min_read)
866        elif dtype == np.float32:
867            y = self.read_frames_float(nframes, fill_value=fill_value, min_read=min_read)
868        elif dtype == np.int32:
869            y = self.read_frames_int(nframes, fill_value=fill_value, min_read=min_read)
870        elif dtype == np.int16:
871            y = self.read_frames_short(nframes, fill_value=fill_value, min_read=min_read)
872        else:
873            raise RuntimeError("Sorry, dtype %s not supported" % str(dtype))
874
875        if y.shape[1] == 1 and not force_2d:
876            y.shape = (y.shape[0],)
877        return y
878
879    cdef read_frames_double(self, sf_count_t nframes, fill_value=None, min_read=0):
880        cdef sf_count_t res
881        cdef cnp.ndarray[cnp.float64_t, ndim=2] ty = np.empty((nframes, self.thisPtr.channels()),
882                                                                dtype=np.float64, order='C')
883
884        res = self.thisPtr.readf(<double*> PyArray_DATA(ty), nframes)
885        if not res == nframes:
886            if fill_value is None:
887                raise RuntimeError("Asked %d frames, read %d" % (nframes, res))
888            elif res <= min_read:
889                raise EOFError()
890            else:
891                ty[res:,:] = fill_value
892        return ty
893
894    cdef read_frames_float(self, sf_count_t nframes, fill_value=None, min_read=0):
895        cdef sf_count_t res
896        # Use C order to cope with interleaving
897        cdef cnp.ndarray[cnp.float32_t, ndim=2] ty = np.empty((nframes, self.thisPtr.channels()),
898                                                                dtype=np.float32, order='C')
899
900        res = self.thisPtr.readf(<float*>PyArray_DATA(ty), nframes)
901        if not res == nframes:
902            if fill_value is None:
903                raise RuntimeError("Asked %d frames, read %d" % (nframes, res))
904            elif res <= min_read:
905                raise EOFError()
906            else:
907                ty[res:,:] = fill_value
908        return ty
909
910    cdef read_frames_int(self, sf_count_t nframes, fill_value=None, min_read=0):
911        cdef sf_count_t res
912        # Use C order to cope with interleaving
913        cdef cnp.ndarray[cnp.int32_t, ndim=2] ty = np.empty((nframes, self.thisPtr.channels()),
914                                                            dtype=np.int32, order='C')
915
916        res = self.thisPtr.readf(<int*>PyArray_DATA(ty), nframes)
917        if not res == nframes:
918            if fill_value is None:
919                raise RuntimeError("Asked %d frames, read %d" % (nframes, res))
920            elif res <= min_read:
921                raise EOFError()
922            else:
923                ty[res:,:] = fill_value
924        return ty
925
926    cdef read_frames_short(self, sf_count_t nframes, fill_value=None, min_read=0):
927        cdef sf_count_t res
928        # Use C order to cope with interleaving
929        cdef cnp.ndarray[cnp.int16_t, ndim=2] ty = np.empty((nframes, self.thisPtr.channels()),
930                                                            dtype=np.short, order='C')
931
932        res = self.thisPtr.readf(<short*>PyArray_DATA(ty), nframes)
933        if res < nframes:
934            if fill_value is None:
935                raise RuntimeError("Asked %d frames, read %d" % (nframes, res))
936            elif res <= min_read:
937                raise EOFError()
938            else:
939                ty[res:,:] = fill_value
940        return ty
941
942    def write_frames(self, cnp.ndarray input):
943        """
944        write 1 or 2 dimensional array into sndfile.
945
946        :param input: <numpy array>
947               containing data to write.
948
949        :return: int representing the number of frames that have been written
950
951        *Notes*
952          * One column per channel.
953          * updates the write pointer.
954          * if the input type is float, and the file encoding is an integer type,
955            you should make sure the input data are normalized normalized data
956            (that is in the range [-1..1] - which will corresponds to the maximum
957            range allowed by the integer bitwidth).
958        """
959        cdef int nc
960        cdef sf_count_t nframes
961
962        if self.thisPtr == NULL or not self.thisPtr:
963            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
964
965        # First, get the number of channels and frames from input
966        if PyArray_NDIM(input) == 2:
967            nc = PyArray_DIMS(input)[1]
968            nframes = input.size / nc
969        elif PyArray_NDIM(input) == 1:
970            nc = 1
971            input = input[:, None]
972            nframes = input.size
973        else:
974            raise ValueError("PySndfile::write_frames::error cannot handle arrays of {0:d} dimensions, please restrict to  2 dimensions".format(PyArray_NDIM(input)))
975
976        # Number of channels should be the one expected
977        if not nc == self.thisPtr.channels():
978            raise ValueError("Expected %d channels, got %d" %
979                             (self.thisPtr.channels(), nc))
980
981        input = np.require(input, requirements = 'C')
982
983        if input.dtype == np.float64:
984            if (self.thisPtr.format() & C_SF_FORMAT_SUBMASK) not in [C_SF_FORMAT_FLOAT, C_SF_FORMAT_DOUBLE]:
985                if (np.max(np.abs(input.flat)) > 1.) :
986                    warnings.warn("write_frames::warning::audio data has been clipped while writing to file {0}.".format(self.filename.decode("UTF-8")))
987            res = self.thisPtr.writef(<double*>PyArray_DATA(input), nframes)
988        elif input.dtype == np.float32:
989            if (self.thisPtr.format() & C_SF_FORMAT_SUBMASK) not in [C_SF_FORMAT_FLOAT, C_SF_FORMAT_DOUBLE]:
990                if (np.max(np.abs(input.flat)) > 1.) :
991                    warnings.warn("write_frames::warning::audio data has been clipped while writing to file {0}.".format(self.filename.decode("UTF-8")))
992            res = self.thisPtr.writef(<float*>PyArray_DATA(input), nframes)
993        elif input.dtype == np.int32:
994            res = self.thisPtr.writef(<int*>PyArray_DATA(input), nframes)
995        elif input.dtype == np.short:
996            res = self.thisPtr.writef(<short*>PyArray_DATA(input), nframes)
997        else:
998            raise RuntimeError("type of input {0} not understood".format(str(input.dtype)))
999
1000        if not(res == nframes):
1001            raise IOError("write_frames::error::wrote {0:d} frames, expected to write {1:d}".format(res, nframes))
1002
1003        return res
1004
1005    def format(self) :
1006        """
1007        :return: <int> raw format specification that was used to create the present PySndfile instance.
1008        """
1009        if self.thisPtr == NULL or not self.thisPtr:
1010            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1011        return self.thisPtr.format()
1012
1013    def major_format_str(self) :
1014        """
1015
1016        :return: short string representation of major format (e.g. aiff)
1017
1018        see :py:func:`pysndfile.get_sndfile_formats` for a complete lst of fileformats
1019
1020        """
1021        if self.thisPtr == NULL or not self.thisPtr:
1022            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1023        return fileformat_id_to_name[self.thisPtr.format() & C_SF_FORMAT_TYPEMASK]
1024
1025    def encoding_str(self) :
1026        """
1027        :return:  string representation of encoding (e.g. pcm16)
1028
1029        see :py:func:`pysndfile.get_sndfile_encodings` for a list of
1030        available encoding strings that are supported by a given sndfile format
1031        """
1032        if self.thisPtr == NULL or not self.thisPtr:
1033            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1034        return encoding_id_to_name[self.thisPtr.format() & C_SF_FORMAT_SUBMASK]
1035
1036    def channels(self) :
1037        """
1038        :return: <int> number of channels of sndfile
1039        """
1040        if self.thisPtr == NULL or not self.thisPtr:
1041            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1042        return self.thisPtr.channels()
1043
1044    def frames(self) :
1045        """
1046        :return: <int> number for frames (number of samples per channel)
1047        """
1048        if self.thisPtr == NULL or not self.thisPtr:
1049            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1050        return self.thisPtr.frames()
1051
1052    def samplerate(self) :
1053        """
1054        :return: <int> samplerate
1055        """
1056        if self.thisPtr == NULL or not self.thisPtr:
1057            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1058        return self.thisPtr.samplerate()
1059
1060    def seekable(self) :
1061        """
1062        :return: <bool> true for soundfiles that support seeking
1063        """
1064        if self.thisPtr == NULL or not self.thisPtr:
1065            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1066        return self.thisPtr.seekable()
1067
1068    def get_strings(self) :
1069        """
1070        get all stringtypes from the sound file.
1071
1072        see :py:data:`stringtype_name_to_id` for the list of strings that are supported
1073        by the libsndfile version you use.
1074
1075        """
1076        cdef const char* string_value
1077        if self.thisPtr == NULL or not self.thisPtr:
1078            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1079
1080        str_dict = {}
1081        for ii  in xrange(C_SF_STR_FIRST, C_SF_STR_LAST):
1082            string_value = self.thisPtr.getString(ii)
1083            if string_value != NULL:
1084                str_dict[stringtype_id_to_name[ii]] = string_value
1085
1086        return str_dict
1087
1088    def set_string(self, stringtype_name, string) :
1089        """
1090        set one of the stringtype to the string given as argument.
1091        If you try to write a stringtype that is not supported by the library
1092        a RuntimeError will be raised
1093        If you try to write a string with length exceeding the length that
1094        can be read by libsndfile version 1.0.28 a RuntimeError will be raised as well
1095        these limits are stored in the dict max_supported_string_length.
1096        """
1097        cdef int res = 0
1098
1099        if self.thisPtr == NULL or not self.thisPtr:
1100            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1101        if stringtype_name not in stringtype_name_to_id :
1102            raise RuntimeError("PySndfile::error::set_string called with an unsupported stringtype:{0}".format(stringtype_name))
1103
1104        my_format = self.major_format_str()
1105        if my_format in max_supported_string_length :
1106            if len(string)> max_supported_string_length[my_format]:
1107                raise RuntimeError("pysndfile::set_string::your string to be written into {} has length {} exceeding the length of strings ({}) supported for reading in libsndfile 1.0.28".format(stringtype_name, len(string), max_supported_string_length[my_format]))
1108        res = self.thisPtr.setString(stringtype_name_to_id[stringtype_name], string)
1109        if res :
1110            raise RuntimeError("PySndfile::error::setting string of type {0}\nerror messge is:{1}".format(stringtype_name, sf_error_number(res)))
1111
1112    def set_strings(self, sf_strings_dict) :
1113        """
1114        set all strings provided as key value pairs in sf_strings_dict.
1115        If you try to write a stringtype that is not  supported by the library
1116        a RuntimeError will be raised.
1117        If you try to write a string with length exceeding the length that
1118        can be read by libsndfile version 1.0.28 a RuntimeError will be raised as well
1119        these limits are stored in the dict max_supported_string_length.
1120        """
1121        for kk in sf_strings_dict:
1122            self.set_string(kk, sf_strings_dict[kk])
1123
1124    def get_cue_count(self):
1125        """
1126        get number of cue markers.
1127
1128
1129        """
1130        # get number of cue mrks that are present in the file
1131
1132        res = self.thisPtr.get_cue_count()
1133        return res
1134
1135    def get_cue_mrks(self) :
1136        """
1137        get all cue markers.
1138
1139        Gets list of tuple of positions and related names of embedded markers for aiff and wav files,
1140        due to a limited support of cue names in libsndfile cue names are not retrieved for wav files.
1141
1142        """
1143        # get number of cue mrks that are present in the file
1144        cdef SF_CUES sf_cues
1145
1146        res = self.thisPtr.command(C_SFC_GET_CUE, &sf_cues, sizeof(sf_cues))
1147        if res == 0:
1148            return []
1149
1150        mrks = []
1151        for ii in range(sf_cues.cue_count):
1152            mrks.append((sf_cues.cue_points[ii].sample_offset, sf_cues.cue_points[ii].name.decode("ASCII")))
1153
1154        return mrks
1155
1156
1157    def error(self) :
1158        """
1159        report error numbers related to the current sound file
1160        """
1161        if self.thisPtr == NULL or not self.thisPtr:
1162            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1163        return self.thisPtr.error()
1164    def strError(self) :
1165        """
1166        report error strings related  to the current sound file
1167        """
1168        if self.thisPtr == NULL or not self.thisPtr:
1169            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1170        return self.thisPtr.strError()
1171
1172    def seek(self, sf_count_t offset, int whence=C_SEEK_SET, mode='rw'):
1173        """
1174        Seek into audio file: similar to python seek function, taking only in
1175        account audio data.
1176
1177        :param offset: <int>
1178                the number of frames (eg two samples for stereo files) to move
1179                relatively to position set by whence.
1180        :param whence: <int>
1181                only 0 (beginning), 1 (current) and 2 (end of the file) are
1182                valid.
1183        :param mode:  <string>
1184                If set to 'rw', both read and write pointers are updated. If
1185                'r' is given, only read pointer is updated, if 'w', only the
1186                write one is (this may of course make sense only if you open
1187                the file in a certain mode).
1188
1189        :return: <int>  the number of frames from the beginning of the file
1190
1191        *Notes*
1192
1193           * Offset relative to audio data: meta-data are ignored.
1194
1195           * if an invalid seek is given (beyond or before the file), an IOError is
1196             raised; note that this is different from the seek method of a File object.
1197
1198        """
1199
1200        if self.thisPtr == NULL or not self.thisPtr:
1201            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1202
1203        cdef sf_count_t pos
1204        if mode == 'rw':
1205            # Update both read and write pointers
1206            pos = self.thisPtr.seek(offset, whence)
1207        elif mode == 'r':
1208            whence = whence | C_SFM_READ
1209            pos = self.thisPtr.seek(offset, whence)
1210        elif mode == 'w':
1211            whence = whence | C_SFM_WRITE
1212            pos = self.thisPtr.seek(offset, whence)
1213        else:
1214            raise ValueError("mode should be one of 'r', 'w' or 'rw' only")
1215
1216        if pos == -1:
1217            msg = "libsndfile error during seek:: {0}".format(self.thisPtr.strError())
1218            raise IOError(msg)
1219        return pos
1220
1221    def rewind(self, mode="rw") :
1222        """\
1223        rewind read/write/read and write position given by mode to start of file
1224        """
1225        cdef sf_count_t pos
1226        cdef int whence = C_SEEK_SET
1227
1228        if self.thisPtr == NULL or not self.thisPtr:
1229            raise RuntimeError("PySndfile::error::no valid soundfilehandle")
1230
1231        if mode == 'rw':
1232            # Update both read and write pointers
1233            pos = self.thisPtr.seek(0, whence)
1234        elif mode == 'r':
1235            whence = whence | C_SFM_READ
1236            pos = self.thisPtr.seek(0, whence)
1237        elif mode == 'w':
1238            whence = whence | C_SFM_WRITE
1239            pos = self.thisPtr.seek(0, whence)
1240        else:
1241            raise ValueError("mode should be one of 'r', 'w' or 'rw' only")
1242
1243        if pos == -1:
1244            msg = "libsndfile error while rewinding:: {0}".format(self.thisPtr.strError())
1245            raise IOError(msg)
1246        return pos
1247
1248cdef _construct_format(major, encoding) :
1249    """
1250    construct a format specification for libsndfile from major format string and encoding string
1251    """
1252    cdef int major_id = fileformat_name_to_id[major]
1253    cdef int enc_id   = encoding_name_to_id[encoding]
1254    return  major_id | enc_id
1255
1256def construct_format(major, encoding) :
1257    """
1258    construct a format specification for libsndfile from major format string and encoding string
1259    """
1260    return  _construct_format(major, encoding)
1261
1262