public static void main(String[] args) {

    int number_of_voices = voice_array.length;
    Random my_roll = new Random();

    Pattern[] rhythm_patterns = james.generate(piece_length, number_of_voices);
    for (int i = 0; i < number_of_voices; i++) {
      MelodicVoice this_voice = new MelodicVoice();
      this_voice.setRange(voice_array[i]);
      AccentListener my_accent_listener = new AccentListener();
      this_voice.setNoteArray(my_accent_listener.listen(rhythm_patterns[i]));
      System.out.println("adding voice " + i + " to unbuiltvoices");
      unbuilt_voices.add(this_voice);
    }
    int ub_size = unbuilt_voices.size();
    for (int i = 0; i < number_of_voices; i++) {
      // int voice_index = my_roll.nextInt(unbuilt_voices.size());
      int voice_index = i;
      System.out.println(" about to build voice pitches for " + voice_index);
      MelodicVoice nextVoice =
          buildVoicePitches(unbuilt_voices.get(voice_index), number_of_voices, my_mode_module);
      ArrayList<MelodicNote> verify_array = nextVoice.getNoteArray();
      System.out.println("Return Me: ");
      for (MelodicNote verify : verify_array) {
        if (verify.getRest()) System.out.println("rest " + verify.getDuration() + "  ");
        else System.out.println(verify.getPitch() + " " + verify.getDuration() + "   ");
      }
      if (i == 0) {
        harmonic_prog = nextVoice;
        harmonic_prog_built = true;
      } else built_voices.add(nextVoice);
      // unbuilt_voices.remove(voice_index);
    }
    unbuilt_voices.clear();
    Pattern music_output = new Pattern();
    music_output.addElement(new Tempo(tempo_bpm));

    for (byte i = 0; i < built_voices.size(); i++) {
      // create a jfugue musicstring from the built voice
      MelodicVoice final_voice = built_voices.get(i);
      ArrayList<MelodicNote> final_note_array = final_voice.getNoteArray();
      Voice jf_voice = new Voice(i);
      music_output.addElement(jf_voice);
      for (MelodicNote final_note : final_note_array) {
        int jf_int = 0;
        Note jf_note = new Note();
        jf_note.setDecimalDuration(final_note.getDuration());
        if (final_note.getPitch() != null && final_note.getRest() == false) {
          jf_int = final_note.getPitch();
          byte jf_note_byte = (byte) jf_int;
          jf_note.setValue(jf_note_byte);
          if (!final_note.getAccent()) jf_note.setAttackVelocity((byte) 40);
        } else {
          // System.out.println("setting jf note to rest");
          jf_note.setRest(true);
          jf_note.setAttackVelocity((byte) 0);
          jf_note.setDecayVelocity((byte) 0);
        }

        music_output.addElement(jf_note);
      } // add the musicstring to a pattern
    }

    // save and play the pattern
    System.out.println(music_output.getMusicString());
    Player my_player = new Player();
    my_player.play(music_output);
    Date today = new Date(System.currentTimeMillis());
    // Date format: "yyyy-MM-dd_HH:mm:ss"
    SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy-HH-mm");
    String dateString = DATE_FORMAT.format(today);
    try {
      my_player.saveMidi(
          music_output,
          new File(
              "C:\\Documents and Settings\\alyssa\\Desktop\\Species Midi\\species-"
                  + tempo_bpm
                  + "-"
                  + dateString
                  + ".mid"));
    } catch (IOException ex) {
    }
  }
  public static ArrayList<PitchCandidate> melodicCheck(
      ArrayList<PitchCandidate> pitch_candidates,
      ModeModule my_mode_module,
      MelodicVoice alter_me,
      Integer pitch_center,
      int voice_pitch_count,
      Integer previous_cp_pitch,
      Integer previous_melodic_interval,
      Boolean is_accent) {
    // Melodic Checks
    // Evaluate each pitch candidate
    for (PitchCandidate myPC : pitch_candidates) {
      int cand_pitch = myPC.getPitch();
      int melody_motion_to_cand = 0;

      System.out.println("melodicCheck evaluating pitch candidate " + cand_pitch);

      // Check if Dissonant with Root
      boolean root_interval_consonant = false;
      int root_interval = abs(cand_pitch - root_key) % 12;
      for (Integer consonance : root_consonances) {
        if (root_interval == consonance) root_interval_consonant = true;
      }
      if (root_interval_consonant) {
        System.out.println(cand_pitch + " root interval consonant");
      } else {
        if (is_accent) {
          myPC.decrementRank(Decrements.dissonant_with_root);
          System.out.println(cand_pitch + " dissonant accent with root");
        } else System.out.println("dissonant with root but note accent = " + is_accent);
      }

      // randomly decrement non-tonics
      if (cand_pitch % 12 != my_mode_module.getTonic() && roll.nextInt(2) == 1) {
        myPC.decrementRank(Decrements.is_not_tonic);
        System.out.println(cand_pitch + " is not tonic");
      }

      // decrement illegal notes
      if (cand_pitch < 0 || cand_pitch > 127) {
        myPC.decrementRank(Decrements.illegal_note);
        System.out.println(cand_pitch + " is illegal note");
      }

      // decrement motion outside of voice range
      if (cand_pitch < alter_me.getRangeMin() || cand_pitch > alter_me.getRangeMax()) {
        myPC.decrementRank(Decrements.outside_range);
        System.out.println(
            cand_pitch + " outside range " + alter_me.getRangeMin() + "-" + alter_me.getRangeMax());
      }

      // decrement too far from pitch center
      if (abs(cand_pitch - pitch_center) > 16) {
        myPC.decrementRank(Decrements.remote_from_pitchcenter);
        System.out.println(cand_pitch + " too far from pitch center" + pitch_center);
      }

      if (voice_pitch_count > 0) {
        melody_motion_to_cand = cand_pitch - previous_cp_pitch;

        // The candidate has already followed the preceding note too often. (method created)
        // look for previous_cp_pitch in PitchCount
        // if it's there get_count
        // if the count is greater than samplesize
        // check if previous_cp_pitch and pitch_candidate in MOtion Counts
        // if so get count - then divide motion count by pitch count
        // get the percentage from mode module
        // if actual count is greater than mode module percentage decrement
        for (PitchCount my_pitch_count : pitch_counts) {
          if (my_pitch_count.getPitch() == previous_cp_pitch % 12)
            if (my_pitch_count.getCount() > sample_size)
              for (MotionCount my_motion_count : motion_counts) {
                // logger.log(Level.INFO, "Entering Motion Counts");
                // System.out.println("pitch_count for " + previous_cp_pitch %12 + " = " +
                // my_pitch_count.getCount());
                // System.out.println("motion count " + my_motion_count.getCount());
                if (my_motion_count.getPreviousPitch() == previous_cp_pitch % 12
                    && my_motion_count.getSucceedingPitch() == cand_pitch % 12) {
                  double actual =
                      (double) my_motion_count.getCount() / (double) my_pitch_count.getCount();
                  System.out.println("actual = " + actual);
                  double thresh = 0.20;
                  if (my_mode_module.getMelodicMotionProbability(
                          cand_pitch, previous_cp_pitch, key_transpose)
                      != null) {
                    thresh =
                        my_mode_module.getMelodicMotionProbability(
                            cand_pitch, previous_cp_pitch, key_transpose);
                    System.out.println(
                        "motion probability of "
                            + previous_cp_pitch
                            + " to "
                            + cand_pitch
                            + " = "
                            + thresh);
                  } else
                    System.out.println(
                        "motion probability of "
                            + previous_cp_pitch
                            + " to "
                            + cand_pitch
                            + " is NULL");

                  if (actual >= thresh) {
                    myPC.decrementRank(Decrements.melodic_motion_quota_exceed);
                    System.out.println(
                        cand_pitch + " is approached too often from " + previous_cp_pitch);
                  }
                }
              }
        }
      }
      if (voice_pitch_count > 1) {
        // Peak/Trough check
        // a melodic phrase should have no more than two peaks and two troughs
        // a peak is defined as a change in melodic direction
        // so when a candidate pitch wants to go in the opposite direction of
        // the previous melodic interval we want to increment the peak or trough count accordingly
        // and determine whether we have more than two peaks or more than two troughs
        // note that the melody can always go higher or lower than the previous peak or trough

        if (previous_melodic_interval < 0
            && melody_motion_to_cand
                > 0) { // will there be a change in direction from - to +  ie trough?
          if (previous_cp_pitch == trough)
            trough_count++; // will this trough = previous trough? then increment
        }
        if (previous_melodic_interval > 0 && melody_motion_to_cand < 0) { // will there be a trough?
          if (previous_cp_pitch == peak)
            peak_count++; // will this trough = previous trough? then increment
        }
        if (peak_count > 1 || trough_count > 1) {
          peak_count--; // remember to decrement these counts since we won't actually use this pitch
          trough_count--;
          myPC.decrementRank(Decrements.peak_trough_quota_exceed);
          System.out.println(cand_pitch + " duplicates previous peak or trough");
        }
        // Motion after Leaps checks
        // First check if the melody does not go in opposite direction of leap
        // then check if there are two successive leaps in the same direction
        if (previous_melodic_interval > 4 && melody_motion_to_cand > 0) {
          myPC.decrementRank(Decrements.bad_motion_after_leap);
          System.out.println(
              melody_motion_to_cand + " to " + cand_pitch + " is bad motion after leap");
          if (melody_motion_to_cand > 4) {
            myPC.decrementRank(Decrements.successive_leaps);
            System.out.println(cand_pitch + " is successive leap");
          }
        }
        if (previous_melodic_interval < -4 && melody_motion_to_cand < 0) {
          myPC.decrementRank(Decrements.bad_motion_after_leap);
          System.out.println(
              melody_motion_to_cand + " to " + cand_pitch + " is bad motion after leap");
          if (melody_motion_to_cand < -4) {
            myPC.decrementRank(Decrements.successive_leaps);
            System.out.println(cand_pitch + " is successive leap");
          }
        }
      }
      // end melody checks
    } // next pitch candidate
    return pitch_candidates;
  }
  public static MelodicVoice buildVoicePitches(
      MelodicVoice alter_me, int number_of_voices, ModeModule my_mode_module) {
    MelodicVoice return_me = new MelodicVoice();

    int previous_cp_pitch = -13;
    int previous_melodic_interval = 0;
    trough = 200;
    trough_count = 0;
    peak = 0;
    peak_count = 0;
    same_consonant_count = 0;
    int voice_pitch_count = 0;
    LinkedList<MelodicNote> chord_prog_stack = new LinkedList<>();

    System.out.println(
        "voiceRange min " + alter_me.getRangeMin() + "   voicerange max " + alter_me.getRangeMax());
    Integer pitch_center =
        my_mode_module.getPitchCenter(alter_me.getRangeMin(), alter_me.getRangeMax());
    System.out.println("pitchcenter = " + pitch_center);

    if (harmonic_prog_built) {
      ArrayList<MelodicNote> chord_prog_temp = harmonic_prog.getNoteArray();
      for (MelodicNote b_voice_note : chord_prog_temp) {
        chord_prog_stack.add(b_voice_note);
      }
    }

    if (!built_voices.isEmpty())
      for (MelodicVoice built_voice : built_voices) {
        LinkedList<MelodicNote> cf_stack = new LinkedList<>();
        ArrayList<MelodicNote> temp = built_voice.getNoteArray();
        for (MelodicNote b_voice_note : temp) {
          cf_stack.add(b_voice_note);
        }
        built_voice_queues.add(cf_stack);
        System.out.println("created stack of melodic notes for each previously built voice ");
      }
    else {

      System.out.println("built voices Empty - start first melody");
    }

    ArrayList<MelodicNote> current_cf = new ArrayList();
    MelodicNote[] holdover_cf = new MelodicNote[built_voice_queues.size()];
    Integer[] previous_cf_pitch = new Integer[built_voice_queues.size()];
    for (int h = 0; h < built_voice_queues.size(); h++) previous_cf_pitch[h] = 1111;
    ArrayList<MelodicNote> pending_rests = new ArrayList();
    Integer current_cp_index = -13;
    ArrayList<Integer> pitch_candidate_values = new ArrayList();
    ArrayList<PitchCandidate> pitch_candidates = new ArrayList();

    MelodicNote this_cf = null;
    Integer melodic_prev_cp = 0;

    for (int i = 0; i < alter_me.getVoiceLength(); i++) { // for each melodic note in the CP voice
      System.out.println("assigning pitch to note " + i + " of " + alter_me.getVoiceLength());

      MelodicNote CP_note = alter_me.getMelodicNote(i);
      if (!CP_note.getRest()) {
        if (current_cp_index >= 0) {
          if (previous_cp_pitch == 9999) previous_cp_pitch = melodic_prev_cp;
          System.out.println(
              "starting melody check on current cp"
                  + current_cp_index
                  + " of "
                  + alter_me.getVoiceLength());
          Boolean got_accent = alter_me.getMelodicNote(current_cp_index).getAccent();
          System.out.println("the current cp note's accent value is " + got_accent);

          // MELODICALLY EVALUATE Pitch Candiates for Previous Note - Choose CP WinnerS - Previous
          // CP = CP Winner
          pitch_candidates =
              melodicCheck(
                  pitch_candidates,
                  my_mode_module,
                  alter_me,
                  pitch_center,
                  voice_pitch_count,
                  previous_cp_pitch,
                  previous_melodic_interval,
                  got_accent);
          Integer cp_winner = pickWinner(pitch_candidates);
          // re-assign variables and move on to next CP note
          pitch_candidates.clear();
          System.out.println("CP winner" + cp_winner + " to note " + current_cp_index);
          MelodicNote current_cp = alter_me.getMelodicNote(current_cp_index);
          current_cp.setPitch(cp_winner);
          return_me.addMelodicNote(current_cp);
          if (!pending_rests.isEmpty())
            for (MelodicNote my_rest : pending_rests) {
              return_me.addMelodicNote(my_rest);
            }
          pending_rests.clear();
          // Calculating Peaks and Troughs and note counts and save variables
          if (previous_cp_pitch != -13) {
            if (voice_pitch_count > 1) {
              if (previous_melodic_interval < 0
                  && cp_winner - previous_cp_pitch
                      > 0) { // will there be a change in direction from - to +  ie trough?
                if (previous_cp_pitch == trough) {
                  trough_count++;

                } else if (previous_cp_pitch != trough) {

                  trough = previous_cp_pitch;
                  trough_count = 1;
                  System.out.println("setting new trough = " + previous_cp_pitch);
                }
              }

              if (previous_melodic_interval > 0
                  && cp_winner - previous_cp_pitch
                      < 0) { // will there be a change in direction from - to +  ie trough?
                if (previous_cp_pitch == peak) peak_count++;
                else {
                  if (previous_cp_pitch > peak) {
                    peak = previous_cp_pitch;
                    peak_count = 1;
                    System.out.println("setting new peak = " + previous_cp_pitch);
                  }
                }
              }
            }

            previous_melodic_interval = cp_winner - previous_cp_pitch;
            System.out.println("previous melodic interval = " + previous_melodic_interval);

            boolean add_pitch = true;
            for (int pc = 0; pc < pitch_counts.size(); pc++) {
              if (pitch_counts.get(pc).getPitch() == previous_cp_pitch % 12) {
                pitch_counts.get(pc).incrementCount();
                add_pitch = false;
              }
            }
            if (add_pitch == true) {
              PitchCount my_pitch_count = new PitchCount(previous_cp_pitch % 12);
              pitch_counts.add(my_pitch_count);
            }

            boolean add_motn = true;
            for (int mc = 0; mc < motion_counts.size(); mc++) {
              if (motion_counts.get(mc).getPreviousPitch() == previous_cp_pitch % 12
                  && motion_counts.get(mc).getSucceedingPitch() == cp_winner % 12) {
                motion_counts.get(mc).incrementCount();
                add_motn = false;
              }
            }
            if (add_motn == true) {
              MotionCount my_motionCount = new MotionCount(previous_cp_pitch % 12, cp_winner % 12);
              motion_counts.add(my_motionCount);
            }
          }

          previous_cp_pitch = cp_winner;
          melodic_prev_cp = cp_winner;
          voice_pitch_count++;
        }

        // get pitch_candidate pitches for this note from mode_module

        if (harmonic_prog_built) {
          do {

            MelodicNote prog_stack_note = (MelodicNote) chord_prog_stack.pop();
            if (prog_stack_note.getPitch() != 0) this_key.setPitch(prog_stack_note.getPitch());
            this_key.setTotalVoiceDuration(prog_stack_note.getPreviousDuration());
            System.out.println("this key = " + this_key.getPitch());
            System.out.println("this key prevduration = " + this_key.getPreviousDuration());
            if (this_key.getPreviousDuration() >= CP_note.getStartTime()) {
              key_transpose = this_key.getPitch() % 12;
            }
          } while (this_key.getPreviousDuration() < CP_note.getPreviousDuration());
        }

        if (voice_pitch_count == 0) { // If there is no previous pitch ie this is the first note
          pitch_candidate_values =
              my_mode_module.getFirstNotePitchCandidates(
                  alter_me.getRangeMin(), alter_me.getRangeMax(), key_transpose);
          System.out.println("using first note pitch candidates");
        } else {
          System.out.println("getting pitch candidates from my modemodule");
          pitch_candidate_values =
              my_mode_module.getPitchCandidates(previous_cp_pitch, key_transpose);
          if (pitch_candidate_values.isEmpty()) System.out.println("EMPTY ARRAY!!!!");
        }
        System.out.println("voice_pitch_count" + voice_pitch_count);
        System.out.print("pitch candidates: ");

        // build pitch_candidate object array with info from rhythm patterns and mode module
        for (Integer pitch_candidate_value : pitch_candidate_values) {
          System.out.print(pitch_candidate_value + " ");
          PitchCandidate myPC = new PitchCandidate();
          myPC.setPitch(pitch_candidate_value);
          pitch_candidates.add(myPC);
        }
        System.out.println();

        // Reset Current CP index
        current_cp_index = i;
      }
      // If note is a Rest  add it to the voice
      else {
        System.out.println(" is rest ");
        if (current_cp_index == -13) {
          return_me.addMelodicNote(CP_note);
          System.out.println("adding rest to beginning of voice");
          continue;
        } else {
          pending_rests.add(CP_note);
          System.out.println("adding rest to pending rest array");
        }
      }
      /*
      Regardless if note or rest run HARMONIC CHECKS on the current cp
      If a rest, then we are still deciding the counterpoint for the
      immediately previous place in the melody.

      If this is the very first voice, then there are no prebuilt voices
      so skip this section
      */
      if (!built_voice_queues.isEmpty()) {
        for (int b = 0; b < built_voice_queues.size(); b++) {
          do {
            boolean CF_root = false;
            if (b == 0) CF_root = true;
            boolean skip_me = false;
            if (!built_voice_queues.get(b).isEmpty()) {
              if (holdover_cf[b] != null) this_cf = holdover_cf[b];
              else
                this_cf =
                    (MelodicNote) built_voice_queues.get(b).pop(); // pop from builtvoicequeues[b];
              System.out.println(" pitch = " + this_cf.getPitch());
              System.out.println(" rest = " + this_cf.getRest());
              System.out.println(" duration up to  = " + this_cf.getPreviousDuration());
            } else {
              System.out.println("cf voice is empty");
              break;
            }
            if (this_cf.getRest()) {
              if (this_cf.getDuration() > .5 || previous_cf_pitch[b] == -1 || CP_note.getRest())
                skip_me = true;
            }

            if (!skip_me) {
              // HARMONICALLY EVALUATE cp candidates against this_cf
              if (previous_cf_pitch[b] != 1111)
                pitch_candidates =
                    harmonicChecks(
                        pitch_candidates,
                        this_cf,
                        CF_root,
                        previous_cf_pitch[b],
                        previous_cp_pitch,
                        CP_note,
                        voice_pitch_count,
                        b);
              else
                pitch_candidates =
                    harmonicChecksSuperBasic(
                        pitch_candidates,
                        this_cf,
                        CF_root,
                        alter_me.getMelodicNote(current_cp_index));
              previous_cp_pitch =
                  9999; // 9999 is assigned in case CP might hold over into next CF note
            } // in which case the while loop repeats. When you break out
            // of while loop previous_cp_pitch will be checked and re-assigned above
            else {
              System.out.println("current cf index" + b + "is null");
              break;
            }
            if (this_cf.getPreviousDuration() > CP_note.getPreviousDuration())
              holdover_cf[b] = this_cf;
            else holdover_cf[b] = null;

            if (!skip_me) previous_cf_pitch[b] = this_cf.getPitch();
          } while (this_cf.getPreviousDuration() < CP_note.getPreviousDuration());
        }
      }
      /*
      If this is the last melodic placeholder, run some closing procedures

      */
      if (i == alter_me.getVoiceLength() - 1) {
        // MELODICALLY EVALUATE Current CP
        System.out.println("Last note of voice");
        Boolean last_accent = true;
        if (!CP_note.getRest()) {
          pitch_candidates =
              melodicCheck(
                  pitch_candidates,
                  my_mode_module,
                  alter_me,
                  pitch_center,
                  voice_pitch_count,
                  previous_cp_pitch,
                  previous_melodic_interval,
                  last_accent);
          Integer cp_winner = pickWinner(pitch_candidates);
          System.out.println("CP winner" + cp_winner);
          pitch_candidates.clear();
          CP_note.setPitch(cp_winner);
          return_me.addMelodicNote(CP_note);
        } else {
          Boolean got_accent = alter_me.getMelodicNote(current_cp_index).getAccent();
          System.out.println("this note's accent value is " + got_accent);
          // MELODICALLY EVALUATE Current CP - Choose CP WinnerS - Previous CP = CP Winner
          pitch_candidates =
              melodicCheck(
                  pitch_candidates,
                  my_mode_module,
                  alter_me,
                  pitch_center,
                  voice_pitch_count,
                  previous_cp_pitch,
                  previous_melodic_interval,
                  got_accent);
          Integer cp_winner = pickWinner(pitch_candidates);
          pitch_candidates.clear();
          System.out.println("CP winner" + cp_winner + " to note " + current_cp_index);
          MelodicNote current_cp = alter_me.getMelodicNote(current_cp_index);
          current_cp.setPitch(cp_winner);
          return_me.addMelodicNote(current_cp);
          if (!pending_rests.isEmpty())
            for (MelodicNote my_rest : pending_rests) {
              return_me.addMelodicNote(my_rest);
            }
          pending_rests.clear();
        }
      }
    } // loop through next CP note

    return return_me;
  } // end method