Using Multiple Languages in Giggity app

Giggity app is used for conferences around the world. It becomes essential that it provides support for native languages as the users may not understand the terminologies written primarily in English from different countries. In this blog, I describe how to add a resource for another language in your app with the example of Giggity.  I recently worked on the addition of French translation in the app. We look at the addition of German in the app.

You can specify resources tailored to the culture of the people who use your app. You can provide any resource type that is appropriate for the language and culture of your users. For example, the following screenshot shows an app displaying string and drawable resources in the device’s default (en_US) locale and the German (de_DE) locale.

It is good practice to use the Android resource framework to separate the localized aspects of your application as much as possible from the core Java functionality:

  • You can put most or all of the contents of your application’s user interface into resource files, as described in this document and in Providing Resources.
  • The behaviour of the user interface, on the other hand, is driven by your Java code. For example, if users input data that needs to be formatted or sorted differently depending on locale, then you would use Java to handle the data programmatically. This document does not cover how to localize your Java code.

Whenever the application runs in a locale for which you have not provided locale-specific text, Android will load the default strings from res/values/strings.xml. If this default file is absent, or if it is missing a string that your application needs, then your application will not run and will show an error. Here is an example of default strings in the app.

<!-- Menu -->
<string name="settings">Settings</string>
<string name="change_day">Change day</string>
<string name="show_hidden">Show hidden items</string>
<string name="timetable">Timetable</string>
<string name="tracks">Tracks</string>
<string name="now_next">Now and next</string>
<string name="my_events">My events</string>
<string name="search">Search</string>

An application can specify many res/<qualifiers>/ directories, each with different qualifiers. To create an alternative resource for a different locale, you use a qualifier that specifies a language or a language-region combination. (The name of a resource directory must conform to the naming scheme described in Providing Alternative Resources, or else it will not compile.) You can specify resources tailored to the culture of the people who use your app. You can provide any resource type that is appropriate for the language and culture of your users. For example, the following screenshot shows an app displaying string and drawable resources in the device’s default (en_US) locale and the German (de_DE) locale.

<!-- Menu -->
<string name="settings">Einstellungen</string>
<string name="change_day">Tag ändern</string>
<string name="timetable">Zeitplan</string>
<string name="tracks">Tracks</string>
<string name="now_next">Jetzt und gleich</string>
<string name="my_events">Meine Veranstaltungen</string>
<string name="search">Suche</string>

Then you can use it in the app like this anywhere you need to use the string. This is an example of putting the options menu in the toolbar in Giggity app.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
   super.onCreateOptionsMenu(menu);

   menu.add(Menu.NONE, 1, 5, R.string.settings)
           .setShortcut('0', 's')
           .setIcon(R.drawable.ic_settings_white_24dp)
           .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
   menu.add(Menu.NONE, 2, 7, R.string.add_dialog)
           .setShortcut('0', 'a')
           .setIcon(R.drawable.ic_add_white_24dp)
           .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);

   return true;
}

References:

Continue ReadingUsing Multiple Languages in Giggity app

Timer Option in Phimpme Android’s Camera

The Phimpme Android application comes in with all the options like clicking a picture, editing them and sharing it with the world using many many connected social media accounts. Not only this, it features a fully functional camera with lots of different functionality which a user wants in their day to day life. One such feature is the Timer option in Phimpme. In Phimpme, the user can go to the camera settings to enable or disable the Timer options and click their photos after setting the timer for a particular duration. After setting the timer and pressing the capture photo button, it also displays a ticker at the UI of the camera to notify the user the amount of time after which the photo will be clicked.

In this tutorial, I will be explaining how we have achieved this feature in the Phimpme application.

Step 1

The first thing we need to do is to display the options to the user in camera settings to enable/disable the timer and to select the specific amount of time for the delay in the capture. To do this we have made use of the pop-up view in which we have programmatically added all the timer values to be displayed to the user using the code snippet below:

final String[] timer_values = getResources().getStringArray(R.array.preference_timer_values);
  String[] timer_entries = getResources().getStringArray(R.array.preference_timer_entries);
String timer_value = sharedPreferences.getString(PreferenceKeys.getTimerPreferenceKey(), "0");
addArrayOptionsToPopup(Arrays.asList(timer_entries), getResources().getString(R.string.preference_timer), true, timer_index, false)

What the function addArrayOptionsToPopup does is that it adds the following arrays to the linear layout of the pop-up view programmatically.

Step 2

After displaying the timer values to the user, we need to think about the functionality of the camera if the timer is enabled. When the user presses the click picture button we check the condition whether the timer is enabled or not. If it is enabled, we make the application to wait for a specific amount of time before clicking the photo. This can be done using the CountDownTimer class which is provided by Android.

new CountDownTimer(timerDelay, 1000) {
   public void onTick(long millisUntilFinished) {
          //Called after each second
       }
       public void onFinish() {
         //Called after timer delay
       }
   }.start();

What the above piece of code does is to wait for the specific amount of time as specified by the timer delay. Suppose the user selects the option to wait for 5 seconds then we set the timerDelay to be 5000, then the above code calls the onTick method after each second where we update the user that how much time is remaining and on the onFinish method we call the takePicture method to capture the image using the following line of code below.

mCamera.takePicture(null, null, mPicture);

This is how we have implemented the option of Timer in the Phimpme Android application. To get the full source code of the Camera, please check out the Phimpme Android GitHub repository listed in the resources section below.

Resources

  1. Android Developer Guide : CountDown Timer – https://developer.android.com/reference/android/os/CountDownTimer.html
  2. StackOverflow – Implementing Timer in Camera – https://stackoverflow.com/questions/35355320/camera-application-timer-implementaion-issue
  3. GitHub – Phimpme Android Repository – https://github.com/fossasia/phimpme-android/
  4. GitHub – Open Camera Source Code – https://github.com/almalence/OpenCamera

 

Continue ReadingTimer Option in Phimpme Android’s Camera

Creating GUI for configuring SUSI Linux Settings

SUSI Linux app provides access to SUSI on Linux distributions on desktop as well as hardware devices like Raspberry Pi. The settings for SUSI Linux are controlled with the use of a config.json file. You may edit the file manually, but to provide safe configurations, we have a config generator script. You may run the script to configure settings like TTS Engine, STT Engine, authentication, choice about the hotword engine etc. Generally, it is easier to configure application settings through a GUI. Thus, we added a GUI for it using PyGTK and Glade.

Glade is a GUI designer for GNOME based Linux systems. I wrote a blog about how to create user interfaces in Glade and access it from Python code in SUSI Linux. Now, for creating UI for Configuration screen, we need to choose an ideal layout. Glade provides various choices like BoxLayout, GridLayout, FlowBox, ListBox , Notebook etc. Since, we need to display only basic settings options, we select the BoxLayout for this purpose.

BoxLayout as the name suggests, forms a box like arrangement for widgets. You can arrange the widgets in either Landscape or Horizontal Layout. We select Application Window as a top-level container and add a BoxLayout container in it. Now, in each box of the BoxLayout, we need to add the widgets like ComboBox and Switch for user’s choice and a Label. This can be done by using a horizontal BoxLayout with corresponding widgets. After arranging the UI in above described manner, we have a GUI like below.

If you see the current window in the preview now, you will find that the ComboBox do not have any items. We need to define items in the ComboBox using a GTKListStore. You may refer to this video tutorial to see how this can be done.

Now, when we see the preview, our GUI is fully functional. We have options for Speech Recognition Service, Text to Speech Service in ComboBox. Other simple settings are available as switches.

Now, we need to add functionality to our UI. We want our code to be modular and structured, therefore, we declare a ConfigurationWindow class. Though the ideal way to handle such cases is inheriting from the Gtk.Window class, but reading the documentation of PyGTK+ 3, I could not find a way to do this for windows created through Glade. Thus, we will use composition for storing the window object. We add window and other widgets present in the UI as properties of ConfigurationWindow class like this.

class ConfigurationWindow:
   def __init__(self) -> None:
       super().__init__()
       builder = Gtk.Builder()
       builder.add_from_file(os.path.join("glade_files/configure.glade"))

       self.window = builder.get_object("configuration_window")
       self.stt_combobox = builder.get_object("stt_combobox")
       self.tts_combobox = builder.get_object("tts_combobox")
       self.auth_switch = builder.get_object("auth_switch")
       self.snowboy_switch = builder.get_object("snowboy_switch")
       self.wake_button_switch = builder.get_object("wake_button_switch")

Now, we need to connect the Signals from our configuration window to the Handler. We declare the Handler as a nested class in the ConfigurationWindow class because its scope of usage is inside the ConfigurationWindow object. Then you may connect signals to an object of the Handler class.

builder.connect_signals(ConfigurationWindow.Handler(self))

Since we may need to modify the state of the widgets, we hold a reference of the parent ConfigurationWindow object in the Handler and pass the self as a parameter to the Handler. You may read more about using the handlers in my previous blog.

In the Handler, we connect to the config.json file and change the parameters of the the file based on the user inputs on the GUI. We handle it for the Text to Speech selection comboBox in the following manner. We also declare two addition Dialogs for handling the input of credentials by the users for the Watson and Bing services.

def on_stt_combobox_changed(self, combo: Gtk.ComboBox):
   selection = combo.get_active()

   if selection == 0:
       config['default_stt'] = 'google'

   elif selection == 1:
       credential_dialog = WatsonCredentialsDialog(self.config_window.window)
       response = credential_dialog.run()

       if response == Gtk.ResponseType.OK:
           username = credential_dialog.username_field.get_text()
           password = credential_dialog.password_field.get_text()
           config['default_stt'] = 'watson'
           config['watson_stt_config']['username'] = username
           config['watson_stt_config']['password'] = password
       else:
           self.config_window.init_stt_combobox()

       credential_dialog.destroy()

   elif selection == 2:
       credential_dialog = BingCredentialDialog(self.config_window.window)
       response = credential_dialog.run()

       if response == Gtk.ResponseType.OK:
           api_key = credential_dialog.api_key_field.get_text()
           config['default_stt'] = 'bing'
           config['bing_speech_api_key']['username'] = api_key
       else:
           self.config_window.init_stt_combobox()

       credential_dialog.destroy()

Now, we declare two more methods to show and exit the Window.

def show_window(self):
   self.window.show_all()
   Gtk.main()

def exit_window(self):
   self.window.destroy()
   Gtk.main_quit()

Now, we may use the ConfigurationWindow class object anywhere from our code. This modularized approach is better when you need to manage multiple windows as you can just declare the Window of a particular type and show it whenever need in your code.

Resources

  • Glade usage Youtube tutorial: https://www.youtube.com/watch?v=vOGK3TveDDk
  • Creating GUI using PyGTK for SUSI Linux: https://blog.fossasia.org/making-gui-for-susi-linux-with-pygtk/
  • PyGObject Documentation: http://pygobject.readthedocs.io/en/latest/getting_started.html
Continue ReadingCreating GUI for configuring SUSI Linux Settings

Marker Click Management in Android Google Map API Version 2

We could display a marker on Google map to point to a particular location. Although it is a simple task sometimes we need to customise it a bit more. Recently I customised marker displayed in Connfa app displaying the location of the sessions on the map loaded from Open Event format. In this blog manipulation related to map marker is explored.

Markers indicate single locations on the map. You can customize your markers by changing the default colour, or replace the marker icon with a custom image. Info windows can provide additional context to a marker. You can place a marker on the map by using following code.

MarkerOptions marker = new MarkerOptions().position(new LatLng(latitude, longitude)).title("Dalton Hall");
googleMap.addMarker(marker);

But as you can see this may not be enough, we need to do operations on clicking the marker too, so we define them in the Marker Click Listener. We declare marker null initially so we check if the marker colour is changed previously or not.

private Marker previousMarker = null;

We check if the marker is initialized to change its colour again to initial colour, we can do other related manipulation like changing the map title here,

Note: the first thing that happens when a marker is clicked or tapped is that any currently showing info window is closed, and the GoogleMap.OnInfoWindowCloseListener is triggered. Then the OnMarkerClickListener is triggered. Therefore, calling isInfoWindowShown() on any marker from the OnMarkerClickListener will return false.

mGoogleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
   @Override
   public boolean onMarkerClick(Marker marker) {
       String locAddress = marker.getTitle();
       fillTextViews(locAddress);
       if (previousMarker != null) {
           previousMarker.setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
       }
       marker.setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
       previousMarker = marker;

       return true;
   }
});

It’s possible to customize the colour of the default marker image by passing a BitmapDescriptor object to the icon() method. You can use a set of predefined colours in the BitmapDescriptorFactory object, or set a custom marker colour with the BitmapDescriptorFactory.defaultMarker(float hue) method. The hue is a value between 0 and 360, representing points on a colour wheel. We use red colour when the marker is not clicked and blue when it is clicked so a user knows which one is clicked.

To conclude you can use an OnMarkerClickListener to listen for click events on the marker. To set this listener on the map, call GoogleMap.setOnMarkerClickListener(OnMarkerClickListener). When a user clicks on a marker, onMarkerClick(Marker) will be called and the marker will be passed through as an argument. This method returns a boolean that indicates whether you have consumed the event (i.e., you want to suppress the default behaviour). If it returns false, then the default behaviour will occur in addition to your custom behaviour. The default behaviour for a marker click event is to show its info window (if available) and move the camera such that the marker is centered on the map.

The final result looks like this, so you the user can see which marker is clicked as its colour is changed,

   

 

References:

  • Google Map APIs Documentation – https://developers.google.com/maps/documentation/android-api/marker
Continue ReadingMarker Click Management in Android Google Map API Version 2

Setting up Travis Continuous Integration in Giggity

Travis is a continuous integration service that enables you to run tests against your latest Android builds. You can setup your projects to run both unit and integration tests, which can also include launching an emulator. I recently added Travis Continuous Integration Connfa, Giggity and Giraffe app. In this blog, I describe how to set up Travis Continuous Integration in an Android Project with reference to Giggity app.

  • Use your GitHub account, sign in to either to Travis CI .org for public repositories or Travis CI .com for private repositories
  • Accept the GitHub access permissions confirmation.
  • Once you’re signed in to Travis CI, and synchronized your GitHub repositories, go to your profile page and enable the repository you want to build:

  • Now you need to add a .travis.yml file into the root of your project. This file will tell how Travis handles the builds. You should check your .travis file on Travis Web Lint before committing any changes to it.
  • You can find the very basic instructions for building an Android project from the Travis documentation. But here we specify the .travis.yml build accordingly for Giggity’s continuous integration. Here, language shows that it is an Android project. We write “language: ruby” if it is a ruby project.  If you need a more customizable environment running in a virtual machine, use the Sudo Enabled infrastructure. Similarly, we define the API, play services and libraries defined as shown.
language: android
sudo: required
jdk: 
 - oraclejdk8
# Use the Travis Container-Based Infrastructure
android:
  components:
    - platform-tools
    - tools
    - build-tools-25.0.3
    - android-25
    
    # For Google APIs
    - addon-google_apis-google-$ANDROID_API_LEVEL
    # Google Play Services
    - extra-google-google_play_services
    # Support library
    - extra-android-support
    # Latest artifacts in local repository
    - extra-google-m2repository
    - extra-android-m2repository
    - android-sdk-license-.+
    - '.+'

before_script:
  - chmod +x gradlew    

script:
  - ./gradlew build

Now when you make a commit or pull request Travis check if all the defines checks pass and it is able to be merged. To be more advanced you can also define if you want to build APKs too with every build.

References:

  • Travis Continuous Integration Documentation – https://docs.travis-ci.com/user/getting-started/
Continue ReadingSetting up Travis Continuous Integration in Giggity

Keeping Order of tickets in Event Wizard in Sync with API on Open Event Frontend

This blog article will illustrate how the various tickets are stored and displayed in order the event organiser decides  on  Open Event Frontend and also, how they are kept in sync with the backend.

First we will take a look at how the user is able to control the order of the tickets using the ticket widget.

{{#each tickets as |ticket index|}}
  {{widgets/forms/ticket-input ticket=ticket
  timezone=data.event.timezone
  canMoveUp=(not-eq index 0)
  canMoveDown=(not-eq ticket.position (dec
  data.event.tickets.length))
  moveTicketUp=(action 'moveTicket' ticket 'up')
  moveTicketDown=(action 'moveTicket' ticket 'down')
  removeTicket=(confirm 'Are you sure you  wish to delete this 
  ticket ?' (action 'removeTicket' ticket))}}
{{/each}}

The canMoveUp and canMoveDown are dynamic properties and are dependent upon the current positions of the tickets in the tickets array.  These properties define whether the up or down arraow or both should be visible alongside the ticket to trigger the moveTicket action.

There is an attribute called position in the ticket model which is responsible for storing the position of the ticket on the backend. Hence it is necessary that the list of the ticket available should always be ordered by position. However, it should be kept in mind, that even if the position attribute of the tickers is changed, it will not actually change the indices of the ticket records in the array fetched from the API. And since we want the ticker order in sync with the backend, i.e. user shouldn’t have to refresh to see the changes in ticket order, we are going to return the tickets via a computed function which sorts them in the required order.

tickets: computed('data.event.tickets.@each.isDeleted', 'data.event.tickets.@each.position', function() {
   return this.get('data.event.tickets').sortBy('position').filterBy('isDeleted', false);
 })

The sortBy method ensures that the tickets are always ordered and this computed property thus watches the position of each of the tickets to look out for any changes. Now we can finally define the moveTicket action to enable modification of position for tickets.

moveTicket(ticket, direction) {
     const index = ticket.get('position');
     const otherTicket = this.get('data.event.tickets').find(otherTicket => otherTicket.get('position') === (direction === 'up' ? (index - 1) : (index + 1)));
     otherTicket.set('position', index);
     ticket.set('position', direction === 'up' ? (index - 1) : (index + 1));
   }

The moveTicket action takes two arguments, ticket and direction. It temporarily stores the position of the current ticket and the position of the ticket which needs to be swapped with the current ticket.Based on the direction the positions are swapped. Since the position of each of the tickets is being watched by the tickets computed array, the change in order becomes apparent immediately.

Now when the User will trigger the save request, the positions of each of the tickets will be updated via a PATCH or POST (if the ticket is new) request.

Also, the positions of all the tickets maybe affected while adding a new ticket or deleting an existing one. In case of a new ticket, the position of the new ticket should be initialised while creating it and it should be below all the other tickets.

addTicket(type, position) {
     const salesStartDateTime = moment();
     const salesEndDateTime = this.get('data.event.startsAt');
     this.get('data.event.tickets').pushObject(this.store.createRecord('ticket', {
       type,
       position,
       salesStartsAt : salesStartDateTime,
       salesEndsAt   : salesEndDateTime
     }));
   }

Deleting a ticket requires updating positions of all the tickets below the deleted ticket. All of the positions need to be shifted one place up.

removeTicket(deleteTicket) {
     const index = deleteTicket.get('position');
     this.get('data.event.tickets').forEach(ticket => {
       if (ticket.get('position') > index) {
         ticket.set('position', ticket.get('position') - 1);
       }
     });
     deleteTicket.deleteRecord();
   }

The tickets whose position is to be updated are filtered by comparison of their position from the position of the deleted ticket.

Resources

Continue ReadingKeeping Order of tickets in Event Wizard in Sync with API on Open Event Frontend

Performing Multivibrator Experiments in PSLab Android App

A Multivibrator is an Oscillator that produces non-sinusoidal signals like Square Wave. Multivibrators are considered to be the building blocks of almost every electronic device.

Multivibrators are the level changing circuit. Every circuit works on two level, “high” and “low”. Multivibrators changes between these two level to produce a particular voltage form.

PSLab Android App helps us to observe the input and the output signals captured from these circuits. This enables student or researchers to study the input and output waveforms. Let’s discuss various Multivibrator Experiments that can be conducted using PSLab and how they are implemented.

 

There are three types of multivibrator:

  1. Astable multivibrator
  2. Bistable multivibrator
  3. Monostable multivibrator

Astable Multivibrator

 

An astable-multivibrator circuit’s output oscillates continuously between its two unstable states. It is a cross-coupled transistor switching circuit. They are also known as Free Multivibrator as any additional inputs or external assistance to oscillate are not required by them. Astable oscillators produce a continuous square wave from its output

Astable are used as clocks and timers, bistable as flip flops, the memory, registers and counters, Schmitt triggers as memory, switches, wave shapers.

The following is the circuit diagram.

In order to observe the behaviour of Astable Multivibrator, LED’s can be also used.

We get the following waveform when captured using the PSLab device.

Monostable Multivibrator

Monostable is also known as one shot multivibrator. In monostable multivibrator, there is one stable state and one astable state. A trigger pulse is required to enter into the astable state or get back to the stable state. The monostable multivibrator is mainly used as a timer.

The following is the schematics of Monostable Multivibrator

Image link – https://circuitdigest.com/electronic-circuits/555-timer-monostable-circuit-diagram

Following signals are captured by the device while conducting the experiment.

Adding Multivibrator Experiment support in PSLab Android

This was simply achieved by reusing Oscilloscope Activity. Oscilloscope Activity is informed about the experiment by using putExtra() and getExtra() methods and Oscilloscope simply aligns its layout according to it.

Analysing Frequencies

In order to analyse the frequencies of the waves captured, we used sine fitting. Sine fitting function simply takes the data points and returns the amplitude, frequency, offset and phase shift of the wave.

Resources

Continue ReadingPerforming Multivibrator Experiments in PSLab Android App

Snackbar for Error Handling in Twitter Followers Insight loklak App

In this blog post am going to explain how the Twitter Followers Insight app handles error which occurs when when no query is passed to the “Search” function i.e., when the search is proceeded with an empty query.

How to handle the Exception

In such cases, I have used SNACKBAR / TOAST. Snackbar / Toast is used to popup an error notification on the screen when an exception occurs.

Script for Snackbar / Toast:

In the below script, initially the error is set to null i.e., no error. The “showError” function is which is being called when there occurs a situation of no query or an empty query. The function below helps to show an error popup which only shows till a time limit.

    $scope.error = null;

    $scope.showError = function() {
        $(".snackbar").addClass("show");
        setTimeout(function(){ $(".snackbar").removeClass("show") }, 3000);
    }

 

In this script, it checks whether the query passed is undefined or empty. If yes, then the error message which was null earlier is changed and that error is showed up on the screen to the end user. Snackbars animate upwards from the edge of the screen.

        if ($scope.query === '' || $scope.query === undefined) {
            $scope.spinner = false;
            $scope.error = "Please enter a valid Username";
            $scope.showError();
            return;
        }

 

Resources:

Continue ReadingSnackbar for Error Handling in Twitter Followers Insight loklak App

Filling Audio Buffer to Generate Waves in the PSLab Android App

The PSLab Android App works as an oscilloscope and a wave generator using the audio jack of the Android device. The implementation of the oscilloscope in the Android device using the in-built mic has been discussed in the blog post “Using the Audio Jack to make an Oscilloscope in the PSLab Android App” and the same has been discussed in the context of wave generator in the blog post “Implement Wave Generation Functionality in the PSLab Android App”. This post is a continuation of the post related to the implementation of wave generation functionality in the PSLab Android App. In this post, the subject matter of discussion is the way to fill the audio buffer so that the resulting wave generated is either a Sine Wave, a Square Wave or a Sawtooth Wave. The resultant audio buffer would be played using the AudioTrack API of Android to generate the corresponding wave. The waves we are trying to generate are periodic waves.

Periodic Wave: A wave whose displacement has a periodic variation with respect to time or distance, or both.

Thus, the problem reduces to generating a pulse which will constitute a single time period of the wave. Suppose we want to generate a sine wave; if we generate a continuous stream of pulses as illustrated in the image below, we would get a continuous sine wave. This is the main concept that we shall try to implement using code.

Initialise AudioTrack Object

AudioTrack object is initialised using the following parameters:

  • STREAM TYPE: Type of stream like STREAM_SYSTEM, STREAM_MUSIC, STREAM_RING, etc. For wave generation purposes we are using stream music. Every stream has its own maximum and minimum volume level.  
  • SAMPLING RATE: It is the rate at which the source samples the audio signal.
  • BUFFER SIZE IN BYTES: Total size of the internal buffer in bytes from where the audio data is read for playback.
  • MODES: There are two modes-
    • MODE_STATIC: Audio data is transferred from Java to the native layer only once before the audio starts playing.
    • MODE_STREAM: Audio data is streamed from Java to the native layer as audio is being played.

getMinBufferSize() returns the estimated minimum buffer size required for an AudioTrack object to be created in the MODE_STREAM mode.

minTrackBufferSize = AudioTrack.getMinBufferSize(SAMPLING_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
audioTrack = new AudioTrack(
       AudioManager.STREAM_MUSIC,
       SAMPLING_RATE,
       AudioFormat.CHANNEL_OUT_MONO,
       AudioFormat.ENCODING_PCM_16BIT,
       minTrackBufferSize,
       AudioTrack.MODE_STREAM);

Fill Audio Buffer to Generate Sine Wave

Depending on the values in the audio buffer, the wave is generated by the AudioTrack object. Therefore, to generate a specific kind of wave, we need to fill the audio buffer with some specific values. The values are governed by the wave equation of the signal that we want to generate.

public short[] createBuffer(int frequency) {
   short[] buffer = new short[minTrackBufferSize];
   double f = frequency;
   double q = 0;
   double level = 16384;
   final double K = 2.0 * Math.PI / SAMPLING_RATE;

   for (int i = 0; i < minTrackBufferSize; i++) {
         f += (frequency - f) / 4096.0;
         q += (q < Math.PI) ? f * K : (f * K) - (2.0 * Math.PI);
         buffer[i] = (short) Math.round(Math.sin(q));
   }
   return buffer;
}

Fill Audio Buffer to Generate Square Wave

To generate a square wave, let’s assume the time period to be t units. So, we need the amplitude to be equal to A for t/2 units and -A for the next t/2 units. Repeating this pulse continuously, we will get a square wave.

buffer[i] = (short) ((q > 0.0) ? 1 : -1);

Fill Audio Buffer to Generate Sawtooth Wave

Ramp signals increases linearly with time. A Ramp pulse has been illustrated in the image below:

We need repeated ramp pulses to generate a continuous sawtooth wave.

buffer[i] = (short) Math.round((q / Math.PI));

Finally, when the audio buffer is generated, write it to the audio sink for playback using write() method exposed by the AudioTrack object.

audioTrack.write(buffer, 0, buffer.length);

Resources

Continue ReadingFilling Audio Buffer to Generate Waves in the PSLab Android App