5

I have this line of chord obtained from text file. For example,

String chordLine = "C     G   Am  C";
String transposedChordLine;

Next, I need to transpose the chordLine into a new transposedChordLineusing the class below using two parameters, a String chord and integer increment of transpose. For example, transpose("C", 2) will return D.

 public class Transposer{
    private int inc;
    private static ArrayList<String> keysSharp;
    private static ArrayList<String> keysFlat;

    Transposer(){
        keysSharp = new ArrayList<String>(Arrays.asList("C", "C#", "D", "D#","E", "F","F#", "G","G#", "A","A#", "B"));
        keysFlat = new ArrayList<String>(Arrays.asList("C", "Db", "D", "Eb","E", "F","Gb", "G","Ab", "A","Bb", "B"));
    }

    public String transpose(String chord,int inc){

        this.inc = inc;

        String newChord;

        if(chord.contains("/")){
            String[] split = chord.split("/");
            newChord = transposeKey(split[0]) + "/" + transposeKey(split[1]);
        }else
            newChord = transposeKey(chord); 
        return newChord;
    }

    private String transposeKey(String key){ // C#m/D# must pass C#m or D#
        String nKey, tempKey;

        if(key.length()>1){ 
            nKey = key.substring(0, 2);
            }
        else{ nKey = key; }


        int oldIndex, newIndex;

        if(key.contains("b")){
            oldIndex = (keysFlat.indexOf(nKey)>-1) ? keysFlat.indexOf(nKey) : keysFlat.indexOf(similarKey(nKey));
            newIndex = (oldIndex + inc + keysFlat.size())%keysFlat.size();
            tempKey = keysFlat.get(newIndex);
            nKey = (key.length() < 3) ? tempKey : key.replace(nKey, tempKey);
                //(nKey + key.substring(nKey.length(), key.length()));
        }
        else if(key.contains("#")){
            oldIndex = (keysSharp.indexOf(nKey)>-1) ? keysSharp.indexOf(nKey) : keysSharp.indexOf(similarKey(nKey));
            newIndex = (oldIndex + inc + keysSharp.size())%keysSharp.size();
            tempKey = keysSharp.get(newIndex);
            nKey = (key.length() < 3) ? tempKey : key.replace(nKey, tempKey);
        }
        else{
            nKey = nKey.substring(0, 1);
            oldIndex = (keysSharp.indexOf(nKey)>-1) ? keysSharp.indexOf(nKey) : keysSharp.indexOf(similarKey(nKey));
            newIndex = (oldIndex + inc + keysSharp.size())%keysSharp.size();
            tempKey = keysSharp.get(newIndex);
            nKey = (key.length() < 2) ? tempKey : key.replace(nKey, tempKey);
        }



        return nKey;
    }


    private String similarKey(String nKey) {
        String newKey;
        switch(nKey){
        case "Cb":
            newKey = "B";
            break;
        case "Fb":
            newKey = "E";
            break;
        case "E#":
            newKey = "F";
            break;
        case "B#":
            newKey = "c";
            break;
        default:
            newKey = null;
        }
        return newKey;
    }
}

How do I replace the chordLine without losing the spaces? increment by 2 should have transposedChordLine="D A Bm D"

Here is my current attempt:

public static void main(String[] args) {

    String chordLine = "C      G            Am       C";
    String transposedChordLine;
    String normalize = chordLine.replaceAll("\\s+", " ");
    String[] split = normalize.split(" ");

    //System.out.println(normalize);


    Transposer tran = new Transposer();
    String[] temp = new String[split.length];

    for(int i=0 ; i<split.length ; i++){
        temp[i] = tran.transpose(split[i], 2);
        //System.out.println(split[i]);
        System.out.print(temp[i]);
    }

    transposedChordLine = chordLine.replaceAll([split], temp); //which is wrong

}
3
  • 1
    Lots of potential solutions. For example: create an object, ChordLine which will instance a single chord line. In this object's constructor you can explicitly keep track of the amount of whitespace between chords n_i and n_i+1. Then, upon transposition, you can simple replace the white space in the output ChordLine Commented Aug 17, 2015 at 5:34
  • Sorry, this is not about your question, but your code is really bad. Don't initialize static members in the constructor. Change the two statics to List<String> and initialize them directly using Arrays.asList. Also, don't use instance fields to pass on parameters to private methods. If inc can be different on calls to transpose, then pass it on as a parameter to the private methods. If inc is always the same, then pass it on the constructor, not on the transpose method. Commented Aug 17, 2015 at 5:55
  • I started java just about a year ago. Thanks for the comment. Commented Aug 17, 2015 at 6:01

3 Answers 3

1

Your tokenization is unusual enough (preserving delimiters), that you probably want to do it yourself. Basically, if you see a token that matches a note, pass it to the transposer. Otherwise, pass along a space. Use a while loop to navigate along the notes. Here's the code that does just that:

private static final Transposer transposer = new Transposer();

public static void main(String[] args) {
  String chordLine = "C      G            Am       C";

  String transposed = transposeChordLine(chordLine);

  System.out.println(transposed);
}

private static String transposeChordLine(String chordLine) {
  char[] chordLineArray = chordLine.toCharArray();

  StringBuilder transposed = new StringBuilder();
  StringBuilder currentToken = new StringBuilder();

  int index = 0;
  while(index < chordLine.length()) {
    if(chordLineArray[index] == ' ') {
      transposed.append(' ');
      currentToken = processToken(transposed, currentToken);
    } else {
      currentToken.append(chordLineArray[index]);
    }
    index++;
  }

  processToken(transposed, currentToken);

  return transposed.toString();
}

private static StringBuilder processToken(StringBuilder transposed,
    StringBuilder currentToken) {
  if(currentToken.length() > 0) {
    String currentChord = currentToken.toString();
    String transposedChord = transposer.transpose(currentChord, 2);
    transposed.append(transposedChord);
    currentToken = new StringBuilder();
  }
  return currentToken;
}

Note: you have some stylistic issues with your code. You can initialize your constant chord maps in the fields themselves; by doing so in the constructor, you overwrite them, doing unnecessary work and potentially causing problems, especially in multithreaded code. Just do them inline in the field declaration. It's also good to wrap these in Collections.unmodifiableList, so they can't be changed when your code is running and therefore it makes it easier to not accidentally make a mistake.

Also, you shouldn't save the inc variable in a field, just pass it through as an argument. This way, if you are using the same object twice, it doesn't preserve state and is therefore threadsafe. I know these things don't matter to your current program but it's good to learn these habits now. Here's the modified Transposer class:

public class Transposer {
  private static final List<String> keysSharp = Collections.unmodifiableList(Arrays.asList("C", "C#", "D", "D#", "E",
        "F", "F#", "G", "G#", "A", "A#", "B"));
  private static final List<String> keysFlat = Collections.unmodifiableList(Arrays.asList("C", "Db", "D", "Eb", "E",
      "F", "Gb", "G", "Ab", "A", "Bb", "B"));

  public String transpose(String chord, int inc) {
    String newChord;

    if (chord.contains("/")) {
      String[] split = chord.split("/");
      newChord = transposeKey(split[0], inc) + "/" + transposeKey(split[1], inc);
    } else
      newChord = transposeKey(chord, inc);
    return newChord;
  }

  private String transposeKey(String key, int inc) { // C#m/D# must pass C#m or D#
    String nKey, tempKey;

    if (key.length() > 1) {
      nKey = key.substring(0, 2);
    } else {
      nKey = key;
    }

    int oldIndex, newIndex;

    if (key.contains("b")) {
      oldIndex = (keysFlat.indexOf(nKey) > -1) ? keysFlat.indexOf(nKey)
          : keysFlat.indexOf(similarKey(nKey));
      newIndex = (oldIndex + inc + keysFlat.size()) % keysFlat.size();
      tempKey = keysFlat.get(newIndex);
      nKey = (key.length() < 3) ? tempKey : key.replace(nKey, tempKey);
      // (nKey + key.substring(nKey.length(), key.length()));
    } else if (key.contains("#")) {
      oldIndex = (keysSharp.indexOf(nKey) > -1) ? keysSharp.indexOf(nKey)
          : keysSharp.indexOf(similarKey(nKey));
      newIndex = (oldIndex + inc + keysSharp.size()) % keysSharp.size();
      tempKey = keysSharp.get(newIndex);
      nKey = (key.length() < 3) ? tempKey : key.replace(nKey, tempKey);
    } else {
      nKey = nKey.substring(0, 1);
      oldIndex = (keysSharp.indexOf(nKey) > -1) ? keysSharp.indexOf(nKey)
          : keysSharp.indexOf(similarKey(nKey));
      newIndex = (oldIndex + inc + keysSharp.size()) % keysSharp.size();
      tempKey = keysSharp.get(newIndex);
      nKey = (key.length() < 2) ? tempKey : key.replace(nKey, tempKey);
    }

    return nKey;
  }

  private String similarKey(String nKey) {
    String newKey;
    switch (nKey) {
    case "Cb":
      newKey = "B";
      break;
    case "Fb":
      newKey = "E";
      break;
    case "E#":
      newKey = "F";
      break;
    case "B#":
      newKey = "c";
      break;
    default:
      newKey = null;
    }
    return newKey;
  }
}
Sign up to request clarification or add additional context in comments.

Comments

1

Here's shorter solution (I added this method to the Transposer class):

public String transposeLine(String chordLine, int inc) {
    Pattern pattern = Pattern.compile("\\S+\\s*"); // can be moved to static final field
    Matcher matcher = pattern.matcher(chordLine);
    StringBuffer sb = new StringBuffer();
    while(matcher.find()) {
        String chord = matcher.group();
        String transposed = transpose(chord.trim(), inc);
        matcher.appendReplacement(sb, 
            String.format(Locale.ENGLISH, "%-"+chord.length()+"s", transposed));
    }
    matcher.appendTail(sb);
    return sb.toString();
}

I'm using regex matcher to create the new String. The regular expression matches the chord name along with all the spaces after that. To ensure that replacement has the same length I use String.format and provide format string like %-XXs where XX is the length of the non-transposed chord with spaces. Note that if there are not enough spaces, then the resulting line become longer.

Usage:

public static void main(String[] args) {
    String chordLine = "C      G            Am       C";

    System.out.println(chordLine);
    for(int i=0; i<12; i++) {
        String result = new Transposer().transposeLine(chordLine, i);
        System.out.println(result);
    }
}

Output:

C      G            Am       C
C      G            Am       C
C#     G#           A#m      C#
D      A            Bm       D
D#     A#           Cm       D#
E      B            C#m      E
F      C            Dm       F
F#     C#           D#m      F#
G      D            Em       G
G#     D#           Fm       G#
A      E            F#m      A
A#     F            Gm       A#
B      F#           G#m      B

Comments

0

Given a chord line, transposer, and an increment for transposing:

String chordLine = "C      G            Am       C";
Transposer tran = new Transposer();
int offset = 2;

To get the transposed chord line while preserving white space, you can use regular expression lookarounds to split at whitespace boundaries, then conditionally process the resulting strings through your transposer, as follows:

String transposed = Arrays.stream(chordLine.split("((?<=\\s)|(?=\\s))")).map(  // use regex to split on every whitespace boundary
    str ->                                                                          // for each string in the split
        Character.isWhitespace(str.charAt(0))                                       // if the string is whitespace
            ? str                                                                   // then keep the whitespace
            : tran.transpose(str, offset)                                           // otherwise, it's a chord, so transpose it
).collect(Collectors.joining());                                                    // re-join all the strings together

Or if you prefer Java 7, use a StringBuilder to build the transposed chord line as you iterate over the tokens:

StringBuilder sb = new StringBuilder();
for (String str : chordLine.split("((?<=\\s)|(?=\\s))")) {
    sb.append(Character.isWhitespace(str.charAt(0)) ? str : tran.transpose(str, offset));
}
String transposed = sb.toString();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.