First I prepared some test files to print to get an idea of what is possible with the printer and optimize the dimensions of the grooves. These record files have circular grooves on them containing sine waves of various frequencies, amplitudes, groove depths, groove widths, and beveled groove edges. (When I say that the groove "contained" a sine wave, I mean the bottom of the groves moves up and down in a sinusoidal pattern around the record). I generated all of these files in Processing using the ModelBuilder library to export straight to STL.



TEST ONE:



My first test record had 72 grooves on it, screen shots of the model are shown in figs 2 through 6 I tested two frequencies of sine waves:



1000 cycles per revolution = 555Hz at 33RPM

500 cycles per revolution = 277Hz at 33RPM



I tested a few different amplitudes, depths, and groove widths for these frequencies and gave each groove a constant bevel size of 2px on each side (you can see in fig 5 how the edges of the groove flare outward). I printed the record in Objet's Vero Clear material, this material is a fairly hard, clear resin. I printed the file with the "smooth" setting to prevent any support material from being deposited in the grooves. Unfortunately, when I was ready to make this print we were having some problems with power in our shop, so I had to use another Objet machine that was not set up for high resolution printing; the best I could do was 300DPI X/Y resolution with 30um Z steps. This is half the resolution that each of these axes is capable of, meaning the print came out at (1/2)3, or 1/8th resolution overall. The results are shown in the video below (the grooves were not deep enough to keep the needle inside, so I had to hold in it place with my hand). The record was also a little big for my record player, I decreased the diameter of my STL file to 11.8" in later versions.





<pre>//sine tests //by Amanda Ghassaei //Dec 2012 //https://www.instructables.com/id/3D-Printed-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.opengl.*; import unlekker.util.*; import unlekker.modelbuilder.*; import ec.util.*; UVertexList recordPerimeterUpper,recordPerimeterLower,recordHoleUpper,recordHoleLower;//storage for perimeter and center hole of record UVertexList lastEdge;//storage for conecting one groove to the next UGeometry geo;//storage for stl geometry //variables float theta;//angle variable float thetaIter = 10000;//how many values of theta per cycle float radius;//variable to calculate radius of grooves int diameter = 12;//diameter of record in inches float innerHole = 0.286;//diameter of center hole in inches float innerRad = 2.35;//radius of innermost groove in inches float outerRad = 5.75;//radius of outermost groove in inches float grooveSpacing = 20;//pixel spacing of grooves float bevel = 2;//pixel width of groove bevel //record parameters float recordHeight = 0.08;//height of record in inches int recordBottom = 0;//height of bottom of record //parameters to test float amplitude[] = {2,4,8};//in units of 16 micron steps (remember this is the amplitude of the sine wave, the total vert displacement will be twice this) int frequency[] = {1000,500,0};//cycles per rotation float depth[] = {0.5,1,0};//how many 16 microns steps below the surface of the record to print the uppermost point of the groove float grooveWidth[] = {1,2,3};//in 600dpi pixels float incrNum = TWO_PI/thetaIter;//calculcate inrementation amount int grooveNum = 0;//variable for keeping track of how long this will take void setup() {//everything that executes in this sketch is contained in the setup() geo = new UGeometry();//place to store geometery of verticies setUpVariables();//convert units, initialize etc setUpRecordShape();//draw basic shape of record drawGrooves();//draw in grooves geo.writeSTL(this, "test.stl");//write stl file from geomtery } void setUpVariables(){ //convert everything to inches float micronsPerInch = 25400;//scalingfactor float dpi = 600; byte micronsPerLayer = 16;//microns per vertical print layer grooveSpacing /= dpi; bevel /= dpi; for(byte i=0;i<3;i++){ amplitude[i] = amplitude[i]*micronsPerLayer/micronsPerInch; depth[i] = depth[i]*micronsPerLayer/micronsPerInch; grooveWidth[i] /= dpi; } } void setUpRecordShape(){ //set up storage recordPerimeterUpper = new UVertexList(); recordPerimeterLower = new UVertexList(); recordHoleUpper = new UVertexList(); recordHoleLower = new UVertexList(); //get verticies for(theta=0;theta<TWO_PI;theta+=incrNum){ //outer edge of record float perimeterX = diameter/2+diameter/2*cos(theta); float perimeterY = diameter/2+diameter/2*sin(theta); recordPerimeterUpper.add(perimeterX,perimeterY,recordHeight); recordPerimeterLower.add(perimeterX,perimeterY,recordBottom); //center hole float centerHoleX = diameter/2+innerHole/2*cos(theta); float centerHoleY = diameter/2+innerHole/2*sin(theta); recordHoleUpper.add(centerHoleX,centerHoleY,recordHeight); recordHoleLower.add(centerHoleX,centerHoleY,recordBottom); } //close vertex lists (closed loops) recordPerimeterUpper.close(); recordPerimeterLower.close(); recordHoleUpper.close(); recordHoleLower.close(); //connect verticies geo.quadStrip(recordHoleUpper,recordHoleLower); geo.quadStrip(recordHoleLower,recordPerimeterLower); geo.quadStrip(recordPerimeterLower,recordPerimeterUpper); //to start, outer edge of record is the last egde we need to connect to with the outmost groove lastEdge = new UVertexList(); lastEdge.add(recordPerimeterUpper); println("record drawn, starting grooves"); grooveNum = 0;//variable for keeping track of how much longer this will take } void drawGrooves(){ UVertexList grooveOuterUpper,grooveOuterLower,grooveInnerUpper,grooveInnerLower;//groove verticies //set up storage grooveOuterUpper = new UVertexList(); grooveOuterLower = new UVertexList(); grooveInnerUpper = new UVertexList(); grooveInnerLower = new UVertexList(); //DRAW GROOVES radius = outerRad;//outermost radius (at 5.75") to start for(byte frequencyIndex=0;frequencyIndex<2;frequencyIndex++){ for(byte amplitudeIndex=0;amplitudeIndex<3;amplitudeIndex++){ for(byte grooveDepthIndex=0;grooveDepthIndex<2;grooveDepthIndex++){ for(byte grooveWidthIndex=0;grooveWidthIndex<3;grooveWidthIndex++){ for(byte copies=0;copies<2;copies++){ //clear lists grooveOuterUpper.reset(); grooveOuterLower.reset(); grooveInnerUpper.reset(); grooveInnerLower.reset(); for(theta=0;theta<TWO_PI;theta+=incrNum){//for theta between 0 and 2pi float sineTheta = sin(theta); float cosineTheta = cos(theta); //calculate height of groove float grooveHeight = recordHeight-depth[grooveDepthIndex]-amplitude[amplitudeIndex]+amplitude[amplitudeIndex]*sin(theta*frequency[frequencyIndex]); grooveOuterUpper.add((diameter/2+(radius+bevel)*cosineTheta),(diameter/2+(radius+bevel)*sineTheta),recordHeight); grooveOuterLower.add((diameter/2+radius*cosineTheta),(diameter/2+radius*sineTheta),grooveHeight); grooveInnerLower.add((diameter/2+(radius-grooveWidth[grooveWidthIndex])*cosineTheta),(diameter/2+(radius-grooveWidth[grooveWidthIndex])*sineTheta),grooveHeight); grooveInnerUpper.add((diameter/2+(radius-grooveWidth[grooveWidthIndex]-bevel)*cosineTheta),(diameter/2+(radius-grooveWidth[grooveWidthIndex]-bevel)*sineTheta),recordHeight); } //close vertex lists (closed loops) grooveOuterUpper.close(); grooveOuterLower.close(); grooveInnerUpper.close(); grooveInnerLower.close(); //connect verticies geo.quadStrip(lastEdge,grooveOuterUpper); geo.quadStrip(grooveOuterUpper,grooveOuterLower); geo.quadStrip(grooveOuterLower,grooveInnerLower); geo.quadStrip(grooveInnerLower,grooveInnerUpper); //set new last edge lastEdge.reset();//clear old data lastEdge.add(grooveInnerUpper); radius -= grooveSpacing+grooveWidth[grooveWidthIndex];//set next radius //tell me how much longer grooveNum++; print(grooveNum); println(" of 72 grooves drawn"); } radius -= 2*grooveSpacing;//extra spacing } radius -= 2*grooveSpacing;//extra spacing } radius -= 2*grooveSpacing;//extra spacing } radius -= 2*grooveSpacing;//extra spacing } geo.quadStrip(lastEdge,recordHoleUpper);//close remaining space between last groove and center hole }

In this video you can hear a periodic frequency sweep on top of the steady sine wave (best heard w headphones). This sweeping sound is caused by the needle moving over the thousands of tiny parallel bumps in the print caused by adjacent print-heads on the Objet machine. This noise is unavoidable, but increasing the strength of the signal will help to make it less noticeable.The Processing sketch that generated this record is given below:

TEST TWO:



In my next test I made a record with 108 grooves, still sine waves, but this time I made the grooves deeper, increase the bevel of each groove to equal half the amplitude of the sine wave, and tried out three different frequencies: 555hz, 277hz, and 139hz (1000, 500, and 250 cycles per revolution at 33.3rpm). I also tested different amplitudes (4, 8 and 16 steps), groove depths (2, and 3 steps below the top of the record), and groove widths (1, 2 and 3 pixels). Since our shop came back online, I switched printers and started printing with Objet's Vero White material, which is similar to Vero Clear in texture, but (as you might image) is a translucent white color. This time I was finally able to print with the full 16 micron and 600 dpi resolution of the printer. Here is a video of the results:











TEST THREE:



In my third test I increased the resolution of my stl file to test out some higher frequency sine waves. I used 22000 points per revolution to draw out the sine waves (as opposed to 10000 in my previous tests), this puts me at about the max resolution I can get with 600dpi (calculated in the last step). I tested three frequencies: 1110hz, 832hz, and 694hz (2000, 1500, and 1250 cycles per revolution at 33.3rpm). I also tested different amplitudes (12 and 16 steps) and groove widths (2 and 3 px). Here is the video:











RESULTS:



At the end of all these tests I learned a few things about 3d printing records with the Objet:



Groove Depth min of 48um below top of record - I found that grooves that kept the waveform at a minimum of 48um (or 3 16 micron steps) below the top of the record kept the needle in place while being played. This was true for all the frequencies I tested.



Groove Width 2px - At lower frequencies I found that the 2px grooves were much less noisy than the 1px, but I didn't hear too much of a difference between 2 and 3px. However, when I tested again with the higher frequencies (2000 cycles/rev) I could hear much more noise on the 3px groove than the 2px.



Frequency Range - at 22000 points per revolution, I easily achieved the upper limit of the human vocal range (about 1.1kHz). Theoretically I should be able to reproduce frequencies equal to half my sampling rate. With a sampling rate of 12kHz (calculated in the last step), the highest frequency I can theoretically achieve is 6kHz. I suspect that the movement of the liquid resin during the curing process will prevent me from actually achieving these frequencies, but if I can just get into the 2kHz range it will still sound reasonably good. Based on the tests I've run so far, I think this is possible.



Dimensions - Although it seems like a 12" record should measure 12" in diameter, I found that printing at 12" made the record slightly too large for my record player. I decreased the diameter down to 11.8" and it worked great.



Max file size of ~300MB - Although Processing is capable of producing much larger files, the Objet Software that runs the printers seems to only handle about 300MB of data at a time. It's possible that increased RAM might bring this up to 500mb, but this still does not give me a lot of room to work with. Although this is plenty for normal CAD purposes, I found out that I would have to be very efficient with the way I packed data onto the STL for the final version of my Processing sketch. One problem with my current sketch is that is has a constant angular sampling rate, this means that the same amount of data is used to describe a groove on the outer edge of the record and a groove near the center of the record. Since the groove at the center of the record is much smaller it would a higher resolution than the outer groove, unfortunately, this extra precision goes to waste because the printer has constant DPI across the entire surface of the record. Eventually, I hope to decrease the angular sampling rate of the inner grooves to save storage space and pack as much audio into the STL file as possible.