<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>theremin &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/theremin/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Tue, 10 Jun 2025 12:50:21 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>theremin &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Optical Theremin - Demo]]></title>
		<link>https://shkspr.mobi/blog/2012/06/optical-theremin-demo/</link>
					<comments>https://shkspr.mobi/blog/2012/06/optical-theremin-demo/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 10 Jun 2012 15:08:49 +0000</pubDate>
				<category><![CDATA[mobile]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[demo]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[hacks]]></category>
		<category><![CDATA[ota12]]></category>
		<category><![CDATA[over the air]]></category>
		<category><![CDATA[theremin]]></category>
		<guid isPermaLink="false">http://shkspr.mobi/blog/?p=5897</guid>

					<description><![CDATA[At Over The Air I demonstrated what I considered a novel use for one of Android&#039;s sensors.  I wanted to create a Theremin - a type of musical instrument which is played by moving one&#039;s hand over it - changing pitch and tone by moving nearer or further away.    My first attempt used the proximity sensor.  However, on all the Android phones I tried the sensor&#039;s accuracy was binary - it could sense…]]></description>
										<content:encoded><![CDATA[<p>At <a href="https://shkspr.mobi/blog/2012/06/over-the-air-2012/">Over The Air</a> I demonstrated what I considered a novel use for one of Android's sensors.  I wanted to create a Theremin - a type of musical instrument which is played by moving one's hand over it - changing pitch and tone by moving nearer or further away.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2012/06/Edent-theremin-ota12.jpg" alt="" width="500" height="333" class="aligncenter size-full wp-image-15496">

<p>My first attempt used the <a href="http://developer.android.com/reference/android/hardware/Sensor.html#TYPE_PROXIMITY">proximity sensor</a>.  However, on all the Android phones I tried the sensor's accuracy was binary - it could sense if something was close by, but not say <em>how</em> close.</p>

<p>So, what else could I use to detect how near or far a hand was from the screen?  I decided to co-opt the <a href="http://developer.android.com/reference/android/hardware/Sensor.html#TYPE_LIGHT">Light Sensor</a>.  This is normally used to automatically adjust the brightness of the screen - making it easier to see in strong light.</p>

<p>When the light sensor is uncovered, the total lux (that's the measure of light) may be 100. As a hand moves closer to it, that value will dip until it reaches 0 (or, on my phone, 4).</p>

<p>We can then represent that light value as a sound - essentially transforming lx into Hz!</p>

<p>This is what is sounds like</p>

<audio controls="controls">
   <source src="https://shkspr.mobi/blog/wp-content/uploads/2012/06/Terence-Eden-Teremin.ogg">
   <source src="https://shkspr.mobi/blog/wp-content/uploads/2012/06/Terence-Eden-Teremin.mp3">
  Your browser does not support the audio element - <a href="https://shkspr.mobi/blog/wp-content/uploads/2012/06/Terence-Eden-Teremin.mp3">download the track</a>.
</audio>

<p>Beautiful, I'm sure you agree!  You can hear <a href="https://www.bbc.co.uk/programmes/p02swr6h">an interview where I discuss this app with the BBC's Jamillah Knowles on the Outriders Podcast</a> (22m 50s in).
</p><figure class="audio">
	<figcaption>🔊 Outriders 05 Jun 12: Moscow and Bletchley Park<br>🎤 BBC Radio 5 live</figcaption>
	
	<audio controls="" loading="lazy" src="https://shkspr.mobi/blog/wp-content/uploads/2012/06/OutridersPodcast-20120605-MoscowAndBletchleyPark.mp3">
		<p>💾 <a href="https://shkspr.mobi/blog/wp-content/uploads/2012/06/OutridersPodcast-20120605-MoscowAndBletchleyPark.mp3">Download this audio file</a>.</p>
	</audio>
</figure><p></p>

<p>If you want to have a play with it, the <a href="https://web.archive.org/web/20130623224607/https://play.google.com/store/apps/details?id=mobi.shkspr.android.theramin">Optical Theremin Demo is in the Google App Store</a>.  Do note, it was coded in a couple of sleep deprived hours, crashes when you exit, and can produce "music" which scares children and animals. You have been warned!</p>

<h2 id="use-the-source-luke"><a href="https://shkspr.mobi/blog/2012/06/optical-theremin-demo/#use-the-source-luke">Use The Source, Luke!</a></h2>

<p>I've included the full source below, but I'd like to pick out two points which may be of interest.</p>

<h3 id="getting-the-lux-value"><a href="https://shkspr.mobi/blog/2012/06/optical-theremin-demo/#getting-the-lux-value">Getting The Lux Value</a></h3>

<p>Firstly, we need to register a listener for the light sensor.</p>

<pre><code class="language-java">@Override public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
   mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
   mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_FASTEST);
}
</code></pre>

<p>Every time the light sensor changes, this method will be called. It takes the light value and performs a simple mathematical transformation on it (adds 10, multiplies by 5).  I found that this gave the most pleasing sound - but you can adjust it to your tastes</p>

<pre><code class="language-java">@Override public void onSensorChanged(SensorEvent event){
   if (event.sensor.getType()==Sensor.TYPE_LIGHT){
       mLux = event.values[0];
       freqOfTone = (mLux +10) * 5;
   }
}
</code></pre>

<h3 id="cum-on-feel-the-noize"><a href="https://shkspr.mobi/blog/2012/06/optical-theremin-demo/#cum-on-feel-the-noize">Cum on Feel the Noize</a></h3>

<p>So, how do we get Android to generate a tone? I faffed around with <a href="http://stackoverflow.com/questions/2413426/playing-an-arbitrary-tone-with-android">this audio generating code from StackOverflow</a> until I could successfully generate a tone.</p>

<p>Essentially, this creates a WAV of a tone and gets it ready to play.</p>

<p>However, this sounded rather boring, so I added some reverb.</p>

<pre><code class="language-java">audioTrack.attachAuxEffect(EnvironmentalReverb.PARAM_DECAY_TIME);
</code></pre>

<p>And that's it!</p>

<p><a href="https://play.google.com/store/apps/details?id=mobi.shkspr.android.theramin">Download the Optical Theremin Demo App</a> - or use the source to create something much more melodious.</p>

<h3 id="full-source"><a href="https://shkspr.mobi/blog/2012/06/optical-theremin-demo/#full-source">Full Source</a></h3>

<pre><code class="language-java">package mobi.shkspr.android.theremin;

import java.util.Random;

import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.audiofx.EnvironmentalReverb;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;

    public class TheraminActivity
        extends Activity
        implements SensorEventListener{
        // originally from http://marblemice.blogspot.com/2010/04/generate-and-play-tone-in-android.html
        // and modified by Steve Pomeroy steve@staticfree.info

        private final int duration = 5; // seconds
        private final int sampleRate = 8000;
        private final int numSamples = duration * sampleRate;
        private final double sample[] = new double[numSamples];
        private double freqOfTone = 440; // hz

        private final byte generatedSnd[] = new byte[2 * numSamples];

        private SensorManager mSensorManager;
        private Sensor mLightSensor;
        private float mLux = 0.0f;
        private String tLux = "Lux is ";

        public AudioTrack audioTrack;

        Handler handler = new Handler();

        @Override public void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);


            mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
            mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

            mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_FASTEST);

        }

        @Override public void onSensorChanged(SensorEvent event){
            if (event.sensor.getType()==Sensor.TYPE_LIGHT){
                mLux = event.values[0];
                String luxStr = String.valueOf(mLux);
                TextView tv = new TextView(this);
                tv.setText(tLux);
                setContentView(tv);
                Random r = new Random();
                freqOfTone = (mLux +10) * 5;
            }

        }
        @Override protected void onResume() {
            super.onResume();
            final Thread thread = new Thread(new Runnable() {
                public void run() {

                    for (int i = 0; i &lt; 300; i++)   {
                        genTone();

                        audioTrack = new AudioTrack(
                                        AudioManager.STREAM_MUSIC,
                                        sampleRate,
                                        AudioFormat.CHANNEL_OUT_MONO,
                                        AudioFormat.ENCODING_PCM_16BIT,
                                        numSamples,
                                        AudioTrack.MODE_STATIC);


                            try {

                                playSound();
                                Thread.sleep(505);
                            } catch (IllegalStateException e) {

                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                    }
                }
            });

            thread.start();
        }

        void genTone(){ // fill out the array
            tLux = "Frequency is " + freqOfTone;
            //Log.d("LUXTAG", "Lux value: " + tLux);

            for (int i = 0; i &lt; numSamples; ++i) {
                sample[i] = Math.sin(2 * Math.PI * i /(sampleRate/freqOfTone));
            }

        // convert to 16 bit pcm sound array
        // assumes the sample buffer is normalised.
            int idx = 0; for (final double dVal : sample) {
                // scale to maximum amplitude
                final short val = (short) ((dVal * 32767)); // in 16 bit wav PCM, first byte is the low order byte
                generatedSnd[idx++] = (byte) (val &amp; 0x00ff);
                generatedSnd[idx++] = (byte) ((val &amp; 0xff00) &gt;&gt;&gt; 8);

            }
        }

        void playSound(){
            genTone();
            try {                   audioTrack.attachAuxEffect(EnvironmentalReverb.PARAM_DECAY_TIME);
                audioTrack.write(generatedSnd, 0, generatedSnd.length); audioTrack.play();
            } catch (IllegalStateException e) {
                audioTrack.release();
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
            // TODO Auto-generated method stub
        }

        @Override
        public void onPause() {
            super.onPause();
            audioTrack.stop();
            audioTrack.flush();
            audioTrack.release();
        }

        @Override
        public void onStop() {
            super.onStop();
            audioTrack.stop();
            audioTrack.flush();
            audioTrack.release();
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            audioTrack.flush();
            audioTrack.stop();
            audioTrack.flush();
            audioTrack.release();
        }
    }
</code></pre>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=5897&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2012/06/optical-theremin-demo/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2012/06/Terence-Eden-Teremin.ogg" length="121342" type="audio/ogg" />
<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2012/06/Terence-Eden-Teremin.mp3" length="198628" type="audio/mpeg" />
<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2012/06/OutridersPodcast-20120605-MoscowAndBletchleyPark.mp3" length="1227152" type="audio/mpeg" />

			</item>
	</channel>
</rss>
