This page will have queries which I am not able to solve. Please help if you know the answer.
Can you
solve the following MS Visual C++ (6.0) problem:
=========================
3.13.13Problem: The Piano
Piano is a multi-document multi-view application that allows composers to record
and play simple musical scores. Each view is a form that displays 13 piano keys
(these are simply elongated buttons). Pressing one of these buttons either plays
a beep of a particular frequency and duration (Windows NT only) or plays a wave
file (if the computer is equipped with a sound card). If the view is in
recording mode, then an object representing the note played is stored in a
musical score (i.e., a list of notes encapsulated by the document class). The
[Duration Control] scroll bar allows users to alter the duration of the next
note to be created/played. The [Record] and [Stop Recording] buttons turn the
recording mode on and off, respectively. When the recording mode is off, the
[Play] button plays back the current score. The [Clear] button erases the score.
Naturally, users can save and load scores.
The [Sounds] menu allows users to associate different sounds with the piano
keys. [Sounds]/[Beep Mode] toggles between playing a beep and playing a wave
file. A check mark next to this item indicates that beep mode is on. In
addition, the [Sounds] menu contains 13 other items, one for each key. Selecting
any of these items displays a [File Dialog] that allows users to select a wave
file to be played when the corresponding key is pressed.
Implement the Piano application.
3.1.1.1 3.13.13.1 CNote
Begin by using the [Insert]/[New Class ...] menu to create a class called CNote.
Instances of CNote represent musical notes. Each CNote encapsulates three
integer variables:
m_frequency = frequency in Hz
m_duration = duration in milliseconds
m_id = number of steps above C (= -1 to 12)
We use m_id = -1 to indicate a rest "note", m_id = 0 is C, m_id = 1 is C#, etc.,
and m_id = 12 is high C.
The CNote constructor expects a duration and an id as inputs:
CNote::CNote(int id /* = 0 */, int dur /* = 100 */) { ??? }
The appropriate frequency is computed by the constructor using the formula:
m_frequency = int(256 * pow(1.05496, id));
This works because 256 represents the frequency of C, and 1.05496 is 21/12, the
frequency ratio of a half step. If id = -1, the rest note, then f = 30000 Hz,
which should be inaudible.
3.1.1.2 3.13.13.2 CPianoDoc
You'll need to add the following include directives at the top of PianoDoc.h:
#include <afxtempl.h>
#include "Note.h"
#include <mmsystem.h> // may require a sound card
Instances of the CPianoDoc class encapsulate:
m_beep = TRUE if beep mode is on, FALSE otherwise
m_score = a CList of recorded CNotes
m_sounds =
a CMap of associations between CNote id's and wave file names
The CPianoDoc class provides member functions for playing a single note, playing
all of the notes in m_score, adding a note to m_score, changing the wave file
associated with a note:
class CPianoDoc : public CDocument
{
public:
BOOL m_beep; // beep vs. wave mode
void AddSound(int id);
void AddNote(CNote& n);
void Play(CNote note);
void Play();
// etc.
private:
CList<CNote, CNote&> m_score;
CMap<int, int, CString, CString&> m_sounds;
};
If beep mode is on, Play(CNote note) calls the global Beep() function:
Beep(note.m_frequency, note.m_duration);
Unfortunately, only the Windows NT platform pays attention to this function's
parameters. If beep mode is off, Play() uses note.m_id to look up the
corresponding wave file, then calls the PlaySound() function declared in <mmsystem.h>
and defined in winmm.lib:
PlaySound(waveFile, NULL, SND_FILENAME | SND_SYNC);
Readers can search the on-line documentation for a complete description of
PlaySound(). In order to successfully link a program that calls PlaySound(),
readers will have to use the [Project Settings] dialog box (select the
[Project]/[Settings] menu item) to add the sound card driver. Set the [Settings
For:] list box to "All Configurations", select the [Link] tab, and type "winmm.lib"
at the end of the libraries listed in the [Object/library modules:] edit box:
(Of course the winmm.lib may not be present if you haven't installed some sort
of sound card on your system.)
Readers can handle all of the [Sounds] menu items in CPianoDoc. For example,
here's the handler for [Sounds]/[D Sound]:
void CPianoDoc::OnSoundsDsound()
{
AddSound(2); // m_id of D = 2
}
The AddSound() function creates and displays a file dialog box, then enters the
path of the selected wave file in the m_sounds map:
void CPianoDoc::AddSound(int id)
{
static LPCTSTR path = NULL;
CFileDialog dialog(TRUE, NULL, path);
dialog.DoModal();
path = dialog.GetPathName();
m_sounds[id] = path;
SetModifiedFlag(TRUE);
}
3.1.1.3 3.13.13.3 CPianoView
The CPianoView objects encapsulate two variables:
#include "Note.h"
class CPianoView : public CFormView
{
int m_duration; // wired to scroll bar by Class Wizard
BOOL m_recording; // = TRUE if recording
// etc.
};
We haven't seen scroll bars, yet. OnInitialUpdate() is a good place to
initialize the range and thumb tab position:
void CPianoView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
ResizeParentToFit();
CScrollBar* pSB = (CScrollBar*) GetDlgItem(IDC_DURATION);
pSB->SetScrollRange(50, 250);
pSB->SetScrollPos(m_duration);
}
Adjusting a horizontal scroll bar sends the WM_HSCROLL message to the scroll
bar's parent window (which is CPianoView in our case). Use the Class Wizard to
add a handler for this message. This handler has two responsibilities: move the
thumb tab to where the user adjusted it to (otherwise it will snap back), and
update m_duration. Scroll bars will be explained in the next chapter.
void CPianoView::OnHScroll(
UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
int pos = pScrollBar->GetScrollPos();
if (pScrollBar != NULL && GetDocument()->m_beep))
{
switch (nSBCode)
{
case SB_THUMBPOSITION:
pScrollBar->SetScrollPos(nPos);
break;
case SB_LINELEFT: // left arrow
if (pos - 10 > 0)
pos = pos - 10;
else
pos = 0;
pScrollBar->SetScrollPos(pos);
break;
case SB_LINERIGHT: // right arrow
if (pos + 10 < 255)
pos = pos + 10;
else
pos = 255;
pScrollBar->SetScrollPos(pos);
break;
} // switch
UpdateData(TRUE);
} // if
CFormView::OnHScroll(nSBCode, nPos, pScrollBar);
}
-- BoB
Hall
Hello Bob: I was browsing through your code, which I found helpfull and I was wondering. In your OnHScroll method, you test to see if pScrollBar is NULL, however, you use it the line before. If it is NULL (because of a System Scroll Bar) won't that through an exception? i.e. NULL->GetScrollPos() = error? If there is a miss-understanding on this, please send me an E-mail at dan.pickett@mail.SecureAgent.com Thank you, Dan