Data wykonania ćwiczenia: 21.10.2014 r.
Data oddania sprawozdania: 28.10.2014 r.
Prowadzący: dr inż. Jan Nikodem Wydział Elektroniki, Informatyka
Grupa:
Termin: Wt TN 13:15
Sprawozdanie z laboratorium
Urządzenia Peryferyjne
Ćwiczenie 11
Obsługa karty muzycznej z wykorzystaniem DirectSound, API i ActiveX
Cel ćwiczenia:
Celem ćwiczenia było napisanie programu, który będzie pracował z plikami dźwiękowymi .wav czyli odtwarzał je, wyświetlał o nich informacje zaszyte w pliku oraz przechwytywał dźwięk z mikrofonu.
Nagłówek Wav:
W nagłówku pliku .wav zapisane są informacje:
Offset (przesunięcie) | Nazwa pola | Rozmiar w bajtach | Wartość | Opis |
---|---|---|---|---|
0 | ID | 4 | 'RIFF' | Identyfikator pliku RIFF |
4 | Rozmiar danych | 4 | Długość pliku - 8 | Liczba określająca długość danych w pliku w bajtach z pominięciem pierwszych 8 bajtów nagłówka |
8 | Format ID | 4 | 'WAVE' | Format pliku |
12 | Opis ID | 4 | 'fmt ' | Początek części opisowej pliku |
16 | Rozmiar opisu | 4 | . | Rozmiar części opisowej, dla fmt wynosi zwykle 16 |
20 | Format Audio | 2 | 0001h | Rodzaj kompresji. 1 - bez kompresji, modulacja PCM. |
22 | Liczba kanałów | 2 | 0x0001, 0x0002, itd. | 1 - mono, 2 - stereo |
24 | Częstotliwość | 4 | 8000, 44100, itd. | Częstotliwość próbkowania w Hz |
28 | Częstotliwość bajtów | 4 | . | Częstotliwość * Liczba kanałów * Rodzielczość / 8 |
32 | Rozmiar próbki | 2 | . | Liczba kanałów * Rodzielczość / 8 |
34 | Rozdzielczość | 2 | 8, 16, itd. | Rozdzielczość w bitach |
36 | Dodatkowe parametry | x | . | Zwykle brak tego pola |
36+x | Dane ID | 4 | 'data' | Początek części z danymi |
40+x | Rozmiar danych | 4 | . | Rozmiar bloku danych |
44+x | Dane | . | . | . |
Nasz program odczytuje te dane z pliku .wav od razu po uruchomieniu a także po kliknięciu na „Odczytaj Nagłówek” oraz wyświetla je w okienku.
Funkcja PlaySound():
Funkcja ta wygląda następująco:
BOOL PlaySound(
LPCTSTR pszSound,
HMODULE hmod,
DWORD fdwSound
);
Natomiast jej wywołanie w naszym programie wygląda tak:
PlaySound(TEXT("dzwiek.wav"), NULL, SND_ SND_SYNC);
gdzie dźwięk.wav to nazwa odtwarzanego przez nas pliku.
ActiveX
Komponenty ActiveX umożliwiają w bardzo łatwy sposób odtworzenie pliku dźwiękowego, jak również sterowanie jego głośnością, przewijanie itd. Z tego względu są bardziej „przyjazne” dla odbiorcy programu jak i dla programisty.
DirectSound
Jest to część pakietu DirectX umożliwiającą szybki dostęp do karty dźwiękowej i m.in. odtwarzanie i nagrywanie dźwięku. Niestety implementacja nie jest już taka łatwa jak w pozostałych sposobach.
Okno główne programu:
Okno pozwala na odtworzenie dźwięku różnymi metodami, odczytanie zawartości nagłówka pliku .wav a także nagranie 5 sekundowego nagrania z mikrofonu oraz odtworzenie go. Przy uruchomieniu programu zostaje wyświetlony komunikat informujący o tym czy w komputerze zainstalowana jest karta dzwiękowa.
Zawartość kodu (tylko napisany przez nas):
void SprawdzKarte() //Funkcja sprawdzająca obecnośćc karty dzwiękowej
{
//WAVEOUTCAPS woc;
unsigned long iNumDevs;
/* Get the number of Digital Audio Out devices in this computer */
iNumDevs = waveOutGetNumDevs();
if (iNumDevs == 0)
AfxMessageBox(_T("Brak karty dzwiekowej !"));
else
AfxMessageBox(_T("Karta dzwiekowa wykryta !"));
}
void Naglowek() //Funkcja odczytująca zawartość nagłówka
{
int* liczba;
short* liczba_krotka;
char bufor[4];
fstream plik;
ostringstream ss,ss1,ss2,ss3,ss4,ss5,ss6,ss7,ss8,ss9,ss10;
plik.open("dzwiek.wav", std::ios::in | std::ios::out | std::ios::binary);
plik.read(bufor, 4);
ss<< bufor[0];
ss << bufor[1];
ss << bufor[2];
ss << bufor[3];
tekst= (string)ss.str();
plik.read(bufor, 4);
liczba = (int*)&bufor;
ss1 << *liczba;
tekst2=(string) ss1.str();
plik.read(bufor, 4);
ss2 << bufor[0];
ss2 << bufor[1];
ss2 << bufor[2];
ss2 << bufor[3];
tekst3 = (string)ss2.str();
plik.read(bufor, 4);
ss3 << bufor[0];
ss3 << bufor[1];
ss3 << bufor[2];
ss3 << bufor[3];
tekst4 = (string)ss3.str();
plik.read(bufor, 4);
liczba = (int*)&bufor;
ss4<< *liczba;
tekst5 = (string)ss4.str();
plik.read(bufor, 2);
liczba_krotka = (short*)&bufor;
ss5<<*liczba_krotka;
tekst6 = (string)ss5.str();
plik.read(bufor, 2);
liczba_krotka = (short*)&bufor;
ss6<< *liczba_krotka;
tekst7 = (string)ss6.str();
plik.read(bufor, 4);
liczba = (int*)&bufor;
ss7 <<*liczba << endl;
tekst8 = (string)ss7.str();
plik.read(bufor, 4);
liczba = (int*)&bufor;
ss8 << *liczba << endl;
tekst9 = (string)ss8.str();
plik.read(bufor, 2);
liczba_krotka = (short*)&bufor;
ss9<< *liczba_krotka;
tekst10 = (string)ss9.str();
plik.read(bufor, 2);
liczba_krotka = (short*)&bufor;
ss10<< *liczba_krotka;
tekst11 = (string)ss10.str();
plik.close();
}
void Cperyferyjne_dzwiek_MFCDlg::OnBnClickedPlaysoundbutton()
{ //Odtwarzanie dzwięku przez PlaySound()
PlaySound(TEXT("dzwiek.wav"), NULL, SND_SYNC);
}
void Cperyferyjne_dzwiek_MFCDlg::OnBnClickedOdczytajnaglowekbutt()
{
Naglowek(); //funkcja wypisująca dane //odczytane z nagłówka
CString ss;
ss.Format(_T("%S"), tekst.c_str());
SetDlgItemText(IDC_EDIT1, ss);
ss.Format(_T("%S"), tekst2.c_str());
SetDlgItemText(IDC_EDIT4, ss);
ss.Format(_T("%S"), tekst3.c_str());
SetDlgItemText(IDC_EDIT5, ss);
ss.Format(_T("%S"), tekst4.c_str());
SetDlgItemText(IDC_EDIT6, ss);
ss.Format(_T("%S"), tekst5.c_str());
SetDlgItemText(IDC_EDIT7, ss);
ss.Format(_T("%S"), tekst6.c_str());
SetDlgItemText(IDC_EDIT8, ss);
ss.Format(_T("%S"), tekst7.c_str());
SetDlgItemText(IDC_EDIT9, ss);
ss.Format(_T("%S"), tekst8.c_str());
SetDlgItemText(IDC_EDIT10, ss);
ss.Format(_T("%S"), tekst9.c_str());
SetDlgItemText(IDC_EDIT11, ss);
ss.Format(_T("%S"), tekst10.c_str());
SetDlgItemText(IDC_EDIT12, ss);
ss.Format(_T("%S"), tekst11.c_str());
SetDlgItemText(IDC_EDIT13, ss);
}
void Cperyferyjne_dzwiek_MFCDlg::OnBnClickedButton1()
{ //Odtwarzanie dzwięku przez WaveOut
FILE *fd;
WAVEFORMATEX wav;
fopen_s(&fd, "dzwiek.wav", "rb");
fseek(fd, 20, SEEK_SET);
fread(&(wav.wFormatTag), 2, 1, fd);
fread(&(wav.nChannels), 2, 1, fd);
fread(&(wav.nSamplesPerSec), 4, 1, fd);
fread(&(wav.nAvgBytesPerSec), 4, 1, fd);
fread(&(wav.nBlockAlign), 2, 1, fd);
fread(&(wav.wBitsPerSample), 2, 1, fd);
wav.cbSize = 0;
if (waveOutOpen(&hwaveout, WAVE_MAPPER, &wav, NULL, NULL, CALLBACK_NULL) != MMSYSERR_NOERROR) {
fclose(fd);
MessageBox(L"Blad otwierania urzadzenia WaveOut");
return;
}
fseek(fd, 40, SEEK_SET);
fread(&(wavhdr.dwBufferLength), 4, 1, fd);
wavhdr.lpData = (LPSTR)malloc(wavhdr.dwBufferLength);
if (wavhdr.lpData == NULL) {
fclose(fd);
return;
}
if (fread(wavhdr.lpData, wavhdr.dwBufferLength, 1, fd) != 1) {
fclose(fd);
MessageBox(L"Blad kopiowania danych do bufora");
}
wavhdr.dwFlags = 0;
wavhdr.dwLoops = 0;
if (waveOutPrepareHeader(hwaveout, &wavhdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) {
MessageBox(L"Blad przygotowywania do odtwarzania");
return;
}
if (waveOutWrite(hwaveout, &wavhdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) {
MessageBox(L"Blad zapisywania do karty dzwiekowej");
return;
}
fclose(fd);
}
LPWAVEFORMATEX wczytajPlik(LPCSTR nazwa, HANDLE &bufor) {
FILE *plik;
plik = fopen(nazwa, "rb");
LPWAVEFORMATEX wav = new WAVEFORMATEX;
if (plik) {
BYTE id[5];
id[4] = 0;
DWORD size;
fread(id, sizeof(BYTE), 4, plik);
if (!strcmp((char *)id, "RIFF")) {
cout << "ChunkID: " << (char*)id << endl;
fread(&size, sizeof(DWORD), 1, plik);
fseek(plik, 20, SEEK_SET);
fread(&(wav->wFormatTag), 2, 1, plik);
fread(&(wav->nChannels), 2, 1, plik);
fread(&(wav->nSamplesPerSec), 4, 1, plik);
fread(&(wav->nAvgBytesPerSec), 4, 1, plik);
fread(&(wav->nBlockAlign), 2, 1, plik);
fread(&(wav->wBitsPerSample), 2, 1, plik);
wav->cbSize = 0;
fseek(plik, 40, SEEK_SET);
fread(&(wavhdr.dwBufferLength), 4, 1, plik);
if (fread(bufor, wavhdr.dwBufferLength, 1, plik) != 1) {
fclose(plik);
return NULL;
}
}
else
fclose(plik);
}
else
fclose(plik);
return wav;
}
//funkcja nagrywajacą z mikrofonu
void Cperyferyjne_dzwiek_MFCDlg::OnBnClickedButton3()
{
unsigned short Channels = 1;
unsigned long SamplesPerSecond = 44100;
unsigned short BitsPerSample = 8;
unsigned long RecordSeconds = 5;
WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
WaveFormat.nChannels = Channels;
WaveFormat.nSamplesPerSec = SamplesPerSecond;
WaveFormat.wBitsPerSample = BitsPerSample;
WaveFormat.nAvgBytesPerSec = SamplesPerSecond * Channels;
WaveFormat.nBlockAlign = (Channels*BitsPerSample) / 8;
WaveFormat.cbSize = 0;
int Res = waveInOpen(&WaveHandle, WAVE_MAPPER, &WaveFormat, 0, 0, WAVE_FORMAT_QUERY);
if (Res == WAVERR_BADFORMAT) return;
Res = waveInOpen(&WaveHandle, WAVE_MAPPER, &WaveFormat, 0, 0, CALLBACK_WINDOW);
BufferSize = RecordSeconds * (BitsPerSample / 8) * SamplesPerSecond * Channels;
Buffer = new char[BufferSize];
WaveHeader.dwBufferLength = BufferSize;
WaveHeader.dwFlags = 0;
WaveHeader.lpData = Buffer;
Res = waveInPrepareHeader(WaveHandle, &WaveHeader, sizeof(WAVEHDR));
if (Res) {
cout << "Blad przygotowania naglowka" << endl;
if (Buffer)
delete Buffer;
return;
}
Res = waveInAddBuffer(WaveHandle, &WaveHeader, sizeof(WAVEHDR));
Res = waveInStart(WaveHandle);
if (Res != MMSYSERR_NOERROR){
cout << "Blad nagrywania dzwieku" << endl;
if (Buffer)
delete Buffer;
return;
}
}
//funkcja oddwarzająca nagrany przez //mikrofon dzwięk
void Cperyferyjne_dzwiek_MFCDlg::OnBnClickedButton4()
{
int Res = waveOutOpen(&WaveOUTHandle, WAVE_MAPPER, &WaveFormat, 0, 0, WAVE_FORMAT_QUERY);
if (Res == WAVERR_BADFORMAT)
return;
Res = waveOutOpen(&WaveOUTHandle, WAVE_MAPPER, &WaveFormat, 0, 0, CALLBACK_WINDOW);
waveOutPrepareHeader(WaveOUTHandle, &WaveHeader, sizeof(WAVEHDR));
waveOutWrite(WaveOUTHandle, &WaveHeader, sizeof(WAVEHDR));
}
Podsumowanie
Dzięki temu ćwiczeniu dowiedzieliśmy się jak łatwo zaimplementować odtwarzanie i nagrywanie plików dźwiękowych w C++. Polega to w dużej mierze na operacjach na gotowych już funkcjach ( np. PlaySound() ).
Źródła:
http://www.kartydzwiekowe.republika.pl/pliki/wave1/wave2.htm
http://msdn.microsoft.com/en-us/library/windows/desktop/dd743680(v=vs.85).aspx