//audio tests //by Amanda Ghassaei //Jan 2013 //https://www.instructables.com/id/Laser-Cut-Record/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ import processing.pdf.*; //parameters String filename = "lovewilltearusapart.txt"; float rpm = 33.3; float samplingRate = 44100; float dpi = 1200;//dpi of cutter float amplitude = 16;//in pixels int frequency = 500;//cycles per rotation float spacing = 10;//space between grooves (in pixels) float minDist = 6.0;//min pixel spacing between points in vector path (to prevent cutter from freaking out) 0.25919998 float diameter = 11.8;//diameter of record in inches float innerHole = 0.286;//diameter of center hole in inches float innerRad = 5;//2.2;//radius of innermost groove in inches float outerRad = 5.75;//radius of outermost groove in inches boolean cutlines = false; //constants float secPerMin = 60; int scaleNum = 72;//scale factor of vectors (default 72 dpi) //storage for a given point in the groove float radCalc; float xVal; float yVal; float theta;//angle variable float thetaIter = samplingRate/2*secPerMin/rpm;//how many values of theta per cycle float radius;//variable to calculate radius of grooves float xValLast = 0.0; float yValLast = 0.0; void setup(){ println(thetaIter); size(36*scaleNum,24*scaleNum); beginRecord(PDF, filename + ".pdf");//save as PDF background(255);//white background noFill();//don't fill loops strokeWeight(0.001);//hairline width //scale pixel distances amplitude = amplitude/dpi*scaleNum; minDist = minDist/dpi*scaleNum; spacing = spacing/dpi*scaleNum; //draw sine waves float incrNum = TWO_PI/thetaIter;//calculcate angular inrementation amount float radIncrNum = (2*amplitude+spacing)/thetaIter;//radial incrementation amount radius = outerRad*scaleNum;//calculate outermost radius (at 5.75") stroke(255,0,0);//red beginShape(); // //draw silent outer groove // for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi // //calculate new point // radCalc = radius; // xVal = width/6+radCalc*cos(theta); // yVal = height/4-radCalc*sin(theta); // if(((xValLast-xVal)*(xValLast-xVal)+(yValLast-yVal)*(yValLast-yVal))>(minDist*minDist)){ // vertex(xVal,yVal); // //store last coordinates in vector path // xValLast = xVal; // yValLast = yVal; // } // radius -= radIncrNum;//decreasing radius forms spiral // } float[] songData = processAudioData(); int numGrooves = 0; int index = 0; while(radius>innerRad*scaleNum){ int numpoints = 0; for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi //calculate new point radCalc = radius+songData[index]; index+=2; xVal = width/6+radCalc*cos(theta); yVal = height/4-radCalc*sin(theta); if(((xValLast-xVal)*(xValLast-xVal)+(yValLast-yVal)*(yValLast-yVal))>(minDist*minDist)){ vertex(xVal,yVal); //store last coordinates in vector path xValLast = xVal; yValLast = yVal; numpoints++; } radius -= radIncrNum;//decreasing radius forms spiral } numGrooves++; println(numGrooves); println(numpoints); } endShape(); if (cutlines){ //draw cut lines (100 units = 1") stroke(0);//draw in black ellipse(width/6,height/4,innerHole*scaleNum,innerHole*scaleNum);//0.286" center hole ellipse(width/6,height/4,diameter*scaleNum,diameter*scaleNum);//12" diameter outer edge } endRecord(); exit(); //tell me when it's over println("Finished."); } float[] processAudioData(){ //get data out of txt file String rawData[] = loadStrings(filename); String rawDataString = rawData[0]; float audioData[] = float(split(rawDataString,','));//separated by commas //normalize audio data to given bitdepth //first find max val float maxval = 0; for(int i=0;i<audioData.length;i++){ if (abs(audioData[i])>maxval){ maxval = abs(audioData[i]); } } //normalize amplitude to max val for(int i=0;i<audioData.length;i++){ audioData[i]*=amplitude/maxval; } return audioData; }

//audio tests //by Amanda Ghassaei //Jan 2013 //https://www.instructables.com/id/Laser-Cut-Record/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ import processing.pdf.*; //parameters String filename = "lovewilltearusapart.txt"; float rpm = 33.3; float samplingRate = 44100; float dpi = 1200;//dpi of cutter float amplitude = 16;//in pixels float spacing = 10;//space between grooves (in pixels) float minDist = 6.0;//min pixel spacing between points in vector path (to prevent cutter from freaking out) 0.25919998 float diameter = 11.8;//diameter of record in inches float innerHole = 0.286;//diameter of center hole in inches float innerRad = 5;//2.2;//radius of innermost groove in inches float outerRad = 5.75;//radius of outermost groove in inches boolean cutlines = false;//cut the inner and outer perimeters //constants float secPerMin = 60; int scaleNum = 72;//scale factor of vectors (default 72 dpi) //storage for a given point in the groove float radCalc; float xVal; float yVal; float theta;//angle variable float thetaIter = 6000;//how many values of theta per cycle float radius;//variable to calculate radius of grooves float xValLast = 0.0; float yValLast = 0.0; void setup(){ size(36*scaleNum,24*scaleNum); beginRecord(PDF, filename + ".pdf");//save as PDF background(255);//white background noFill();//don't fill loops strokeWeight(0.001);//hairline width //scale pixel distances amplitude = amplitude/dpi*scaleNum; minDist = minDist/dpi*scaleNum; spacing = spacing/dpi*scaleNum; //draw sine waves float incrNum = TWO_PI/thetaIter;//calculcate angular inrementation amount float radIncrNum = (2*amplitude+spacing)/thetaIter;//radial incrementation amount radius = outerRad*scaleNum;//calculate outermost radius (at 5.75") stroke(255,0,0);//red beginShape(); // //draw silent outer groove // for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi // //calculate new point // radCalc = radius; // xVal = width/6+radCalc*cos(theta); // yVal = height/4-radCalc*sin(theta); // if(((xValLast-xVal)*(xValLast-xVal)+(yValLast-yVal)*(yValLast-yVal))>(minDist*minDist)){ // vertex(xVal,yVal); // //store last coordinates in vector path // xValLast = xVal; // yValLast = yVal; // } // radius -= radIncrNum;//decreasing radius forms spiral // } float[] songData = processAudioData(); int numGrooves = 0; int index = 0; while(radius>innerRad*scaleNum){ int numpoints = 0; for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi //calculate new point radCalc = radius+songData[index]; index+=(samplingRate*secPerMin/rpm)/thetaIter;//go to next spot in audio data xVal = width/6+radCalc*cos(theta); yVal = 3*height/4-radCalc*sin(theta); if(((xValLast-xVal)*(xValLast-xVal)+(yValLast-yVal)*(yValLast-yVal))>(minDist*minDist)){ vertex(xVal,yVal); //store last coordinates in vector path xValLast = xVal; yValLast = yVal; numpoints++; } radius -= radIncrNum;//decreasing radius forms spiral } numGrooves++; println(numGrooves); println(numpoints); } endShape(); if (cutlines){ //draw cut lines (100 units = 1") stroke(0);//draw in black ellipse(width/6,3*height/4,innerHole*scaleNum,innerHole*scaleNum);//0.286" center hole ellipse(width/6,3*height/4,diameter*scaleNum,diameter*scaleNum);//12" diameter outer edge } endRecord(); exit(); //tell me when it's over println("Finished."); } float[] processAudioData(){ //get data out of txt file String rawData[] = loadStrings(filename); String rawDataString = rawData[0]; float audioData[] = float(split(rawDataString,','));//separated by commas //normalize audio data to given bitdepth //first find max val float maxval = 0; for(int i=0;i<audioData.length;i++){ if (abs(audioData[i])>maxval){ maxval = abs(audioData[i]); } } //normalize amplitude to max val for(int i=0;i<audioData.length;i++){ audioData[i]*=amplitude/maxval; } return audioData; }

//audio tests //by Amanda Ghassaei //Jan 2013 //https://www.instructables.com/id/Laser-Cut-Record/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ import processing.pdf.*; //parameters String filename = "idioteque.txt"; float rpm = 45.0; float samplingRate = 44100; float dpi = 1200;//dpi of cutter float amplitude = 10;//in pixels float spacing = 10;//space between grooves (in pixels) float minDist = 6.0;//min pixel spacing between points in vector path (to prevent cutter from freaking out) 0.25919998 float diameter = 11.8;//diameter of record in inches float innerHole = 0.286;//diameter of center hole in inches float innerRad = 2.2;//2.2;//radius of innermost groove in inches float outerRad = 5.75;//radius of outermost groove in inches boolean cutlines = true;//cut the inner and outer perimeters //constants float secPerMin = 60; int scaleNum = 72;//scale factor of vectors (default 72 dpi) //storage for a given point in the groove float radCalc; float xVal; float yVal; float theta;//angle variable float thetaIter = 5880;//how many values of theta per cycle float radius;//variable to calculate radius of grooves float xValLast = 0.0; float yValLast = 0.0; void setup(){ size(36*scaleNum,24*scaleNum); beginRecord(PDF, filename + ".pdf");//save as PDF background(255);//white background noFill();//don't fill loops strokeWeight(0.001);//hairline width //scale pixel distances amplitude = amplitude/dpi*scaleNum; minDist = minDist/dpi*scaleNum; spacing = spacing/dpi*scaleNum; //draw sine waves float incrNum = TWO_PI/thetaIter;//calculcate angular inrementation amount float radIncrNum = (2*amplitude+spacing)/thetaIter;//radial incrementation amount radius = outerRad*scaleNum;//calculate outermost radius (at 5.75") stroke(255,0,0);//red beginShape(); //draw silent outer groove for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi //calculate new point radCalc = radius; xVal = width/6+radCalc*cos(theta); yVal = height/4-radCalc*sin(theta); if(((xValLast-xVal)*(xValLast-xVal)+(yValLast-yVal)*(yValLast-yVal))>(minDist*minDist)){ vertex(xVal,yVal); //store last coordinates in vector path xValLast = xVal; yValLast = yVal; } radius -= radIncrNum;//decreasing radius forms spiral } float[] songData = processAudioData(); int numGrooves = 0; int index = 0; int indexIncr = int((samplingRate*secPerMin/rpm)/thetaIter); while(radius>innerRad*scaleNum && index < songData.length-6000){ int numpoints = 0; for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi //calculate new point radCalc = radius+songData[index]; index+=indexIncr;//go to next spot in audio data xVal = width/6+radCalc*cos(theta); yVal = height/4-radCalc*sin(theta); if(((xValLast-xVal)*(xValLast-xVal)+(yValLast-yVal)*(yValLast-yVal))>(minDist*minDist)){ vertex(xVal,yVal); //store last coordinates in vector path xValLast = xVal; yValLast = yVal; numpoints++; } radius -= radIncrNum;//decreasing radius forms spiral } numGrooves++; println(numGrooves); println(numpoints); } //draw silent inner locked groove for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi //calculate new point radCalc = radius; xVal = width/6+radCalc*cos(theta); yVal = height/4-radCalc*sin(theta); if(((xValLast-xVal)*(xValLast-xVal)+(yValLast-yVal)*(yValLast-yVal))>(minDist*minDist)){ vertex(xVal,yVal); //store last coordinates in vector path xValLast = xVal; yValLast = yVal; } radius -= radIncrNum;//decreasing radius forms spiral } for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi //calculate new point xVal = width/6+radius*cos(theta); yVal = height/4-radius*sin(theta); if(((xValLast-xVal)*(xValLast-xVal)+(yValLast-yVal)*(yValLast-yVal))>(minDist*minDist)){ vertex(xVal,yVal); //store last coordinates in vector path xValLast = xVal; yValLast = yVal; } } endShape(); if (cutlines){ //draw cut lines (100 units = 1") stroke(0);//draw in black ellipse(width/6,height/4,innerHole*scaleNum,innerHole*scaleNum);//0.286" center hole ellipse(width/6,height/4,diameter*scaleNum,diameter*scaleNum);//12" diameter outer edge } endRecord(); exit(); //tell me when it's over println("Finished."); } float[] processAudioData(){ //get data out of txt file String rawData[] = loadStrings(filename); String rawDataString = rawData[0]; float audioData[] = float(split(rawDataString,','));//separated by commas //normalize audio data to given bitdepth //first find max val float maxval = 0; for(int i=0;i<audioData.length;i++){ if (abs(audioData[i])>maxval){ maxval = abs(audioData[i]); } } //normalize amplitude to max val for(int i=0;i<audioData.length;i++){ audioData[i]*=amplitude/maxval; } return audioData; }

I did a ton of sine tests for my 3D printed record , but I was anxious to launch into the audio for this much sooner so I just went for it. Even though on some level I knew this would be a bit of a disaster, here's my first attempt with audio:The song is Love Will Tear Us Apart by Joy Division. My favorite of all the records I 3d printed was definitely the Joy Division one (the song Disorder) , I like the creepy vibe the distortion gives it.This laser cut joy division track is not quite there yet (though decently recognizable). You can hear a lot of crunchiness on the drum beats, if you could look closely at the record, you would see that these areas of high frequencies were melted into oblivion by the cutter. In this attempt I didn't make any effort to set a max frequency of the cuts, and the tighter cuts required by these sections apparently caused the laser to linger too long on the material.I learned some things about the laser cutter from this attempt. The first couple of tries I made on this cut caused the laser to freeze up almost immediately. At first I thought I was overloading the machine with data, but then I realized that the machine does not like to receive extremely dense vector paths. In fact, I found that if two points on a vector path are within about 6 pixels of each other, the laser will quit. I had to amend my code to account for this. Here is the Processing code I used:As with the 3d printed record, I pulled the raw audio data from the original wav file using Python before sending it to Processing , that code can be found here . And again, if someone knows a way to bypass this step, please feel free to leave a comment, I would much rather keep everything in Processing.In my next test I set a limit on the angular distance between consecutive points, hoping to minimize melting of the material. Here's the code:...and the video:The cut came out much cleaner, and you can hear significantly less distortion on the audio, but I thought I could still make it better. In the next test I set the samples per revolution to a constant number (6000) and removed the minimum angular distance logic from my code.At 6000 samples per cycle the sampling frequency of the audio is:Here's the code:and the video:Though it's a little hard to hear because of all the skipping, the cut came out much cleaner on this test. You can also hear that the audio sounds slowed down, this was a rounding issue in my code that I dealt with later. In my next test I decreased the amplitude of the wave to 12px to see if I could get the needle to stay in the groove.There are still a few issues. Most notably, the record is warped from the cutting process. Also the speed of the audio is still screwed up. In my final version I fixed the speed issue (it was a rounding problem) and tried taping the acrylic down to the bed to see if that would help with the warping. The settings I used on the laser cutter are:In this cut I actually applied the proper RIAA equalization as well and used an anti-aliasing low pass filter of samplingRate/2 = 2.25kHz.Here is the final code:and the final product:There was still some warping, but the tempo issues are completely resolved. It's interesting to hear how much the audio quality degrades from the outside of the disk to the middle of the disk - this is due to lowered surface speed of the record as you move toward the center (explained in step 2 ).