Hi everyone,
just a brief update on the LAC2011 programme. The conference will have
- 25 papers on various aspects of FLOSS for audio and music
- An invited session on music programming languages and parallelism
(with
representatives of the major systems for audio/music programming)
- Five workshops varying from tools for broadcast to composition
systems.
- Two electronic music concerts and a Club Night
- Four installations.
If you're hoping to come, even if you're not sure yet, please register
on
the conference site, so we can keep stats on prospective attendees.
http://lac.linuxaudio.org/2011/?page=registration
Hoping to see you in Maynooth in May.
=======================
Dr Victor Lazzarini
Senior Lecturer
Dept. of Music
NUI Maynooth Ireland
tel.: +353 1 708 3545
Victor dot Lazzarini AT nuim dot ie
> I'd be interested to hear how various projects handles this internally, if
> anybody wants to chip in?
In mine, each parameter has two additional members:
- controller_id (32 bit int)
- SYSEX string. (for SYSEX based automation).
Controller ID is several sub-fields. The high order 8 bits are the type of
MIDI message...
enum EcontrollerType{
Learn=-2,
None=-1,
NullType=0, // ignore.
CC = 2,
RPN,
NRPN,
SYSEX,
Bender,
ChannelPressure, // monophonic aftertouch.
BPM,
TransportPlaying,
SongPosition,
Pitch = 19, // only polyphonic allowed after here. see
ControllerType::isPolyphonic().
Gate,
VelocityOn,
VelocityOff,
PolyAftertouch,
Trigger,
Active, // note-stealing. float. Active (10) = not stolen.
0.0 - Voice in rapid-mute (steal) phase.
unused1, //Reset, // int. Voice assigned new Voice ID or
re-used after period of inactivity, kill any residue of previous use. poly
ADSRs need reset to zero, filters/delays need clearing.
VirtualVoiceId, // int. usually same as MIDI note number.
};
..The next 24 bits identify the CC, RPN, or NRPN number (if relevant). With
polyphonic controllers, like poly-pressure, the key-number gets squeezed in
there too.
SYSEX string is for SYSEX controllers, it's just a unsigned char array
containing the SYSEX string with imbedded 'format specifiers' at the
'variable' parts. E.g. "FO 41 10 42 12 VV F7" ..."VV" being the value that
changes.
This system, AFAIK, handles MIDI automation for pretty much any type of MIDI
event.
Best Regards,
Jeff
> -----Original Message-----
> From: linux-audio-dev-bounces(a)lists.linuxaudio.org [mailto:linux-audio-
> dev-bounces(a)lists.linuxaudio.org] On Behalf Of linux-audio-dev-
> request(a)lists.linuxaudio.org
> Sent: Thursday, 24 March 2011 1:00 a.m.
> To: linux-audio-dev(a)lists.linuxaudio.org
> Subject: Linux-audio-dev Digest, Vol 49, Issue 16
>
> Send Linux-audio-dev mailing list submissions to
> linux-audio-dev(a)lists.linuxaudio.org
>
> To subscribe or unsubscribe via the World Wide Web, visit
> http://lists.linuxaudio.org/listinfo/linux-audio-dev
> or, via email, send a message with subject or body 'help' to
> linux-audio-dev-request(a)lists.linuxaudio.org
>
> You can reach the person managing the list at
> linux-audio-dev-owner(a)lists.linuxaudio.org
>
> When replying, please edit your Subject line so it is more specific
> than "Re: Contents of Linux-audio-dev digest..."
>
>
> Today's Topics:
>
> 1. Denemo is searching for a student for the Google Summer of
> Code (5000$) (Nils)
> 2. MIDI maps : C++ functions (Harry Van Haaren)
>
>
> ----------------------------------------------------------------------
>
> Message: 1
> Date: Tue, 22 Mar 2011 23:45:44 +0100
> From: Nils <nils(a)hammerfeste.com>
> Subject: [LAD] Denemo is searching for a student for the Google Summer
> of Code (5000$)
> To: linux-audio-dev(a)lists.linuxaudio.org
> Message-ID: <20110322234544.14c881d8(a)hammerfeste.com>
> Content-Type: text/plain; charset=US-ASCII
>
> Hello list!
>
> Google Summer of Code (GSoC) is a global program that offers student
> developers stipends to write code for various open source software
> projects. A participating student gets 5000$ for ca. three months of work.
> [Link 1]
>
> Denemo is participating and we would like to encourage possible students
> to contact us through our mailinglist [see attached link 2] or #denemo on
> irc.freenode.org.
>
> Our top priority project is the following, but we are also ready for
> student ideas.
>
> "Make midi/audio output realtime-capable
>
> Denemo creates MIDI messages to use them with its internal Fluidsynth-
> sampler or send them out via the Jack Audio Connection Kit. This procedure
> is not realtime-safe yet. Graphical computation and controlling Denemo
> during playback creates playback-glitches. In order to fix this the
> midi/audio subsystem should run in its own realtime-thread and get
> priority. Needed student skills: Programming in C, experience in threads
> and realtime programming. Useful: Knowledge of MIDI and the Jack Audio
> Connection Kit."
>
>
> Links:
> [0] http://www.denemo.org
> [1] http://www.google-melange.com/
> [2] http://www.denemo.org/index.php/Community
> [3] http://www.gnu.org/software/soc-projects/ideas-2011.html#denemo
>
> P.S.
> If you are not a student feel free do contact us anyway and help Denemo
> like the rest of the team :)
>
>
> ------------------------------
>
> Message: 2
> Date: Wed, 23 Mar 2011 00:44:24 +0000
> From: Harry Van Haaren <harryhaaren(a)gmail.com>
> Subject: [LAD] MIDI maps : C++ functions
> To: Linux Audio Developers <linux-audio-dev(a)lists.linuxaudio.org>
> Message-ID:
> <AANLkTimae2N_4LBRC-Zh_1qisTrPOByv+QRfGepwFW5B(a)mail.gmail.com>
> Content-Type: text/plain; charset="iso-8859-1"
>
> Hey guys,
>
> I'm wondering how to approach creating a MIDI map to all controllers
> available in the GUI. Needless to say I can hard code in a MIDI CC, and
> from
> JACK's process callback call the function that I want to map that control
> to, but that's a little rigid.
>
> I like Ardour's one-click map idea, and I'm wondering how its implemented.
> Here's what I concluded so far:
> On mouse_3 down, pop up dialog, send message to JACK process to keep next
> MIDI input stored somewhere. That's the CC to map
>
> Where I'm getting stuck is how to make each CC point to a different
> "parameter" in the software, or a different function in the code. Function
> pointers come to mind, but somehow I don't like that idea much. Creating a
> generic interface to map every control in the entire engine might work,
> but
> I think that may be a little overkill?
>
> I'd be interested to hear how various projects handles this internally, if
> anybody wants to chip in?
> Cheers, -Harry
>
Hello list!
Google Summer of Code (GSoC) is a global program that offers student developers stipends to write code for various open source software projects. A participating student gets 5000$ for ca. three months of work. [Link 1]
Denemo is participating and we would like to encourage possible students to contact us through our mailinglist [see attached link 2] or #denemo on irc.freenode.org.
Our top priority project is the following, but we are also ready for student ideas.
"Make midi/audio output realtime-capable
Denemo creates MIDI messages to use them with its internal Fluidsynth-sampler or send them out via the Jack Audio Connection Kit. This procedure is not realtime-safe yet. Graphical computation and controlling Denemo during playback creates playback-glitches. In order to fix this the midi/audio subsystem should run in its own realtime-thread and get priority. Needed student skills: Programming in C, experience in threads and realtime programming. Useful: Knowledge of MIDI and the Jack Audio Connection Kit."
Links:
[0] http://www.denemo.org
[1] http://www.google-melange.com/
[2] http://www.denemo.org/index.php/Community
[3] http://www.gnu.org/software/soc-projects/ideas-2011.html#denemo
P.S.
If you are not a student feel free do contact us anyway and help Denemo like the rest of the team :)
Hi all,
I'm glad to announce the release of NASPRO 0.2.90.
NASPRO (http://naspro.atheme.org/) is meant to be a cross-platform
sound processing software architecture built around the LV2 plugin
standard (http://lv2plug.in/).
The goal of the project is to develop a series of tools to make it
easy and convenient to use LV2 for sound processing on any (relevant)
platform and for everybody: end users, host developers, plugin
developers, distributors and scientists/researchers.
This release is a total redesign/rewrite that more or less
reimplements the features of the previous release, but in a much
cleaner and maintainable way.
It includes:
* NASPRO core: the portable runtime library at the bottom of the architecture;
* NASPRO Bridge it: a little helper library to develop
insert-your-API-here to LV2 bridges;
* NASPRO bridges: a collection of bridges to LV2 which, once
installed, allow you to use plugins developed for other plugin
standards in LV2 hosts.
In particular, the NASPRO bridges collection includes two bridges: a
LADSPA (http://www.ladspa.org/) 1.1 and a DSSI
(http://dssi.sourceforge.net/) 1.0.0/1.1.0 bridge.
All of the code is released under the LGPL 2.1
(http://www.gnu.org/licenses/lgpl-2.1.html) license.
More information is available on the project's website.
Enjoy!
Stefano D'Angelo
I would like to announce the release of flarps-0.1.0
Flarps is an arpeggiator for Linux. It uses JACK MIDI to generate
melodies, and it can be controlled live by editing a text file in
a simple text-editor.
Flarps is designed around the notion of 'steps'. A step describes
the relative distance between two MIDI notes, and has a horizontal
and a vertical component. Vertically, a difference in pitch is
specified, and horizontally a distance in time (rhythm).
You can find a lot more information about flarps here:
http://flarps.sourceforge.net/
You can download it directly at:
http://sourceforge.net/projects/flarps/files/flarps-0.1.0.tar.bz2/download
Or you can clone the git repository with:
git clone git://flarps.git.sourceforge.net/gitroot/flarps/flarps
For bug reports, feature requests, support requests etc., please use
the facilities at the sourceforge project site.
New ideas or patches for this project are very welcome.
If you want to collaborate on this project, you can find
me on the linux-audio-dev mailing list.
lievenmoors
Hi!
I guess this could be important for some of you:
Debian is currently dropping QT3 and KDE3 from unstable. This in turn
means that apps depending on those libs either need to be ported to
QT4/KDE4 or will also be removed.
As always, saying Debian sooner or later also means Ubuntu and
derivatives.
Of course, all software remains installable from squeeze (current
release) for the next few years (let me lie: 5?).
I know that fmit and creox use QT3, probably some other audio software,
too.
The creox author has no time for porting, so if somebody feels bored or
is looking for a little project, you might want to consider this.
Cheers
--
mail: adi(a)thur.de http://adi.thur.de PGP/GPG: key via keyserver
Citējot *Fons Adriaensen <fons(a)linuxaudio.org> [1]*:
> > How from ...1024 or 2048 or 4096... FFT return values i
> calculate
> > power magnitudes for all bands,
> > and finally values for visual 10-20 hopping bars, like in
> Winamp ,
> > XMMS , QMMP ... ?
>
> If you want such a display the FFT is not a good way to do it.
> It's possible but not simple if you want a correct result.
What is good/best way to calculate values for visual 10-20 hopping
bars like in GUI audio players ?
> > 43303.2715 + 796.7285 = 44100 or 44100 - 43303.2715 =
> 796.7285>
> > Why Frequency 796.7285 is mirrored as Frequency 43303.2715 ,
> and magnitude for both Frequencies is divided by 2 ????
>
> Because you are using a complex FFT, and the imaginary part
> of your signal is zero. That means that the spectrum must be
> symmetric.
>
> > Is here way direct calculate full magnitude and without
> Frequency
> > mirroring , in band 0 Hz ... FSampl/2 ONLY ,
>
> Use an FFT operating on real data instead of complex.
Can U gimme pointers to such functions ?
What about all Radix algorithms ?
Tnx in advance
Alf
---------- Pārsūtītās vēstules beigas ----------
Links:
------
[1] mailto:fons@linuxaudio.org
Hi Experts.
I am Physics student, and i wanna write referat about Fourier
transformations, also about FFT 1D real case.
Hope this is best place for ask this, and here are best experts.
I wanaa demonstrate how diverse window functions changes measured
spectrum,
how much CPU ressources take diverse FFT algorithms ...
Yet i understand [i hope so] how works windowing .
Is somewhere available copy-paste self contained C example functions
or makros for diverse FFT algorithms
FFT, QFT, Goertzel, radix-2, radix-4, split-radix, mixed-radix ... ?
Which variables in FFT function must/should be defined as static,
register ... for best performance ?
What typical comes in to FFT function ? Pointer to already windowed
array of samples ?
What return FFT ?
What exact physical dimensions return FFT , if input function
dimensions was - voltage depends from (time) U=U(t) ?
How from ...1024 or 2048 or 4096... FFT return values i calculate
power magnitudes for all bands,
and finally values for visual 10-20 hopping bars, like in Winamp ,
XMMS , QMMP ... ?
If i exact know my FFT window size [for example 4096 samples] and
window type , and it will be constant forever,
is it possible to calculate window(,sine,cosine,) and store all
values in constant array,
so that in runtime it do not need be calculated , but i can just
take values form array ?
I have google_d about FFT and have found such proggie [see below]
I have it little bit remixed , i generate pure sine frekwenz =
796.7285 HZ ,
and in output file i got so what :
[ 71] 764.4287 Hz: Re= 0.0000000011142182 Im= 0.0000002368905824 M=
0.0000002368932028
[ 72] 775.1953 Hz: Re= 0.0000000011147694 Im= 0.0000003578625618 M=
0.0000003578642981
[ 73] 785.9619 Hz: Re= 0.0000000011164234 Im= 0.0000007207092628 M=
0.0000007207101275
[ 74] 796.7285 Hz: Re=-0.0000022785007614 Im=-0.5000000048748561 M=
0.5000000048800476
[ 75] 807.4951 Hz: Re= 0.0000000011098065 Im=-0.0000007304711756 M=
0.0000007304720186
[ 76] 818.2617 Hz: Re= 0.0000000011114605 Im=-0.0000003676273975 M=
0.0000003676290776
[ 77] 829.0283 Hz: Re= 0.0000000011120118 Im=-0.0000002466579503 M=
0.0000002466604569
...
...
...
[4019] 43270.9717 Hz: Re= 0.0000000011120118 Im= 0.0000002466579503
M= 0.0000002466604569
[4020] 43281.7383 Hz: Re= 0.0000000011114605 Im= 0.0000003676273975
M= 0.0000003676290776
[4021] 43292.5049 Hz: Re= 0.0000000011098065 Im= 0.0000007304711756
M= 0.0000007304720186
[4022] 43303.2715 Hz: Re=-0.0000022785015510 Im= 0.5000000048748419
M= 0.5000000048800334
[4023] 43314.0381 Hz: Re= 0.0000000011164234 Im=-0.0000007207092628
M= 0.0000007207101275
[4024] 43324.8047 Hz: Re= 0.0000000011147694 Im=-0.0000003578625618
M= 0.0000003578642981
[4025] 43335.5713 Hz: Re= 0.0000000011142182 Im=-0.0000002368905824
M= 0.0000002368932028
Where
43303.2715 + 796.7285 = 44100 or 44100 - 43303.2715 = 796.7285
Why Frequency 796.7285 is mirrored as Frequency 43303.2715 , and
magnitude for both Frequencies is divided by 2 ????
Is here way direct calculate full magnitude and without Frequency
mirroring , in band 0 Hz ... FSampl/2 ONLY ,
and not in full band - 0 Hz ... FSampl ??
In this case - how do i calculate corresponding frequency of each
band that returns FFT, if sample frequency is 32K, 44K or 48K ?
Pleaz do not point me to FFTW[3] and such libs , i must self
write/combine code .
Except FFTW has best short self contained 1D functions for
copy-paste :)
Each pointer and example is welcomed.
Tnx in advance @ all.
Alfs Kurmis.
====
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#define BUFFER 4096 //2048
//#define BUFFER 256
# define M_PI_LD 3.1415926535897932384626433832795029L /* pi
*/
//void readwave(const char *filename, double *tr);
short FFT(short int dir, long m, double *x, double *y);
double fstep;
int main (int argc, char *argv [])
{
int i;
double f = 0.0;
double amplitude = 0.; double samplerate = 44100.,
frekwenz=796.7285; double xarg=0. , argplus = 0. , pti ;
double real[BUFFER], img[BUFFER];
// ==== =====
argplus = ( frekwenz *2.0*M_PI )/samplerate;
//printf("Reading %d bytes from %s.rn", BUFFER, argv[1]);
for(i=0;i<BUFFER;i++)
{ // xarg=0.0; argplus=0.2; floarArg =0.0001;
pti = sin ( xarg ) /* * 32767.0*/ ;
/*if ( kante>0 ) { if( pti > 0.0 ){ pti = 23767.0;
}else{pti = -23767.0;} } /**/
xarg = xarg +argplus;//+floarArg;
if ( xarg > (4.0*M_PI) ) xarg = xarg - (4.0*M_PI);
real[i] = pti;
}
printf("F= %7.2f Hz nn", (samplerate*argplus)/(2*M_PI) );
memset(img, 0, sizeof(img)); /* Fill all the imaginary parts with
zeros */
//fstep = (double) samplerate / (double) (BUFFER*2);
fstep = (double) samplerate / (double) (BUFFER);
printf("Frequency step : %10.6frn", fstep);
FFT(1, /*11*/ 12, real, img); /* Fast Fourier Transform with 2^11
bins */
// FFT(1, 7, real, img); /* Fast Fourier Transform with 2^11 bins
*/
/* Write fourier transformed data to stdio */
i = 0;
while(i < BUFFER)
{
amplitude = sqrt((real[i]*real[i]) + (img[i]*img[i]));
//printf("(%4d) %.2f Hz: Re=%f Im=%f P=%frn", i, f, real[i],
img[i], amplitude);
printf("[%4d] %8.4f Hz: Re=%22.16f Im=%22.16f M=%22.16fn", i,
f, real[i], img[i], amplitude);
i++;
f += fstep;
}
return 0 ;
} // main
/*
This computes an in-place complex-to-complex FFT
x and y are the real and imaginary arrays of 2^m points.
dir = 1 gives forward transform
dir = -1 gives reverse transform
*/
short FFT(short int dir, long m, double *x, double *y)
{
long n,i,i1,j,k,i2,l,l1,l2;
double c1,c2,tx,ty,t1,t2,u1,u2,z;
/* Calculate the number of points N = M ^2 */
n = 1; for (i=0;i<m;i++) n *= 2;
printf("FFT -->> (n) 2 ^ %ld = %ldn", m, n);
/* Do the bit reversal */
i2 = n >> 1;
j = 0;
for (i=0;i<n-1;i++) {
if (i < j) {
tx = x[i]; ty = y[i];
x[i] = x[j]; y[i] = y[j];
x[j] = tx; y[j] = ty;
}
k = i2;
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
} // for (i=0;i<n-1;i++)
/* Compute the FFT */
c1 = -1.0;
c2 = 0.0;
l2 = 1;
for (l=0;l<m;l++) {
l1 = l2;
l2 <<= 1;
u1 = 1.0;
u2 = 0.0;
for (j=0;j<l1;j++) {
for (i=j;i<n;i+=l2) {
i1 = i + l1;
t1 = u1 * x[i1] - u2 * y[i1]; t2 = u1 * y[i1] + u2 *
x[i1];
x[i1] = x[i] - t1; y[i1] = y[i] - t2;
x[i] += t1; y[i] += t2;
}
z = u1 * c1 - u2 * c2;
u2 = u1 * c2 + u2 * c1;
u1 = z;
}
c2 = sqrt((1.0 - c1) / 2.0);
if (dir == 1)
c2 = -c2;
c1 = sqrt((1.0 + c1) / 2.0);
}
/* Scaling for forward transform */
if (dir == 1) {
for (i=0;i<n;i++) {
x[i] /= n; y[i] /= n;
}
}
return(1); //return(TRUE);
}
----
Hi experts.
I have started my small project - mp3 database for radio.
http://martini.pudele.com/radio/mp3_database/mp3_database.html
How do i normalize by peak [not RMS] and trim silences in begin and
end of WAV files ?
Silences somewhere in middle of file i wanna leave untouched.
I wanna in first step detect MAX sample in whole WAV file.
For example we gotta MAX sample 10 000, then Apmliefier_coefficient
will be 32 000/10 000 = 3,2 .
In second step i wanna trim silences at begin and below -80 dB [or 2
bit noise]
For this in same file each sample multiple by Apmliefier_coefficient
, and see - result is over -80 dB or not.
If not, then first N samples will not written in trimmed file, but
first sample that is over -80 dB [in any channel] ,
and all further samples written in new file.
Now we must just follow which sample [in any channel] is over -80
dB.
After write is complete, we can just truncate after last sample that
was over -80 dB, and write header.
So far i have found SOX vanna reverse da file for end silence trim,
and for each step produce tmp file.
Is here C API , program, script, way to do so what without any
temporary files ?
I have written script for normalize, but what ir best way for
normalize ?
What about mp3 and ogg automatic normalize and frames trim ?
Tnx in advance
Alf
====
#!/bin/bash
for i in *.wav; do
val=${i%.wav}
echo "** Check peak for $i **"
ampl=`sox "$i" -t wav /dev/null stat -v 2>&1`
waveaus=${i%.wav}.wave
wert1="1.1"; wert2=$ampl;
wahr=$(echo "$wert1 > $wert2" | bc)
if [ $wahr = 1 ]; then
echo " $wert1 > $wert2 , Do Nuthin"
else
echo " $wert1 <= $wert2 , Do process"
echo "** Amplifying volume by -=$ampl=- to fake a normalize
$val.wav -- $waveaus"
ampl2=$(echo $ampl*0.9995 | bc -l)
echo "ampl2 = $ampl2"
sox -v $ampl2 "$i" -t wav "$waveaus"
fi
echo ""
done
----
From: Stefano D'Angelo <zanga.mail(a)gmail.com>
Date: 2011/2/27
Subject: Re: [LAD] RDF libraries, was Re: [ANN] IR: LV2 Convolution Reverb
To: Giuseppe Zompatori <siliconjoe(a)gmail.com>
Cc: linux-audio-dev(a)lists.linuxaudio.org
2011/2/27 Stefano D'Angelo <zanga.mail(a)gmail.com>:
>
>
> Ciao Giuseppe,
>
Ciao Stefano,
Taking this email to a new thread.
>Well... they seem to have a lot of stuff there. :-)
>
>However, I wonder how they do it... I think they are probably using
>some black box modeling, since multiple nonlinearities+feedback in a
>single system is very hard to model.
>
They are very silent on this sadly, don't know what they are doing.
>
>The kind of stuff I'm trying to do is accurately model a class A amp
>with a single triode using white box techniques... to give you an idea
>of what it sounds like see this:
>http://www.youtube.com/watch?v=cdNtmaIdLdo - it is part of my MSc
>thesis presentation (100.000 lire guitar, dated and slow laptop, cheap
>speaker and cheap camera... only the sound card is good).
>
>I guess you speak Italian (at least your name suggests that), so enjoy
>my weird southern accent. :-P
>
Very interesting, I tried compiling your thesis with permafrost to try
this out (obtaining the source from the pdf has been hell BTW) but it
bails with an "m_pi" undeclared input/output function...
Anyway, are you limited to the simulation of a half triode with white
box techniques? I think you should model at least both halves of a
triode if you're after accuracy, a single triode amplifier won't even
work in real life (I build tube amps, I know) ;)
Also class A amplifiers aren't very popular amongst guitar players
(mainly because of their clipping behavior). You also want a
multi-stage preamp with different filtering/biasing points between
stages.
You might think I am crazy but that's what you'll discover yourself by
observing schematics to popular guitar amps.
Here's a simple (early Fender-like) amp topology:
Tube n. 1
-------------------------------------------------------
Tube n. 2 Tube n. 3 and
4
| |
|
|
1st triode -> Tone stack -> post tone stack recovery triode -> P.I.
(Phase inverter) triodes -> (at least 2) Pentodes -> O.T. (Output
Transformer) -> Speakers
^
^
|
|
Presence
pot<--------------------------------negative-feedback---------------------------------------------
This is the easiest PP (Push Pull) class A/B amp I could come up with
(sounds pretty darn good in real life). It has got a tone stack, 4
tubes (2 triodes and two pentodes) and an OT/speakers, do you think
this is feasible computational-wise with permafrost?
>Well, they say guitarix has improved, yet the last time I was all but
>satisfied with it. You may want to take a look at invada plugins, if
>you haven't already.
Invada has a simple generic tube drive function AFAIK, I still prefer
the CAPS* amp over it as it's at least based on a real amp.
>Stammi bene,
>
>Stefano
Anche tu!
-Giuseppe