Tonerzeugung
-
Hallo,
wir haben derzeit an der Uni etwas C++-Programmierung. Eine Aufgabe ist es, Töne zu erzeugen. Ich würde gern etwas vorarbeiten, da mir die bisherigen Aufgaben sehr viel Spaß gemacht haben. Nun stehe ich hier jedoch vor einer Aufgabe, die ich nicht selbst bewältigen kann.
Hier die Aufgabenstellung:
http://www.directupload.net/file/d/3979/o3bty7y6_pdf.htmKönnte mir eventuell jemand erst einmal ein kleines Beispiel bringen, wie ich Töne aus dem PC bekomme? Ich hoffe das funktioniert bei mir auch, ich Programmiere am Mac mit Parallels Desktop und Windows 7.
Vielen Dank.
-
Hallo,
gehts dir um die Hardwareansteuerung oder um das Erzeugen der Töne?
Wenn es dir nur um zweiteres geht, nimm eine WAV Bibliothek*. Dann kannst du direkt die Amplitudenwerte in ein Array schreiben und mit der WAV Bibliothek dieses Array in eine WAV Datei speichern. Diese kannst du dann mit einem Mediaplayer deiner Wahl wiedergeben.
Du kannst dich somit voll und ganz auf das Erzeugen der Töne konzentrieren. Kannst ja mal eine Rechteckschwingung erzeugen, dann hörst du sehr schön die hochfrequenten Anteile. Im Gegensatz zu einer "schönen" Sinusschwingung, die hört sich viel angenehmer an.
* Eine kleine, oft benutzte Bibliothek: http://www.mega-nerd.com/libsndfile/
-
Vielen Dank für die schnelle Antwort. Werde mir das direkt mal anschauen.
Leider soll es nun genau so gemacht werden wie beschrieben mit der Hardwareansteuerung :/.Uns wird folgender Code gegeben:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <conio.h> #include <Windows.h> #include "OlsApi.h" void main() { printf("Tonerzeugung mit dem PC\n"); InitializeOls(); // eigenes Programm DeinitializeOls(); _getch(); }
und da soll dann was dazu geschrieben werden, sodass eine Melodie entsteht.
-
ok.
Na dann musst du das Teil eben programmieren. Die PC Architektur besitzt einen IO und einen Memorybus. Normalerweise sagst du schreib auf Adresse xy im RAM. Jetzt handelt es sich eben um Adressen am IO Bus.
Die Hardware hat verschiedene Adressen, der besagte Timer ist unter Adresse 0x43 (hex!) ansprechbar.Schau dir nochmal dein PDF an, da ist eh schon ein Beispiel drinnen:
WriteIoPortByte(0x43,0xB6);Dabei wird auf Adresse 0x43 der Wert 0xB6 geschrieben.
Ist also folgendermaßen zu verstehen:
char* adresse=0x43;
*adresse=0xB6;Nur dass du das eben über WriteIoPortByte(...) machen musst.
Gut, du kannst somit also auf dem IO Bus schreiben. Was jetzt noch fehlt ist rauszufinden, welche Werte zu übergeben sind. Das Controllregister ist in deinem PDF eh schön dargestellt. Bit 7 und 6 sind für den zu wählenden Kanal zuständig. Du brauchst Kanal 2, also setzt du 1 0.
Und so machst du das mit allen anderen Bits auch: du schaust, was sie aussagen, und setzt sie entsprechend.Du musst die einzelnen Bit jetzt nur noch zu einem Byte zusammenbauen. Das geht mit Shiftoperator << und den bitweisen Verknüpfungen |, &, ... Einfach bei Google nachsehen im Zweifelsfall.
-
OK, also ich verstehe das so:
An Portadresse 0X43 wird die "Konfiguration" des Controlregisters gesendet, in dem Fall 0XB6. Das heißt 10110110.
Wenn ich nun noch einen Ton erzeugen will, beispielsweise c (Frequenz 262 Hz), dann muss ich ja ausgehend von der Eingangsfrequenz (f = 1193180 Hz) den Tellerfaktor n bestimmen (hier dann 4554dez = 11CA hex) und an 0X42 senden oder?
Also WriteIoPortByte(0x42,0x11CA). Oder ist das jetzt gänzlich falsch? Ich muss ja weiterhin auch noch auf MSB und LSB achten irgendwie?!
Sorry, wenn ich ganz daneben liege, habe sowas noch nie gemacht.
-
Also WriteIoPortByte(0x42,0x11CA). Oder ist das jetzt gänzlich falsch? Ich muss ja weiterhin auch noch auf MSB und LSB achten irgendwie?!
ich nehme an, WriteIoPortByte schreibt immer nur ein Byte.
Müsstest also 0x11CA aufteilen in zwei Bytes, also 0x11 und 0xCA.
Und dann zweimal aufrufen:
WriteIoPortByte(0x42,0xCA); // LSB
WriteIoPortByte(0x42,0x11); // MSB
-
Ahhh ok, ja das klingt einleuchtend.
Und dann sollte schon ein Ton heraus kommen?
-
Patrick1990 schrieb:
Ahhh ok, ja das klingt einleuchtend.
Und dann sollte schon ein Ton heraus kommen?probiers doch einfach
-
Ok, ich bekomme folgende Mitteilung:
http://fs1.directupload.net/images/150507/temp/rtngsoin.png
Scheinbar ist das unter Parallels Desktop nicht möglich.
-
Steht doch eh da was (vermutlich) das Problem ist.
Stell dein Projekt auf Win32 um und probier es nochmal.
-
Wenn ich das unter Project --> Properties ändere erscheinen mir mehrere Fehlermeldungen:
1>------ Build started: Project: A2_Tonerzeugung, Configuration: Debug Win32 ------ 1>Tonerzeugung.obj : error LNK2019: unresolved external symbol "unsigned long __stdcall GetDllStatus(void)" (?GetDllStatus@@YGKXZ) referenced in function _main 1>Tonerzeugung.obj : error LNK2019: unresolved external symbol "int __stdcall InitializeOls(void)" (?InitializeOls@@YGHXZ) referenced in function _main 1>Tonerzeugung.obj : error LNK2019: unresolved external symbol "void __stdcall DeinitializeOls(void)" (?DeinitializeOls@@YGXXZ) referenced in function _main 1>Tonerzeugung.obj : error LNK2019: unresolved external symbol "void __stdcall WriteIoPortByte(unsigned short,unsigned char)" (?WriteIoPortByte@@YGXGE@Z) referenced in function _main 1>\\psf\Home\Desktop\A2_Tonerzeugung\Debug\A2_Tonerzeugung.exe : fatal error LNK1120: 4 unresolved externals ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Vielleicht sollte ich besser ein 64-Bit Betriebssystem aufspielen?
-
Du mußt dem Linker noch die Library (.lib-Datei) zu der "OlsApi.h" mitgeben (+ evtl. noch die Linkerpfade anpassen).
-
Oha davon habe ich keine Ahnung. Wie geht sowas?
-
Projekteigenschaften, Linker, Eingabe bzw. Input, und dann den Pfad zur LIB angeben.
Siehe auch Internet, hier erster Treffer auf stackoverflow:
http://stackoverflow.com/questions/20058864/how-to-include-libraries-in-visual-studio-2012
-
Das sieht aus als wolltest Du über den PC-Speaker Sound ausgeben.
Da kann man aus dem alten Buch PC-Intern von Michael Tischler (DATA-BECKER)
dessen Interuptierende Sprachausgabe betrachten.Unter Windows kannst Du zb. einen Text sprechen lassen wie folg:
PseudoCode:
#include <sphelper.h> static unsigned __stdcall ClientSay(void *ptr) { CString str((char *)ptr); if (FAILED(::CoInitialize(NULL)))return 0; CComPtr<ISpVoice> cpVoice; CComPtr<ISpObjectToken> cpVoiceToken; CComPtr<IEnumSpObjectTokens> cpEnum; ULONG ulCount(0); if (FAILED(cpVoice.CoCreateInstance(CLSID_SpVoice)))return 0; if (FAILED(SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum)))return 0; if (FAILED(cpEnum->GetCount(&ulCount)))return 0; cpVoiceToken.Release(); if (FAILED(cpEnum->Next(1, &cpVoiceToken, NULL)))return 0; if (FAILED(cpVoice->SetVoice(cpVoiceToken)))return 0; cpVoice->Speak(str.AllocSysString(), SPF_ASYNC, NULL); while (cpVoice->WaitUntilDone(33) == S_FALSE); ::CoUninitialize(); return 0; }
Ansonnsten kannst Du alle Frequenzen über die AudioHardware ausgeben:
CAudioFreqGen::CAudioFreqGen(void) { uDeviceID = WAVE_MAPPER; pwfx = NULL; dwCallback = NULL; dwCallbackInstance = NULL; fdwOpen = CALLBACK_NULL; unsigned char *buf = NULL; m_lastfrq = 0; ghwo = 0; PrepareDevice(); } CAudioFreqGen::~CAudioFreqGen(void) { CloseDevice(); } char *CAudioFreqGen::PrepareDevice(void) { pwfx=new WAVEFORMATEX; pwfx->wFormatTag=WAVE_FORMAT_PCM; pwfx->nChannels=1; pwfx->nSamplesPerSec=SPS; pwfx->nAvgBytesPerSec=SPS; pwfx->nBlockAlign=1; pwfx->wBitsPerSample=8; mmres=waveOutOpen(&ghwo,uDeviceID,pwfx,dwCallback,dwCallbackInstance,fdwOpen); delete pwfx; if(mmres != MMSYSERR_NOERROR) { return "Cant open device."; } buf=new unsigned char[SPS]; if(buf == NULL) { CloseDevice(); return "Cant allocate memory."; } memset(buf,0,SPS); pwh.lpData=(LPSTR)buf; pwh.dwBufferLength=SPS; pwh.dwFlags=WHDR_BEGINLOOP; pwh.dwLoops=1; mmres = waveOutPrepareHeader(ghwo,&pwh,sizeof(pwh)); pwh.dwFlags=WHDR_BEGINLOOP|WHDR_ENDLOOP|WHDR_PREPARED; pwh.dwLoops=0xFFFFFFFF; // Number of loops == about 136 years:) if(mmres != MMSYSERR_NOERROR) { CloseDevice(); return "Device prepare error."; } return NULL; } //--------------------------------------------------------------------------- char *CAudioFreqGen::CloseDevice(void) { if(buf) delete [] buf; if(ghwo) { mmres=waveOutReset(ghwo); if(mmres != MMSYSERR_NOERROR) return "Cant close device."; mmres=waveOutClose(ghwo); if(mmres != MMSYSERR_NOERROR) return "Cant close device."; ghwo = 0; } return NULL; } //--------------------------------------------------------------------------- void CAudioFreqGen::Generate(int freq,char amp,float phase) { if(ghwo) if(m_lastfrq != freq) { m_lastfrq = freq; char tmp[SPS]; register float x=freq*2*3.1416/SPS; for(int cnt=0;cnt<SPS;cnt++) tmp[cnt]=(unsigned char)(cos(phase+cnt*x)*amp+127); memcpy(&buf[0],&tmp[0],SPS); } }
Grüße und Erfolg
Karsten
-
Ich habe nun noch einmal ein 64-Bit Windows als virtuelle Maschine laufen. Nun läuft das Programm, jedoch gibt er aus: Rueckgabewert=5. Er müsste eigentlich ja 0 sein.