Empecé a escribir un programa en C ++ 11 que analizaría 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 (3er menor, 3er 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, crear todas las clases derivadas (CMajorTriad, C, CMajorScale, CMajorKey, etc.) rápidamente se volvería ridículamente complejo, incluidas todas las otras notas, así como la enarmonía. 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, también polimorfizar con esto no funcionará (¿cómo determinar qué súper métodos realizar? Llamar a todos los constructores de súper clases no debería suceder aquí)
¿Hay alguna idea de diseño o sugerencia que la gente tenga para ofrecer? No he podido encontrar nada en Google con respecto al modelado de la armonía tonal desde una perspectiva OO. Hay demasiadas relaciones entre todos los conceptos aquí.
fuente
Respuestas:
Creo que el mejor enfoque es reproducir las relaciones reales entre estas entidades.
Por ejemplo, podrías tener:
un
Note
objeto, cuyas propiedades sonnombre (C, D, E, F, G, A, B)
accidental (natural, plano, afilado)
frecuencia u otro identificador de tono único
un
Chord
objeto, cuyas propiedades sonuna matriz de
Note
objetosnombre
accidental
calidad (mayor, menor, disminuida, aumentada, suspendida)
adiciones (7, 7+, 6, 9, 9+, 4)
un
Scale
objeto, cuyas propiedades sonuna matriz de
Note
objetosnombre
tipo (mayor, menor natural, menor melódico, menor armónico)
modo (jónico, dórico, frigio, lidio, mixolidiano, eólico, locriano)
Luego, si su entrada es textual, puede crear notas con una cadena que incluya el nombre de la nota, accidental y (si lo necesita) octava.
Por ejemplo (pseudocódigo, no sé C ++):
Luego, en la
Note
clase, puede analizar la cadena y establecer las propiedades.A
Chord
podría ser construido por sus notas:... o por una cadena que incluye nombre, calidad y notas adicionales:
No sé qué hará exactamente su aplicación, así que estas son solo ideas.
¡Buena suerte con tu fascinante proyecto!
fuente
Un consejo genérico.
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 competitivos.
Usar C ++ en esta etapa podría no ser tan productivo como otros lenguajes. (Este problema es evidente en los fragmentos de código con los que tiene que lidiar
typedef
y losvirtual
destructores). Incluso si el objetivo del proyecto es producir código C ++, podría ser productivo hacer el diseño inicial de la clase en otro lenguaje. (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 de la música).
Presta especial atención a desambiguate. Aunque las ambigüedades abundan en las descripciones en inglés (textuales), estas ambigüedades deben resolverse al diseñar las clases OOP.
Hablamos de G y G afilados como notas. Hablamos de sol mayor y sol menor como escalas. Por lo tanto,
Note
yScale
no son conceptos intercambiables. No podría haber ningún objeto que pueda ser simultáneamente una instancia de aNote
y aScale
.Esta página contiene algunos diagramas que ilustran la relación: http://www.howmusicworks.org/600/ChordScale-Relations/Chord-and-Scale-Relations
Para otro ejemplo, "una Tríada que comienza con G en una escala mayor de C " no tiene el mismo significado que "una Tríada que comienza con C en una escala mayor de G ".
En esta etapa temprana, la
Token
clase (la superclase de todo) no tiene justificación, porque 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
Note
clase que sea el centro del diagrama de clase, luego agregue gradualmente las relaciones (piezas de datos que deben asociarse con tuplas deNote
s) al diagrama de relación de clase.Una nota C es una instancia de la
Note
clase. Una nota C devolverá propiedades relacionadas con esta nota, como tríadas relacionadas, y su posición relativa (Interval
) con respecto a unaScale
que comience 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 ...)
fuente
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 ajuste. Un sistema de ajuste también es una lista de intervalos.
Cómo los nombras es solo un artefacto cultural.
El artículo de la teoría de la música de Wikipedia es un buen punto de partida.
fuente
Me parece fascinante esta discusión.
¿Las notas se ingresan 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 se escribe como menor 3er. Es bastante fácil si sabes cómo se escribió la nota. Imposible determinar si solo tiene los dos tonos para continuar.
En este caso, creo que también necesitará una forma de aumentar / disminuir el tono junto con los métodos .Sharpen () y .Flatten () mencionados, como .SemiToneUp (), .FullToneDown (), etc. que puede encontrar notas posteriores en una escala sin "colorearlas" como objetos punzantes / planos.
Tengo que estar 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, incluidos todos los intervalos como semitonos, independientemente del valor de la nota inicial ("C", "F", "G #") podrá determinar que una secuencia de tres notas tiene el raíz, 3ra mayor (M3), luego 3ra menor (m3) sería una tríada mayor. Del mismo modo, m3 + M3 es una tríada menor, m3 + m3 disminuido, M3 + M3 aumentado. Además, esto le daría una forma de encapsular la búsqueda del 11º, 13º disminuido, etc. sin codificarlos explícitamente para las 12 notas base, y sus octavas hacia arriba y hacia abajo.
Una vez hecho esto, todavía te quedan algunos problemas por resolver.
Tome 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 menor Augment 5 (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, organizarlas en un valor de semitono ascendente y probarlas con las formas de acordes conocidas . Luego, use cada nota ingresada como nota raíz y realice el mismo conjunto de evaluaciones.
Puede ponderar las formas de acordes para que los más comunes (mayor, menor) tengan prioridad sobre las formas de acordes aumentadas, suspendidas, elektra, etc., pero un análisis preciso requeriría presentar todas las formas de acordes 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 comparar contra las formas conocidas para los partidos.
Esto ha sido muy divertido. ¡Gracias!
fuente
Suena como un caso para las plantillas. Parece que tienes un
template <?> class Major : public Chord;
soMajor<C>
-aChord
, como esMajor<B>
. Del mismo modo, también tiene unaNote<?>
plantilla con instanciasNote<C>
yNote<D>
.Lo único que he dejado fuera es la
?
parte. Parece que tienes unenum {A,B,C,D,E,F,G}
pero no sé cómo nombrarías esa enumeración.fuente
Gracias por todas las sugerencias, de alguna manera me las arreglé para perder las respuestas adicionales. Hasta ahora mis clases se han diseñado así:
Para resolver mis problemas de cálculo de intervalos y acordes, decidí usar el búfer circular, que me permite recorrer el búfer desde cualquier punto, hacia adelante, hasta que encuentre la siguiente nota que coincida.
Para encontrar el intervalo interpretado: atraviese el búfer de notas reales, deténgase cuando las letras coincidan (solo la letra, no la nota real o la posición) para que c - g # = 5
Para encontrar la distancia real, atraviese otro búfer de 12 enteros, deténgase cuando la posición de la nota más alta sea la misma que el valor del búfer en el índice, nuevamente esto solo se mueve hacia adelante. Pero el desplazamiento puede estar en cualquier lugar (es decir, buffer.at (-10))
ahora sé tanto el intervalo interpretado como la distancia física entre los dos. entonces el nombre del intervalo ya está medio completo.
ahora puedo interpretar el intervalo, es decir. si el intervalo es 5 y la distancia es 8, entonces es un quinto 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í.
fuente