The art of using compression glitches as visual tools for creating art has been around a some time now. Datamoshing yields many hits, as do glitch. But how could you apply the same effects to audio and get more interesting results then we have to day with the mp3 FFT artifact?
That question was thrown out in a conversation between me and Rosa late one night at Norbergfestivalen. I recently met her when we carpooled to the festival. Anyways, she’s writing her doctorate about these things and that was one of the questions she was thinking about. To bad she addressed that to me, because I started thinking about it also.
I’ve gone through and tried a few things, but none of them sounded very good. Basic RLE decoding of data to a waveform. Fourier compression data corruption test’s. Packaging raw data in a zip-file, change the contest of the zip data, and then re-import the raw-data. None of these where very interesting, but they are easy to do without any programming. I also listened to the scratched mp3-cd’s I have in my car. Where the most interesting effects where a sort of LP-like stuttering. But not interesting enough.
So what to do to make things a little more interesting? Well one has to look at the problem from another angle I suppose.
I have to create a compression-algorithm that makes more sense to break. Sounds stupid? I guess, but if we cant be stupid for the sake of art or comedy when are we allowed to be stupid?
Right. I came up with a modified RLE-algorithm. It’s pretty basic. waveforms are 2D, and the next sample can move up, down, or not move at all. With that you can compress your data with information on where to move for the next sample, and if there is a pattern to it it’s easy to find and repeat. A triangle example follows the data could be: “move up 1 step for every sample 127 times”.
This can also make for seriously fun glitches if you edit the compressed data, or as nice samples if you just randomly generate the compressed data.
As you can see below the implementation is very naive, there are no key frames, the comparison algorithm works by stripping away data (converting from double to float). One could also implement a better RLE that finds patterns. None of this however was very important for me when I wanted to test this idea.
There are a few parameters I’ve been toying around with on a few different wav files. These have yielded results that I’m mildly satisfied with. I don’t know what my expectations where when I started out on this journey, but there has been some success.
I tried it out on a few different sounds, the result’s are a little longer and they don’t share parameters:
bicycle_bell
bicycle_bell_glitched
scream_females.wav
scream_females_glitched
I’ve toyed with the parameters in injectErrors() function. as you can se from the commented out code there are a few different things to play with when trying out the algorithm.
Where to go now?
Well I don’t know, perhaps wrap this in a standalone program, look more into other compression techniques to try out, make a VST out of it. Maybe I’ll just leave it. Possibly I’ll try to make some nice noise songs with this as either a generation device or as an effect on noisy samples. Most possibly I’ll let it mutate itself over time, feeding the same sample back over and over and over and over. Maybe one should try and apply this to MIDI note data. That could be awesome.
#include "stdafx.h"
#include "wav.h"
#include "wav_save.h"
#include
SoundDataStruct* p_wav;
void compressWaveform()
{
int i;
//first run is to change everything to relative path's
double oldData = p_wav->dataP[0];
p_wav->dataRLEWP[0] = p_wav->dataP[0];
for (i = 1; i < p_wav->length; i++)
{
p_wav->dataRLEWP[i] = oldData - p_wav->dataP[i];
oldData = p_wav->dataP[i];
}
p_wav->keyframedata = (keyFrameStruct*)malloc(sizeof(keyFrameStruct)*(p_wav->length/100));
//add keyframes
int pos=0;
for (i = 1; i < p_wav->length; i+=100)
{
p_wav->keyframedata[pos].data=p_wav->dataP[i];
p_wav->keyframedata[pos].position =i;
pos++;
}
//now we RLE Encode it for real.
int writeplace=0;
for (i = 0; i < p_wav->length; i++)
{
int runLength = 1;
while( i+1 < p_wav->length && (float)p_wav->dataRLEWP[i] == (float)p_wav->dataRLEWP[i+1] ) {
runLength++;
i++;
}
p_wav->dataRLEP[writeplace]=runLength;
writeplace++;
p_wav->dataRLEP[writeplace]=p_wav->dataRLEWP[i];
writeplace++;
}
p_wav->RLELengthP = writeplace;
printf("compression is %d \n",p_wav->length-p_wav->RLELengthP);
}
double clamp(double in)
{
if(in>1.0)
return 1.0;
else if(in<-1.0)
return -1.0;
else return in;
}
void decompressWaveform()
{
int i,x;
int writeplace =0;
int newLength=0;
//find length for the new sample.
for (i = 0; i < p_wav->RLELengthP; i+=2)
newLength+=p_wav->dataRLEP[i];
printf("newLength % = %d \n ",newLength);
p_wav->dataU = (double *)malloc(sizeof(double)*newLength);
p_wav->dataRLEWU = (double *)malloc(sizeof(double)*newLength);
//undo RLE Encoding
for (i = 0; i < p_wav->RLELengthP; i+=2)
{
for(x=0;x<(int)p_wav->dataRLEP[i];x++)
{
p_wav->dataRLEWU[writeplace] = p_wav->dataRLEP[i+1];
writeplace++;
}
}
p_wav->RLEWLengthU = writeplace;
//undo RLEW Encoding
int pos=0;
p_wav->dataU[0]= p_wav->dataRLEWU[0];
for (i = 1; i < p_wav->RLEWLengthU; i++)
{
p_wav->dataU[i] = p_wav->dataU[i-1]+p_wav->dataRLEWU[i];
p_wav->dataU[i] = clamp(p_wav->dataU[i]);
}
}
void compareWaveforms()
{
int i;
if(p_wav->length != p_wav->RLEWLengthU)
{
printf("data not equal\n");
printf("%d\n",p_wav->length);
printf("%d\n",p_wav->RLEWLengthU);
}
for (i = 0; i < p_wav->RLEWLengthU; i++)
{
if(i>p_wav->length)
break;
if(i>p_wav->RLEWLengthU)
break;
}
}
void injectErrors()
{
int i=0;
//randomizing number of repeat's
for (i = 0; i < p_wav->RLELengthP; i+=2)
{
if(rand()%200==1)
{
p_wav->dataRLEP[i]=(rand()%512)+1024;
// p_wav->dataRLEP[i] -= ((double) (rand() / (double)RAND_MAX)*0.4f)-0.2f;
p_wav->dataRLEP[i] -= ((double) (rand() / (double)RAND_MAX));
}
}
//randomizing data to repeat
for (i = 1; i < p_wav->RLELengthP; i+=2)
{
if(rand()%4000==1)
{
//p_wav->dataRLEP[i] -= ((double) (rand() / (double)RAND_MAX)*0.4f)-0.2f;
}
}
}
void savewav()
{
float calculatedlengthofnewsample = float(p_wav->RLEWLengthU)/44100.0;
Init_save(calculatedlengthofnewsample+1.0);
int i;
for ( i=0; i
RLEWLengthU; i+=1 )
{
SaveWavData(i, p_wav->dataU[i] );
}
Do("temporary.wav"); //save might crash if called to many times
freemeData();
//---------- end render block
}
int _tmain(int argc, _TCHAR* argv[])
{
srand( 1234 );
/* get input wav */
p_wav = (SoundDataStruct*)malloc(sizeof(SoundDataStruct));
wav_init( "bicycle_bell.wav", p_wav );
compressWaveform();
decompressWaveform();
compareWaveforms();
/* the fun begins here*/
injectErrors();
decompressWaveform();
compareWaveforms();
/* save wav */
savewav();
return 0;
}
yes daddy, give it to me
You want program compiled? yes no?
The effects really strongly remind me of the geometer VST from Destroy FX http://destroyfx.smartelectronix.com/
I think maybe it’s all the clipping though 🙂
Pingback: Compression | Anh Nguyen