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.
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 SongPlayer extends AppCompatActivity {
private Viewport graphViewPort;
private Handler handler;
private Runnable runnable;
private GraphView graph;
private DataPoint[] points;
private LineGraphSeries<DataPoint> series;
private int seriesNr;
final static double GRAPH_STARTING_X = -0.8;
final static double GRAPH_ENDING_X = 0.4;
private double orderNr = GRAPH_STARTING_X;
private List<Pitch> pitchesList;
List<List<Pitch>> wordsList;
@SuppressLint("ClickableViewAccessibility")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song_player);
Intent intent = getIntent();
//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.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionevent) {
final int action = motionevent.getAction();
if (action == MotionEvent.ACTION_DOWN) {
//Start playing audio
playMedia();
//Start moving graph
drawAndMoveGraph();
} else if (action == MotionEvent.ACTION_UP) {
//Stop moving graph and set them to the beginning
resetGraph();
//Stop Playing audio
stopAudio();
}//end else
return false;
} //end onTouch
}); //end b my button
}
private void drawAndMoveGraph(){
runnable = new Runnable() {
public void run() {
System.out.println(orderNr);
graphViewPort.setMinX(orderNr);
graphViewPort.setMaxX(orderNr + 1);
graph.invalidate();
if(pitchesList.size() != orderNr){
orderNr = orderNr + 0.1;
// System.out.println(orderNr);
}
handler.postDelayed(this, 100);
}
};
runnable.run();
}
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
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); //mitu korraga näeb
graph.getViewport().setScrollable(true);
}
private void playMedia(int songIndex){
StorageUtil storage = new StorageUtil(getApplicationContext());
storage.storeAudio(audioList);
storage.storeAudioIndex(songIndex);
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(storage.loadAudio().get(storage.loadAudioIndex()).getData());
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.prepareAsync();
// mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
}
});
}
private void stopAudio(){
mediaPlayer.stop();
mediaPlayer.release();
}
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. . Blue line is the first incoming word.