fixed issue for php call and run command for audio editing
parent
dc49fa56bc
commit
082b928878
@ -0,0 +1,278 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="styles/jquery-ui.css" rel="stylesheet" type="text/css">
|
||||
<link href="styles/radioactive.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<!-- <link href="styles/smallscreen.css" rel="stylesheet" type="text/css"> -->
|
||||
<title>Radioactive Monstrosities</title>
|
||||
<link rel="shortcut icon" href="images/headphones_logo.png" />
|
||||
<style>
|
||||
h3 {font-weight: normal !important; float: left;}
|
||||
body {background:none !important;}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- table of voices -->
|
||||
<h1>Radio-active <div class="tooltip-wrap">Monstrosities<div class="tooltip-content-right" ><div>
|
||||
<?php
|
||||
include 'texts-radioactive/female_monstrosity.txt';
|
||||
?>
|
||||
</div></div></div></h1>
|
||||
<!-- <h3>CONTRIBUTORS' VOICES</h3> -->
|
||||
<table class="radioactive">
|
||||
<tr>
|
||||
<th><div class="tooltip-wrap">./collective_voice<div class="tooltip-content-right" >The speaker's voice is channeled through multiple voices and in this case through distorted mediated voices of the same person</div></div></th>
|
||||
<th><div class="tooltip-wrap">./echo_voice<div class="tooltip-content-right">A voice that is more distant from the speaker, sounding like being
|
||||
in an outer space inside the medium. This voice resembles sounds from an online call</div></div></th>
|
||||
<th><div class="tooltip-wrap">./lowpith_voice<div class="tooltip-content-right" >A voice that sounds more male because of lowering its pitch</div></div></th>
|
||||
<th><div class="tooltip-wrap">./lowpass_voice<div class="tooltip-content-right" >A voice that sounds like "shrill" if it is in high frequencies.
|
||||
This script doesn't allow high frequencies to pass through</div></div></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<?php
|
||||
$items=array();
|
||||
$handle=fopen('./uploads/5/index.jsons','r');
|
||||
if ($handle) {
|
||||
while (($line=fgets($handle)) !== false) {
|
||||
$item=json_decode($line,true);
|
||||
$items[]=$item;
|
||||
|
||||
}
|
||||
}
|
||||
$items=array_reverse($items);
|
||||
echo '<td>';
|
||||
foreach($items as $item) {
|
||||
$url=substr($item['file'],3);
|
||||
if (strpos($item['type'], 'collective') !== false){
|
||||
echo '<div class=file>';
|
||||
echo '<audio src='.$url.' controls></audio> ';
|
||||
echo $item['name'];
|
||||
echo '</div><br />';
|
||||
}
|
||||
}
|
||||
echo '</td>';
|
||||
echo '<td>';
|
||||
foreach($items as $item) {
|
||||
$url=substr($item['file'],3);
|
||||
exec("~/virtualenvs/radioactive/bin/python3 scripts/echo.py").$url;
|
||||
if (strpos($item['type'], 'echo') !== false){
|
||||
echo '<div class=file>';
|
||||
echo '<audio src='.$url.' controls></audio> ';
|
||||
echo $item['name'];
|
||||
echo '</div><br />';
|
||||
}
|
||||
}
|
||||
echo '</td>';
|
||||
echo '<td>';
|
||||
foreach($items as $item) {
|
||||
$url=substr($item['file'],3);
|
||||
if (strpos($item['type'], 'lowpitch') !== false){
|
||||
echo '<div class=file>';
|
||||
echo '<audio src='.$url.' controls></audio> ';
|
||||
echo $item['name'];
|
||||
echo '</div><br />';
|
||||
}
|
||||
}
|
||||
echo '</td>';
|
||||
echo '<td>';
|
||||
foreach($items as $item) {
|
||||
$url=substr($item['file'],3);
|
||||
if (strpos($item['type'], 'lowpass') !== false){
|
||||
echo '<div class=file>';
|
||||
echo '<audio src='.$url.' controls></audio> ';
|
||||
echo $item['name'];
|
||||
echo '</div><br />';
|
||||
}
|
||||
}
|
||||
echo '</td>';
|
||||
?>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<!-- recorder -->
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div align="center">
|
||||
<div class="recorder">
|
||||
<input type="button" class="start" value="Record" />
|
||||
<input type="button" class="stop" value="Stop" />
|
||||
<pre class="status"></pre>
|
||||
</div>
|
||||
|
||||
<div id="playerContainer"></div>
|
||||
<br />
|
||||
<div><button id="button_upload" onclick="upload()">Upload</button></div>
|
||||
<br />
|
||||
<div id="saved_msg"></div>
|
||||
|
||||
|
||||
<div id="dataUrlcontainer" hidden></div>
|
||||
<pre id="log" hidden></pre>
|
||||
</div>
|
||||
<div class="tooltip-wrap"><i class="fa fa-copyright fa-flip-horizontal"></i><div class="tooltip-content-right" ><div>
|
||||
[Angeliki Diakrousi, Radioactive Monstrosities, 2020. Rotterdam].
|
||||
Copyleft: This is a free work, you can copy, distribute, and modify it under the terms of the Free Art License
|
||||
http://artlibre.org/licence/lal/en/
|
||||
You can choose the type of lisence you want
|
||||
If you want your full name to appear in the contributors send me your name here: angeliki@genderchangers.org
|
||||
The copyrights of the voices belong to you. I ask you to use them in the performance radio-active monstrosities where I will narrate ...Only for this recording. You can choose to include your name or not.Your voice recordings will be used and credited accordingly
|
||||
</div></div></div>
|
||||
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
<div class="tooltip-wrap"><i class="fa fa-file"></i><div class="tooltip-content-right" ><div>
|
||||
<?php
|
||||
include 'texts-radioactive/about.txt';
|
||||
?>
|
||||
</div></div></div><br>
|
||||
<div class="tooltip-wrap"><i class="fa fa-gears"></i><div class="tooltip-content-right" ><div>
|
||||
<?php
|
||||
include 'texts-radioactive/instructions.txt';
|
||||
?>
|
||||
</div></div></div><br>
|
||||
|
||||
<a href="https://gitlab.com/nglk/radioactive-web">git</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- texts-radioactive and references -->
|
||||
<tr>
|
||||
<?php
|
||||
echo '<td>';
|
||||
include 'texts-radioactive/voice_collective.txt';
|
||||
echo '</td>';
|
||||
echo '<td>';
|
||||
include 'texts-radioactive/voice_echo.txt';
|
||||
echo '</td>';
|
||||
echo '<td>';
|
||||
include 'texts-radioactive/voice_lowpitch.txt';
|
||||
echo '</td>';
|
||||
echo '<td>';
|
||||
include 'texts-radioactive/voice_lowpass.txt';
|
||||
echo '</td>';
|
||||
?>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
|
||||
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/mp3recorder.js"></script>
|
||||
|
||||
<script src="js/draggable.js"></script>
|
||||
<script src="js/jquery-1.12.4.js"></script>
|
||||
<script src="js/jquery-ui.js"></script>
|
||||
<script src="js/jquery.ui.touch-punch.min.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
<!-- scripts for recorder -->
|
||||
<script>
|
||||
var audio_context;
|
||||
|
||||
function __log(e, data) {
|
||||
log.innerHTML += "\n" + e + " " + (data || '');
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
||||
try {
|
||||
// webkit shim
|
||||
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
navigator.getUserMedia = ( navigator.getUserMedia ||
|
||||
navigator.webkitGetUserMedia ||
|
||||
navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia);
|
||||
window.URL = window.URL || window.webkitURL;
|
||||
|
||||
var audio_context = new AudioContext;
|
||||
__log('Audio context set up.');
|
||||
__log('navigator.getUserMedia ' + (navigator.getUserMedia ? 'available.' : 'not present!'));
|
||||
} catch (e) {
|
||||
alert('No web audio support in this browser!');
|
||||
}
|
||||
|
||||
$('.recorder .start').on('click', function() {
|
||||
$this = $(this);
|
||||
$recorder = $this.parent();
|
||||
|
||||
navigator.getUserMedia({audio: true}, function(stream) {
|
||||
var recorderObject = new MP3Recorder(audio_context, stream, { statusContainer: $recorder.find('.status'), statusMethod: 'replace' });
|
||||
$recorder.data('recorderObject', recorderObject);
|
||||
|
||||
recorderObject.start();
|
||||
}, function(e) { });
|
||||
});
|
||||
|
||||
document.getElementById("button_upload").style.display='none';
|
||||
|
||||
$('.recorder .stop').on('click', function() {
|
||||
$this = $(this);
|
||||
$recorder = $this.parent();
|
||||
|
||||
recorderObject = $recorder.data('recorderObject');
|
||||
recorderObject.stop();
|
||||
|
||||
recorderObject.exportMP3(function(base64_data) {
|
||||
var url = 'data:audio/mp3;base64,' + base64_data;
|
||||
var au = document.createElement('audio');
|
||||
|
||||
document.getElementById("playerContainer").innerHTML = "";
|
||||
// console.log(url)
|
||||
document.getElementById("button_upload").style.display='block';
|
||||
|
||||
var duc = document.getElementById("dataUrlcontainer");
|
||||
duc.innerHTML = url;
|
||||
|
||||
au.controls = true;
|
||||
au.src = url;
|
||||
//$recorder.append(au);
|
||||
$('#playerContainer').append(au);
|
||||
|
||||
|
||||
recorderObject.logStatus('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
function upload(){
|
||||
var name = prompt('Enter a title or/and your name','Unnamed clip');
|
||||
var type = prompt('Enter type of distortion. Choose between collective,echo,lowpitch,lowpass','No Type');
|
||||
var license = prompt('Enter a license. Choose between collective,echo,lowpitch,lowpass','No Type');
|
||||
var dataURL = document.getElementById("dataUrlcontainer").innerHTML;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "scripts/uploadMp3_5.php",
|
||||
data: {
|
||||
base64: dataURL,
|
||||
name: name,
|
||||
type: type
|
||||
}
|
||||
}).done(function(o) {
|
||||
console.log('saved');
|
||||
document.getElementById("saved_msg").innerHTML = "Uploaded!! Refresh and see your voice message in the list below :<";
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,22 @@
|
||||
#!/home/lain/virtualenvs/radioactive/bin/python3
|
||||
import cgi,cgitb
|
||||
cgitb.enable()
|
||||
import pydub
|
||||
from pydub import AudioSegment
|
||||
from pysndfx import AudioEffectsChain
|
||||
import sys
|
||||
|
||||
# print (sys.argv[1])
|
||||
mp3_audio = AudioSegment.from_file("../../uploads/5/nainai_echo.mp3", format="mp3")
|
||||
# mp3_audio = AudioSegment.from_file('page_echo.mp3', format="mp3")
|
||||
mp3_audio.export("../../uploads/5/nainai_echo.wav", format="wav")
|
||||
|
||||
infile = '../../uploads/5/nainai_echo.wav'
|
||||
outfile = '../../uploads/5/nainai_echo_output.wav'
|
||||
|
||||
|
||||
AudioEffectsChain().reverb()(infile, outfile)
|
||||
|
||||
form = cgi.FieldStorage()
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
#! /home/lain/virtualenvs/radioactive/bin/python3
|
||||
# Import the package and create an audio effects chain function.
|
||||
import pydub
|
||||
from pydub import AudioSegment
|
||||
from pysndfx import AudioEffectsChain
|
||||
import sys
|
||||
|
||||
# print (sys.argv[1])
|
||||
mp3_audio = AudioSegment.from_file(sys.argv[1], format="mp3")
|
||||
# mp3_audio = AudioSegment.from_file('page_echo.mp3', format="mp3")
|
||||
mp3_audio.export("temp.wav", format="wav")
|
||||
|
||||
infile = 'temp.wav'
|
||||
|
||||
# infile = sys.argv[1]
|
||||
outfile = sys.argv[1]+'.wav'
|
||||
|
||||
|
||||
AudioEffectsChain().reverb()(infile, outfile)
|
@ -0,0 +1,4 @@
|
||||
# coding=utf-8
|
||||
from .dsp import AudioEffectsChain
|
||||
|
||||
__all__ = ['AudioEffectsChain']
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,543 @@
|
||||
# coding=utf-8
|
||||
"""A lightweight Python wrapper of SoX's effects."""
|
||||
import shlex
|
||||
from io import BufferedReader, BufferedWriter
|
||||
from subprocess import PIPE, Popen
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .sndfiles import (
|
||||
FileBufferInput,
|
||||
FileBufferOutput,
|
||||
FilePathInput,
|
||||
FilePathOutput,
|
||||
NumpyArrayInput,
|
||||
NumpyArrayOutput,
|
||||
logger,
|
||||
)
|
||||
|
||||
|
||||
def mutually_exclusive(*args):
|
||||
return sum(arg is not None for arg in args) < 2
|
||||
|
||||
|
||||
class AudioEffectsChain:
|
||||
def __init__(self):
|
||||
self.command = []
|
||||
|
||||
def equalizer(self, frequency, q=1.0, db=-3.0):
|
||||
"""equalizer takes three parameters: filter center frequency in Hz, "q"
|
||||
or band-width (default=1.0), and a signed number for gain or
|
||||
attenuation in dB.
|
||||
|
||||
Beware of clipping when using positive gain.
|
||||
"""
|
||||
self.command.append('equalizer')
|
||||
self.command.append(frequency)
|
||||
self.command.append(str(q) + 'q')
|
||||
self.command.append(db)
|
||||
return self
|
||||
|
||||
def bandpass(self, frequency, q=1.0):
|
||||
"""bandpass takes 2 parameters: filter center frequency in Hz and "q"
|
||||
or band-width (default=1.0).
|
||||
|
||||
It gradually removes frequencies outside the band specified.
|
||||
"""
|
||||
self.command.append('bandpass')
|
||||
self.command.append(frequency)
|
||||
self.command.append(str(q) + 'q')
|
||||
return self
|
||||
|
||||
def bandreject(self, frequency, q=1.0):
|
||||
"""bandreject takes 2 parameters: filter center frequency in Hz and "q"
|
||||
or band-width (default=1.0).
|
||||
|
||||
It gradually removes frequencies within the band specified.
|
||||
"""
|
||||
self.command.append('bandreject')
|
||||
self.command.append(frequency)
|
||||
self.command.append(str(q) + 'q')
|
||||
return self
|
||||
|
||||
def lowshelf(self, gain=-20.0, frequency=100, slope=0.5):
|
||||
"""lowshelf takes 3 parameters: a signed number for gain or attenuation
|
||||
in dB, filter frequency in Hz and slope (default=0.5, maximum=1.0).
|
||||
|
||||
Beware of Clipping when using positive gain.
|
||||
"""
|
||||
self.command.append('bass')
|
||||
self.command.append(gain)
|
||||
self.command.append(frequency)
|
||||
self.command.append(slope)
|
||||
return self
|
||||
|
||||
def highshelf(self, gain=-20.0, frequency=3000, slope=0.5):
|
||||
"""highshelf takes 3 parameters: a signed number for gain or
|
||||
attenuation in dB, filter frequency in Hz and slope (default=0.5).
|
||||
|
||||
Beware of clipping when using positive gain.
|
||||
"""
|
||||
self.command.append('treble')
|
||||
self.command.append(gain)
|
||||
self.command.append(frequency)
|
||||
self.command.append(slope)
|
||||
return self
|
||||
|
||||
def highpass(self, frequency, q=0.707):
|
||||
"""highpass takes 2 parameters: filter frequency in Hz below which
|
||||
frequencies will be attenuated and q (default=0.707).
|
||||
|
||||
Beware of clipping when using high q values.
|
||||
"""
|
||||
self.command.append('highpass')
|
||||
self.command.append(frequency)
|
||||
self.command.append(str(q) + 'q')
|
||||
return self
|
||||
|
||||
def lowpass(self, frequency, q=0.707):
|
||||
"""lowpass takes 2 parameters: filter frequency in Hz above which
|
||||
frequencies will be attenuated and q (default=0.707).
|
||||
|
||||
Beware of clipping when using high q values.
|
||||
"""
|
||||
self.command.append('lowpass')
|
||||
self.command.append(frequency)
|
||||
self.command.append(str(q) + 'q')
|
||||
return self
|
||||
|
||||
def limiter(self, gain=3.0):
|
||||
"""limiter takes one parameter: gain in dB.
|
||||
|
||||
Beware of adding too much gain, as it can cause audible
|
||||
distortion. See the compand effect for a more capable limiter.
|
||||
"""
|
||||
self.command.append('gain')
|
||||
self.command.append('-l')
|
||||
self.command.append(gain)
|
||||
return self
|
||||
|
||||
def normalize(self):
|
||||
"""normalize has no parameters.
|
||||
|
||||
It boosts level so that the loudest part of your file reaches
|
||||
maximum, without clipping.
|
||||
"""
|
||||
self.command.append('gain')
|
||||
self.command.append('-n')
|
||||
return self
|
||||
|
||||
def compand(self, attack=0.2, decay=1, soft_knee=2.0, threshold=-20, db_from=-20.0, db_to=-20.0):
|
||||
"""compand takes 6 parameters:
|
||||
|
||||
attack (seconds), decay (seconds), soft_knee (ex. 6 results
|
||||
in 6:1 compression ratio), threshold (a negative value
|
||||
in dB), the level below which the signal will NOT be companded
|
||||
(a negative value in dB), the level above which the signal will
|
||||
NOT be companded (a negative value in dB). This effect
|
||||
manipulates dynamic range of the input file.
|
||||
"""
|
||||
self.command.append('compand')
|
||||
self.command.append(str(attack) + ',' + str(decay))
|
||||
self.command.append(str(soft_knee) + ':' + str(threshold) + ',' + str(db_from) + ',' + str(db_to))
|
||||
return self
|
||||
|
||||
def sinc(self,
|
||||
high_pass_frequency=None,
|
||||
low_pass_frequency=None,
|
||||
left_t=None,
|
||||
left_n=None,
|
||||
right_t=None,
|
||||
right_n=None,
|
||||
attenuation=None,
|
||||
beta=None,
|
||||
phase=None,
|
||||
M=None,
|
||||
I=None,
|
||||
L=None):
|
||||
"""sinc takes 12 parameters:
|
||||
|
||||
high_pass_frequency in Hz,
|
||||
low_pass_frequency in Hz,
|
||||
left_t,
|
||||
left_n,
|
||||
right_t,
|
||||
right_n,
|
||||
attenuation in dB,
|
||||
beta,
|
||||
phase,
|
||||
M,
|
||||
I,
|
||||
L
|
||||
|
||||
This effect creates a steep bandpass or
|
||||
bandreject filter. You may specify as few as the first two
|
||||
parameters. Setting the high-pass parameter to a lower value
|
||||
than the low-pass creates a band-reject filter.
|
||||
"""
|
||||
self.command.append("sinc")
|
||||
if not mutually_exclusive(attenuation, beta):
|
||||
raise ValueError("Attenuation (-a) and beta (-b) are mutually exclusive arguments.")
|
||||
if attenuation is not None and beta is None:
|
||||
self.command.append('-a')
|
||||
self.command.append(str(attenuation))
|
||||
elif attenuation is None and beta is not None:
|
||||
self.command.append('-b')
|
||||
self.command.append(str(beta))
|
||||
|
||||
if not mutually_exclusive(phase, M, I, L):
|
||||
raise ValueError("Phase (-p), -M, L, and -I are mutually exclusive arguments.")
|
||||
if phase is not None:
|
||||
self.command.append('-p')
|
||||
self.command.append(str(phase))
|
||||
elif M is not None:
|
||||
self.command.append('-M')
|
||||
elif I is not None:
|
||||
self.command.append('-I')
|
||||
elif L is not None:
|
||||
self.command.append('-L')
|
||||
|
||||
if not mutually_exclusive(left_t, left_t):
|
||||
raise ValueError("Transition bands options (-t or -n) are mutually exclusive.")
|
||||
if left_t is not None:
|
||||
self.command.append('-t')
|
||||
self.command.append(str(left_t))
|
||||
if left_n is not None:
|
||||
self.command.append('-n')
|
||||
self.command.append(str(left_n))
|
||||
|
||||
if high_pass_frequency is not None and low_pass_frequency is None:
|
||||
self.command.append(str(high_pass_frequency))
|
||||
elif high_pass_frequency is not None and low_pass_frequency is not None:
|
||||
self.command.append(str(high_pass_frequency) + '-' + str(low_pass_frequency))
|
||||
elif high_pass_frequency is None and low_pass_frequency is not None:
|
||||
self.command.append(str(low_pass_frequency))
|
||||
|
||||
if not mutually_exclusive(right_t, right_t):
|
||||
raise ValueError("Transition bands options (-t or -n) are mutually exclusive.")
|
||||
if right_t is not None:
|
||||
self.command.append('-t')
|
||||
self.command.append(str(right_t))
|
||||
if right_n is not None:
|
||||
self.command.append('-n')
|
||||
self.command.append(str(right_n))
|
||||
return self
|
||||
|
||||
def bend(self, bends, frame_rate=None, over_sample=None):
|
||||
"""TODO Add docstring."""
|
||||
self.command.append("bend")
|
||||
if frame_rate is not None and isinstance(frame_rate, int):
|
||||
self.command.append('-f %s' % frame_rate)
|
||||
if over_sample is not None and isinstance(over_sample, int):
|
||||
self.command.append('-o %s' % over_sample)
|
||||
for bend in bends:
|
||||
self.command.append(','.join(bend))
|
||||
return self
|
||||
|
||||
def chorus(self, gain_in, gain_out, decays):
|
||||
"""TODO Add docstring."""
|
||||
self.command.append("chorus")
|
||||
self.command.append(gain_in)
|
||||
self.command.append(gain_out)
|
||||
for decay in decays:
|
||||
modulation = decay.pop()
|
||||
numerical = decay
|
||||
self.command.append(' '.join(map(str, numerical)) + ' -' + modulation)
|
||||
return self
|
||||
|
||||
def delay(self,
|
||||
gain_in=0.8,
|
||||
gain_out=0.5,
|
||||
delays=list((1000, 1800)),
|
||||
decays=list((0.3, 0.25)),
|
||||
parallel=False):
|
||||
"""delay takes 4 parameters: input gain (max 1), output gain
|
||||
and then two lists, delays and decays.
|
||||
|
||||
Each list is a pair of comma seperated values within
|
||||
parenthesis.
|
||||
"""
|
||||
self.command.append('echo' + ('s' if parallel else ''))
|
||||
self.command.append(gain_in)
|
||||
self.command.append(gain_out)
|
||||
self.command.extend(list(sum(zip(delays, decays), ())))
|
||||
return self
|
||||
|
||||
def echo(self, **kwargs):
|
||||
"""TODO Add docstring."""
|
||||
self.delay(**kwargs)
|
||||
|
||||
def fade(self):
|
||||
"""TODO Add docstring."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def flanger(self, delay=0, depth=2, regen=0, width=71, speed=0.5, shape='sine', phase=25, interp='linear'):
|
||||
"""TODO Add docstring."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def gain(self, db):
|
||||
"""gain takes one paramter: gain in dB."""
|
||||
self.command.append('gain')
|
||||
self.command.append(db)
|
||||
return self
|
||||
|
||||
def mcompand(self):
|
||||
"""TODO Add docstring."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def noise_reduction(self, amount=0.5):
|
||||
"""TODO Add docstring."""
|
||||
# TODO Run sox once with noiseprof on silent portions to generate a noise profile.
|
||||
raise NotImplementedError()
|
||||
|
||||
def oops(self):
|
||||
"""TODO Add docstring."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def overdrive(self, gain=20, colour=20):
|
||||
"""overdrive takes 2 parameters: gain in dB and colour which effects
|
||||
the character of the distortion effet.
|
||||
|
||||
Both have a default value of 20. TODO - changing color does not seem to have an audible effect
|
||||
"""
|
||||
self.command.append('overdrive')
|
||||
self.command.append(gain)
|
||||
self.command.append(colour)
|
||||
return self
|
||||
|
||||
def phaser(self,
|
||||
gain_in=0.9,
|
||||
gain_out=0.8,
|
||||
delay=1,
|
||||
decay=0.25,
|
||||
speed=2,
|
||||
triangular=False):
|
||||
"""phaser takes 6 parameters: input gain (max 1.0), output gain (max
|
||||
1.0), delay, decay, speed and LFO shape=trianglar (which must be set to
|
||||
True or False)"""
|
||||
self.command.append("phaser")
|
||||
self.command.append(gain_in)
|
||||
self.command.append(gain_out)
|
||||
self.command.append(delay)
|
||||
self.command.append(decay)
|
||||
self.command.append(speed)
|
||||
if triangular:
|
||||
self.command.append('-t')
|
||||
else:
|
||||
self.command.append('-s')
|
||||
return self
|
||||
|
||||
def pitch(self, shift,
|
||||
use_tree=False,
|
||||
segment=82,
|
||||
search=14.68,
|
||||
overlap=12):
|
||||
"""pitch takes 4 parameters: user_tree (True or False), segment, search
|
||||
and overlap."""
|
||||
self.command.append("pitch")
|
||||
if use_tree:
|
||||
self.command.append('-q')
|
||||
self.command.append(shift)
|
||||
self.command.append(segment)
|
||||
self.command.append(search)
|
||||
self.command.append(overlap)
|
||||
return self
|
||||
|
||||
def loop(self):
|
||||
"""TODO Add docstring."""
|
||||
self.command.append('repeat')
|
||||
self.command.append('-')
|
||||
return self
|
||||
|
||||
def reverb(self,
|
||||
reverberance=50,
|
||||
hf_damping=50,
|
||||
room_scale=100,
|
||||
stereo_depth=100,
|
||||
pre_delay=20,
|
||||
wet_gain=0,
|
||||
wet_only=False):
|
||||
"""reverb takes 7 parameters: reverberance, high-freqnency damping,
|
||||
room scale, stereo depth, pre-delay, wet gain and wet only (True or
|
||||
False)"""
|
||||
self.command.append('reverb')
|
||||
if wet_only:
|
||||
self.command.append('-w')
|
||||
self.command.append(reverberance)
|
||||
self.command.append(hf_damping)
|
||||
self.command.append(room_scale)
|
||||
self.command.append(stereo_depth)
|
||||
self.command.append(pre_delay)
|
||||
self.command.append(wet_gain)
|
||||
return self
|
||||
|
||||
def reverse(self):
|
||||
"""reverse takes no parameters.
|
||||
|
||||
It plays the input sound backwards.
|
||||
"""
|
||||
self.command.append("reverse")
|
||||
return self
|
||||
|
||||
def speed(self, factor, use_semitones=False):
|
||||
"""speed takes 2 parameters: factor and use-semitones (True or False).
|
||||
|
||||
When use-semitones = False, a factor of 2 doubles the speed and raises the pitch an octave. The same result is achieved with factor = 1200 and use semitones = True.
|
||||
"""
|
||||
self.command.append("speed")
|
||||
self.command.append(factor if not use_semitones else str(factor) + "c")
|
||||
return self
|
||||
|
||||
def synth(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def tempo(self,
|
||||
factor,
|
||||
use_tree=False,
|
||||
opt_flag=None,
|
||||
segment=82,
|
||||
search=14.68,
|
||||
overlap=12):
|
||||
"""tempo takes 6 parameters: factor, use tree (True or False), option
|
||||
flag, segment, search and overlap).
|
||||
|
||||
This effect changes the duration of the sound without modifying
|
||||
pitch.
|
||||
"""
|
||||
self.command.append("tempo")
|
||||
|
||||
if use_tree:
|
||||
self.command.append('-q')
|
||||
if opt_flag in ('l', 'm', 's'):
|
||||
self.command.append('-%s' % opt_flag)
|
||||
self.command.append(factor)
|
||||
self.command.append(segment)
|
||||
self.command.append(search)
|
||||
self.command.append(overlap)
|
||||
return self
|
||||
|
||||
def tremolo(self, freq, depth=40):
|
||||
"""tremolo takes two parameters: frequency and depth (max 100)"""
|
||||
self.command.append("tremolo")
|
||||
self.command.append(freq)
|
||||
self.command.append(depth)
|
||||
return self
|
||||
|
||||
|
||||
def trim(self, positions):
|
||||
"""TODO Add docstring."""
|
||||
self.command.append("trim")
|
||||
for position in positions:
|
||||
# TODO: check if the position means something
|
||||
self.command.append(position)
|
||||
return self
|
||||
|
||||
def upsample(self, factor):
|
||||
"""TODO Add docstring."""
|
||||
self.command.append("upsample")
|
||||
self.command.append(factor)
|
||||
return self
|
||||
|
||||
def vad(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def vol(self, gain, type="amplitude", limiter_gain=None):
|
||||
"""vol takes three parameters: gain, gain-type (amplitude, power or dB)
|
||||
and limiter gain."""
|
||||
self.command.append("vol")
|
||||
if type in ["amplitude", "power", "dB"]:
|
||||
self.command.append(type)
|
||||
else:
|
||||
raise ValueError("Type has to be dB, amplitude or power.")
|
||||
if limiter_gain is not None:
|
||||
self.command.append(str(limiter_gain))
|
||||
print(self.command)
|
||||
return self
|
||||
|
||||
def custom(self, command):
|
||||
"""Run arbitrary SoX effect commands.
|
||||
|
||||
Examples:
|
||||
custom('echo 0.8 0.9 1000 0.3') for an echo effect.
|
||||
|
||||
References:
|
||||
- https://linux.die.net/man/1/soxexam
|
||||
- http://sox.sourceforge.net/sox.html
|
||||
- http://tldp.org/LDP/LG/issue73/chung.html
|
||||
- http://dsl.org/cookbook/cookbook_29.html
|
||||
"""
|
||||
self.command.append(command)
|
||||
return self
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
src,
|
||||
dst=np.ndarray,
|
||||
sample_in=44100, # used only for arrays
|
||||
sample_out=None,
|
||||
encoding_out=None,
|
||||
channels_out=None,
|
||||
allow_clipping=True):
|
||||
|
||||
# depending on the input, using the right object to set up the input data arguments
|
||||
stdin = None
|
||||
if isinstance(src, str):
|
||||
infile = FilePathInput(src)
|
||||
stdin = src
|
||||
elif isinstance(src, np.ndarray):
|
||||
infile = NumpyArrayInput(src, sample_in)
|
||||
stdin = src
|
||||
elif isinstance(src, BufferedReader):
|
||||
infile = FileBufferInput(src)
|
||||
stdin = infile.data # retrieving the data from the file reader (np array)
|
||||
else:
|
||||
infile = None
|
||||
|
||||
# finding out which output encoding to use in case the output is ndarray
|
||||
if encoding_out is None and dst is np.ndarray:
|
||||
if isinstance(stdin, np.ndarray):
|
||||
encoding_out = stdin.dtype.type
|
||||
elif isinstance(stdin, str):
|
||||
encoding_out = np.float32
|
||||
# finding out which channel count to use (defaults to the input file's channel count)
|
||||
if channels_out is None:
|
||||
channels_out = infile.channels
|
||||
if sample_out is None: # if the output samplerate isn't specified, default to input's
|
||||
sample_out = sample_in
|
||||
|
||||
# same as for the input data, but for the destination
|
||||
if isinstance(dst, str):
|
||||
outfile = FilePathOutput(dst, sample_out, channels_out)
|
||||
elif dst is np.ndarray:
|
||||
outfile = NumpyArrayOutput(encoding_out, sample_out, channels_out)
|
||||
elif isinstance(dst, BufferedWriter):
|
||||
outfile = FileBufferOutput(dst, sample_out, channels_out)
|
||||
else:
|
||||
outfile = None
|
||||
|
||||
cmd = shlex.split(
|
||||
' '.join([
|
||||
'sox',
|
||||
'-N',
|
||||
'-V1' if allow_clipping else '-V2',
|
||||
infile.cmd_prefix if infile is not None else '-d',
|
||||
outfile.cmd_suffix if outfile is not None else '-d',
|
||||
] + list(map(str, self.command))),
|
||||
posix=False,
|
||||
)
|
||||
|
||||
logger.debug("Running command : %s" % cmd)
|
||||
if isinstance(stdin, np.ndarray):
|
||||
stdout, stderr = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(stdin.tobytes(order='F'))
|
||||
else:
|
||||
stdout, stderr = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()
|
||||
|
||||
if stderr:
|
||||
raise RuntimeError(stderr.decode())
|
||||
elif stdout:
|
||||
outsound = np.fromstring(stdout, dtype=encoding_out)
|
||||
if channels_out > 1:
|
||||
outsound = outsound.reshape((channels_out, int(len(outsound) / channels_out)), order='F')
|
||||
if isinstance(outfile, FileBufferOutput):
|
||||
outfile.write(outsound)
|
||||
return outsound
|
@ -0,0 +1,99 @@
|
||||
import logging
|
||||
import shlex
|
||||
import wave
|
||||
from subprocess import PIPE, Popen
|
||||
|
||||
import numpy as np
|
||||
|
||||
ENCODINGS_MAPPING = {
|
||||
np.int16: 's16',
|
||||
np.float32: 'f32',
|
||||
np.float64: 'f64',
|
||||
}
|
||||
|
||||
PIPE_CHAR = '-'
|
||||
|
||||
logger = logging.getLogger('pysndfx')
|
||||
|
||||
|
||||
class SoxInput(object):
|
||||
pipe = '-'
|
||||
|
||||
def __init__(self):
|
||||
self.cmd_prefix = None
|
||||
|
||||
|
||||
class FilePathInput(SoxInput):
|
||||
def __init__(self, filepath):
|
||||
super(FilePathInput, self).__init__()
|
||||
info_cmd = 'sox --i -c ' + filepath
|
||||
logger.debug("Running info command : %s" % info_cmd)
|
||||
stdout, stderr = Popen(shlex.split(info_cmd, posix=False), stdout=PIPE, stderr=PIPE).communicate()
|
||||
self.channels = int(stdout)
|
||||
self.cmd_prefix = filepath
|
||||
|
||||
|
||||
class FileBufferInput(SoxInput):
|
||||
def __init__(self, fp):
|
||||
super(FileBufferInput, self).__init__()
|
||||
wave_file = wave.open(fp, mode='rb') # wave.open() seems to support only 16bit encodings
|
||||
self.channels = wave_file.getnchannels()
|
||||
self.data = np.frombuffer(wave_file.readframes(wave_file.getnframes()), dtype=np.int16)
|
||||
self.cmd_prefix = ' '.join([
|
||||
'-t s16', # int16 encoding by default
|
||||
'-r ' + str(wave_file.getframerate()),
|
||||
'-c ' + str(self.channels),
|
||||
PIPE_CHAR,
|
||||
])
|
||||
|
||||
|
||||
class NumpyArrayInput(SoxInput):
|
||||
def __init__(self, snd_array, rate):
|
||||
super(NumpyArrayInput, self).__init__()
|
||||
self.channels = snd_array.ndim
|
||||
self.cmd_prefix = ' '.join([
|
||||
'-t ' + ENCODINGS_MAPPING[snd_array.dtype.type],
|
||||
'-r ' + str(rate),
|
||||
'-c ' + str(self.channels),
|
||||
PIPE_CHAR,
|
||||
])
|
||||
|
||||
|
||||
class SoxOutput(object):
|
||||
def __init__(self):
|
||||
self.cmd_suffix = None
|
||||
|
||||
|
||||
class FilePathOutput(SoxOutput):
|
||||
def __init__(self, filepath, samplerate, channels):
|
||||
super(FilePathOutput, self).__init__()
|
||||
self.cmd_suffix = ' '.join(['-r ' + str(samplerate), '-c ' + str(channels), filepath])
|
||||
|
||||
|
||||
class FileBufferOutput(SoxOutput):
|
||||
def __init__(self, fp, samplerate, channels):
|
||||
super(FileBufferOutput, self).__init__()
|
||||
self.writer = wave.open(fp, mode='wb')
|
||||
self.writer.setnchannels(channels)
|
||||
self.writer.setframerate(samplerate)
|
||||
self.writer.setsampwidth(2)
|
||||
self.cmd_suffix = ' '.join([
|
||||
'-t ' + ENCODINGS_MAPPING[np.int16],
|
||||
'-r ' + str(samplerate),
|
||||
'-c ' + str(channels),
|
||||
PIPE_CHAR,
|
||||
])
|
||||
|
||||
def write(self, data):
|
||||
self.writer.writeframesraw(data)
|
||||
|
||||
|
||||
class NumpyArrayOutput(SoxOutput):
|
||||
def __init__(self, encoding, samplerate, channels):
|
||||
super(NumpyArrayOutput, self).__init__()
|
||||
self.cmd_suffix = ' '.join([
|
||||
'-t ' + ENCODINGS_MAPPING[encoding],
|
||||
'-r ' + str(samplerate),
|
||||
'-c ' + str(channels),
|
||||
PIPE_CHAR,
|
||||
])
|
@ -1,27 +0,0 @@
|
||||
# Import the package and create an audio effects chain function.
|
||||
from pydub import AudioSegment
|
||||
from pysndfx import AudioEffectsChain
|
||||
import sys
|
||||
|
||||
|
||||
mp3_audio = AudioSegment.from_file(sys.argv[1], format="mp3")
|
||||
mp3_audio.export("../uploads/5/page_echo.wav", format="wav")
|
||||
|
||||
|
||||
infile = '../uploads/5/page_echo.wav'
|
||||
outfile = '../uploads/5/out.wav'
|
||||
|
||||
# Apply phaser and reverb directly to an audio file.
|
||||
#fx(infile, outfile)
|
||||
AudioEffectsChain().reverb()(infile, outfile)
|
||||
|
||||
# Or, apply the effects directly to a ndarray.
|
||||
#from librosa import load
|
||||
#y, sr = load(infile, sr=None)
|
||||
#y = fx(y)
|
||||
|
||||
# Apply the effects and return the results as a ndarray.
|
||||
#y = fx(infile)
|
||||
|
||||
# Apply the effects to a ndarray but store the resulting audio to disk.
|
||||
#fx(x, outfile)
|
@ -0,0 +1,4 @@
|
||||
import sys
|
||||
|
||||
sys.argv[1]='well'
|
||||
print (a)
|
@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>demo web file python3.2</title>
|
||||
</head>
|
||||
<body>
|
||||
<form name="pyform" method="POST" action="/scripts/cgi-bin/echo.cgi">
|
||||
<input type="text" name="fname" />
|
||||
<input type="submit" name="submit" value="Submit" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,4 @@
|
||||
This web-audio interface was made by <a href="https://w-i-t-m.net" target="
|
||||
_">Angeliki Diakrousi</a>. It challenges the ways we are listening to certain female sounding voices that are perceived as inappropriate - because of the quality of their sound, their gender, the medium's distortions but also stereotypes and collective memories that they often awake. These are verbal expressions that have been associated with forms of monstrosity since ancient times. Contributors are invited to record themselves with their own microphones, expressing their thoughts and choose a type of distortion. They are invited to participate in forming new imaginaries around a technologically mediated collective voice that through its 'monstrosity' can reveal other forms of speech that embrace their damage. They can choose what type of mediated voice they want to do that. A series of writings reveals stories and theories on the topic. The interface allows any voice to be recorded and saved in the artist's server where the website is hosted. There they get distorted through several scripts. Then the new sounds are categorized, depending on the chosen distortion, and published back in the platform. They can be played at the same time or in any desired order. The interface was first made for the performance "Radioactive Monstrosities" as part of the event <a href="https://www.facebook.com/events/524864625047957/"> NL_CL #2 : FLESH</a> hosted by <a href="https://instrumentinventors.org/">iii</a> and <a href="https://netherlands-coding-live.github.io/">Netherlands Coding Live</a> and its conceptualization is an ongoing process related to my graduation research <a href="eaiaiaiaoi.w-i-t-m.net"> Let's Amplify Unspeakable Things</a> and previous <a href="http://w-i-t-m.net/2019/radio-active-female-monstrosity_2019.html">performances </a><p>
|
||||
<i>Credits</i>: the platform is inspired by Vocable Code <a href="http://siusoon.net/vocable-code/">http://siusoon.net/vocable-code/</a>. Regarding the references I would like to thank Alice, Gert, Joana for sharing material with me, as well Amy for her publication made for the workshop <a href="http://w-i-t-m.net/2020/ecstatic-speech-2020.html">Eclectic Speech</a>. Thanks to the people that contributed and donated their voices.<p>
|
||||
|
@ -1,9 +1,7 @@
|
||||
Instructions:<p>
|
||||
Press record<br>
|
||||
Speak to the microphone<br>
|
||||
Press stop<br>
|
||||
Listen to it <br>
|
||||
Press upload<br>
|
||||
Type your name or nickname<br>
|
||||
Choose the distortion<br>
|
||||
Choose license<br>
|
||||
*Press record and allow microphone share<br>
|
||||
*Speak to the microphone for a couple of seconds (max size:1M). The texts may be a source of inspiration<br>
|
||||
*Press stop and listen to it <br>
|
||||
*Press upload<br>
|
||||
*Type your name or nickname<br>
|
||||
*Choose a type of distortion and license<br>
|
||||
|
Loading…
Reference in New Issue