OO Design, bagaimana model Tonal Harmony?

12

Saya sudah mulai menulis sebuah program dalam C ++ 11 yang akan menganalisis akord, skala, dan harmoni. Masalah terbesar yang saya miliki dalam fase desain saya, adalah bahwa not 'C' adalah not, jenis akor (Cmaj, Cmin, C7, dll), dan jenis kunci (kunci Cmajor, Cminor). Masalah yang sama muncul dengan interval (minor ke-3, ke-3 utama).

Saya menggunakan kelas dasar, Token, itu adalah kelas dasar untuk semua 'simbol' dalam program. jadi misalnya:

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;

Seperti yang Anda lihat, untuk membuat semua kelas turunan (CMajorTriad, C, CMajorScale, CMajorKey, dll) akan dengan cepat menjadi sangat rumit termasuk semua catatan lainnya, serta enharmonics. multiple inheritance tidak akan berfungsi, yaitu:

class C : public Note, Triad, Key, Scale

kelas C, tidak bisa semuanya pada saat yang bersamaan. Itu kontekstual, juga polymorphing dengan ini tidak akan bekerja (bagaimana menentukan metode super untuk melakukan? Memanggil setiap konstruktor kelas super tidak boleh terjadi di sini)

Apakah ada ide atau saran desain yang ditawarkan orang? Saya belum dapat menemukan apa pun di Google dalam hal memodelkan harmoni tonal dari perspektif OO. Ada terlalu banyak hubungan di antara semua konsep di sini.

Igneous01
sumber
8
Mengapa 'C' menjadi kelas? Saya akan membayangkan 'Note', 'Chord', dll. Akan menjadi kelas, yang dapat memiliki nilai enumerasi di mana enum 'C' mungkin berperan.
Rotem
Jika pengguna memasukkan-> chord CEG, perlu menyimpulkan apa catatan untuk membentuk akord yang sesuai. Saya berpikir untuk mengirimkan vektor <Notes> sebagai params ke metode execute (), yang semuanya akan ditangani secara polimorfik. Namun menggunakan enumerator akan masuk akal, tapi kemudian saya harus instantiate setiap objek dengan enum yang ingin saya gunakan.
Igneous01
Saya dengan @Rotem yang satu ini: Kadang-kadang, Anda hanya perlu lebih memilih komposisi objek daripada warisan.
Spoike
Sepertinya bagi saya mungkin ada gunanya memikirkan apa yang ingin Anda lakukan dengan kelas note / chord / scale ini. Apakah Anda akan menghasilkan lembaran musik? File midi? Apakah transformasi pada skor (transposisi, menggandakan semua panjang not, menambahkan trills ke seluruh not di atas not tertentu, dll.)? Setelah Anda memiliki struktur kelas yang memungkinkan, pikirkan bagaimana Anda akan menyelesaikan tugas-tugas itu. Jika terlihat canggung, mungkin Anda menginginkan struktur kelas yang berbeda.
MatrixFrog

Jawaban:

9

Saya pikir pendekatan terbaik adalah mereproduksi hubungan nyata antara entitas-entitas ini.

Misalnya, Anda dapat memiliki:

  • sebuah Noteobjek, yang sifat yang

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

    • tidak disengaja (alami, datar, tajam)

    • frekuensi atau pengidentifikasi nada unik lainnya

  • sebuah Chordobjek, yang sifat yang

    • berbagai Noteobjek

    • nama

    • kebetulan

    • kualitas (utama, kecil, berkurang, ditambah, ditangguhkan)

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

  • sebuah Scaleobjek, yang sifat yang

    • berbagai Noteobjek

    • nama

    • jenis (utama, minor alami, minor melodi, minor harmonik)

    • mode (ion, dorian, frigia, lydia, mixolidian, aeolian, lokal)

Kemudian, jika input Anda adalah teks, Anda dapat membuat catatan dengan string termasuk nama catatan, tidak disengaja dan (jika Anda membutuhkannya) oktaf.

Sebagai contoh (kodesemu, saya tidak tahu C ++):

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

Kemudian, di Notekelas Anda dapat mengurai string dan mengatur properti.

A Chorddapat dibangun dengan catatannya:

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

... atau dengan string termasuk nama, kualitas, dan catatan tambahan:

chord = new Chord('Cmaj7');

Saya tidak tahu persis apa yang akan dilakukan aplikasi Anda, jadi ini hanya ide.

Semoga sukses dengan proyek menarik Anda!

lortabac
sumber
4

Beberapa saran umum.


Jika ada banyak ketidakpastian yang diharapkan dalam desain kelas (seperti dalam situasi Anda), saya akan merekomendasikan bereksperimen dengan berbagai desain kelas yang bersaing.

Menggunakan C ++ pada tahap ini mungkin tidak seproduktif bahasa lain. (Masalah ini jelas dalam fragmen kode Anda harus berurusan dengan typedefdan virtualdestruktor.) Bahkan jika tujuan proyek adalah untuk menghasilkan kode C ++, mungkin produktif untuk melakukan desain kelas awal dalam bahasa lain. (Misalnya Java, meskipun ada banyak pilihan.)

Jangan memilih C ++ hanya karena multiple inheritance. Multiple inheritance memiliki kegunaannya tetapi itu bukan cara yang tepat untuk memodelkan masalah ini (teori musik).


Berikan perhatian khusus pada disambiguasi. Meskipun ambiguitas berlimpah dalam deskripsi bahasa Inggris (tekstual), ambiguitas ini harus diselesaikan ketika merancang kelas OOP.

Kita berbicara tentang G dan G tajam sebagai catatan. Kita berbicara tentang G mayor dan G minor sebagai skala. Dengan demikian, Notedan Scalebukan konsep yang dapat dipertukarkan. Tidak mungkin ada objek yang dapat secara bersamaan menjadi instance dari a Notedan a Scale.

Halaman ini berisi beberapa diagram yang menggambarkan hubungan: http://www.howmusicworks.org/600/ChordScale-Relations/Chord-and-Scale-Relations

Sebagai contoh lain, "Triad yang dimulai dengan G pada skala C mayor " tidak memiliki arti yang sama dengan "Triad yang dimulai dengan C pada skala G mayor ".

Pada tahap awal ini, Tokenkelas (superclass of everything) tidak beralasan, karena mencegah disambiguasi. Ini bisa diperkenalkan nanti jika diperlukan (didukung oleh fragmen kode yang menunjukkan bagaimana ini bisa berguna.)


Untuk memulainya, mulailah dengan Notekelas yang merupakan pusat diagram kelas, kemudian secara bertahap tambahkan hubungan (potongan data yang perlu dikaitkan dengan tupel Notes) ke diagram hubungan kelas.

Sebuah C catatan adalah turunan dari Notekelas. Sebuah C catatan akan kembali properti yang berkaitan dengan catatan ini, seperti triad terkait, dan posisi relatif ( Interval) sehubungan dengan Scaleyang dimulai dengan C catatan.

Hubungan antara instance dari kelas yang sama (misalnya, antara catatan C dan catatan E ) harus dimodelkan sebagai properti, bukan warisan.

Selain itu, banyak hubungan antar kelas dalam contoh Anda juga lebih tepat dimodelkan sebagai properti. Contoh:

(contoh kode tertunda karena saya perlu mempelajari kembali teori musik ...)

rwong
sumber
Pikiran yang menarik, tetapi bagaimana seseorang menangani menentukan kualitas akor dalam konteks analisis harmonik? C chord instance perlu memiliki properti berkualitas, set ke minor (yang ok) tapi lalu bagaimana dengan dominan / diminished / augmented / minor 7s, 9, 11 chord? Ada banyak akord yang dapat dimiliki oleh satu not. Bagaimana saya menentukan berbagai tipe akord dan kualitasnya di bagian analisis kode?
Igneous01
Saya tahu sedikit teori musik, jadi saya tidak dapat menjawab pertanyaan Anda. Salah satu cara yang dapat membantu saya memahami adalah dengan menemukan tabel yang berisi daftar semua catatan yang terlibat dalam konsep-konsep itu. Pertanyaan untuk akor dapat mengambil parameter tambahan.
rwong
2
Berikut adalah daftar yang sangat bagus dari semua akord yang mungkin: en.wikipedia.org/wiki/List_of_chords Semua akord dapat diterapkan pada nada apa pun, yang penting dengan situasi saya adalah bahwa enharmonics benar: yaitu. Cflat mayor! = BMajor, Mereka secara fisik akor yang sama pada piano, tetapi fungsi harmonik mereka sangat berbeda di atas kertas. Saya berpikir bahwa enumerator untuk sharping / flatting note akan paling masuk akal untuk instance note. Dengan cara C.Sharpen () = C #, dan C.Flatten () = Cb, ini dapat memudahkan saya untuk memvalidasi akor pengguna.
Igneous01
2

Pada dasarnya, not musik adalah frekuensi dan interval musik adalah rasio frekuensi.

Segala sesuatu yang lain dapat dibangun di atas itu.

Chord adalah daftar interval. Skala adalah nada dasar dan sistem tuning. Sistem penyetelan juga merupakan daftar interval.

Bagaimana Anda menamainya hanyalah artefak budaya.

Artikel teori Musik Wikipedia adalah titik awal yang bagus.

mouviciel
sumber
Menarik, meskipun saya tidak yakin bahwa perlu untuk memodelkan sistem dalam hal realitas fisik yang mendasarinya. Ingat, model ini diperlukan untuk membantu dalam mengartikulasikan satu aspek tertentu, tidak harus komprehensif atau bahkan akurat. Meskipun pendekatan Anda akurat dan komprehensif, mungkin ini terlalu rendah untuk penggunaan OP.
Konrad Rudolph
@KonradRudolph - Dengan posisi ekstrem saya, saya hanya ingin menunjukkan bahwa seseorang tidak boleh mencampur model yang mendasarinya dengan lapisan presentasi, dengan cara yang mirip dengan waktu musim panas: Komputasi jauh lebih mudah pada model itu sendiri. Saya setuju bahwa level abstraksi yang paling membantu bukanlah yang saya sarankan, tetapi saya merasa bahwa level abstraksi yang disarankan oleh OP juga tidak memadai.
mouviciel
Tujuan dari program ini bukan untuk selalu menampilkan realitas fisik musik. Tetapi bagi orang-orang yang mempelajari teori (seperti saya) untuk dapat dengan cepat memplot beberapa akord dan memiliki program menafsirkan kemampuan terbaiknya bagaimana akord ini terkait satu sama lain dalam arti harmonis. Saya bisa menganalisanya dengan cara mencoba dan benar membaca ukuran skor dengan ukuran, tetapi ini adalah alat lain untuk membuat segalanya lebih mudah, dan untuk dapat fokus pada detail yang lebih baik ketika menganalisis.
Igneous01
1

Saya menemukan diskusi ini menarik.

Apakah catatan dimasukkan melalui midi (atau beberapa jenis alat penangkap nada) atau dimasukkan dengan mengetik huruf dan simbol?

Dalam hal interval dari C ke D-sharp / E-flat:

Meskipun D-sharp dan E-flat adalah nada yang sama (sekitar 311Hz jika A = 440Hz), interval dari C -> D-sharp ditulis sebagai augmented 2nd, sedangkan interval dari C -> E-flat ditulis sebagai minor 3. Cukup mudah jika Anda tahu bagaimana catatan itu ditulis. Tidak mungkin untuk menentukan apakah Anda hanya memiliki dua nada untuk melanjutkan.

Dalam hal ini, saya percaya Anda juga akan membutuhkan cara untuk menambah / mengurangi nada bersama dengan metode .Sharpen () dan .Flatten () yang disebutkan, seperti .SemiToneUp (), .FullToneDown (), dll. Jadi Anda dapat menemukan catatan subesquent dalam skala tanpa "mewarnai" mereka sebagai benda tajam / flat.

Saya harus setuju dengan @Rotem bahwa "C" bukan kelas di dalam dan dari dirinya sendiri, melainkan sebuah instance dari kelas Note.

Jika Anda mendefinisikan properti untuk catatan, termasuk semua interval sebagai semi-nada, maka terlepas dari nilai catatan awal ("C", "F", "G #") Anda akan dapat mengatakan bahwa urutan catatan tiga yang memiliki root, mayor 3 (M3), lalu minor 3 (m3) akan menjadi triad utama. Demikian pula, m3 + M3 adalah triad kecil, m3 + m3 berkurang, M3 + M3 ditambah. Selain itu, ini akan memberi Anda cara untuk merangkum menemukan 11, berkurang 13, dll. Tanpa secara eksplisit mengkodekan mereka untuk semua 12 catatan dasar, dan oktaf mereka naik dan turun.

Setelah selesai, Anda masih memiliki beberapa masalah untuk diselesaikan.

Ambil triad C, E, G. Sebagai seorang musisi, saya melihat ini dengan jelas sebagai akord Cmaj. Namun, pengembang dalam diri saya dapat menafsirkan tambahan ini sebagai E minor Augment 5 (Root E + m3 + a5) atau Gsus4 ke-6 no 5 (RootG + 4 + 6).

Jadi, untuk menjawab pertanyaan Anda tentang melakukan analisis, saya pikir cara terbaik untuk menentukan modalitas (maj, minor, dll) adalah dengan mengambil semua catatan yang dimasukkan, mengaturnya dalam nilai semi naik, dan mengujinya terhadap bentuk akor yang dikenal . Kemudian, gunakan setiap catatan yang dimasukkan sebagai catatan root, dan lakukan serangkaian evaluasi yang sama.

Anda bisa memberi bobot pada bentuk akor sehingga lebih umum (mayor, minor) lebih diutamakan daripada bentuk akor yang diperbesar, ditangguhkan, elektra, dll., Tetapi analisis yang akurat akan membutuhkan penyajian semua bentuk akor yang cocok sebagai solusi yang memungkinkan.

Lagi-lagi artikel wikipedia yang direferensikan melakukan pekerjaan yang baik untuk mendaftarkan kelas pitch, jadi seharusnya sederhana (walaupun membosankan) untuk membuat kode model chord, mengambil catatan yang dimasukkan, menugaskan mereka ke kelas pitch / interval, dan kemudian membandingkan terhadap bentuk yang diketahui untuk pertandingan.

Ini sangat menyenangkan. Terima kasih!

John
sumber
Mereka sedang diinput, melalui teks untuk saat ini. Namun di kemudian hari saya mungkin dapat menggunakan midi jika program dienkapsulasi dengan benar. Langkah bayi sekarang: D
Igneous01
0

Kedengarannya seperti kasing untuk templat. Anda tampaknya memiliki template <?> class Major : public Chord;apa Major<C>adanya Chord, seperti apa adanya Major<B>. Demikian pula, Anda juga memiliki Note<?>templat dengan instance Note<C>dan Note<D>.

Satu-satunya hal yang saya tinggalkan adalah ?bagian. Tampaknya Anda memiliki enum {A,B,C,D,E,F,G}tetapi saya tidak tahu bagaimana Anda akan menyebut enum itu.

MSalters
sumber
0

Terima kasih atas semua sarannya, entah bagaimana saya berhasil melewatkan tanggapan ekstra. Sejauh ini kelas saya telah dirancang seperti ini:

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

Untuk menyelesaikan masalah perhitungan interval dan akor, saya memutuskan untuk menggunakan buffer sirkular, yang memungkinkan saya untuk melintasi buffer dari titik mana pun, maju, hingga saya menemukan catatan berikutnya yang cocok.

Untuk menemukan interval yang ditafsirkan - lewati buffer note nyata, berhentilah ketika hurufnya cocok (hanya hurufnya, bukan not atau posisi sebenarnya) jadi c - g # = 5

Untuk menemukan jarak nyata- lintasi buffer lain dari 12 bilangan bulat, berhentilah ketika posisi nada teratas sama dengan nilai buffer pada indeks, sekali lagi ini hanya bergerak maju. Tapi offset bisa di mana saja (mis. Buffer.at (-10))

sekarang saya tahu interval yang ditafsirkan, dan jarak fisik antara keduanya. jadi nama interval sudah setengah lengkap.

sekarang saya dapat menafsirkan interval, yaitu. jika intervalnya 5, dan jaraknya 8, maka itu adalah augmented 5th.

Sejauh ini note dan interval berfungsi seperti yang diharapkan, sekarang saya hanya perlu menangani pengenal akor.

Terima kasih sekali lagi, saya akan membaca kembali beberapa tanggapan ini dan memasukkan beberapa ide di sini.

Igneous01
sumber