Guest User

samsung rescue of vinyl records

a guest
Apr 6th, 2013
722
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import java.awt.Color;
  2. import java.awt.Frame;
  3. import java.awt.Graphics;
  4. import java.awt.event.WindowAdapter;
  5. import java.awt.event.WindowEvent;
  6. import java.awt.image.BufferedImage;
  7. import java.io.ByteArrayInputStream;
  8. import java.io.ByteArrayOutputStream;
  9. import java.io.File;
  10. import javax.imageio.ImageIO;
  11. import javax.sound.sampled.AudioFileFormat;
  12. import javax.sound.sampled.AudioFormat;
  13. import javax.sound.sampled.AudioInputStream;
  14. import javax.sound.sampled.AudioSystem;
  15. import javax.sound.sampled.SourceDataLine;
  16.  
  17. public class GramProcessor {
  18.     private final static AudioFormat af = new AudioFormat((float) 16000, 8, 1, false,
  19.             false);
  20.  
  21.     // this variables are used to control ui position while debugging
  22.     private static final float zoom = 2.5f;
  23.     private final int yoff = -00;
  24.     private final int xoff = -00;
  25.  
  26.     // this are some good constants
  27.     private static final int widthWindowSize = 5;
  28.     private static final int threshold = 180;
  29.     private static final float angleStep = (float) (Math.PI / 4000);
  30.  
  31.     private Frame f = new Frame("window");
  32.     private Graphics g;
  33.  
  34.     private float radius; // current position
  35.     private float angle = 0;
  36.  
  37.     private int numBad = 0; // this variable contains number of possibly
  38.                             // non-track
  39.     // points we've found. if we're off-the-track too long it
  40.     // means track has ended
  41.     int faithSteps = 0; // number of steps we've done since last needle setup.
  42.     float lastGoodAngle; // last angle we've found that what is definitely track
  43.  
  44.     public static void main(String[] args) throws Exception {
  45.         if (args.length < 1) {
  46.             System.err.println("wrong args");
  47.             System.exit(1);
  48.         }
  49.         BufferedImage img = ImageIO.read(new File(args[0]));
  50.         byte[] raw = new GramProcessor().process(img);
  51.         AudioSystem.write(new AudioInputStream(new ByteArrayInputStream(raw), af, raw.length), AudioFileFormat.Type.WAVE, new File(args[0] + ".wav"));
  52.     }
  53.  
  54.     /**
  55.      * processes image
  56.      * @param img image to process
  57.      * @return raw PCM in af format
  58.      * @throws Exception
  59.      */
  60.     public byte[] process(BufferedImage img) throws Exception {
  61.         ByteArrayOutputStream out = new ByteArrayOutputStream();
  62.         f.addWindowListener(new WindowAdapter() {
  63.             @Override
  64.             public void windowClosing(WindowEvent e) {
  65.                 System.exit(0);
  66.             }
  67.         });
  68.  
  69.         // initializing audio engine... 16000 khz looks good enough
  70.         SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
  71.         sdl.open(af);
  72.         sdl.start();
  73.  
  74.         int xsize = img.getWidth();
  75.         int ysize = img.getHeight();
  76.         int xmid = xsize / 2;
  77.         int ymid = ysize / 2;
  78.  
  79.         f.setSize((int) (xsize / zoom), (int) (ysize / zoom));
  80.         f.setVisible(true);
  81.         g = f.getGraphics();
  82.         g.setColor(Color.RED);
  83.         g.drawImage(img, xoff, yoff, (int) (xsize / zoom),
  84.                 (int) (ysize / zoom), null);
  85.  
  86.         int trackY;
  87.  
  88.         // here we are going down in the center of the image searching for point
  89.         // of track. we get medium point of first track we find
  90.         {
  91.             boolean inTrack = false;
  92.  
  93.             int trackYStart = -1, trackYEnd = -1;
  94.  
  95.             for (int y = 0; y < ysize; y++) {
  96.                 if (getWhiteness(img, xmid, y, true) > threshold) {
  97.                     inTrack = true;
  98.                     if (trackYStart < 0) {
  99.                         trackYStart = y;
  100.                     }
  101.                 } else if (inTrack) {
  102.                     trackYEnd = y - 1;
  103.                     break;
  104.                 }
  105.             }
  106.             trackY = (trackYStart + trackYEnd) / 2;
  107.         }
  108.  
  109.         // so now we at topmost position of outer track
  110.         angle = 0;
  111.         radius = ymid - trackY;
  112.         numBad = 0;
  113.  
  114.         // let's roll clockwise to find track's beginning
  115.         while (numBad < 4) {
  116.             doStep(-1, img, xmid, ymid, false);
  117.         }
  118.         angle += 4 * angleStep;
  119.  
  120.         lastGoodAngle = angle;
  121.         faithSteps = 0;
  122.         numBad = 0;
  123.  
  124.         // no we read track counter-clockwise till the end
  125.         while (numBad < 35) {
  126.             int wb = doStep(1, img, xmid, ymid, true);
  127.             // and play bytes we've read
  128.             sdl.write(new byte[] { (byte) (wb) }, 0, 1);
  129.             out.write(wb);
  130.         }
  131.  
  132.         // stopping audio engine
  133.         sdl.drain();
  134.         sdl.stop();
  135.         sdl.close();
  136.         return out.toByteArray();
  137.     }
  138.  
  139.     /**
  140.      * this function makes step on the track and corrects needle position if needed
  141.      *
  142.      * @param sign
  143.      *            if negative, CW, otherwise CCW
  144.      * @param img
  145.      *            img we read
  146.      * @param xmid
  147.      *            image center
  148.      * @param ymid
  149.      *            image center
  150.      * @param doVisualisation
  151.      *            marks if we must display progress in UI
  152.      * @return byte we've read on this step
  153.      */
  154.     private byte doStep(int sign, BufferedImage img, int xmid, int ymid,
  155.             boolean doVisualisation) {
  156.         angle += Math.signum(sign) * angleStep;
  157.         faithSteps++;
  158.         int wtn = getWhiteness(img, xmid, ymid, radius, angle, doVisualisation);
  159.  
  160.         if (wtn > threshold) {
  161.             lastGoodAngle = angle;
  162.         }
  163.  
  164.         if ((wtn < threshold) || (faithSteps > 50)) {
  165.             // we may need to correct the needle: we're off-the track or we're
  166.             // on the track too long and we may read blurred pixels near track
  167.             // edge.
  168.  
  169.             float delta = 0;
  170.             int iterationsNum = 3;
  171.             int goodIterationsNum = 0;
  172.  
  173.             // so we go to last point we knew we're on the track and find
  174.             // track's middle point at that angle. We do this for some angles
  175.             // before this angle too cause there are artifacts sometimes
  176.             for (int i = 0; i < iterationsNum; i++) {
  177.                 float windowMinGood = -1;
  178.                 float windowMaxGood = -1;
  179.                 faithSteps = 0;
  180.  
  181.                 // finding track middle point is easy: we vary current radius
  182.                 // and see if we're on track or not
  183.                 for (int windowIterator = 0; windowIterator < widthWindowSize * 2; windowIterator++) {
  184.                     int probe = getWhiteness(img, xmid, ymid, radius
  185.                             - widthWindowSize + windowIterator, lastGoodAngle
  186.                             - i * angleStep, doVisualisation);
  187.                     if (probe > threshold) {
  188.                         if (windowMinGood < 0)
  189.                             windowMinGood = windowIterator;
  190.                         windowMaxGood = windowIterator;
  191.                     } else if (windowMaxGood >= 0) {
  192.                         break;
  193.                     }
  194.                 }
  195.                 if (windowMaxGood > 0) { // we've found track, let's add it's
  196.                                             // actual radius
  197.                     delta += (windowMinGood + windowMaxGood) / 2;
  198.                     goodIterationsNum++;
  199.                 }
  200.             }
  201.  
  202.             if (goodIterationsNum > 0)
  203.                 radius = radius - widthWindowSize + delta / goodIterationsNum;
  204.  
  205.             wtn = getWhiteness(img, xmid, ymid, radius, angle, doVisualisation);
  206.             numBad++;
  207.         } else {
  208.             numBad = 0;
  209.         }
  210.         return (byte) (wtn);
  211.     }
  212.  
  213.     /**
  214.      * this function gets brightness value by radial coords
  215.      *
  216.      * @param img
  217.      * @param xmid
  218.      * @param ymid
  219.      * @param radius
  220.      * @param angle
  221.      * @param doVisualisation
  222.      * @return
  223.      */
  224.     private int getWhiteness(BufferedImage img, int xmid, int ymid,
  225.             float radius, float angle, boolean doVisualisation) {
  226.         int x = (int) (xmid - radius * Math.sin(angle));
  227.         int y = (int) (ymid - radius * Math.cos(angle));
  228.         return getWhiteness(img, x, y, doVisualisation);
  229.     }
  230.  
  231.     /**
  232.      * this function gets brightness value by decart coords
  233.      *
  234.      * @param img
  235.      * @param xi
  236.      * @param yi
  237.      * @param doVisualisation
  238.      * @return
  239.      */
  240.     private int getWhiteness(BufferedImage img, int xi, int yi,
  241.             boolean doVisualisation) {
  242.         int res = img.getRGB(xi, yi) & 0xff;
  243.         if (doVisualisation) {
  244.             if (res > threshold)
  245.                 g.setColor(Color.GREEN);
  246.             else
  247.                 g.setColor(Color.RED);
  248.             g.drawLine((int) (xi / zoom) + xoff, (int) (yi / zoom) + yoff,
  249.                     (int) (xi / zoom) + xoff, (int) (yi / zoom) + yoff);
  250.         }
  251.         return res;
  252.     }
  253. }
RAW Paste Data