I'm trying to get this code to produce an array comprised of unique random musical notes. It will, based on parameters given make a note and assign an accidental sign to it, with a possibility of either displaying as its flat of sharp counterpart. For example, If the random generator said the note "A" will be flat, there's a chance it will show up as either Ab or G#.
Now, I haven't handled the half-step notes (e.g. Cb is turned into B#, etc.), but that's mainly because the first part isn't working. The problem I have now is the code I'm using doesn't seem to work consistently. It will correctly translate the notes at some times and mess them up on others. There's no systematic pattern of error, either, at least, not from I've tried to decipher.
I'm using XNA for this to display the notes on the screen, but the real problem is with the data. I'm using a Note class I made.
Here's the code I'm using:
First the Note class:
public class Note
{
public enum Notes
{
A,
B,
C,
D,
E,
F,
G
}
public Notes noteLetter { get; private set; }
public Notes translatedLetter { get; private set; }
public bool isFlat { get; private set; }
public bool showSharp { get; private set; }
private const int NOTE_COUNT = 7;
/// <summary>
/// Creates a Note
/// </summary>
/// <param name="note">The basic note</param>
/// <param name="flat">Is the note flat?</param>
/// <param name="sharp">Will be shown as it's sharp counterpart?</param>
public Note(Notes note, bool flat, bool sharp)
{
noteLetter = note;
isFlat = flat;
showSharp = sharp;
if (isFlat && showSharp)
{
int hashCode = noteLetter.GetHashCode();
translatedLetter = (Notes)((hashCode + NOTE_COUNT - 1) % NOTE_COUNT);
}
else
{
translatedLetter = noteLetter;
showSharp = isFlat;
}
}
}
Then the Game1.cs file:
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
KeyboardState KeyBState, prevKeybState;
Texture2D sprite;
Rectangle noteSource;
Rectangle accSource;
const int ACCIDENTAL_START_X = 840;
const int BORDER_OFFSET = 12;
Vector2 notePosition;
SpriteFont font;
/// <summary>
/// The array of notes, row/column format
/// </summary>
Note[,] box;
const int ROW_SIZE = 3, COLUMN_SIZE = 4;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
box = new Note[ROW_SIZE, COLUMN_SIZE];
noteSource = new Rectangle(0, 0, 120, 120);
accSource = new Rectangle(ACCIDENTAL_START_X, 0, 50, 50);
notePosition = Vector2.Zero;
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
MakeTable();
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
sprite = Content.Load<Texture2D>("Note Pieces2");
font = Content.Load<SpriteFont>("font");
// TODO: use this.Content to load your game content here
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
KeyBState = Keyboard.GetState();
if (KeyBState.IsKeyDown(Keys.Escape))
this.Exit();
if (KeyBState.IsKeyDown(Keys.Space) && prevKeybState.IsKeyUp(Keys.Space))
MakeTable();
// TODO: Add your update logic here
prevKeybState = KeyBState;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
for (int r = 0; r < ROW_SIZE; r++)
{
for (int c = 0; c < COLUMN_SIZE; c++)
{
Note temp = box[r, c];
notePosition.Y = r * noteSource.Width + (r * BORDER_OFFSET);
notePosition.X = c * noteSource.Height + (c * BORDER_OFFSET);
noteSource.X = temp.translatedLetter.GetHashCode() * noteSource.Width;
spriteBatch.Draw(sprite, notePosition, noteSource, Color.White);
notePosition.X += noteSource.Width / 2;
notePosition.Y += 7;
spriteBatch.DrawString(font,
temp.noteLetter.ToString() +
(temp.isFlat ? "b " : " ") +
temp.translatedLetter +
(temp.showSharp ? "#" : ""), notePosition, Color.Black);
notePosition.X -= noteSource.Width / 2;
notePosition.Y -= 7;
if (temp.isFlat)
{
notePosition.X += 63;
notePosition.Y += 63;
accSource.X = ACCIDENTAL_START_X;
accSource.Y = 0;
if (temp.showSharp)
{
accSource.X += (temp.translatedLetter.GetHashCode() % 2) * (accSource.Width * 2);
accSource.Y += ((int)(temp.translatedLetter.GetHashCode() / 2)) * accSource.Height;
spriteBatch.Draw(sprite, notePosition, accSource, Color.White);
}
/*
else
{
accSource.X += (temp.translatedLetter.GetHashCode() % 2) * (accSource.Width * 2) + accSource.Width;
accSource.Y += ((int)(temp.translatedLetter.GetHashCode() / 2)) * accSource.Height;
spriteBatch.Draw(sprite, notePosition, accSource, Color.White);
}
*/
}
}
}
spriteBatch.End();
base.Draw(gameTime);
}
/// <summary>
/// Makes a new random table of notes
/// </summary>
private void MakeTable()
{
//make everything null
for (int r = 0; r < ROW_SIZE; r++)
{
for (int c = 0; c < COLUMN_SIZE; c++)
{
box[r, c] = null;
}
}
//then start
Random rand = new Random();
for (int i = 0; i < 12; i++)
{
bool isUnique;
bool noteEntered;
do
{
Note newNote = new Note((Note.Notes)rand.Next(0, 7),
rand.Next(0, 2) == 0 ? true : false,
rand.Next(0, 2) == 0 ? true : false);
isUnique = true;
noteEntered = false;
//Check to see if there exists a note like this
for (int r = 0; r < ROW_SIZE; r++)
{
for (int c = 0; c < COLUMN_SIZE; c++)
{
Note temp = box[r, c];
if (noteEntered || !isUnique)
continue;
else if (temp != null &&
temp.noteLetter == newNote.noteLetter &&
temp.isFlat == newNote.isFlat)
{
isUnique = false;
}
else if (temp == null)
{
box[r, c] = newNote;
noteEntered = true;
}
}
}
} while (!isUnique);
}
}
}
The main code running everything is the MakeTable() method, but I put in everything just in case there's a problem somewhere else. To the best of my knowledge, other than graphics libraries, I'm not using any libraries related to the XNA Framework that would cause this to go wrong. I'm using a rebuild of XNA made by CodePlex. I know they're still working on some things, but this shouldn't be an issue. Can someone help me with this?