This file is indexed.

/usr/lib/python3/dist-packages/pyolib/expression.py is in python3-pyo 0.8.8-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
from __future__ import absolute_import

"""
Prefix expression evaluators.

API documentation
=================

* This API is in alpha stage and subject to future changes!

Builtin functions
-----------------

Arithmetic operators:

(+ x y) : returns the sum of two values.
(- x y) : substracts the second value to the first and returns the result.
(* x y) : returns the multiplication of two values.
(/ x y) : returns the quotient of x/y.
(^ x y) : returns x to the power y.
(% x y) : returns the floating-point remainder of x/y.
(neg x) : returns the negative of x.

Moving phase operators:

(++ x y) : increments its internal state by x and wrap around 0.0 and y.
(-- x y) : decrements its internal state by x and wrap around 0.0 and y.
(~ x y) : generates a periodic ramp from 0 to 1 with frequency x and phase y.

Conditional operators:

(< x y) : returns 1 if x is less than y, otherwise returns 0.
(<= x y) : returns 1 if x is less than or equal to y, otherwise returns 0.
(> x y) : returns 1 if x is greater than y, otherwise returns 0.
(>= x y) : returns 1 if x is greater than or equal to y, otherwise returns 0.
(== x y) : returns 1 if x is equal to y, otherwise returns 0.
(!= x y) : returns 1 if x is not equal to y, otherwise returns 0.
(if (cond) (then) (else)) : returns then for any non-zero value of cond, otherwise returns else.
(and x y) : returns 1 if both x and y are not 0, otherwise returns 0.
(or x y) : returns 1 if one of x or y are not 0, otherwise returns 0.

Trigonometric functions:

(sin x) : returns the sine of an angle of x radians.
(cos x) : returns the cosine of an angle of x radians.
(tan x) : returns the tangent of x radians.
(tanh x) : returns the hyperbolic tangent of x radians.
(atan x) : returns the principal value of the arc tangent of x, expressed in radians.
(atan2 x y) : returns the principal value of the arc tangent of y/x, expressed in radians.

Power and logarithmic functions:

(sqrt x) : returns the square root of x.
(log x) : returns the natural logarithm of x.
(log2 x) : returns the binary (base-2) logarithm of x.
(log10 x) : returns the common (base-10) logarithm of x.
(pow x y) : returns x to the power y.

Clipping functions:

(abs x) : returns the absolute value of x.
(floor x) : rounds x downward, returning the largest integral value that is not greater than x.
(ceil x) : rounds x upward, returning the smallest integral value that is not less than x.
(exp  x) : returns the constant e to the power x.
(round x) : returns the integral value that is nearest to x.
(min x y) : returns the smaller of its arguments: either x or y.
(max x y) : returns the larger of its arguments: either x or y.
(wrap x) : wraps x between 0 and 1.

Random fuctions:

(randf x y) : returns a pseudo-random floating-point number in the range between x and y.
(randi x y) : returns a pseudo-random integral number in the range between x and y.

Filter functions:

(sah x y) : samples and holds x value whenever y is smaller than its previous state.
(rpole x y) : real one-pole recursive filter. returns x + last_out * y.
(rzero x y) : real one-zero non-recursive filter. returns x - last_x * y.

Constants:

(const x) : returns x.
(pi) : returns an approximated value of pi.
(twopi) : returns a constant with value pi*2.
(e) : returns an approximated value of e.

Comments
--------

A comment starts with two slashs ( // ) and ends at the end of the line:

    // This is a comment!

Input and Output signals
------------------------

User has access to the last buffer size of input and output samples.

To use samples from past input, use $x[n] notation, where n is the position
from the current time. $x[0] is the current input, $x[-1] is the previous
one and $x[-buffersize] is the last available input sample.

To use samples from past output, use $y[n] notation, where n is the position
from the current time. $y[-1] is the previous output and $y[-buffersize] is
the last available output sample.

Here an example of a first-order IIR lowpass filter expression:

    // A first-order IIR lowpass filter
    + $x[0] (* (- $y[-1] $x[0]) 0.99)


Defining custom functions
-------------------------

The define keyword starts the definition of a custom function.

(define funcname (body))

funcname is the name used to call the function in the expression and
body is the sequence of functions to execute. Arguments of the function
are extracted directly from the body. They must be named $1, $2, $3, ..., $9.

Example of a sine wave function:

    (define osc (
        sin (* (twopi) (~ $1))
        )
    )
    // play a sine wave
    * (osc 440) 0.3


State variables
---------------

User can create state variable with the keyword "let". This is useful
to set an intermediate state to be used in multiple places in the
processing chain. The syntax is:

(let #var (body))

The variable name must begin with a "#".

    (let #sr 44100)
    (let #freq 1000)
    (let #coeff (
        ^ (e) (/ (* (* -2 (pi)) #freq) #sr)
        )
    )
    + $x[0] (* (- $y[-1] $x[0]) #coeff)

The variable is private to a function if created inside a custom function.

    (let #freq 250) // global #freq variable
    (define osc (
        (let #freq (* $1 $2)) // local #freq variable
        sin (* (twopi) (~ #freq))
        )
    )
    * (+ (osc 1 #freq) (osc 2 #freq)) 0.2

State variables can be used to do 1 sample feedback if used before created.
Undefined variables are initialized to 0.

    (define oscloop (
            (let #xsin
                (sin (+ (* (~ $1) (twopi)) (* #xsin $2))) // #xsin used before...
            ) // ... "let" statement finished!
            #xsin // oscloop function outputs #xsin variable
        )
    )
    * (oscloop 200 0.7) 0.3


User variables
--------------

User variables are created with the keyword "var".

(var #var (init))

The variable name must begin with a "#".

They are computed only at initialization, but can be changed from the python
script with method calls (varname is a string and value is a float):

obj.setVar(varname, value)

Library importation
-------------------

Custom functions can be defined in an external file and imported with the
"load" function:

(load path/to/the/file)

The content of the file will be inserted where the load function is called
and all functions defined inside the file will then be accessible. The path
can be absolute or relative to the current working directory.

Examples
--------

A first-order IIR lowpass filter:

    (var #sr 44100)
    (var #cutoff 1000)
    (let #coeff (exp (/ (* (* -2 (pi)) #cutoff) #sr)))
    + $x[0] (* (- $y[-1] $x[0]) #coeff)

A LFO'ed hyperbolic tangent distortion:

    // $1 = lfo frequency, $2 = lfo depth
    (define lfo (
            (+ (* (sin (* (twopi) (~ $1))) (- $2 1)) $2)
        )
    )
    tanh (* $x[0] (lfo .25 10))

A triangle waveform generator (use Sig(0) as input argument to bypass input):

    (var #freq 440)
    // $1 = oscillator frequency
    (define triangle (
            (let #ph (~ $1))
            (- (* (min #ph (- 1 #ph)) 4) 1)
        )
    )
    triangle #freq

"""

"""
Copyright 2015-16 Olivier Belanger

This file is part of pyo, a python module to help digital signal
processing script creation.

pyo is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

pyo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with pyo.  If not, see <http://www.gnu.org/licenses/>.
"""

import os, sys
from ._core import *
from ._maps import *
from ._widgets import createExprEditorWindow

if sys.version_info[0] < 3:
    def to_unicode(s):
        try:
            s = unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
        except:
            pass
        return s
else:
    def to_unicode(s):
        return s

class Expr(PyoObject):
    """
    Prefix audio expression evaluator.

    Expr implements a tiny functional programming language that can be
    used to write synthesis or signal processing algorithms.

    For documentation about the language, see the module's documentation.

    :Parent: :py:class:`PyoObject`

    :Args:

        input: PyoObject
            Input signal to process.
        expr: str, optional
            Expression to evaluate as a string.

    >>> s = Server().boot()
    >>> s.start()
    >>> proc = '''
    >>> (var #boost 1)
    >>> (tanh (* $x[0] #boost))
    >>> '''
    >>> sf = SfPlayer(SNDS_PATH + "/transparent.aif", loop=True)
    >>> ex = Expr(sf, proc, mul=0.4).out()
    >>> lfo = Sine(freq=1).range(1, 20)
    >>> def change():
    ...     ex.setVar("#boost", lfo.get())
    >>> pat = Pattern(change, 0.02).play()

    """
    def __init__(self, input, expr='', mul=1, add=0):
        pyoArgsAssert(self, "osOO", input, expr, mul, add)
        PyoObject.__init__(self, mul, add)
        self._editor = None
        self._input = input
        self._expr = expr
        expr = self._preproc(expr)
        self._in_fader = InputFader(input)
        in_fader, expr, mul, add, lmax = convertArgsToLists(self._in_fader, expr, mul, add)
        self._base_objs = [Expr_base(wrap(in_fader,i), to_unicode(wrap(expr,i)), wrap(mul,i), wrap(add,i)) for i in range(lmax)]
        self.play()

    def setInput(self, x, fadetime=0.05):
        """
        Replace the `input` attribute.

        :Args:

            x: PyoObject
                New signal to process.
            fadetime: float, optional
                Crossfade time between old and new input. Defaults to 0.05.

        """
        pyoArgsAssert(self, "oN", x, fadetime)
        self._input = x
        self._in_fader.setInput(x, fadetime)

    def setExpr(self, x):
        """
        Replace the `expr` attribute.

        :Args:

            x: string
                New expression to process.

        """
        pyoArgsAssert(self, "s", x)
        self._expr = x
        if self._editor is not None:
            self._editor.update(x)
        x = self._preproc(x)
        x = to_unicode(x)
        x, lmax = convertArgsToLists(x)
        [obj.setExpr(wrap(x,i)) for i, obj in enumerate(self._base_objs)]

    def printNodes(self):
        """
        Print the list of current nodes.

        """
        [obj.printNodes() for i, obj in enumerate(self._base_objs)]

    def setVar(self, varname, value):
        pyoArgsAssert(self, "sn", varname, value)
        varname, value, lmax = convertArgsToLists(varname, value)
        [obj.setVar(wrap(varname,j), wrap(value,j)) for i, obj in enumerate(self._base_objs) for j in range(lmax)]

    def _get_matching_bracket_pos(self, x, p1):
        count = 1
        p2 = p1 + 1
        while True:
            if x[p2] == "(":
                count += 1
            elif x[p2] == ")":
                count -= 1
            if count == 0:
                break
            p2 += 1
            if p2 == len(x):
                break
        if count != 0:
            return -1
        else:
            return p2

    def _replace(self, x, lst):
        lst.reverse()
        for key, body in lst:
            # find how many args waiting in function's body
            numargs = 0
            doll = body.find("$")
            while doll != -1:
                arg = int(body[doll+1])
                if arg > numargs:
                    numargs = arg
                doll = body.find("$", doll+1)
            occurences = 0
            pos = x.find(key)
            while pos != -1:
                if x[pos-1] in " \t\n()" and x[pos+len(key)] in " \t\n()":
                    # replace "#vars" with unique symbol
                    body2 = body.replace("-%s" % key, ".%s.%d" % (key, occurences))
                    occurences += 1
                    # find limits
                    is_inside_brackets = True
                    start = pos - 1
                    while x[start] != "(":
                        start -= 1
                        if start < 0:
                            start = 0
                            is_inside_brackets = False
                            break
                    if is_inside_brackets:
                        end = self._get_matching_bracket_pos(x, start)
                    else:
                        end = len(x)
                    # retrieve args
                    args = []
                    p1 = pos + len(key)
                    p2 = -1
                    for i in range(numargs):
                        while x[p1] in " \t\n":
                            p1 += 1
                        if x[p1] == "(":
                            p2 = self._get_matching_bracket_pos(x, p1)
                            if p2 == -1 or p2 >= end:
                                raise Exception("Mismatched brackets in function arguments.")
                            p2 += 1
                        else:
                            p2 = p1 + 1
                            if p2 < end:
                                while x[p2] not in " \t\n()":
                                    p2 += 1
                                    if p2 == end:
                                        break
                        if x[p1:p2] != ")":
                            args.append(x[p1:p2])
                        if p2 == end:
                            break
                        else:
                            p1 = p2
                    # discard extra args
                    if p2 != end and p2 != -1:
                        x = x[:p2] + x[end:]
                    # replace args
                    if args != []:
                        newbody = body2
                        for i in range(numargs):
                            if i < len(args):
                                arg = args[i]
                            else:
                                arg = '0.0'
                            newbody = newbody.replace("$%d" % (i+1), arg)
                        x = x[:pos] + newbody + x[p2:]
                    else:
                        x = x[:pos] + body2 + x[pos+len(key):]
                pos = x.find(key, pos+1)
        return x

    def _change_var_names(self, funcname, funcbody):
        d = {}
        letpos = funcbody.find("let ")
        while letpos != -1:
            pos = funcbody.find("#", letpos)
            if pos == -1:
                raise Exception("No #var defined inside a let function.")
            p1 = pos
            p2 = p1 + 1
            while funcbody[p2] not in " \t\n()":
                p2 += 1
            label = funcbody[p1:p2]
            d[label] = label + "-" + funcname
            letpos = funcbody.find("let ", letpos + 4)
        for label, newlabel in d.items():
            funcbody = funcbody.replace(label, newlabel)
        return funcbody

    def _preproc(self, x):
        # replace load functions with file body
        while "(load" in x:
            p1 = x.find("(load")
            p2 = self._get_matching_bracket_pos(x, p1)
            p2 += 1
            text = x[p1:p2]
            path = text.replace("(load", "").replace(")", "").strip()
            if os.path.isfile(path):
                with open(path, "r") as f:
                    text = f.read()
            x = x[:p1] + text + x[p2:]

        # remove comments
        while "//" in x:
            start = x.find("//")
            end = x.find("\n", start)
            x = x[:start] + x[end:]

        # expand defined functions
        _defined = []
        while "define" in x:
            start = x.find("(define")
            p1 = start + 7
            # get function name
            while x[p1] in " \t\n":
                p1 += 1
            p2 = p1+1
            while x[p2] not in " \t\n":
                p2 += 1
            funcname = x[p1:p2]
            # get function body
            p1 = p2 + 1
            while x[p1] != "(":
                p1 += 1
            p2 = self._get_matching_bracket_pos(x, p1)
            if p2 == -1:
                raise Exception("Mismatched brackets in function body.")
            p2 += 1
            funcbody = x[p1:p2]
            # get end of the definition
            while x[p2] in " \t\n":
                p2 += 1
            if x[p2] != ")":
                raise Exception("Missing ending bracket in function definition.")
            stop = p2
            # save in dictionary and remove definition from the string
            funcbody = self._change_var_names(funcname, funcbody)
            _defined.append([funcname, funcbody])
            x = x[:start] + x[stop+1:]
        # replace calls to function with their function body
        x = self._replace(x, _defined)
        x = x.strip()
        return x

    def editor(self, title="Expr Editor", wxnoserver=False):
        """
        Opens the text editor for this object.

        :Args:

            title: string, optional
                Title of the window. If none is provided, the name of the
                class is used.
            wxnoserver: boolean, optional
                With wxPython graphical toolkit, if True, tells the
                interpreter that there will be no server window.

        If `wxnoserver` is set to True, the interpreter will not wait for
        the server GUI before showing the controller window.

        """
        createExprEditorWindow(self, title, wxnoserver)

    @property
    def input(self):
        """PyoObject. Input signal to process."""
        return self._input
    @input.setter
    def input(self, x): self.setInput(x)

    @property
    def expr(self):
        """string. New expression to process."""
        return self._expr
    @expr.setter
    def expr(self, x): self.setExpr(x)