Search This Blog

Wednesday, March 28, 2012

Importing Java source code to eclipse from netbeans

A friend wrote a Java program for analysis with a GUI, etc and I needed to import the source, edit it, compile, etc.

These are the steps to get it up and running in Eclipse.  I've been using eclipse lately instead of netbeans, no comment on which is better, but Eclipse is up and running on my machine and seems lighter on my laptop.



  • ·         Download and unzip eclipse, windows 32 or 64 bit whatever your machine can run
o   You may need to download java re from oracle if you don’t already have it on your machine http://download.cnet.com/Java-Runtime-Environment-JRE/3001-2378_4-10009607.html?spi=b4caa20b0a59eef280e6db2bbc380730  You may have to tell eclipse where the JRE is also.  File->Properties-> Java Build Path-> Libraries, add library. Often C:\Program Files\Java\


  • ·         Unzip the file and click on eclipse icon
  • ·         Set your workspace to a directory where you want to keep your projects
  • ·         Go to workbench in the opening GUI
  • ·         File->New->Java Project
  • ·         Name the project and set JRE to JavaSE-1.6 or whatever you have installed
  • ·         You will see the project appear in the browser with a src and the JRE system library as sub folders
  • ·         Right click on the project src folder and create a new package, call it the same as at the top of the Java files, “backplane2”
  • ·         Select and drag all the .java files onto the src/package name you just created in the Package Explorer” tree on the left.   You will get a [+] and a dialog will pop up asking if you want to copy the files.  Click OK
  • ·         You shouldn’t see any red marks on the java files if all the files and package names are clean
  • ·         Create a run Configuration
o   Run->Run Configurations
o   Java Application
o   New (icon in upper left)
o   Browse and select the project name and Main Class for your project you created
o   Click “Include system libraries when searching”
·      

  •             If you have a GUI  Form file in your source, you can right click on it, select Edit With->Windows Builder to enable the editor to let you edit the file with the graphical GUI builder instead of just the text editor.

  •       Resolve any errors that appear in the files by right clicking, etc.  You may find you are missing a file, need to import a library, etc.  Right click will tell you what's wrong.




Friday, March 23, 2012

Speech Jammer Android App

I saw this whacko japanese article, and decided it might be a fun Android app to build.  I'm still on my Android app jag, maybe someday soon I'll get back to hardware.

http://www2.electronicproducts.com/How_to_build_a_SpeechJammer_Gun-article-fajb_speechjammer_march2012-html.aspx
and
http://arxiv.org/abs/1202.6106v2
Japanese speech jammer gun

Basically if you hear an echo of your own voice within a certain delay range, it bugs you so much, that you stop talking.   This is a way to interrupt a speaker who is droning on and on.  I'll let you figure out when this possibly could be an appropriate action on your part, but hey, that's not my problem.  Maybe if it was your phone you could claim it was some weird accident or tech freak out.  If you have one of these ridiculous devices in the picture, I'm sure you'd find yourself kicked out of whatever venue you were in.

I saw this, and it seems like a silly nerdy project, but it seemed (wrongly) that any android device can record, delay and play back an echo with an adjustable delay.  The only question is if the processing time can be made short enough.  An Arduino could probably do it too.  There may be some minimum delay acheivable, and I may get echos.   I'll have to see.    Of course they had a directional microphone and a directional speaker which I won't have.   But my solution will be free and fun.   Depending on how it works out, I might build an Arduino version with better sound hardware.

The basic app here will record with the mic, process the sound, then play it back.  The fun will be in what processing I do.   This could be a voice changer, or a speech jammer, etc.

First I need to be able to record audio in real time.   I don't want to write audio to the SD card, I just want to fill a small buffer with it and feed it back to AudioTrack.

Took the code from the first answer in this post.  A few tweaks and it compiled.

http://stackoverflow.com/questions/4525206/android-audiorecord-class-process-live-mic-audio-quickly-set-up-callback-func


At first it didn't work, it always failed to initialize Audio recorder, until, duh, I added the permission line to the manifest to allow use of the recorder.  Then recording began to work.

        <uses-permission android:name="android.permission.RECORD_AUDIO"/>

Allright, now it looks like it's recording, no errors, and I printed the buffer size to the Log and it appears to fill.   Now I have to figure out how to access the data and delay and pass it to AudioTrack.

I duplicated the AudioIn Thread class with an AudioOut thread.  I moved the buffer up to be a global variable so both threads could talk to it.  After some horsing around with buffer sizes, I got this code to work, the audio "echo" works and sounds good.  There were some interesting results.


//private short[][]   sound_buffers  = new short[256][160]; 
private short sound[] = new short[1000];



private ToggleButton mToggle;
private TextView mTextView;
public boolean stopped = true;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mToggle = (ToggleButton) findViewById(R.id.toggleButton1);
        mToggle.setChecked(false);
        mTextView = (TextView) findViewById(R.id.textViewEquation);
mTextView.setText("OFF");
// Hook up button presses to the appropriate event handlers.
   ((ToggleButton) findViewById(R.id.toggleButton1)).setOnClickListener(mToggleListener);
 
        
    }
    
    
    /** Called when the activity is exited */
    public void onDestroy(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        stopped = true;
mTextView.setText("OFF");
        
    }
    
    /** Called when the activity is paused. */
    public void onPause(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        stopped = true;
mTextView.setText("OFF");
        
    }
    
   



 /**

     * A call-back for when the user presses the toggle button
     */
    OnClickListener mToggleListener = new OnClickListener() {
        public void onClick(View v) {
     

    stopped = true;
    mTextView.setText("OFF");
 
        //check if light is off, if so, turn it on
        if (mToggle.isChecked()) { //turn on the light
        stopped = false;  //allows the loop in the child thread to run
        mTextView.setText("ON");
       
              try {   
              new AudioIn().start();
             } catch(Throwable x) { 
               Log.d("Record","Error reading voice audio",x);
             }
                     
              try {   
              new AudioOut().start();
             } catch(Throwable x) { 
               Log.d("Playback","Error playing voice audio",x);
             }    
                     
        }
        }
    };


    
    
    
    private class AudioIn extends Thread { 


        private AudioIn() { 
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                start();
             }


        @Override
        public void run() { 
               AudioRecord recorder = null;
               int         ix       = 0;
               stopped = false;


               try { // ... initialise


                     int N = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);


                      recorder = new AudioRecord(AudioSource.MIC,
                                                 8000,
                                                 AudioFormat.CHANNEL_IN_MONO,
                                                 AudioFormat.ENCODING_PCM_16BIT,
                                                 N*2);   


                      recorder.startRecording();


                      // ... loop


                      while(!stopped) { 
                         short[] buffer = sound_buffers[ix++ % sound_buffers.length];


                         N = recorder.read(buffer,0,buffer.length);                     
                     }
                } catch(Throwable x) { 
                  Log.d("Record","Error reading voice audio",x);
                } finally { 
                  //close(recorder);
                  recorder.release();
                  stopped = true;
                }
            }


         private void close() { 
             stopped = true;
           }


       }
    
    private class AudioOut extends Thread { 


        private AudioOut() { 
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                start();
             }


        @Override
        public void run() { 
        int sampleRate = 8000;
        AudioTrack player = null;
        int ix  = 0;
        int written = 0;
        stopped = false;


               try { // ... initialise
              
              int N = AudioTrack.getMinBufferSize(8000,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT);
              //int N= 10000;
              
              player  = new AudioTrack(AudioManager.STREAM_MUSIC,
                sampleRate, AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_16BIT, N,
                AudioTrack.MODE_STREAM);
              
              player.play();


                      // ... loop


                      while(!stopped) { 
                     
                     short[] buffer = sound_buffers[ix++ % sound_buffers.length];


                     written = player.write(buffer, 0, buffer.length);                        
                     }
                } catch(Throwable x) { 
                  Log.d("Playback","Error playing audio",x);
                } finally { 
                  //close
                player.stop();
                stopped = true;
                }
            }


         private void close() { 
             stopped = true;
           }


       }


The interesting result is probably what you might have predicted.  You say a word.   Ten seconds later you hear it played back, which the phone hears and and plays it back again on top of the first sound ten seconds later, then again, and again getting louder each time until is is impossibly loud and you turn it off.  So the processing is going to have to do echo cancelling or only record when it's not playing.  This will require some interesting processing.  I'm not sure how the speaker phone works, or if it just is half duplex and blanks the recording while playing audio.

The delay is about 10 seconds between speaking and hearing your voice back.  That is because there is an array of buffers, providing a lot of lag.  If that is what you are going for, fine, but I wanted a short delay so I replaced the array with a single buffer.

I was a little surprised that this worked just fine.

As a global variable I defined a small buffer

private short sound[] = new short[1000];

Then in the Audio In loop above I loaded it
        recorder.read(sound,0,sound.length);

And finally in Audio out loop above I read it and played it back
        player.write(sound, 0, sound.length);


That worked, and shortened the delay, but now you HAVE to use headphones with the phone or the echos max out the phone in a few seconds. The delay seems to be half a second, and that is as short as it is going to get given that I have the buffers set to min buffer size.

You could use the directional mike and speaker, and hope you don't create feedback. There is probably echo cancelling libraries out there,  However that may be more trouble than this silly app may be worth.   So I'm going to post this dual record/playback thread code and move on to other projects.

Saturday, March 17, 2012

Tuning fork and the Amazon Android app store


I tried submitting two apps so far to the Amazon app store in parallel with the Google market.   Both were rejected.  This post is just a minor frustration release.   My goal in submitting in parallel was to reach the Amazon Kindle Fire which can't see the Google market, unless you jail break it.

The LED blink app, they wrote back and said it didn't work, the camera flash light didn't come on.   Well, OK, they didn't say what phone they used.   I have found there is trouble with some common phones.  I took it in stride.  It wasn't worth trying to get it approved.

I tried again with a simple app that couldn't possibly go wrong.  Well it did.
I submitted the Tuning Fork App.


Get it on Google Play








The second app, my Note Calculator and Tuning fork, they had this to say:


There were some issues with your application submission: issue#1 -If the user navigates away from the application by pressing the device Home key the application audio can still be heard on the device Home screen. Steps to reproduce: 1. Launch the application 2. Select 'Off', notice the application audio plays 3. Press the device Home key 4. Notice while the user is on the device Home screen the application audio is still generated from the background --- issue#2 - If the user receives a voice call while the application is running the application audio can be heard during the voice call. Steps to reproduce: 1. Launch the application 2. Ensure application audio is playing 3. Receive a voice call 4. Notice the application audio is heard during the voice call --- issue#3 - If the allows an alarm event to be sounded while the application is running, the application audio is heard while the device alarm is running. Steps to reproduce: 1. Launch the application 2. Ensure application audio is playing on the main menu 3. Allow an alarm event to be sounded 4. Notice the application audio is heard while the alarm event is in use --- issue#4 - If the user takes a picture while the application is running, the application audio is heard while the device camera is running. Steps to reproduce: 1. Launch the application 2. Ensure application audio is playing on the main menu 3. Hold the device Home key and select the camera from the recent app list 4. Notice the application audio is heard while the device camera is in use
Please correct the issue(s) we found with your app submission.
When completed, go to the Amazon Appstore Developer Portal to re-submit your application.
If you have any questions about your app, please use the Contact Us link in your Developer Portal account: https://developer.amazon.com/help/contact-us.html.

Sincerely,

Amazon Appstore Account Team
================================

Arrrrgh. I wanted it to work that way!  It is not a bug.  I'm supposed to monitor all events on the phone, and mute my sound if ANY other function requests the audio.!?  I actually designed the app to continue to play when you navigated away from it in case you wanted to use another app at the same time.  Going back to the app you can turn it off.   The LED blinker works this way if you want to take video while the light is flashing.

The next day after writing the app, I decided to use this as a learning experience.  How exactly do I turn off the sound when other things happen.   It must be easier than I thought.

I started by adding this code
--------------------------------------------------------------------------
 @Override
    protected void onPause() {
    super.onDestroy();
    // kill the child thread
    running = false;     }
--------------------------------------------------------------------------

This worked well.   If I hit the home button, or a call came in, the app stopped.  Only problem was when I went back to the app, it thought it was on and the button was pressed, but the sound was off.  A little more housekeeping on the various entry and exit routines fixed the problem.



Added some overrides to clean up when the project starts and stops...
 @Override
   protected void onPause() {
    super.onDestroy();
    // kill the child thread
    running = false;     getWindow().setBackgroundDrawableResource(R.drawable.tuningfork_still);
            mToggle.setChecked(false);
 }

 @Override
 protected void onResume() {
   super.onResume();
   mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
   running = false; getWindow().setBackgroundDrawableResource(R.drawable.tuningfork_still);
        mToggle.setChecked(false);
 }
 @Override
 protected void onStop() {
   mSensorManager.unregisterListener(mSensorListener);
   running = false; getWindow().setBackgroundDrawableResource(R.drawable.tuningfork_still);
        mToggle.setChecked(false);
   super.onStop();
 }
 
The side effect of all this is that if the user sets a tone, gets interrupted, they have to go back and reset the tone.  I'd like the app to remember where it was left.  A little harder but valuable skill.

http://stackoverflow.com/questions/151777/how-do-i-save-an-android-applications-state

Added this code to save the state

@Override    public void onSaveInstanceState(Bundle savedInstanceState) {      // Save UI state changes to the savedInstanceState.      // This bundle will be passed to onCreate if the process is      // killed and restarted.      savedInstanceState.putInt("Pitch", mPitchBar.getProgress());      savedInstanceState.putInt("Note", mSineFreqBar.getProgress());      // etc.      super.onSaveInstanceState(savedInstanceState);    }
@Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
      super.onRestoreInstanceState(savedInstanceState);
      // Restore UI state from the savedInstanceState.
      // This bundle has also been passed to onCreate.
      int lastPitch = savedInstanceState.getInt("Pitch");
      int lastNote = savedInstanceState.getInt("Note");
    }
It seemed to work under some scenarios, where the user navigated away and came back, but not if the program was destroyed and restarted.  Not perfect but good enough for now.  This is a useful function I need to learn more about for more complicated apps.

Alright you evil Amazon app store flunkie!  I'll admit the app is better now than it was before.  Previously if the tone was playing you could close the app and then not know how to turn off that annoying pitch.   I'll resubmit and see what happens, and update the google play app as well.    I'm betting the Amazon app store will reject it again for some other reason.

Success!  Apparently I've learned that you need to fill out the onPause section of your app.  :)


Appstore Developer Portal Logo 

Congratulations! The following app has been approved and will be included in the Amazon Appstore for Android:
Note Calculator & Tuning Fork
Please note that we may review your app separately for Kindle Fire.
To manage your account details, as well as any marketing material updates for your app, please use the Amazon Appstore Developer Portal. If you have any questions about your app, please use the Contact Us link in your Developer Portal account.
We look forward to doing business with you.
Sincerely,
The Amazon Appstore Account Team
================================



Here is the link,  buy it for free!



I did see that it was listed as compatible with my Droid, but not the Kindle.  Maybe they haven't approved it for that yet.  I will wait and see.

 Meanwhile I looked at some of the other apps of this type, and mine is kind of lame.   Oh well, it was just for fun, and I'm going to try to add note detection and tuning capability.


Sunday, March 11, 2012

Android tone generator app

Finally published my audio app today, this turned out to be be a long and painful road.   I leaned a lot about Android audio, but the app took so long it was no longer fun.  In the end I published a scale down app just so I could hold my head up and not have to declare failure.  But more about that later.  I'm still polishing my skills and entertaining myself, so I'm developing simple Android apps in preparation for some killer app ideas down the road.     I need to master playing mathematically generated audio files.

Made this icon for the signal generator

First I decided to expand the audio tone generator functions in my starter app to make a publishable signal generator and audio test app.  I thought this would go well with my hobby electronics bent.    The flashlight strobe app had the issue that many phones use different hardware, and I got a lot of complaints that it didn't work on certain phones.  This app sticks to commonly compatible hardware.

I'm making a point not to search for apps like the one I want to develop. That is way too depressing because everything imaginable has already been written.  The way to learn is to do, so I'm making apps that I feel would be useful.  It is much more exciting to maintain the illusion of being creative.  Plus I don't want to be accused of copying anything.

I'll use the background thread asynctask function from the light blinker app to generate the tone.  The previous app just made a tone for a short duration and locked up the UI while it did so.  I want to be able to make a continuous tone, and modulate it with the slider bar and have it running in a background thread.

One big help is that suddenly the tone generator works in the emulator.  Previously I could play tones from my phone, but the emulator was mute.   My PC has been rebooted and maybe the soundcard was just out to lunch.  But it works now, I can hear the tones from the PC.  It does seem that the loading in the emulator leads to more interruptions in the sound playback than on a real device.

I started with these functions to generate a tone

// http://stackoverflow.com/questions/2413426/playing-an-arbitrary-tone-with-android
    // functions for tone generation
    void genTone(double freqOfTone){
        // fill out the array
        for (int i = 0; i < 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 (double dVal : sample) {
            // scale to maximum amplitude
            short val = (short) ((dVal * 32767));
            // in 16 bit wav PCM, first byte is the low order byte
            generatedSnd[idx++] = (byte) (val & 0x00ff);
            generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
        }
    }
    void playSound(){
        final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT, numSamples,
                AudioTrack.MODE_STATIC);
        audioTrack.write(generatedSnd, 0, generatedSnd.length);
        audioTrack.play();
    }
 
           
In order to make the tone continuous, and not a series of chopped tones, I need to use STREAM not STATIC mode.  Need to write continuously to the audio track, turns out the buffer is blocking so you just keep writing.  This mod made a nice continuous tone:

void playSound(){
    final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
            sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
            AudioFormat.ENCODING_PCM_16BIT, numSamples,
            AudioTrack.MODE_STREAM);
    audioTrack.play();
    while (blinking==true){
    audioTrack.write(generatedSnd, 0, generatedSnd.length); 
    }
}

Awesome, I have the soul of the app.  I copied over my light blinking app and pasted this function into the Asynctask, and it worked great.   that is why it says "blinking" .  That variable is controlled by the GUI and breaks the loop when the user pushes the button.   The tone turns on and off with the toggle switch and the background thread is working great, the UI doesn't lock up.

With a few minutes hacking I replaced the light blinking with the tone generation in my previous app.  Now the radio buttons play different tones and the slider sets the tone frequency.    Just proof of concept, the buttons all say the wrong thing, etc.

The second half of the proof of concept that the slider bar can control  the tone while it plays.  What will actually happen is the slider bar will change the variables for tone gen function that will calculate the data, and then that will get queued into the audio playback.   Hopefully the lag won't be so great the user gets annoyed.   I put a new tone generation step in the loop.
void playSound(){
    final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
            sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
            AudioFormat.ENCODING_PCM_16BIT, numSamples,
            AudioTrack.MODE_STREAM);
    audioTrack.play();
    while (blinking==true){
    audioTrack.write(generatedSnd, 0, generatedSnd.length);
    genTone(infreqOfTone); 
    }
}

Unfortunately the tone is interrupted when the genTone function runs, I guess it is taking too long and the buffer empties.  So this math needs to be done in the UI thread and passed to the audio playing thread.  I pasted the genTone() function into the slider and toggle listeners, and ba da bing!   The UI is a tad bit laggy and I'll work on that, but now the slider changes the tone as it plays, and the tone is continuous with no clicks or gaps.

The basics are working, next I need to build the polished app.  Here goes the PCR list!   This is my working list I'm using to built the app.

Side note ...Found a neat tool for making icons and such.  Didn't try it yet but it looks like it could save time on the artwork.
http://android-ui-utils.googlecode.com/hg/asset-studio/dist/index.html

I started in on building the GUI, and found I had way too many functions to put in.  The GUI will be a mile long list.  I'd like to organize it into tabs, pages, something.

I good description of the sliding drawer and some xml that actually worked, however this isn't what I want.
http://android-er.blogspot.com/2011/03/slidingdrawer.html
This hides buttons and slides them onto the screen.  Cool but not right for this app.

Found a page on tabhost.
http://dewful.com/?p=15
Turns out the double quote " symbol I copied out of this example was the wrong double quote ", and I spent an hour chasing the weirdest bugs you ever saw.  The symbol was not a real quote and looked like one.  That is really a way to screw up your compiler.  After that it worked and I made a UI with multiple tab pages.

Figured out how to make multiple tabs with different check boxes on each using the above example.   Added content1, content2, content3 to the tab setup

tabs.setup();
        TabSpec tspec1 = tabs.newTabSpec("First Tab");
        tspec1.setIndicator("One");
        tspec1.setContent(R.id.content1);
        tabs.addTab(tspec1);
        TabSpec tspec2 = tabs.newTabSpec("Second Tab");
        tspec2.setIndicator("Two");
        tspec2.setContent(R.id.content2);
        tabs.addTab(tspec2);
        TabSpec tspec3 = tabs.newTabSpec("Third Tab");
        tspec3.setIndicator("Three");
        tspec3.setContent(R.id.content3);
        tabs.addTab(tspec3);

Also added three content1, content2, content3 linear layouts under the tab widget in the main.xml

Now to draw up the GUI.....

  • Develop the feature list
    • Generate arbitrary tone
    • Adjust sample rate for maximum purity
    • Add harmonics (Striking this feature for the first rev)
    • sine, square, triangle ramp
    • Option to constrain output to musical notes only
    • Display frequency in Hz
    • Respond live to slider bars
    • On/off switch
    • graph of the tone being played, maybe this is an enhancement.
    • slider for trise, tfall, tduty
    • White noise, Pink noise
    • Audio test sounds


Found an issue with tone generation, when I put the waveform on the scope.  The tone sounds fine but occasionally the phase jumps.  I thought this was due my wave file not starting and stopping at zero. and mismatch between the frequency and the sample rate and number of samples chosen.   I rewrote the audio generation to avoid all these issues.  However it looks like the problem persists.   It seems the audio buffer empties occasionally.   I rewrote things again to put in a big file to start, and then feed it with small chunks after that.   I've played with this a lot and I don't know if the problem is the buffer emptying and restarting, or the sample rate jumping in some way to make it come out even.

I found the equations for generating musical notes.  I'll make that a mode.
http://www.phy.mtu.edu/~suits/NoteFreqCalcs.html

In order to make the tones more pure, I decided to dynamically set the sample rate to be a multiple of the tone frequency.  This sounded good, but hosed up things.  Previously I was continuously writing data into an audio stream.  I"d simply change the math and the calculated wave would change and the tone would change.  Now that I want to also change sample rate, I have to kill the AudioTrack and restart it every time the frequency changes.  The end result is a less smooth interface, but the goal here is to have a quality audio output.

The app is getting a bit out of hand, too many functions.  As a result it is taking too long to get rev 1.0 published.   I'm going to pare back some of the bells and whistles so I can get something published.

Implementing the square wave functions.   I'll use the same functions as the sine wave, but calculate a square instead.  Used an excel spreadsheet to come up with this function
=-1+(B4<$H$4)*2+(B4<$H$5)*(2*B4/$H$5-2)-((B4>=$H$4)*(B4<($H$4+$H$6)))*(-2+2*((B4-$H$4)/$H$6))
where $H$4=duty, $H$5=rise, $H$6=fall, based on 100 samples
The sliders callback limits the legal values of rise and fall to be less than the appropriate part of the duty cycle

I imported all the audio test files, and now I'm having trouble with soundpool.   All my files are big, ~882K.   If I switch from sound to sound, I get and error that AudioFlinger could not create track, status: -12
I think this is due to running out of memory in soundpool.   Randomly tracks won't play, the app crashes.   Getting discouraged.   Taken so long it ceases to be fun, and I'm doing this for fun.

In the process of debugging the soundPool issue I stumbled across all the audio signal generator apps already on the market.  I had been trying not to see this because it becomes too depressing to develop anything if you see it's already been done.  In this case I saw many many very nice apps already out there and my app is full of bugs.  Time for a rethink.

I decided to make a musical Tone generator using the note generation section I had above.
http://www.phy.mtu.edu/~suits/NoteFreqCalcs.html   I found that kind of cool, I never really knew the formulas behind the musical scale.

44100 sample rate for high quality sine waves
Calculate and display the frequency
Display the musical note and octave
Live slider for note - later canned this because of clicking as it slid, made unpleasant sounds.
Pitch control of the A440 tune so other scale tunings can be made

There is lots of room to expand to play scales and intervals and chords.  However I kept a lid on it.  This project is way out of hand and the ideas for other apps have backed up in my head to the point it might explode.

Had to rewite the tone generation because it's difficult to maintain a constant sample rate, and make the tones start and stop at zero so they can loop without a click or phase jump. Made a spreadsheet to work it out.


// Based on but modified and improved from
 // http://stackoverflow.com/questions/2413426/playing-an-arbitrary-tone-with-android
 // functions for tone generation
 void genTone(double freqOfTone){

//clean out the arrays
for (int i = 0; i < targetSamples * 2; ++i) {
        sample[i] = 0;
     }
for (int i = 0; i < targetSamples * 2 * 2; ++i) {
         generatedSnd[i] = (byte) 0x0000;
}

// calculate adjustments to make the sample start and stop evenly
numCycles = (int) (0.5 +  freqOfTone * targetSamples/sampleRate);
numSamples = (int) (0.5 + numCycles * sampleRate/freqOfTone);

     // fill out the array
     for (int i = 0; i < numSamples; ++i) {
         sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone));
     }
     // convert to 16 bit pcm sound array
     // assumes the sample buffer is normalized.
     int idx = 0;
     for (double dVal : sample) {
    // scale loudness by frequency
    double amplitude = (double) (32767 * 5/(Math.log(freqOfTone)));
    if (amplitude > 32767) amplitude = 32767;
    // scale signal to amplitude
         short val = (short) (dVal * amplitude);
         // in 16 bit wav PCM, first byte is the low order byte
         generatedSnd[idx++] = (byte) (val & 0x00ff);
         generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
     }
 }
 void playSound(){
     final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
             sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
             AudioFormat.ENCODING_PCM_16BIT, numSamples*2,
             AudioTrack.MODE_STREAM);
     audioTrack.write(generatedSnd, 0, numSamples*2);
     audioTrack.play(); 
     while (running==true){
      audioTrack.write(generatedSnd, 0, numSamples*2);
     }
     audioTrack.stop();
     running = false;

 }



GUI should show the musical note calculation.   I need to use relative layout, which took a lot of time to move and tweak and set the values and anchor points.   There must be an easier way, but I worked through it slowly bouncing between the graphical view, the code, and the emulator until it did what i wanted.

    <RelativeLayout
            android:id="@+id/relativeLayout2"
            android:layout_width="fill_parent"
            android:layout_height="108dp" >
            <TextView
                android:id="@+id/textViewHz"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:maxLines="1"
                android:maxWidth="80dp"
                android:minWidth="80dp"
                android:text="440.0000000000"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="@color/red" android:layout_marginLeft="10dp" android:textStyle="normal|bold" android:textSize="22dp"/>
            <TextView
                android:id="@+id/textView5"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBaseline="@+id/textViewHz"
                android:layout_alignBottom="@+id/textViewHz"
                android:layout_toRightOf="@+id/textViewHz"
                android:text="Hz = "
                android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@color/red" android:textSize="22dp" android:textStyle="normal"/>
            <TextView
                android:id="@+id/textViewFreq"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBaseline="@+id/textView5"
                android:layout_alignBottom="@+id/textView5"
                android:layout_toRightOf="@+id/textView5"
                android:maxLines="1"
                android:maxWidth="80dp"
                android:text="440.000000000000"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="@color/blue" android:textStyle="bold" android:minWidth="80dp" android:textSize="22dp"/>
            <TextView
                android:id="@+id/textViewForm"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_toRightOf="@+id/textViewFreq"
                android:text="·2"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="@color/black"
                android:textSize="32dp" android:textStyle="bold"/>
            <TextView
                android:id="@+id/textViewParen"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_above="@+id/textViewFreq"
                android:layout_toRightOf="@+id/textViewForm"
                android:text="("
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:textSize="32dp" android:textColor="@color/black"/>

            <TextView
                android:id="@+id/textViewN"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignRight="@+id/TextView01"
                android:layout_alignTop="@+id/textViewParen"
                android:layout_marginLeft="5dp"
                android:layout_toRightOf="@+id/textViewParen"
                android:text="9"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="@color/purple"
                android:textStyle="bold" />
            <TextView
                android:id="@+id/textView6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBaseline="@+id/textViewN"
                android:layout_alignBottom="@+id/textViewN"
                android:layout_toRightOf="@+id/textViewParen"
                android:text="____"
                android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@color/black"/>
            <TextView
                android:id="@+id/textView3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@+id/textViewFreq"
                android:layout_alignLeft="@id/textViewN"
                android:layout_below="@+id/textViewN"
                android:text="12"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="@color/black" />
            <TextView
                android:id="@+id/TextView01"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignTop="@+id/textView6"
                android:layout_marginLeft="3dp"
                android:layout_toRightOf="@+id/textView6"
                android:text=")"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:textSize="32dp" android:textColor="@color/black"/>
        </RelativeLayout>
Used this page to get the note names and octaves correct.
http://en.wikipedia.org/wiki/Scientific_pitch_notation

Went with a virtual turning fork motif.    Used a background image that vibrates and detect a shake or strike to start.



Want to detect a shake to start the tuning fork vibrating.  Copied code from here and it worked awesome on the first try.  I ended up adding a 1 second timer to debounce the shaking.  During a shake it would turn on and off a couple times which would have annoyed the user.
http://stackoverflow.com/questions/2317428/android-i-want-to-shake-it

Finally I published it.  Not at all the app I started out with, but now I can get back to new ideas and declare victory.  W00T!  I'm not super proud, and you can still get the audio to click sometimes.  I've scoped the output until I'm blue in the face, and tweaked the code, but I can't totally eliminate it.

Switched the icon to a tuning fork.

Download the app here:
https://play.google.com/store/search?q=pub:Siliconfish

This link lets you make a link like the ones below
http://developer.android.com/guide/publishing/publishing.html#BuildaButton


Get it on Google Play

Android app on Google Play

Tuesday, March 6, 2012

Experimenting with Free Android Tethering


A friend made fun of me for not using free tethering when I was complaining that there was no open WiFi where we were.  Tethering means using my 3G Android phone as a internet connection for my PC.  Verizon charges a fee for this, plus a high data usage fee, so I had always avoided it.  My friend said there were apps for that, and that it is free if I download one.  In the past I'd heard that carriers monitor IP addresses and try to detect when people are tethering when they aren't supposed to.   I'll experiment, see if I can get free tethering working, and if Verizon complains.

Another drawback to tethering is data usage. While my plan is still unlimited, those days are numbered and Verizon has announced throttling.   So tethering is still not something I want to do a lot, but in a pinch it might save the day.

If you go to the android market, you see that apps are out there, I decided to try this one.
https://market.android.com/details?id=com.mstream.easytether_beta&feature=search_result&rdid=com.mstream.easytether_beta&rdot=1

But you find that Verizon has blocked access.  Note the message in the yellow box on the left.



I thought I would need to sideload like this link says
http://www.maximumpc.com/article/features/how_sideload_android_apps
But it turned out I didn't have to do that.

I googled the app name and "apk" which is the extension of the compiled files for the apps on Android.  I used my PC for this search.  I found this page, which has a download link.
http://www.androidapk.us/apps/EasyTether-Lite-9030.html


Then I went to the download page, and saw the barcode in the picture above.   I took a photo of the barcode with my phone, using Google goggles, and it took me to an android market page THAT ALLOWED ME TO DOWNLOAD and install directly to my phone!  No sideloads, filemanagers, no nothing, just downloaded the app from the web.  This got around Verizon's block.

After that you have to download and install drivers to the PC.
http://mobile-stream.com/easytether/drivers

It looks like the free version has some limitations as well.  It won't allow you to access https sites.  A serious limitation, but   I understand, people gotta eat.  Even software developers.  Threw out scary messages about unsigned and unverified software warnings from the PC.  Maybe my next post will be about how I wrecked my computer with dodgy software.

I was able to click through all the installs on both the phone and PC with no problems.  It appears this software uses the app development USB link to communicate with the phone over USB, rather than conventional tethering, so it may be possible that Verizon won't flag this as an illegal tether.   I'll let you know.

I did immediately see another network pop up on my PC, "Network 4" with internet access!  Easy.   This post was written using my laptop and my Motorola Droid 2 global as the network connection.   Success!  Free!  W00t!   Uhhhh.... maybe not perfect.   I am finding that I can't access my email and a couple other sites because of the https limitation, so you might want to consider buying the full version.   I'm going to use if for a while to see if Verizon complains before committing to pay.  I'll update the post with what happens.