The One Winged Angel Project
I am writing a program to play One Winged Angel, through the PC speaker. I've finished setting up the framework and now I'm working on playing the music.
Here is my framework:
play.h
* play.h
*
*
* Created by Aaron Hayes on 3/27/07.
* Copyright 2007 Aaron Hayes. All rights reserved.
*
*/
#ifndef _PLAY_H_
#define _PLAY_H_
#include <iostream>
#include <windows.h>
using std::cin;
using std::cout;
using std::endl;
class Play;
class Beat
{
friend Play& operator<<(Play& pin, Beat* beat);
private:
int m_nFract;
public:
/*
Use the following in this function:
WHOLE
HALF
QUATER
EIGHTH
SIXTEENTH
use in the form of play << ... << play.fractional(HALF) << ...
remember you must specify BPM with setBPM first
*/
static Beat* change(int newBeat);
};
class Play
{
private:
//current length and note multipliers
int m_nLength;
int m_nBPM;
float m_fMult;
public:
//user accessible constants
float C[9];
//member functions
Play();
int OnConv(char note);
void OnPlay(char *notes, int length);
void OnTwinkle();
void OnUserPlay();
void OnWowWow();
friend Play& operator<<(Play& pin, char *notes);
friend Play& operator<<(Play& pin, int length);
friend Play& operator<<(Play& pin, float mult);
friend Play& operator<<(Play& pin, double mult);
friend Play& operator<<(Play& pin, Beat* beat);
//special note: this must be done BEFORE specifying fractional notes
void setBPM(int BPM);
};
#define WHOLE 1
#define HALF 2
#define QUARTER 4
#define EIGHTH 8
#define SIXTEENTH 16
Play& operator<<(Play& pin, char *notes);
Play& operator<<(Play& pin, int length);
Play& operator<<(Play& pin, float mult);
Play& operator<<(Play& pin, Beat* beat);
#endif
play.cpp
* play.cpp
*
*
* Created by Aaron Hayes on 3/27/07.
* Copyright 2007 Aaron Hayes. All rights reserved.
*
*/
#include "play.h"
Beat* Beat::change(int newBeat)
{
Beat *beat = new Beat;
beat->m_nFract=newBeat;
return beat;
}
Play::Play()
{
this->m_fMult=1;
this->m_nLength=100;
this->C[0] = 0.0625;
this->C[1] = 0.125;
this->C[2] = 0.25;
this->C[3] = 0.5;
this->C[4] = 1.0;
this->C[5] = 2.0;
this->C[6] = 4.0;
this->C[7] = 8.0;
this->C[8] = 16.0;
this->m_nBPM=100;
}
int Play::OnConv(char note)
{
switch(note)
{
case 'c':
return (int)((float)262*this->m_fMult);
case 'd':
return (int)((float)294*this->m_fMult);
case 'e':
return (int)((float)330*this->m_fMult);
case 'f':
return (int)((float)349*this->m_fMult);
case 'g':
return (int)((float)392*this->m_fMult);
case 'a':
return (int)((float)440*this->m_fMult);
case 'b':
return (int)((float)494*this->m_fMult);
default:
return 32767;
}
}
void Play::OnPlay(char *notes, int length)
{
int i = 0;
while(notes[i]!=0)
{
Beep(OnConv(notes[i]), length);
i++;
}
}
void Play::OnTwinkle()
{
char a[50] = "c c g g a a g f f e e d d c ";
char b[50] = "f f e e d d c ";
OnPlay(a, 100);
OnPlay(b, 100);
OnPlay(b, 100);
OnPlay(a, 100);
}
void Play::OnUserPlay()
{
while(1){
char sMusic[500];
cout << "Enter notes (c d e f g a b), using spaces to pause: ";
cin.getline(sMusic, 500);
OnPlay(sMusic, 100);
cout << "Play again? (y/n): ";
cin >> sMusic;
if(sMusic[0] != 'y')
break;
cin.get();
}
}
void Play::OnWowWow()
{
char *p = "cdefgabagfedc";
for(int i = 0; i < 20; i++)
OnPlay(p, 20);
}
void Play::setBPM(int BPM)
{
if(BPM > 0)
this->m_nBPM=BPM;
}
/*
if(fraction <=0)
return *this;
int duration = 60*1000;
duration/=this->m_nBPM;
duration/=fraction;
this->m_nLength=duration;
return *this;
*/
Play& operator<<(Play& pin, char *notes)
{
pin.OnPlay(notes, pin.m_nLength);
return pin;
}
Play& operator<<(Play& pin, int length)
{
pin.m_nLength = length;
return pin;
}
Play& operator<<(Play& pin, float mult)
{
pin.m_fMult=mult;
return pin;
}
Play& operator<<(Play& pin, double mult)
{
pin.m_fMult=(float)mult;
return pin;
}
Play& operator<<(Play& pin, Beat* beat)
{
if(beat->m_nFract <= 0)
return pin;
int duration = 60*1000;
duration/=pin.m_nBPM;
duration/=beat->m_nFract;
pin.m_nLength=duration;
delete beat;
return pin;
}
main.cpp
void main()
{
Play play;
play.setBPM(135);
play << Beat::change(WHOLE);
#define C1 play.C[1]
#define C2 play.C[2]
#define C3 play.C[3]
#define C4 play.C[4]
#define C5 play.C[5]
#define HHALF Beat::change(HALF)
#define HHOLE Beat::change(WHOLE)
#define QUART Beat::change(QUARTER)
//1
play << C4 << "gcgc" << "gcgc" << C5 << HHALF
<< "de" << QUART << "fe" << HHALF
<< "dedfe";
//4
play << "de" << QUART << "fe" << HHALF
<< "dedfe" << HHOLE << C4 << "gcgc" << "gcgc";
//7
play << HHALF << "b" << C5 << "c" << QUART
<< "dc" << HHALF << C4 << "b" << C5 << "c" << C4 << "b"
<< C5 << "dc" << "b" << C5 << "c" << QUART
<< "dc" << HHALF << C4 << "b" << C5 << "c" << C4 << "b"
<< C5 << "dc";
//9
play << C4 << "c" << C5 << "c" << C4 << "bbaagf"
<< "cfeeddc" << C3 << "b" << HHOLE << C4 << "gcgc";
//12
play << "gcgc" << "gcgc" << "gcgc" << "gcgc";
//16
play << "gcgg" << C5 << "c" << HHALF << "ccc" << HHOLE
<< C4 << "b" << HHALF << "bbb" << HHOLE << "a"
<< HHALF << "ggg" << HHOLE << "g" << HHALF
<< "fff" << HHOLE << "f" << HHALF << "eee" << HHOLE
<< "d" << HHALF << "ddd";
//20
play << HHOLE << "c" << HHALF << "ccc" << C3
<< HHOLE << "b" << C4 << HHALF << "ccd" << QUART
<< "egb" << C5 << "f" << HHOLE << "e" << QUART << C4
<< "cegb" << C5 << HHALF << "c" << QUART << "de"
<< HHALF << "fgfe" << "dd" << C4 << "ba";
//23
play << HHOLE << "b" << HHALF << "agf" << C5 << "ddc" << C4
<< HHOLE << "b" << HHALF << "efgdeg" << HHOLE << "gggg";
//26
play << "gggg" << "gg" << C3 << "aa";
//28
play << HHALF << "b" << C4 << "f" << C3 << "b" << C4 << "f"
<< C3 << "b" << C4 << "f" << C3 << "b" << C4 << "f"
<< C3 << "b" << C4 << "f" << C3 << "b" << C4 << "f"
<< C3 << "b" << C4 << "f" << C3 << "b" << C4 << "f";
//30
play << C3 << "b" << C4 << "f" << C3 << "b" << C4 << "f"
<< C3 << "b" << C4 << "f" << C3 << "b" << C4 << "f"
<< C3 << "b" << C4 << "f" << C3 << "b" << C4 << "f"
<< C3 << "b" << C4 << "f" << C3 << "b" << C4 << "f";
//32
play << C3 << "bbbbbbbb" << "bbbbbbbb" << "bbbbbbbb";
//35
play << "bbbbbbbb" << "bbbbbbbb" << "bbbbbbbb";
//38
play << "bbbbbbbb" << "bbbbbbb" << C4 << "e" << C5 << "bbbb"
<< C4 << "ffgb";
//42
play << HHOLE << "bb" << HHALF << "bb" << HHOLE << "b" << HHALF
<< "bb" << HHOLE << "b" << HHALF << "bb" << HHOLE << "b"
<< "bb" << HHALF << "bb" << HHOLE << "b" << HHALF << "bb"
<< HHOLE << "b" << HHALF << "bb" << HHOLE << "b" << C5
<< HHALF << "dc" << HHOLE << C4 << "b" << HHALF << " fff";
//47
play << "eegg" << HHOLE << "f" << HHALF << "eg" << C5 << "dc" << C4
<< HHOLE << "b" << HHALF << " fff" << "eegg" << HHOLE << "f"
<< HHALF << "eg" << HHOLE << "bfbf";
//51
play << HHALF << "bfab" << "bfab" << HHOLE << "bfbf" << HHALF << "bfab"
<< "bfab" << C5 << "dc" << C4 << HHOLE << "b" << HHALF << " fff" << "ee";
//55
play << "gg" << HHOLE << "f" << HHALF << "egde" << HHOLE << "eeee";
//57
play << "eeee" << "f" << C5 << "c" << C4 << "f" << C5 << "c" << C4
<< "f" << C5 << "c" << C4 << "f" << C5 << "c" << C4;
//60
play << "f" << C5 << "c" << C4 << "f" << C5 << "c" << C4 << "f" << C5
<< "c" << C4 << "f" << C5 << "c" << C4 << "f" << C5 << "c" << C4
<< "f" << C5 << "c" << C4 << "f" << C5 << "c" << C4 << "f" << C5
<< "c" << C4<< "f" << C5 << "c" << C4<< "f" << C5 << "c" << C4;
//65
play << "f" << C5 << "c" << C4 << "fa" << QUART << "b" << C5 << "fdf"
<< C4 << "b" << C5 << "fdf" << C4 << "b" << C5 << "fff"
<< C4 << "b" << C5 << "fdf" << C4 << "b" << C5 << "fdf" << C4
<< "b" << C5 << "dgd" << C4 << "b" << C5 << "fdf" << C4
<< "b" << C5 << "dgd";
//68
play << C4 << "b" << C5 << "fdf" << C4 << "b" << C5 << "fdf" << C4 << "b"
<< C5 << "fff" << C4 << "b" << C5 << "fdf" << C4 << "b" << C5 << "fdf"
<< C4 << "b" << C5 << "dgd" << C4 << "b" << C5 << "fdf" << C4 << "b"
<< C5 << "dgd" << HHALF << "fe" << HHOLE << "d" << " " << HHALF
<< "de";
//71
play << HHOLE << C4 << "b " << HHALF << "ba" << HHOLE << "aaaa" << "aaaa"
<< C5 << "c" << C4 << "g" << C5 << "c" << C4 << "g"
<< C5 << "c" << C4 << "g" << C5 << "c" << C4 << "g";
//76
play << "aeae" << "aeae" << "gdgd" << C5 << "gfed";
//80
play << C5 << "c" << C4 << "g" << C5 << "c" << C4 << "g"
<< C5 << "c" << C4 << "g" << C5 << "c" << C4 << "g"
<< "aeae" << "aeae";
//84
play << "gdgd" << "gdgd" << HHALF << C5 << "gege" << QUART << "gf"
<< HHALF << "ede";
//87
play << QUART << "gf" << HHALF << "edefegf" << "gege" << QUART << "gf"
<< HHALF << "ede";
//89
play << QUART << "gf" << HHALF << "edefegf" << "gege" << QUART << "gf"
<< HHALF << "ede";
//91
play << QUART << "gf" << HHALF << "edefegf" << "gege" << QUART << "gf"
<< HHALF << "ede";
//93
play << QUART << C5 << "gf" << HHALF << "edefegf" << C4 << "g" << C5 << "c"
<< HHOLE << " c" << C4 << "bb" << "a gfe";
// 96
play << "dg " << play.C[2] << HHALF << "cbcbcbcb" << "cbcbcbcb" << C4
<< "b" << C5 << "ff" << C4 << "b" << C5 << "ff";
//100
play << C4 << "b" << C5 << "ff" << C4 << "b" << C5 << "ff" << C4 << "a" << C5
<< "ee" << QUART << "d" << C4 << "bged" << C3 << "b" << HHALF << "a"
<< C4 << "a" << C3 << "a" << C4 << "a" ;
//103
play << C3 << "a" << C4 << "a" << C3 << "a" << C4 << "a" << HHOLE << "bbbag"
<< "gggg" << "ffffe" << "dddd";
//108
play << C3 << HHALF << "b" << C4 << "c" << C3 << "a" << C4 << "c" << C3 << "b"
<< C4 << "c" << C3 << HHOLE << "a"
//
<< HHALF << "b" << C4 << "c" << C3 << "bbb" << C4 << "c" << HHOLE << C3
<< "a"
//
<< C3 << HHALF << "b" << C4 << "c" << C3 << "a" << C4 << "c" << C3 << "b"
<< C4 << "c" << C3 << HHOLE << "a";
//111
play << HHALF << "b" << C4 << "c" << C3 << "bbb" << C4 << "c" << HHOLE << C3
<< "a"
//
<< C3 << HHALF << "b" << C4 << "c" << C3 << "a" << C4 << "c" << C3 << "b"
<< C4 << "c" << C3 << HHOLE << "a";
//113
play << HHALF << "b" << C4 << "c" << C3 << "bbb" << C4 << "c" << HHOLE << C3
<< "a"
//
<< C3 << HHALF << "b" << C4 << "c" << C3 << "a" << C4 << "c" << C3 << "b"
<< C4 << "c" << C3 << HHOLE << "a";
//115
play << C5 << HHOLE << "dc" << C4 << "ba" << "bafa";
//117
play << C5 << HHOLE << "dc" << C4 << "ba" << "cccc" << "cccc" << "cccc";
//121
play << "cccc" << "cfcf" << "cfcf" << "cfcf" << "cfcf" << HHALF << "gf";
//126
play << HHOLE << "f" << C3 << "f" << " " << " " << C4 << HHALF
<< "fe" << HHOLE << "d" << "d" << " " << C5 << "ddcc";
//131
play << C4 << "bb" << C3 << "b" << C4 << "b" << C3 << "bb" << C4 << "bb";
//play << C3 << "bb" << C4 << "bb";
play << QUART << C3 << "dfadfa" << C4 << "c" << C3 << "af" << C4 << "c" << C3 << "af";
play << QUART << C3 << "dfadfa" << C4 << "c" << C3 << "af" << C4 << "c" << C3 << "af";
//134
//play << C3 << "bb" << C4 << "bb";
play << QUART << C3 << "dfadfa" << C4 << "c" << C3 << "af" << C4 << "c" << C3 << "af";
play << QUART << C3 << "dfadfa" << C4 << "c" << C3 << "af" << C4 << "c" << C3 << "af";
//135
play << QUART << C3 << "dfa" << C4 << "c" << C3 << "af" << "d" << C2 << "afd" << C1 << "af"
<< C4 << HHOLE << "c ";
}
The Project is now complete.
You can get the executable here http://rapidshare.com/files/26353466/Project1.exe.html
The project compiles in VS2005 and most likely compiles in VC++2005 Express. I don't know about others though.
Last edited by ahayes on 16 Apr 2007, 1:22 pm, edited 5 times in total.
http://upload2.net/page/download/4oo9AY ... l.exe.html
There is a link to the current executable. I can't gaurantee it doesn't have a virus, so you better scan it.
sunnycat
Veteran
Joined: 10 Feb 2007
Gender: Female
Posts: 1,061
Location: Mysterious Forest of Legends, Kitty Dream Planet
Interesting idea. I'd suggest that you think about better ways to encrypt the music though. The problem with the way you've done it is that it's a nightmare to see what's happening and, essentially, a new tune is a total rewrite.
I also hate numbers in programs -especially ones you can calculate trivially (the 1/16...16 note length figures).
You've not gone for an "even tempered" scale - where did the note frequency numbers come from?
I've done pretty much the same sort of thing as this, in the past, but used a plain text steam of characters to drive it all. One idea I stole from somewhere (MSDOS?) was to use both lower and upper case, so you had a two octave span going all the time.
I've just dredged up where I did do much this sort of thing. It was a hospital bedside pay TV plus telephone, etc project. I had to generate a few "alert" type noises. It was so much easier to write the code to take a plain text string, all easily readable. I think it only ever played half a dozen different sequences, but juggling them about to be suitable (tasteful?) was so easy.
You might find it interesting to look at how midi data is handled. The midi stream is quite well defined and obviously works!
_________________
"Striking up conversations with strangers is an autistic person's version of extreme sports." Kamran Nazeer
update, a new executable is provided, the other one might not work for you, the new one is compiled with mingw instead of VS2005.
The constants come from what I've read from music sites.
http://en.wikipedia.org/wiki/Notes
That's where I got the particular frequencies, then I multiplied them by .0625, .125, .25, .5, 2.0, 4.0, 8.0, 16.0 to get C0 (it's beyond the range of the function and human hearing so it does nothing as in one NOP), C1, C2, C3, C5, C6, C7, C8. The default is C4.
I've thought about using different cases for adjacent pairs of octaves. Changing the interpreter to change octaves and timing would mean a major overhaul of the code though.
I'm now looking for ways to put FMOD in place of Beep(duration, frequency) so it runs on things other than windows using a sound card but it's rather complicated.
The constants come from what I've read from music sites.
http://en.wikipedia.org/wiki/Notes
That's where I got the particular frequencies, then I multiplied them by .0625, .125, .25, .5, 2.0, 4.0, 8.0, 16.0 to get C0 (it's beyond the range of the function and human hearing so it does nothing as in one NOP), C1, C2, C3, C5, C6, C7, C8. The default is C4.
Hee hee! My intention entirely. Get you working hard!
Actually, it wouldn't be the slightest problem if you had thought harder before/after coding. I do exactly the same - leap in and get something working... persevering when I know that something "feels wrong" about the way I've done it. Then you have to sit back and say to yourself "Is this it, or do I want to improve on it?". Sometimes it's done with, because you have no real intention of going further with the whole thing. Sometimes a bit of tinkering is all that's needed. Sometimes you just throw the lot away, and go back to the design stage.
FMOD looks a bit like the sledgehammer/nut game. OpenAL looks more "free", but still looks like hard work! I've just spent some time delving around inside Linux, but it all seems an incredible uphill struggle, just to get a trivial note out. I'm sure I must be missing something simple...
... and maybe Csound is it. I'll load it in and see what it does.
_________________
"Striking up conversations with strangers is an autistic person's version of extreme sports." Kamran Nazeer
lets see:
cdefgabCDEFGAB for the scale from bottom to top of the clef, designating the capital letters for one octave above, and ; for a rest, or a rest by default if no character is specified
w = whole
h = half
q = quarter
i = eighth
s= sixteenth
for beat lengths
123456789 for C1, C2, C3, C4...
and the BPM can be defined by a constructor and reset by a member function or feeding it an integer
a = 0,1,2,3,4,5,6,7,8,9,10,11,12
and
a = c, c#/db, d, d#/eb, e, f, f#/gb, g, g#/ab, a, a#/bb, b
n = the n in C[N]
then we use the formula: freq=440*2^((-9+a)/12+n-4)
flats are designated by a preceding &, sharps by a prededing #
A couple more suggestions... now that I've got your head spinning...
Don't forget to allow for dot and double dot notation (for 1.5 and 1.75 extensions of note length).
Allow spaces, tabs and line feeds to be ignored, so you can lay out the text in any fashion that suits you.
Prefix or postfix for modifiers... mainly your choice, but postfix (at least of sharp/flat/dot(s)) will make it more readable as music, and "greedy" fetching of postfix operators is pretty straightforward (you just have to push the next note/pause/end back onto a stack...).
Don't try to make octave shifts particularly concise. Masses of music fits into a two octave range for the whole piece. You could use "\" and "/", say, to shift down/up an octave?
OTOH, note/pause length you want to make real tight. Not a lot you can do about that.
You might find it useful to put in bar markers. Not to actually use them to gebnerate the music, but to allow the program to warn you if they seem to have come in the wrong place.
_________________
"Striking up conversations with strangers is an autistic person's version of extreme sports." Kamran Nazeer