├── CHANGELOG
├── QUICK-INSTALL.md
├── README.md
├── automix.php
├── config.php
├── download.php
├── index.php
├── install-remote.sh
├── jamulus-headless.service
├── jamulus-sudoers.txt
├── screenshots
├── screenshot1.png
├── screenshot2.png
├── screenshot3.png
└── screenshot4.png
└── worker.php
/CHANGELOG:
--------------------------------------------------------------------------------
1 | JAMULUS RECORDNG REMOTE
2 |
3 | 0.4 (2021-01-08)
4 | - added y/n question to install-service.sh and install-remote.sh
5 | - on index.php, added display of free memory on server
6 | - moved commands definition from config.php to worker.php
7 |
8 | 0.3 (2021-01-04)
9 | - changed label of Reload button to "Refresh list"
10 | - corrections in instructions
11 | - new QUICK INSTALL guide
12 |
13 | 0.3 (2020-12-29)
14 | - Initial version
15 |
--------------------------------------------------------------------------------
/QUICK-INSTALL.md:
--------------------------------------------------------------------------------
1 | ## Quick install
2 | This is a no-brain sequence of commands that you may run on a fresh instance of Ubuntu 18.04 to install everything from Jamulus to the Recording remote.
3 | You can just copy the following lines and paste them in the server shell:
4 |
5 | ```
6 | wget https://raw.githubusercontent.com/corrados/jamulus/master/distributions/installscripts/install4ubuntu.sh
7 |
8 | sh install4ubuntu.sh
9 |
10 | sudo apt-get install zip
11 |
12 | wget https://github.com/vdellamea/jamulus-server-remote/archive/main.zip
13 |
14 | unzip main.zip
15 |
16 | cd jamulus-server-remote-main
17 |
18 | sh install-service.sh
19 |
20 | sh install-remote.sh
21 | ```
22 |
23 | After this, the system is already up and running: you can reach it via `http://your.ip.address` where the IP address is the same you use to connect via Jamulus.
24 | However, **please remember to change the passwords!** You may edit the `config.php` file with the `nano` text editor:
25 |
26 | `sudo nano /var/www/html/config.php`
27 |
28 | You have to modify at least:
29 |
30 | `$ADMINPASSWORD= "your password";`
31 |
32 | `$MUSICIANSPASSWORD= "another password";`
33 |
34 | And eventually also:
35 |
36 | `$SERVERNAME="Your band name";`
37 |
38 | When you have finished, `CTRL+X` will let you save and exit (confirm with `y`).
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Jamulus Recording Remote - 0.6 (2021-05-12)
2 |
3 | A light-weight web-based interface for Jamulus headless server when installed on a Linux system. No frills, supersimple. Version 0.6 is compatible with Jamulus 3.7; users of previous versions should download version 0.4.1.
4 |
5 | Jamulus Recording Remote allows to start and stop recordings, and at the end zip them to be downloaded via the Web. The current version is tested on Ubunto 20.04 Minimal. Installing on an already running system requires some adaptation (details in the Details section below).
6 |
7 | **Warning: use it at your own risk**
8 | *Jamulus Recording Remote has not yet been thoroughly examined for security issues, thus use it at your own risk, in particular if on a server running continuously. In particular, to be safe, Apache has to be set on https only.*
9 |
10 | ## Prerequisites
11 | Jamulus should be installed according to official [instructions](https://jamulus.io/wiki/Server-Linux) for a headless server, i.e., with a service named `jamulus-headless`.
12 |
13 | The rest of this README refers to Ubuntu 20.04, but it should be easily adaptable to other Linux platforms.
14 |
15 | ## Update from previous version
16 |
17 | If you already had the Remote + experimental automix installed, just replace the html pages.
18 |
19 | ## Quick install
20 |
21 | **If you have a personalised install of Jamulus already running, do not follow these instructions, but read the details at the bottom to adapt the system to your local needs.**
22 |
23 | Be sure to have zip:
24 |
25 | `sudo apt-get install zip`
26 |
27 | Download the code:
28 |
29 | `wget https://github.com/vdellamea/jamulus-server-remote/archive/main.zip`
30 |
31 | Unzip it:
32 |
33 | `unzip main.zip`
34 |
35 | enter the unzipped directory:
36 |
37 | `cd jamulus-server-remote-main`
38 |
39 | Then run the `install-remote.sh` script to install the web-based remote. With a browser, go to the server address (IP or domain), and you will find the interface; enter the password you have set in the configuration (`/var/www/html/config.php`).
40 |
41 | The service section of the service description should appear as below: some of the fields are different from the standard one, some are new. If you are in a new installation, just copy the `jamulus-headless.service` provided here in the right place.
42 | ```
43 | [Service]
44 | Type=simple
45 | User=jamulus
46 | Group=www-data
47 | UMask=0002
48 | NoNewPrivileges=true
49 | ProtectSystem=true
50 | ProtectHome=false
51 | Nice=-20
52 | IOSchedulingClass=realtime
53 | IOSchedulingPriority=0
54 | ```
55 |
56 | ## Configuration
57 |
58 | In principle, the `config.php` (under `/var/www/html`) is the only place where to put hands: password, paths, and also the real shell commands to allow for personalization. Change the following values according to your local configuration or taste:
59 |
60 | This is at your taste: server, band name, your cat name...:
61 |
62 | `$SERVERNAME="Your band name";`
63 |
64 | Please change the passwords:
65 |
66 | `$ADMINPASSWORD= "******";`
67 |
68 | `$MUSICIANSPASSWORD= "******";`
69 |
70 |
71 |
72 | If you set this one to true, in the Session box you can see some extra output, which can help in debugging:
73 |
74 | `$DEBUG=false;`
75 |
76 | ## Usage
77 | Access to the commands is protected by the password you set in the configuration file. Musicians too need to enter a password to access zipped files.
78 |
79 |
80 |
81 | At each first access, the interface expects Jamulus to have *recording disabled* (there is no obvious way to determine its status from code). Thus the "toggle on/off" button is off, and the "Start new" is disabled. This also means that just one admin at a time must access the interface, to avoid mishaps. Then, the toggle button activate/disactivate recording, the Start new button start a new recording.
82 |
83 |
84 |
85 | At the end of each execution, buttons trigger a refresh of the Sessions textarea, where recordings are shown with their size. However, you may also reload to update the size of the last recording.
86 |
87 | At the end, you can zip all the original files of the session (as `orig-YYYY-MM-DD.zip` file). "Delete WAVs" deletes all sessions (the WAV files); "Delete ZIPs" deletes all ZIP files, so be careful.
88 |
89 |
90 | ## Automix and consolidate
91 |
92 | The *automix* will generate an automatically mixed stereo MP3 from each recording session. Of course, the automix is just a rough preview, with no level adjustment. Panning is done in two ways, discussed below.
93 |
94 | In addition to that, a *consolidate* feature is present because needed for automix, but it is also usable independently. This is aimed at producing WAV (or other formats like mp3 and flac) files that can be used with DAWs different from Reaper and Audacity: tracks all begin at time 0, so it is easy to import them in any DAW, not only Reaper and Audacity.
95 |
96 | After having run automix or consolidate, you may download the zipped version of the files as `mix-YYYY-MM-DD.zip` and `consolidated-YYYY-MM-DD.zip`.
97 |
98 | Beware: you need additional storage to run the scripts (although only temporarily).
99 |
100 | Automix and consolidate can be run also independently from the web system, by running `php automix.php` with appropriate parameters:
101 |
102 | --automix (default) / --consolidate
103 |
104 | Is a full Recordings directory or a single session?
105 |
106 | --single (default) / --all
107 |
108 | Files:
109 |
110 | --in path_to_recordings directory
111 |
112 | --out path_to_generated (default: current dir)
113 |
114 | Options:
115 |
116 | --format (wav,mp3,opus, ...) (default: mp3 for automix, wav for consolidate)
117 |
118 | --normalize audio normalization, default off - not good yet
119 |
120 | --debug add extra output
121 |
122 | --help this one
123 |
124 | ### Automix configuration
125 |
126 | Without any specific configuration, the system attempts to pan the tracks in a uniformly distributed way. One player: centered; two: one per side (but not totally); three: one centered, one left, one right; etc. They are ordered from left to right in alphabetical order by the profile name, and this may help to set the panning for large groups: e.g., you may ask singers to prefix their name with a number (01, 02, 03...).
127 |
128 | However, since the system is prevalently aimed at private servers, in config.php you may set the name of each musician/singer exactly as in their Jamulus profile, and set the position relative to left (1.0= all left, 0= all right, 0.5= center, etc). Tracks are recognised by the profile name, and thus can be panned in an informed way (e.g., drums and bass in the middle, guitars well spaced, etc).
129 |
130 | ```
131 | $BANDMATES=array(
132 | 'Jimi' =>0.55,
133 | 'Eric' =>0.45,
134 | 'John' =>0.5,
135 | 'Patti' =>0.5,
136 | 'Stevie'=> 0.3,
137 | );
138 | ```
139 |
140 | If you run `automix.php` independently, the same settings are to be put in the automix.php file, because it does not use config.php.
141 |
142 | In addition to that, you may set the format for the consolidated tracks, as mp3, wav, flac (actually, any format managed by ffmpeg). Normalization is not yet good.
143 |
144 | ```
145 | $CFORMAT="mp3"; // also flac, wav, etc
146 | $AUDIONORMALIZATION=false; //Experimental - not yet good
147 | ```
148 |
149 | *No need to check the rest if you installed from scratch as described above.*
150 |
151 | # Details for adapting to different platforms
152 |
153 | The following description is aimed at explaining what the installation script does, and it can be useful for those that want to install the interface on an already running server, or on a different distribution, or for any other reason.
154 |
155 | ## Installation
156 | This is a motivated walk through the install-remoth script. Download the code from this repository.
157 |
158 | Install dependencies: this should be adapted to the package manager of your distribution (yum, dnf). Some other software might be missing, like zip/unzip, some might be already installed (e.g., acl).
159 | ```
160 | sudo apt-get install apache2 php libapache2-mod-php ffmpeg acl
161 | ```
162 | E.g., for Fedora:
163 | ```
164 | sudo dnf install httpd php ffmpeg zip
165 | ```
166 |
167 |
168 | Prepare web document root: in principle this should be sufficient for most distributions, however you might want to put the PHP files in some subdirectory.
169 | ```
170 | sudo rm /var/www/html/index.html
171 | sudo cp *.php /var/www/html/
172 | ```
173 | Ownership of the files should be given to the user running apache. E.g., in Fedora it is not `www-data` but `apache`. It should be changed wherever www-data appears.
174 | ```
175 | sudo chown -R www-data /var/www/html/
176 | sudo chgrp -R www-data /var/www/html/
177 | ```
178 |
179 | If you followed the original instructions, the jamulus user is not created with a home dir. The following aims at setting it.
180 | ```
181 | sudo usermod -d /home/jamulus jamulus
182 | sudo mkhomedir_helper jamulus
183 | ```
184 |
185 | Then you need recording, mix and consolidate directories. These instruction should be good on any platform, except for the chgrp to www-data that might need adaptation.
186 | ```
187 | # recording songs directory
188 | sudo mkdir /home/jamulus/recording
189 | sudo chgrp www-data /home/jamulus/recording/
190 | sudo chmod g+rwx /home/jamulus/recording/
191 | sudo chmod g+s /home/jamulus/recording/
192 | sudo setfacl -d -m g::rwx /home/jamulus/recording/
193 | # mixed songs directory
194 | sudo mkdir /home/jamulus/mix
195 | sudo chgrp www-data /home/jamulus/mix/
196 | sudo chmod g+rwx /home/jamulus/mix/
197 | sudo chmod g+s /home/jamulus/mix/
198 | sudo setfacl -d -m g::rwx /home/jamulus/mix/
199 | # consolidated tracks directory
200 | sudo mkdir /home/jamulus/consolidated
201 | sudo chgrp www-data /home/jamulus/consolidated/
202 | sudo chmod g+rwx /home/jamulus/consolidated/
203 | sudo chmod g+s /home/jamulus/consolidated/
204 | sudo setfacl -d -m g::rwx /home/jamulus/consolidated/
205 | ```
206 |
207 | Finally, you have to add sudo capabilities to Apache for 2 commands. This is the tricky part. You have to give privileges to Apache for running commands as `sudo` by adding a file in `sudoers.d`. However, any mistake in doing this may result in loosing sudo privileges, thus use exclusively the `sudo visudo` command if you have to modify something, because it does syntax checks.
208 | The jamulus-sudoers.txt file should be adapted both regarding the user to which privileges are given, and the name of the service to be called (jamulus-headless vs jamulus or whatever you called it). It is highly suggested to use visudo to edit it, so copy it in place, then `sudo visudo /etc/sudoers.d/jamulus` . Be very careful. `visudo` does syntax checking and avoids mistakes, but if you use a different editor and make a mistake, all sudo privileges become locked. If visudo starts with the `vi` editor and you are not a nerd, try `sudo EDITOR=/bin/nano visudo /etc/sudoers.d/jamulus`.
209 |
210 | This might not be sufficient due to extra layers of protection in the system (SELinux, default on Fedora and Centos). You might need to [disable it](https://www.cyberciti.biz/faq/disable-selinux-on-centos-7-rhel-7-fedora-linux/) or do further work to enable Apache to call systemctl, but I cannot help on this.
211 |
212 | ```
213 | sudo cp jamulus-sudoers.txt /etc/sudoers.d/jamulus
214 | ```
215 | The content is
216 | ```
217 | www-data ALL=(ALL)NOPASSWD: /bin/systemctl kill -s SIGUSR1 jamulus
218 | www-data ALL=(ALL)NOPASSWD: /bin/systemctl kill -s SIGUSR2 jamulus
219 | ```
220 |
221 | Shell commands used by the Remote may need personalization if you want to adapt the scripts to an already existing installation (e.g., service name, the same as in the sudoers file. Here the current commands you can find in `worker.php` (two for sure that may need modifications are toggle and newrec, for the service name):
222 | ```php
223 | "toggle" => "sudo /bin/systemctl kill -s SIGUSR2 jamulus-headless ",
224 | "newrec" => "sudo /bin/systemctl kill -s SIGUSR1 jamulus-headless ",
225 | "compress" => "cd $RECORDINGS ; rm orig-$today.zip; zip -r orig-$today.zip Jam* ",
226 | "listrec" => "du -sh $RECORDINGS/Jam* ",
227 | "freespace" => "df -h --output=avail $RECORDINGS ",
228 | "delwav" => "rm -fr $RECORDINGS/Jam* ",
229 | "delzip" => "rm -fr $RECORDINGS/*.zip ",
230 | "ffmpeg" => "ffmpeg -loglevel quiet ",
231 | "checkstereo" => "ffmpeg -i ",
232 | "maxvolume" =>"ffmpeg -i ",
233 | "ffprobe" => "ffprobe -show_entries stream=duration -of compact=p=0:nk=1 -v 0 ",
234 | "zipmix" => "rm $RECORDINGS/mix-$today.zip; cd $MIX; zip $RECORDINGS/mix-$today.zip *.mp3 ; rm $MIX/*.mp3 ",
235 | "cleancons" => "rm $RECORDINGS/consolidated-$today.zip",
236 | "zipcons1" => "cd $CONSOLIDATED; zip -r $RECORDINGS/consolidated-$today.zip Jam* ; rm -fr Jam* ",
237 | "cleantmp" => "rm -fr /var/tmp/Jam-* ",
238 | ```
239 |
240 | The service file now allows for writing in the home directory of the user, which is created when creating the user. However, this is not mandatory: if you installed everything according to official instructions, the jamulus user likely will not have a home directory. If you want to put the recordings elsewhere, check that the privileges set in the service file are adequate (and sorry, I am not able to help on this).
241 | These are the extras vs. the standard install:
242 | ```
243 | Group=www-data
244 | UMask=0002
245 | ProtectHome=false
246 | ```
247 |
248 |
249 |
250 |
251 |
252 |
--------------------------------------------------------------------------------
/automix.php:
--------------------------------------------------------------------------------
1 | value,
14 | // name exactly as in Jamulus profile,
15 | // value: 1= left only, 0= right only, 0.5= center, etc
16 | // Names not specified are automatically panned.
17 |
18 | $BANDMATES=array(
19 | 'Jimi' =>0.85,
20 | 'Eric' =>0.45,
21 | 'Carol' =>0.53,
22 | 'Patti' =>0.5,
23 | 'Stevie'=> 0.11,
24 | );
25 | // File format for consolidated tracks
26 | //TODO set bitrate
27 | $CFORMAT="mp3"; //.wav, .opus ... any format managed by ffmpeg
28 | //default dir is current dir for automix
29 | $MIX="./";
30 | $DEBUG=true;
31 | $OPERATION='automix';
32 | $DIRTYPE='single';
33 | $AUDIONORMALIZATION=false;
34 |
35 | //command line options
36 | $optlist=array("automix","consolidate","single", "all","debug","format::","in:", "out:", "help", "normalize");
37 |
38 | $options=getopt("",$optlist);
39 | if(isset($options['debug'])) $DEBUG=true;
40 | if(isset($options['consolidate'])) {$OPERATION='consolidate'; $CFORMAT='wav';}
41 | if(isset($options['all'])) $DIRTYPE='all';
42 | if(isset($options['format'])) $CFORMAT=$options['format'];
43 | if(isset($options['in'])) $RECORDINGS=$options['in'];
44 | if(isset($options['out'])) $MIX=$options['out'];
45 | if(isset($options['normalize'])) $AUDIONORMALIZATION=true;
46 |
47 | if($DEBUG) var_dump($options);
48 |
49 | if(isset($options['help']) || !isset($RECORDINGS)) die(automix_help());
50 |
51 | $today=date("Ymd");
52 |
53 | //If you need to adapt the Remote to your server, check the following commands
54 | if(!$DEBUG) $FFMPEG_LOG="-loglevel quiet ";
55 | $COMMANDS=array(
56 | "ffmpeg" => "ffmpeg $FFMPEG_LOG -hide_banner ",//-loglevel quiet
57 | "checkstereo" => "ffmpeg $FFMPEG_LOG -hide_banner -i ",
58 | "maxvolume" =>" $FFMPEG_LOG -hide_banner -i ",
59 | "zipmix" => "zip mix.zip *.mp3 ; rm $MIX/*.mp3 ",
60 | "ffprobe" => "ffprobe -hide_banner -show_entries stream=duration -of compact=p=0:nk=1 -v 0 ",
61 | );
62 |
63 | print("AUTOMIX 0.51 - part of Jamulus Recording Remote\n");
64 |
65 | //if($argc<3) die("php automix.php single|all path_to_recordings\n");
66 |
67 | if($OPERATION=='automix' && $DIRTYPE=='all') {
68 | print("Generating automix for all the sessions in $RECORDINGS.\n");
69 | $sessions=glob("$RECORDINGS/Jam-*",GLOB_ONLYDIR);
70 | foreach($sessions as $s) {
71 | print("- $s \n");
72 | $from=$s;
73 | $to="/var/tmp/".basename($from)."/";
74 | mkdir($to);
75 | consolidate_tracks($from,$to, ".wav");
76 | $out=generate_mix($to);
77 | if($DEBUG) print_r($out);
78 | }
79 | }
80 | else if($OPERATION=='automix' && $DIRTYPE=='single'){
81 | print("Generating automix for session $RECORDINGS.\n");
82 | $to="/var/tmp/".basename($RECORDINGS)."/";
83 | mkdir($to);
84 | consolidate_tracks($RECORDINGS,$to, ".wav");
85 | $out=generate_mix($to);
86 | if($DEBUG) print_r($out);
87 | }
88 | else if($OPERATION=='consolidate' && $DIRTYPE=='single'){
89 | print("Consolidating tracks for session $RECORDINGS to $MIX .\n");
90 | mkdir($MIX);
91 | consolidate_tracks($RECORDINGS,$MIX."/", $CFORMAT);
92 | }
93 | else if($OPERATION=='consolidate' && $DIRTYPE=='all'){
94 | print("Consolidating tracks for all the sessions in $RECORDINGS .\n");
95 | $sessions=glob("$RECORDINGS/Jam-*",GLOB_ONLYDIR);
96 | foreach($sessions as $s) {
97 | print("- $s \n");
98 | $from=$s;
99 | $to=$MIX."/".basename($from)."/";
100 | if($DEBUG) print("TO: $MIX - $from - $to. \n");
101 | mkdir($to);
102 | consolidate_tracks($from,$to, $CFORMAT);
103 | }
104 | }
105 | else die(automix_help());
106 |
107 | }
108 |
109 |
110 | //FUNCTIONS
111 |
112 | function generate_mix($session) {
113 | global $RECORDINGS;
114 | global $MIX;
115 | global $COMMANDS;
116 | global $BANDMATES;
117 | global $DEBUG;
118 | $ffmpeg=$COMMANDS['ffmpeg'];
119 | $checkstereo=$COMMANDS['checkstereo'];
120 | $filename=basename($session);
121 | $dir=glob($session."/*.wav");
122 | $chantotal=0;
123 | $unknowns=0;
124 |
125 | //scan tracks, check known clients, check mono/stereo
126 | foreach($dir as $t) {
127 | //Is it a known client or not?
128 | if(!known(basename($t))) $unknowns++;
129 |
130 | exec($checkstereo.$t." 2>&1 | grep Guessed",$out,$rec);
131 | $stereo=substr(trim($out[0]),-6)=="stereo";
132 | $tracks[$t]=$stereo;
133 | $chantotal1=$chantotal+1;
134 | if($stereo) {
135 | $channels[$t]=array("c".$chantotal,"c".$chantotal1);
136 | $chantotal=$chantotal+2;
137 | }
138 | else
139 | {
140 | $channels[$t]=array("c".$chantotal,"c".$chantotal);
141 | $chantotal=$chantotal+1;
142 | }
143 | unset($out);
144 | }
145 |
146 | $clients=sizeof($tracks);
147 |
148 | //STEREO PANNING
149 | if($unknowns>0) $offset=0.5/$unknowns;
150 | $step=$offset*2;
151 | $lmult=-0.5;
152 | $numchans=0;
153 | foreach($channels as $t=>$chans){
154 | $inputs.="-i $t ";
155 | //if client is known, use the value from config
156 | if(known(basename($t))) {
157 | $ltmp=$BANDMATES[known_name(basename($t))];
158 | $rtmp=1-$ltmp;
159 | }
160 | else {
161 | if($numchans==0)
162 | $rtmp=$offset;
163 | else
164 | $rtmp=$rtmp+$step;
165 |
166 | $ltmp=1-$rtmp;
167 | $numchans++;
168 | if($DEBUG) print("PANNING ".basename($t).": $ltmp - $rtmp \n");
169 | }
170 | $ltmp=round($ltmp,3);
171 | $rtmp=round($rtmp,3);
172 | $left.=$ltmp."*".$chans[0]."+";
173 | $right.=$rtmp."*".$chans[1]."+";
174 | }
175 |
176 | // remove trailing '+'
177 | $left=substr($left,0,-1);
178 | $right=substr($right,0,-1);
179 |
180 | $volumeplus=$clients;
181 | $automixcommand=
182 | "$ffmpeg $inputs -filter_complex \"amerge=inputs=". $clients.
183 | ",volume=$volumeplus"."dB,pan=stereo|FL<$left|FR<$right".
184 | "[a]\" -map \"[a]\" $MIX"."$filename.mp3\n";
185 |
186 | exec($automixcommand,$out,$ret);
187 | if($DEBUG) {
188 | print("AUTOMIX:".$automixcommand."\n");
189 | print_r($out);
190 | }
191 | unset($out);
192 | }
193 |
194 |
195 | ///////////////////////////////////////////
196 | function consolidate_tracks($dir, $outdir, $format) {
197 | global $AUDIONORMALIZATION;
198 | global $COMMANDS;
199 | global $DEBUG;
200 | $ffprobe=$COMMANDS['ffprobe'];
201 | $checkmaxvolume=$COMMANDS['maxvolume'];
202 |
203 | // scan .lof file to read track offsets in seconds
204 | $lof=file($dir."/".basename($dir).".lof");
205 | foreach($lof as $f){
206 | $tmp=explode(" ",trim($f));
207 | $offset[str_replace("\"","",$tmp[1])]=$tmp[3];
208 | }
209 |
210 | $list=glob($dir."/*.wav");
211 |
212 | $maxduration=0;//this will become the total length of the session
213 |
214 | foreach($list as $t){
215 | $tmp=explode("-",substr(basename($t),0,-4));
216 | //check duration of each track
217 | exec($ffprobe.$t,
218 | $out);
219 | $duration=$out[0];unset($out);
220 | if($DEBUG) print("DURATION: ".$ffprobe.$t."\n");
221 |
222 | //check max volume
223 | $maxvolumecommand=$checkmaxvolume.$t.
224 | ' -af "volumedetect" -f null /dev/null 2>&1 |grep max_volume';
225 | exec($maxvolumecommand,$outvol,$retvol);
226 | if($DEBUG) print("VOLUME: ".$maxvolumecommand."\n");
227 | $outvol2=explode(":",trim($outvol[0]));
228 | $outvol2=explode(" ",substr($outvol2[1],0,-2));
229 | $maxvolume=$outvol2[1];
230 | unset($outvol);
231 | unset($outvol2);
232 |
233 | //if a client name is available, use it
234 | if(!isset($tracks[$tmp[1]]['name'])) $tracks[$tmp[1]]['name']='____';
235 | if($tmp[0]<>'____') $tracks[$tmp[1]]['name']=$tmp[0];
236 |
237 | // real duration is offset + duration
238 | $newdur=$offset[basename($t)]+$duration;
239 | // look for the longest
240 | if($newdur>$maxduration) $maxduration=$newdur;
241 |
242 | $tracks[$tmp[1]]['segments'][$t]=
243 | array( 'frame'=>$tmp[2],
244 | 'channels'=>$tmp[3],
245 | 'offset'=>$offset[basename($t)],
246 | 'duration'=>$duration,
247 | 'newdur'=>$newdur,
248 | 'maxvolume'=>$maxvolume,
249 | );
250 | }
251 |
252 | if($DEBUG) print_r($tracks);
253 |
254 | foreach ($tracks as $ip=>$t){
255 | $trackdur=0;
256 | $maxoffset=0;
257 | $inputs=""; $delays=""; $amix="";
258 | //$format contains the file extension: ffmpeg will generate in such format
259 | //TODO set bitrate!
260 | $outname=$outdir.$t['name']."-consolidated.".$format;
261 | if($t['name']=='____') $outname=$outdir.$ip."-consolidated.".$format;
262 | $c=0;
263 |
264 | //sort by offset to reorder when many WAVs
265 | //and decide whether the channel should be stereo or mono
266 | $orderedwavs=array();
267 | foreach($t['segments'] as $tr=>$s){
268 | $orderedwavs[$tr]=$s['offset'];
269 | $numberofchannels=$s['channels'];
270 | }
271 | asort($orderedwavs);
272 |
273 | $monostereo='mono';if($numberofchannels==2) $monostereo='stereo';
274 |
275 | $previousdur=0;
276 | foreach($orderedwavs as $tr=>$o){
277 | $s=$t['segments'][$tr];
278 | $inputs.="-i $tr ";
279 | $delay=round(1000*($s['offset']-$previousdur),0);
280 | $maxvolume=-$s['maxvolume'];
281 |
282 | //Volumes are all brought to 0dB
283 | //this tries to save who had set the volume too low
284 | $volumeincrease="";
285 | if($maxvolume>0 && $maxvolume<7)
286 | $volumeincrease=" ,volume=".$maxvolume."dB";
287 | $delays.=
288 | "[$c]aformat=sample_fmts=s16:sample_rates=48000".
289 | ":channel_layouts=$monostereo".
290 | ",adelay=".$delay."|".$delay.$volumeincrease."[a$c]; ";
291 | $amix.="[a$c]";
292 | $c++;
293 | if($s['offset']>$maxoffset) {
294 | $trackdur=($s['offset']+$s['duration']);
295 | $maxoffset=$s['offset'];
296 | }
297 | $previousdur=$s['offset']+$s['duration'];
298 | }
299 |
300 |
301 | $total=$c;
302 | $silence="";$silencedelay="";$silenceamix="";
303 | $missingtime=round($maxduration-$previousdur,3);
304 | $trackdur=round(1000*$trackdur,0);
305 | // if the consolidated track is shorter than the maximum duration,
306 | // add a silence track that lasts as the maximum
307 | if($missingtime>0) {
308 | $total=$c+1;
309 | $silence=" -f lavfi -i anullsrc=r=48000 ";
310 | $tracksilence=round($maxduration,3);
311 | $silencedelay="[$c]atrim=duration=".$missingtime."[a$c];";
312 | $silenceamix="[a$c]";
313 | }
314 |
315 | //Audio normalization: does not give reliable results
316 | if($AUDIONORMALIZATION) $audionorm=", dynaudnorm=t=0.1 ";
317 | // if($AUDIONORMALIZATION) $audionorm=", loudnorm=tp=0.0, aresample=48000";
318 |
319 | $command="ffmpeg -hide_banner $inputs $silence".
320 | " -filter_complex \"$delays $silencedelay $amix".
321 | $silenceamix."concat=n=$total:v=0:a=1 $audionorm". "\" $outname\n";
322 |
323 |
324 | exec($command, $outcommand);
325 | if($DEBUG) {
326 | print("CONSOLIDATE: ".$command."\n");
327 | print_r($outcommand);
328 | }
329 | unset($outcommand);
330 | }
331 | }
332 |
333 | function known($track) {
334 | global $BANDMATES;
335 | $ret=false;
336 | foreach($BANDMATES as $m=>$n)
337 | if($ret=(substr($track,0,strlen($m))==$m))
338 | break;
339 | return $ret;
340 | }
341 |
342 | function known_name($track) {
343 | global $BANDMATES;
344 | $ret=false;
345 | foreach($BANDMATES as $m=>$n) {
346 | $ret=substr($track,0,strlen($m));
347 | if($ret==$m) break;
348 | }
349 | return $ret;
350 | }
351 |
352 | function isCommandLineInterface()
353 | {
354 | return (php_sapi_name() === 'cli');
355 | }
356 |
357 | function automix_help() {
358 | ?>
359 | AUTOMIX v0.51 - part of Jamulus Recording Remote
360 | Choose an operation:
361 | --automix (default)
362 | --consolidate
363 | Is a full Recordings directory or a single session?
364 | --single (default)
365 | --all
366 | Files:
367 | --in path_to_recordings directory
368 | --out path_to_generated (default: current dir)
369 | Options:
370 | --format (wav,mp3,opus) (default: mp3 for automix, wav for consolidate)
371 | --normalize audio normalization, default off - not good yet
372 | --debug add extra output
373 | --help this one
374 |
378 |
379 |
380 |
--------------------------------------------------------------------------------
/config.php:
--------------------------------------------------------------------------------
1 | left percentage (right is 1-left)
22 | $BANDMATES=array(
23 | 'Jimi' =>0.55,
24 | 'Eric' =>0.45,
25 | 'John' =>0.5,
26 | 'Patti' =>0.5,
27 | 'Stevie'=> 0.3,
28 | );
29 |
30 | ?>
31 |
--------------------------------------------------------------------------------
/download.php:
--------------------------------------------------------------------------------
1 |
36 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
64 | 67 | 68 | 71 |
72 | 73 |85 |
204 | Consolidated files 205 |
206 | 207 | 211 |