OO Design, ¿cómo modelar la armonía tonal?

12

Empecé a escribir un programa en C ++ 11 que analizaría los acordes, escalas y armonía. El mayor problema que tengo en mi fase de diseño es que la nota 'C' es una nota, un tipo de acorde (Cmaj, Cmin, C7, etc.) y un tipo de clave (la clave de Cmajor, Cminor). El mismo problema surge con los intervalos (tercero menor, tercero mayor).

Estoy usando una clase base, Token, que es la clase base para todos los 'símbolos' en el programa. así por ejemplo:

class Token {
public:
    typedef shared_ptr<Token> pointer_type;
    Token() {}
    virtual ~Token() {}
};

class Command : public Token {
public:
    Command() {}
    pointer_type execute();
}

class Note : public Token;

class Triad : public Token; class MajorTriad : public Triad; // CMajorTriad, etc

class Key : public Token; class MinorKey : public Key; // Natural Minor, Harmonic minor,etc

class Scale : public Token;

Como puede ver, la creación de todas las clases derivadas (CMajorTriad, C, CMajorScale, CMajorKey, etc.) se volvería rápidamente ridículamente compleja e incluiría todas las demás notas, así como también los elementos armónicos. la herencia múltiple no funcionaría, es decir:

class C : public Note, Triad, Key, Scale

clase C, no pueden ser todas estas cosas al mismo tiempo. Es contextual, además, no funcionará polimorfizar con esto (¿cómo determinar qué súper métodos realizar? Llamar a todos los constructores de súper clases no debería ocurrir aquí)

¿Hay ideas de diseño o sugerencias que las personas tengan para ofrecer? No he podido encontrar nada en google en lo que respecta a modelar la armonía tonal desde una perspectiva OO. Hay demasiadas relaciones entre todos los conceptos aquí.

    
pregunta Igneous01 09.12.2012 - 13:55

6 respuestas

9

Creo que el mejor enfoque es reproducir las relaciones reales entre estas entidades.

Por ejemplo, podría tener:

  • un objeto Note , cuyas propiedades son

    • nombre (C, D, E, F, G, A, B)

    • accidental (natural, plano, afilado)

    • frecuencia u otro identificador de tono único

  • un objeto Chord , cuyas propiedades son

    • una matriz de Note objetos

    • nombre

    • accidental

    • calidad (mayor, menor, disminuida, aumentada, suspendida)

    • adiciones (7, 7+, 6, 9, 9+, 4)

  • un objeto Scale , cuyas propiedades son

    • una matriz de Note objetos

    • nombre

    • tipo

      (mayor, menor natural, menor melódico, menor armónico)

    • modo (jónico, doriano, frigio, lydiano, mixolidiano, eólico, locriano)

Luego, si su entrada es textual, puede crear notas con una cadena que incluye el nombre de la nota, accidental y (si lo necesita) una octava.

Por ejemplo (pseudocódigo, no sé C ++):

note = new Note('F#2');

Luego, en la clase Note puedes analizar la cadena y establecer las propiedades.

Un Chord podría ser construido por sus notas:

chord = new Chord(['C2', 'E2', 'G2']);

... o por una cadena que incluya nombre, calidad y notas adicionales:

chord = new Chord('Cmaj7');

No sé qué hará tu aplicación exactamente, por lo que estas son solo ideas.

¡Buena suerte con tu fascinante proyecto!

    
respondido por el lortabac 11.12.2012 - 11:59
4

Algunos consejos genéricos.

Si se espera mucha incertidumbre en el diseño de la clase (como en su situación), recomendaría experimentar con diferentes diseños de clase de la competencia.

El uso de C ++ en esta etapa puede no ser tan productivo como otros idiomas. (Este problema es evidente en los fragmentos de código que tienen que lidiar con los destructores typedef y virtual ). Incluso si el objetivo del proyecto es producir código C ++, puede ser productivo realizar el diseño de la clase inicial en otro idioma. (Por ejemplo, Java, aunque hay muchas opciones).

No elija C ++ solo por herencia múltiple. La herencia múltiple tiene sus usos, pero no es la forma correcta de modelar este problema (teoría musical).

Presta especial atención a la desambiguación. Aunque las ambigüedades son abundantes en las descripciones en inglés (textuales), estas ambigüedades deben resolverse al diseñar las clases de POO.

Hablamos de G y G sharp como notas. Hablamos de Sol mayor y Sol menor como escalas. Por lo tanto, Note y Scale no son conceptos intercambiables. No podría haber ningún objeto que pueda ser simultáneamente una instancia de Note y Scale .

Esta página contiene algunos diagramas que ilustran la relación: enlace

Para otro ejemplo, "una Tríada que comienza con G en una escala C mayor " no tiene el mismo significado que "a Tríada que comienza con C en una escala Sol mayor ".

En esta etapa temprana, la clase Token (la superclase de todo) no está justificada, ya que evita la desambiguación. Podría introducirse más tarde si es necesario (respaldado por un fragmento de código que demuestra cómo esto podría ser útil).

Para comenzar, comience con una clase Note que es el centro del diagrama de clase, luego agregue gradualmente las relaciones (fragmentos de datos que deben asociarse con tuplas de Note s) al diagrama de relación de clase .

Una nota C es una instancia de la clase Note . Una nota C devolverá las propiedades relacionadas con esta nota, como las tríadas relacionadas, y su posición relativa ( Interval ) con respecto a un Scale que comienza con una nota C .

Las relaciones entre instancias de la misma clase (por ejemplo, entre una nota C y una nota E ) deben modelarse como propiedades, no como herencia.

Además, muchas de las relaciones entre clases en sus ejemplos también se modelan más apropiadamente como propiedades. Ejemplo:

(los ejemplos de código están pendientes porque necesito volver a aprender teoría musical ...)

    
respondido por el rwong 09.12.2012 - 14:19
2

Básicamente, las notas musicales son frecuencias y los intervalos musicales son relaciones de frecuencia.

Todo lo demás se puede construir sobre eso.

Un acorde es una lista de intervalos. Una escala es una nota fundamental y un sistema de afinación. Un sistema de ajuste también es una lista de intervalos.

Su nombre es solo un artefacto cultural.

El artículo de teoría musical de Wikipedia es un buen punto de partida.

    
respondido por el mouviciel 14.01.2013 - 20:24
1

Me parece fascinante esta discusión.

¿Se están ingresando las notas a través de midi (o algún tipo de dispositivo de captura de tonos) o se ingresan escribiendo las letras y los símbolos?

En el caso del intervalo de C a D-sharp / E-flat:

Aunque D-sharp y E-flat son el mismo tono (alrededor de 311Hz si A = 440Hz), el intervalo de C - > D-sharp se escribe un segundo aumentado, mientras que el intervalo de C - > E-flat es escrito como un tercero menor. Bastante fácil si sabes cómo se escribió la nota. Es imposible determinar si solo tienes los dos tonos para continuar.

En este caso, creo que también va a necesitar una forma de incrementar / disminuir el tono junto con los métodos .Sharpen () y .Flatten () mencionados, como .SemiToneUp (), .FullToneDown (), etc. para que pueda encontrar notas subsiguientes en una escala sin "colorearlas" como objetos cortantes / planos.

Estoy de acuerdo con @Rotem en que "C" no es una clase en sí misma, sino una instanciación de la clase Note.

Si define las propiedades de una nota, incluyendo todos los intervalos como semitonos, entonces, independientemente del valor de la nota inicial ("C", "F", "G #"), podría indicar que hay una secuencia de tres notas. que tiene la raíz, tercera mayor (M3), luego menor tercera (m3) sería una tríada mayor. De manera similar, m3 + M3 es una tríada menor, m3 + m3 disminuida, M3 + M3 aumentada. Además, esto le daría una manera de encapsular el hallazgo del 11, el 13 disminuido, etc. sin codificarlos explícitamente para las 12 notas base y sus octavas arriba y abajo.

Una vez hecho esto, aún te quedan algunos problemas por resolver.

Toma la tríada C, E, G. Como músico, veo esto claramente como un acorde de Cmaj. Sin embargo, el desarrollador en mí puede interpretar esto adicionalmente como E Augment 5 menor (Root E + m3 + a5) o Gsus4 6th no 5th (RootG + 4 + 6).

Entonces, para responder a su pregunta sobre el análisis, creo que la mejor manera de determinar la modalidad (mayor, menor, etc.) sería tomar todas las notas ingresadas, ordenarlas en un valor de semitono ascendente y compararlas con la formas de acordes conocidas. Luego, use cada nota ingresada como la nota raíz y realice el mismo conjunto de evaluaciones.

Puede ponderar las formas de acorde de modo que las formas de acorde más comunes (mayor, menor) tengan prioridad sobre las formas de acorde aumentadas, suspendidas, elektra, etc., pero un análisis preciso requeriría presentar todas las formas de acorde coincidentes como posibles soluciones.

Nuevamente, el artículo de wikipedia al que se hace referencia hace un buen trabajo al enumerar las clases de tono, por lo que debería ser simple (aunque tedioso) codificar los modelos de los acordes, tomar las notas ingresadas, asignarlas a clases / intervalos de tono, y luego compare con las formas conocidas para encontrar coincidencias.

Esto ha sido muy divertido. Gracias!

    
respondido por el John 10.12.2012 - 20:01
0

Suena como un caso para las plantillas. Parece que tienes un template <?> class Major : public Chord; por lo que Major<C> es-a Chord , como es Major<B> . De forma similar, también tiene una plantilla Note<?> con las instancias Note<C> y Note<D> .

Lo único que he omitido es la parte ? . Parece que tienes un enum {A,B,C,D,E,F,G} pero no sé cómo nombrarías esa enumeración.

    
respondido por el MSalters 11.12.2012 - 13:30
0

Gracias por todas las sugerencias, de alguna manera logré pasar por alto las respuestas adicionales. Hasta ahora mis clases han sido diseñadas así:

Note
enum Qualities - { DFLAT = -2, FLAT, NATURAL, SHARP, DSHARP }
char letter[1] // 1 char letter
string name // real name of note
int value // absolute value, the position on the keyboard for a real note (ie. c is always 0)
int position // relative position on keyboard, when adding sharp/flat, position is modified
Qualities quality // the quality of the note ie sharp flat

Para resolver mis problemas de cálculo de intervalos y acordes, decidí usar el búfer circular, que me permite atravesar el búfer desde cualquier punto, hasta que encuentre la siguiente nota que coincida.

Para encontrar el intervalo interpretado - atravesar el búfer de notas reales, deténgase cuando las letras coincidan (solo la letra, no la nota o posición real), entonces c - g # = 5

Para encontrar la distancia real, atravesar otro búfer de 12 enteros, deténgase cuando la posición de la nota superior sea la misma que el valor del búfer en el índice, de nuevo, esto solo está avanzando. Pero el desplazamiento puede estar en cualquier lugar (es decir, buffer.at (-10))

Ahora conozco tanto el intervalo interpretado como la distancia física entre los dos. así que el nombre del intervalo ya está medio completado.

ahora soy capaz de interpretar el intervalo, es decir. si el intervalo es 5 y la distancia es 8, entonces es un 5º aumentado.

Hasta ahora, la nota y el intervalo funcionan como se esperaba, ahora solo tengo que abordar el identificador de acorde.

Gracias de nuevo, volveré a leer algunas de estas respuestas e incorporaré algunas ideas aquí.

    
respondido por el Igneous01 14.01.2013 - 19:37

Lea otras preguntas en las etiquetas