DEV Community

Magno Júnior
Magno Júnior

Posted on

Abordando grafos através das escalas musicais

Abordando grafos através das escalas musicais

Image description

Como desenvolvedor de software e também amante de música, embora não toque nenhum instrumento, mas com um raso conhecimento em teoria musical. Resolvi unificar essas duas coisas nesse artigo, para apresentar uma breve explicação sobre grafos e como implementar um algoritmo que traga a escala maior de determinada nota musical utilizando a estrutura de grafo.

O que é um grafo?

A princípio, o que seria um grafo? Um grafo é uma estrutura de dados, baseada na teoria dos grafos (caso queira se aprofundar mais, leia sobre o problema das 7 pontes de Königsberg, de Leonhard Euler), organizada em vértices e arestas, onde os vértices são interconectados entre si através das arestas, seguindo um determinado padrão/regra que determine uma relação entre eles. Trazendo para dentro do contexto das escalas musicais, é sabido que a escala maior de C (dó) é: D (ré), E (mi), F (fá), G (sol), A (lá), B (si), C (dó).

Desse modo, a vértice C (dó) estaria conectada às vértices D (ré), E (mi), F (fá), G (sol), A (lá), B (si), C (dó)

Image description

Na imagem acima está como ficaria o grafo seguindo a escala de C (dó), note que cada vértice que faz parte da escala maior de C (dó) está conectado na vértice C, as linhas fazendo essa conexão seriam as arestas.

Implementando o algoritmo para a escala maior

A escala maior de uma nota consiste nas 7 notas em sequência seguindo o padrão: tom, tom, semitom, tom, tom, tom, semitom, a partir da nota tônica (a nota de onde está sendo tirada a escala). Esse tom e semitom seria a distância entre duas notas, ex: D (ré) está a um tom de distância de E (mi) que por sua vez está meio tom (semitom) de distância de F (fá).

Dado o contexto e qual o padrão para se obter uma escala maior de uma nota, nossa implementação em java ficaria assim:

package notesScale;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MajorScaleGraph {

 private int[] majorScalePattern = { 2, 2, 1, 2, 2, 2, 1 }; // iniciando um array com os passos de tom e semitom
 private String[] notes = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; // inciando um array com as notas

 private Map<String, List<String>> nodes = new HashMap<String, List<String>>(); // iniciando um hashmap para representar as vertices e com quais estao conectadas

 private void addNode(String node) {
  nodes.put(node, new ArrayList<String>()); // adicionando uma vertice no grafo
 }

 private void addEdge(String node, String value) {
  List<String> edges = nodes.get(node);
  edges.add(value); // adicinando uma aresta em um vertice, para conecta-lo em outro vertice.

  nodes.put(node, edges); // atualizando as arestas do vertice
 }

 public void init() {
  // percorre a lista de notas de gera a escala maior de cada uma
  for (int i = 0; i < notes.length; i++) {
   String note = notes[i];

   addNode(note);

   int nextNoteFromScaleIndex = i;

   // executa o padrao e conecta nota encontrada na vertice da nota tonica da escala
   for (int tone : majorScalePattern) {
    nextNoteFromScaleIndex = (nextNoteFromScaleIndex + tone) % 12;

    String nextNoteFromScale = notes[nextNoteFromScaleIndex];

    addEdge(note, nextNoteFromScale);
   }
  }
 }

 // retorna a escala de acordo com a nota
 public List<String> getMajorScaleFromNote(String note) {
  List<String> majorScale = nodes.get(note);

  return majorScale;
 }
}
Enter fullscreen mode Exit fullscreen mode

Essa seria a classe que representaria nosso grafo, nela tem toda a regra de negócio do nosso grafo e como ele deve funcionar.

A nossa classe principal ficaria assim:

package notesScale;

public class Main {
 public static void main(String[] args) {
  MajorScaleGraph graph = new MajorScaleGraph(); // instanciando nosso grafo
  graph.init(); // chamando o metodo init para iniciar o grafo e ja conectar os vertices entre si

  System.out.println(graph.getMajorScaleFromNote("C")); // tras a escala maior de acordo com a nota passada como parametro
  // output: [D, E, F, G, A, B, C]

  System.out.println(graph.getMajorScaleFromNote("D"));
  // output: [E, F#, G, A, B, C#, D]
 }
}
Enter fullscreen mode Exit fullscreen mode

No final, depois de organizado nosso grafo ficaria organizado dessa forma:

Image description

Conclusão

Neste artigo, foi abordado de forma bem simples um pouco do conceito e funcionamento de um grafo, sobre como ele é organizado, também foi implementando um algoritmo em java que gera as escalas maiores das notas musicais para exemplificar a estrutura de um grafo. Espero que tenham curtido e até mais. Obrigado pela leitura e pela atenção!

Caso queira conferir o repositório e até mesmo fazer sua própria implementação e/ou implementar as escalar menores, segue o link do repositório: https://github.com/magnojunior07/noteScale

Top comments (2)

Collapse
 
vieirandre profile image
André Vieira

Muito bom! Subi um pull request com algumas melhorias no código.

Em resumo:

  • o array que guarda o padrão da escala maior é marcado como: static, já que representa algo a nível de classe, não instância; e final, pra garantir a imutabilidade
  • em vez de uma lista de strings para as notas, é criado um enum “Note”, pra aumentar a segurança de tipo e prover consistência à maneira como as notas são referenciadas
  • um conjunto é usado pra armazenar os nós do grafo, garantindo a não duplicidade de valores. A escolha pelo LinkedHashSet se deu pois ele também assegura a ordem de inserção — mantendo o comportamento da lista original
Collapse
 
magnojunior07 profile image
Magno Júnior

Opa @vieirandre, muito obrigado pelo feedback e pela sugestão de melhoria! Irei sim implementar as sugestões de melhoria