Noise Ramp: App for generating delayed whitenoise
TL;DR##
I wrote a small app that gradually starts generating white noise after you fall asleep.
Introduction##
Suppose construction noises wakes you up at 6:30am. A white noise generator would help mask this construction noise. However, it would also make it a bit harder to fall asleep. Ideally, the whitenoise generator would gradually start generating noise after you fall asleep. I couldn't find an app that does this. So I wrote one (github).
Generating whitenoise##
Whitenoise is the easiest type of noise to generate. A whitenoise signal is just a sequence of random unrelated values. Repeatedly fill a byte array with random values and then pass it into AudioTrack
.
In order to use AudioTrack
you need specify a sample rate. This is important. Since humans can hear up to 20KHz, choosing a sample rate significantly lower than 40KHz will result in noise that is constricted to a subset of the human hearing range (see Nyquist Theorem for background). This isn't truly white noise. Nonetheless, I choose a sample rate of 5KHz since low frequency noise is more pleasant and still effective for masking construction noises. If you are willing to use fast fourier transforms you can generate interesting noise distributions (ex: brown noise). I don't believe it is worth the effort.
Playing audio in the background##
To my surprise, existing whitenoise apps don't properly play audio when their app's are backgrounded. They interact with the AudioTrack
API directly inside an Activity
.
The correct approach is to use a Service
. Do not use an IntentService
since you aren't performing a queue of jobs. If you want to lower the chance of your service being killed, make your service into a foreground service by displaying a persistent notification for it via startForground(id, notification)
.
Since the service runs in the same process as your activity, you can interact with the service without the onBind()
boilerplate. Ie, stopping the service is a simple matter of writing code like NoiseService.INSTANCE.stop()
. I've seen multiple examples of code that blindly delegates interactions between activities and services via binder objects for no good reason. Don't do this.
Scheduling noise for later##
If you want to delay the whitenoise service's playback, use the AlarmManager
to schedule a delayed broadcast. Inside the broadcast receiver, start an intent for your service.
Note that this scheduled broadcast will get deleted on OS restart. So you may want to check if the broadcast is still scheduled in some circumstances in order to update your UI or reschedule the broadcast. You can use the following code inside your Activity
to check this. This code checks if the pending intent already exists somewhere. If it does already exist, it must be inside the alarm manager.
boolean alarmStored = PendingIntent.getBroadcast(this, YOUR_ALARM_REQUEST_CODE,
createAlarmIntent(), PendingIntent.FLAG_NO_CREATE) != null;