2

I'm working on a project, where I need to show the pitches of the words (from the song) in a GraphView. I wrote a program that would get the pitches from a song and create a .txt file from it.

Then I wrote a class, that reads the file and creates a list from it. So in the end I would have a list that consists of words and word contains pitches. In the MakePitchesList you can see what the pitches output from a song looks like. I have 0,14:23281,61 on every line. The first part of the line is timeOccurance meaning, when this pitch was heard and the second part is the pitch itself. So in this example the timeOccurance would be 0,14 and pitch at that given time 23281,61.

Here are the three classes that make a wordsList out of a pitch .txt file.

public class Pitch{

    float occuranceTime;
    float pitch;


    public void setOccuranceTime(float occuranceTime) {
        this.occuranceTime = occuranceTime;
    }

    public float getOccuranceTime() {
        return occuranceTime;
    }

    public void setPitch(float pitch) {
        this.pitch = pitch;
    }

    public float getPitch() {
        return pitch;
    }
}

public class MakePitchesList {


    String[] pitches;
    List<Pitch> listOfPitches = new ArrayList<Pitch>();

    public List<Pitch> getListOfPitches(){
        getPitches();
        for (String pitchString: pitches) {
            Pitch pitch = new Pitch();
            makeListOfPitches(pitch, pitchString);
        }
        return listOfPitches;
    }

    public void makeListOfPitches(Pitch pitch, String pitchString){
        pitch.setPitch(getPitchesInfo(pitchString, 1));
        pitch.setOccuranceTime(getPitchesInfo(pitchString, 0));
        listOfPitches.add(pitch);
    }


    public String[] getPitches() {
        pitches = pitchesRaw.split("\\r?\\n");
        return pitches;
    }

    private float getPitchesInfo(String pitch, int position){
        String[] frequencyAndTime = pitch.split("\\:");
        if(position == 0){
            return Float.parseFloat(frequencyAndTime[0].replace(',', '.'));
        }
        if(position == 1){
            return Float.parseFloat(frequencyAndTime[1].replace(',', '.'));
        }
        else return 0;
    }

    String pitchesRaw =
            "0,14:23281,61\n" +
            "0,23:53,65\n" +
            "0,37:72,53\n" +
            "0,56:86,09\n" +
            "0,60:88,58\n" +
            "0,65:87,45\n" +
            "0,70:87,11\n" +
            "0,74:89,56\n" +
            "0,79:96,22\n" +
            "0,84:23288,24\n" +
            "0,88:103,92\n" +
            "0,93:107,46\n" +
            "0,98:108,02\n" +
            "1,02:107,51\n" +
            "1,07:104,92\n" +
            "1,11:105,94\n" +
            "1,16:106,40\n" +
            "1,21:104,43\n" +
            "1,25:104,93\n" +
            "1,30:108,01\n" +
            "1,35:316,81\n" +
            "1,39:103,98\n" +
            "1,44:23297,42\n" +
            "1,49:23357,42\n" +
            "1,53:23359,74\n" +
            "1,58:23393,04\n" +
            "1,63:23244,18\n" +
            "1,67:23220,51\n" +
            "1,72:23250,06\n" +
            "1,76:23288,84\n" +
            "1,81:23241,81\n" +
            "1,86:23295,22\n" +
            "1,90:23268,04\n" +
            "1,95:23252,78\n" +
            "2,00:23224,22\n" +
            "2,04:23429,71\n" +
            "2,09:23214,58\n" +
            "2,14:23240,70\n" +
            "2,18:23237,71\n" +
            "2,23:23231,22\n" +
            "2,28:23222,77\n" +
            "2,32:23239,73\n" +
            "2,37:23235,98\n" +
            "2,41:23222,16\n" +
            "2,46:23224,01\n" +
            "2,51:23214,26\n" +
            "2,55:23223,20\n" +
            "2,60:23234,11\n" +
            "2,65:23221,65\n" +
            "2,69:23213,45\n" +
            "2,74:23217,44\n" +
            "2,79:23235,93\n" +
            "2,83:11122,79\n" +
            "2,88:23234,58\n" +
            "2,93:23229,52\n" +
            "2,97:23255,48\n" +
            "3,02:23254,44\n" +
            "3,07:23355,41\n" +
            "3,44:105,48\n" +
            "3,48:115,45\n" +
            "3,53:117,78\n" +
            "3,58:127,36\n" +
            "3,62:131,24\n" +
            "3,67:130,33\n" +
            "3,72:131,93\n" +
            "3,76:127,32\n" +
            "3,81:117,18\n" +
            "3,85:117,80\n" +
            "3,90:117,15\n" +
            "3,95:121,04\n" +
            "3,99:131,22\n" +
            "4,04:130,38\n" +
            "4,09:130,34\n" +
            "4,13:129,57\n" +
            "4,18:120,38\n" +
            "4,23:121,06\n" +
            "4,32:100,12\n" +
            "4,37:23483,16\n" +
            "4,41:112,95\n" +
            "4,46:23448,04\n" +
            "4,50:23396,09\n" +
            "4,55:23292,90\n" +
            "4,60:117,21\n" +
            "4,64:116,58\n" +
            "4,69:116,62\n" +
            "4,74:119,18\n" +
            "4,78:131,19\n" +
            "4,83:130,34\n" +
            "4,88:129,59\n" +
            "4,92:132,64\n" +
            "4,97:129,68\n" +
            "5,02:132,71\n" +
            "5,06:133,57\n" +
            "5,11:128,94\n" +
            "5,15:131,09\n" +
            "5,20:132,75\n" +
            "5,25:129,68\n" +
            "5,29:131,26\n" +
            "5,34:131,22\n" +
            "5,39:130,38\n" +
            "5,43:146,01\n" +
            "5,48:140,43\n" +
            "5,57:23450,16\n" +
            "5,62:130,46\n" +
            "5,67:132,02\n" +
            "5,71:23243,22\n" +
            "5,76:23456,28\n" +
            "5,85:23246,64\n" +
            "5,90:23274,97\n" +
            "5,94:23310,30\n" +
            "5,99:23229,71\n" +
            "6,08:23214,33\n" +
            "6,13:23221,53\n" +
            "6,18:23263,48\n" +
            "6,22:23213,17\n" +
            "6,27:23235,04\n" +
            "6,32:23222,02\n" +
            "6,36:23214,90\n" +
            "6,41:23230,05\n" +
            "6,46:23212,55\n" +
            "6,50:23221,33\n" +
            "6,55:23226,70\n" +
            "6,59:23217,07\n" +
            "6,64:23272,07\n" +
            "6,69:11102,74\n" +
            "6,73:23263,38\n" +
            "6,78:23217,53\n" +
            "6,97:23243,63\n" +
            "7,11:23214,11\n" +
            "7,15:23229,58\n" +
            "7,20:23225,70\n" +
            "7,24:23244,82\n" +
            "7,29:23243,09\n" +
            "7,34:23249,66\n" +
            "7,38:23226,67\n" +
            "7,43:23246,31\n" +
            "7,48:23258,55\n" +
            "7,52:23230,34\n" +
            "7,57:23225,60\n" +
            "7,62:23280,25\n" +
            "7,66:23238,08\n" +
            "7,71:23221,47\n" +
            "7,85:117,87\n" +
            "7,89:117,19\n" +
            "7,94:117,21\n" +
            "7,99:117,21\n" +
            "8,03:116,57\n" +
            "8,08:119,10\n" +
            "8,13:44,01\n" +
            "8,17:129,52\n" +
            "8,22:132,72\n" +
            "8,27:143,19\n" +
            "8,31:141,13\n" +
            "8,36:139,35\n" +
            "8,45:132,82\n" +
            "8,50:129,76\n" +
            "8,54:130,43\n" +
            "8,68:94,20\n" +
            "8,78:132,70\n" +
            "8,82:130,43\n" +
            "8,87:129,60\n" +
            "8,92:130,56\n" +
            "8,96:128,92\n" +
            "9,01:119,19\n" +
            "9,06:118,45\n" +
            "9,10:103,41\n" +
            "9,15:103,41\n" +
            "9,20:103,89\n" +
            "9,24:106,46\n" +
            "9,29:214,93\n" +
            "9,33:23427,95\n" +
            "9,38:23356,01\n" +
            "9,43:106,41\n" +
            "9,47:100,57\n" +
            "9,52:106,39\n" +
            "9,57:104,40\n" +
            "9,61:99,70\n" +
            "9,66:106,42\n" +
            "9,71:103,50\n" +
            "9,75:104,47\n" +
            "9,80:106,97\n" +
            "9,85:99,68\n" +
            "9,89:23454,22\n" +
            "9,94:23299,56\n" +
            "9,98:23275,30\n" +
            "10,03:23222,72\n" +
            "10,08:23246,09\n" +
            "10,12:23221,14\n" +
            "10,17:23240,54\n" +
            "10,22:23246,81\n" +
            "10,26:23224,74\n" +
            "10,31:23249,41\n" +
            "10,36:23214,79\n" +
            "10,40:23213,46\n" +
            "10,45:23259,51\n" +
            "10,50:23217,39\n" +
            "10,54:23215,36\n" +
            "10,59:23224,87\n" +
            "10,63:23242,27\n" +
            "10,68:23270,82\n" +
            "10,73:23243,19\n" +
            "10,77:23222,75\n" +
            "10,82:23268,78\n" +
            "10,87:23321,62\n" +
            "10,91:23259,65\n" +
            "11,05:23226,24\n" +
            "11,10:23222,92\n" +
            "11,15:23218,83\n" +
            "11,19:23211,71\n" +
            "11,24:11112,28\n" +
            "11,28:23261,03\n" +
            "11,33:23265,31\n" +
            "11,38:23245,92\n" +
            "11,42:57,09\n" +
            "11,61:103,45\n" +
            "11,66:103,91\n" +
            "11,70:102,02\n" +
            "11,75:107,96\n" +
            "11,80:105,43\n" +
            "11,84:104,46\n" +
            "11,89:116,64\n" +
            "11,94:115,99\n" +
            "11,98:114,77\n" +
            "12,03:121,72\n" +
            "12,07:123,16\n" +
            "12,12:125,12\n" +
            "12,17:128,85\n" +
            "12,21:120,37\n" +
            "12,26:116,52\n" +
            "12,31:130,55\n" +
            "12,35:131,06\n" +
            "12,40:131,89\n" +
            "12,45:128,88\n" +
            "12,49:23397,75\n" +
            "12,59:118,45\n" +
            "12,63:116,54\n" +
            "12,68:119,70\n" +
            "12,72:115,45\n" +
            "12,77:115,30\n" +
            "12,82:119,86\n" +
            "12,86:116,59\n" +
            "12,91:114,13\n" +
            "12,96:119,04\n" +
            "13,00:118,47\n" +
            "13,05:115,38\n" +
            "13,10:128,92\n";
}

public class MakeWordsList {

    List<Pitch> pitches;

    public List<List<Pitch>> getWordsList(List<Pitch> pitchList) {
        return makeWordsList(pitchList);
    }

    List<Pitch> oneWord = new ArrayList<>();
    List<List<Pitch>> wordList = new ArrayList<>();


    public List<List<Pitch>> makeWordsList(List<Pitch> pitchList){
        pitches = pitchList;
        int pauseCounter = 0;

        for (int i = 0; i < pitchList.size(); i++) {
            if(pitchList.get(i).getPitch() > 10000){
                pauseCounter++;
            } else {

                if(pauseCounter > 0){
                    if(pauseCounter >= 5){
                        wordList.add(oneWord);
                        oneWord = new ArrayList<>();
                    }
                    pauseCounter = 0;
                }
                oneWord.add(pitchList.get(i));
            }
        }
        if(oneWord.size() > 0){
            wordList.add(oneWord);
        }
        return wordList;
    }
}

Now from that wordsList I create a scrollable GraphView that on paper would look something like this. GraphView lookalike on paper

Now I have set the boundaries for the GraphView: MinX = -0.8; MaxX = 0.4. This way I'm showing 1 sec at a time on screen. Then I start a thread so it would change the coordinates of the GraphView by 0.1 every 100ms, which should add up as 1 every sec.

Here it is in code:

public class MainActivity extends AppCompatActivity {

    final static double GRAPH_STARTING_X = -0.8;
    final static double GRAPH_ENDING_X = 0.4;
    List<java.util.List<Pitch>> wordsList;
    private Viewport graphViewPort;
    private Handler handler;
    private Runnable runnable;
    private GraphView graph;
    private DataPoint[] points;
    private LineGraphSeries<DataPoint> series;
    private int seriesNr;
    private double orderNr = GRAPH_STARTING_X;
    private ImageButton playRecordButton;
    private List<Pitch> pitchesList;
    private int songIndex;
    private MediaPlayer mediaPlayer;
    private boolean isPlaying = false;


    @SuppressLint("ClickableViewAccessibility")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Get Textview and Button
        playRecordButton = (ImageButton) findViewById(R.id.playRecord);

        //Initialize pitches and words
        MakePitchesList listOfPithes = new MakePitchesList();
        MakeWordsList listOfWords = new MakeWordsList();
        pitchesList = listOfPithes.getListOfPitches();
        wordsList = listOfWords.getWordsList(pitchesList);

        //Initialize graph
        graph = (GraphView) findViewById(R.id.graph);
        initGraph();

        //ViewPort
        graphViewPort = graph.getViewport();

        //Handler
        handler = new Handler();

        playRecordButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(!isPlaying){
                    playMedia(songIndex);

                    //Start moving graph
                    //Moved to PlayMedia - setOnPreparedListen
//                    drawAndMoveGraph();

                    isPlaying = true;
                } else if(isPlaying){

                    //Move graph to starting position
                    resetGraph();

                    //Stop Playing audio
                    stopAudio();

                    isPlaying = false;

                }
            }
        });


    }

    //Moves the graph every 100ms by 0.1 which results in 1 every second
    private void drawAndMoveGraph() {
        runnable = new Runnable() {
            public void run() {
                graphViewPort.setMinX(orderNr);
                graphViewPort.setMaxX(orderNr + 1);
                graph.invalidate();
                if (pitchesList.size() != orderNr) {
                    orderNr = orderNr + 0.1;
                }


                handler.postDelayed(this, 100);
            }
        };
        runnable.run();
    }

    //Load up and paint the graph
    private void initGraph() {
        for (int i = 0; i < wordsList.size(); i++) {
            seriesNr = 0;
            points = new DataPoint[wordsList.get(i).size()];
            for (Pitch pitch : wordsList.get(i)) {
                points[seriesNr] = new DataPoint(pitch.getOccuranceTime(), pitch.getPitch());
                seriesNr++;
            }
            series = new LineGraphSeries<>(points);
            series.setThickness(15);
            graph.addSeries(series);
        }

        //VocalTestPoints - Just for vocal testing
        PointsGraphSeries<DataPoint> series = new PointsGraphSeries<>(new DataPoint[]{
                new DataPoint(0, 50),
                new DataPoint(0, 75),
                new DataPoint(0, 100),
                new DataPoint(0, 125),
                new DataPoint(0, 150),
                new DataPoint(0, 175),
                new DataPoint(0, 200),
                new DataPoint(0, 225),
                new DataPoint(0, 250),
                new DataPoint(0, 275),
        });
        series.setSize(5);
        series.setColor(Color.YELLOW);
        graph.addSeries(series);


        // set manual X bounds
        graph.getViewport().setYAxisBoundsManual(true);
        graph.getViewport().setMinY(-50);
        graph.getViewport().setMaxY(400);

        graph.getViewport().setXAxisBoundsManual(true);
        graph.getViewport().setMinX(GRAPH_STARTING_X);
        graph.getViewport().setMaxX(GRAPH_ENDING_X);
        graph.getViewport().setScrollable(true);
    }


//    Play the chosen song and start moving the graph
    private void playMedia(int songIndex) {

        Uri uri = Uri.parse("android.resource://com.example.thermaltakei7.testsinging/" + R.raw.perfect);

        mediaPlayer = new MediaPlayer();

        //Reset so that the MediaPlayer is not pointing to another data source
        mediaPlayer.reset();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        try {
            mediaPlayer.setDataSource(getApplicationContext(), uri);
        } catch (IOException e) {
            e.printStackTrace();
        }
        mediaPlayer.prepareAsync();
        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                mediaPlayer.start();
                drawAndMoveGraph();
            }
        });


    }

    //Stop audio from playing
    private void stopAudio() {
        mediaPlayer.stop();
        mediaPlayer.release();

    }

    //Reset graph to its initial position
    private void resetGraph() {
        handler.removeCallbacks(runnable);
        graphViewPort.setMinX(GRAPH_STARTING_X);
        graphViewPort.setMaxX(GRAPH_ENDING_X);
        graph.invalidate();
        orderNr = GRAPH_STARTING_X;
    }


}

To sum up what the code does: it gets the wordsList from the pitches, initializes the graph, starts listening button click. When button is clicked, it starts moving graph as described above and plays audio.

The problem is that the audio and moving graphView go out of sync. For the first 10sec or so it works like it should but then the lines start falling behind. I would really appreciate if someone took their time to understand all of this mess.

Maybe picture of the actual final graphView will also help. GraphView on Android device.

Blue line is the first incoming word.

The audio's timing and line should meet at "0".

I have also created a small sample that only contains the classes and topics mentioned above. It can be accessed from here: https://github.com/richardboss/testSinging

This code containst a song in its raw folder so it's a bit bigger than it should be, but this was the fastest way to show a demo of what I was trying to say.

Any ideas, suggestions, help is really welcome. Or comments on the code style.

Richard
  • 1,087
  • 18
  • 52

0 Answers0