I create audio and effects in my game on the fly with a very simple sound synthesis. Basically, I have some methods that can reproduce sound with frequency, amplitude and duration.
For short phrases and melodies, I would like to get a basic notation so that I can easily rewrite or add new melodies to the code (in the end, maybe I could read from files, but this is probably too much).
I am not sure how to implement this.
I started by creating an enum EqualTemperamentTuning that contains all 88 basic piano notes with a MIDI # field and a frequency field. This, at least, means that I can deal with note names, not frequencies.
public enum EqualTemperamentTuning { A_0 (1, 27.5), A_SHARP_0 (2, 29.1352), ... C_8 (88, 4186.01); private int num; private double frequency; EqualTemperamentTuning(int num, double frequency){ this.num = num; this.frequency = frequency; } public double getFrequency(){ return frequency; } public double getNum(){ return num; } }
Then I started creating more objects, first a note that contains EqualTemperamentTuning, amplitude and length:
public class Note { private double frequency; private double amplitude; private int length; public Note(EqualTemperamentTuning tuning, double amplitude, int length){ this.frequency = tuning.getFrequency(); this.amplitude = amplitude; this.length = length; } public double getFrequency(){ return frequency; } public double getAmplitude(){ return amplitude; } public int getLength(){ return length; } }
Finally, to define the melody I want to play, I created the NotePhrase class:
public class NotePhrase { private Note[] notes; public NotePhrase(Note[] notes){ this.notes = notes; } public double getFrequency(int counter){
Then, in my sound generation class, I have a loop (with a counter) that generates samples from the wave generator. Each time I need a new sample for playback, it sets the wave frequency according to the NotePhrase.getFrequency (int counter) method above. This should (I have not tested it yet!) Just play the NotePhrase melody according to the frequency and amplitude (to add).
The problem is that it does not seem very elegant and, more specifically, it is very difficult to "write" a melody in any understandable way. I have to encode a whole bunch of new Note objects and then build a NotePhrase object with an array of them ... It didn't seem to me that I would program a bunch of these melodies, and then easily switch between them later.
Indeed, I would like to create an enumeration of melodies or something like that, where I can easily hard-code the clear configuration for each melody, and then when I want to use them, just pass the enumeration type to the audio player ...
The best I have:
private static enum Melody { NOKIA_RINGTONE ( new Note(EqualTemperamentTuning.E_5, 0.5, 1), new Note(EqualTemperamentTuning.D_5, 0.5, 1)) ; private Note[] notes = new Note[2]; Melody (Note note1, Note note2){ this.notes[0] = note1; this.notes[1] = note2; } }
What would I then load into the NotePhrase object at runtime. This is not very good, because I have to create a new constructor for melodies with a different number of notes. If I do it the other way around and build an enumeration with an array of notes, then I just βwriteβ the melody in another place and in two parts, which seem even more confusing ...
So, I'm stuck on how to structure this correctly? those. what classes to create and what information they should keep ... I want to get this "right", because maybe I would like to expand the notation in the future to include effects (echo, etc.), and I already found with it is unlikely for me that the right classes, structure, and relationships (even names) can make my programs very easy or hard to understand.
Apologies for the essay, this may not be a very well-worded question (s), but as a Java and OOP beginner, any tips would be greatly appreciated!
EDIT **
Thanks for the answers and suggestions, very helpful. Thinking about the answers given in this case made me rethink my overall audio implementation, which is pretty shaky right now. Not sure who I should mark as correct, though, since I'm really going to take all the recommendations on board and try from there.