This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 7 years ago.
When trying to compile my code in Code::Blocks I receive a bunch of Undefined reference errors. The code is below.
Header
#ifndef BLACKJACK_GAME
#define BLACKJACK_GAME
// A game of blackjack has a deck and two hands
#include "Deck.h"
#include "BlackjackHand.h"
// Different ways the game can end. The first states take precedence over
// over the latter states so check for the early ones first (like blackjack)!
// Note that a "natural 21" is when you are delt two cards totaling 21.
enum eGameState {
GAME_BLACKJACK_PUSH, // Both player and dealer have a natural 21
GAME_DEALER_BLACKJACK, // Dealer has a natural 21
GAME_PLAYER_BLACKJACK, // Player has a natural 21
GAME_DEALER_BUST, // The dealer has more than 21
GAME_PLAYER_BUST, // The player has more than 21
GAME_DEALER_WIN, // The dealer's score is higher than players
GAME_PLAYER_WIN, // The player's score is higher than dealers
GAME_PUSH // The scores are tied
};
class BlackjackGame
{
public:
BlackjackGame();
// Clear the players hands and shuffle the deck
void newGame();
// Play a single round of blackjack showing the output using
// the 'PDcurses' library (for color and card suits).
void playGameCurses();
// Display the deck on the screen (for debugging purposes)
void printDeckCurses();
// Print both players hands to the screen using 'PDCurses'
// If pShowDealerScore is true then the dealer's score is printed
// Otherwise, the dealer's score is shown as '??'
void printHandsCurses(bool pShowDealerScore = false) const;
// Determine the state of the game. Note that if you have not yet
// played a round since you constructed this object or called newGame()
// Then the state returned is not accurate.
eGameState getGameState() const;
private:
// A deck of cards for shufflying and dealing hands
Deck mDeck;
// The two hands for the player and the dealer
BlackjackHand mPlayer, mDealer;
// A helper function for prompting the player (internal use only)
char promptPlayerCurses();
// A helper function to quit the game (internal use only)
void quitGameCurses();
};
#endif
CPP
#include "BlackjackGame.h"
// The library used for our text based user interface
#include <curses.h>
// Normal text color for PDCurses (defined in main)
#define NORM_TEXT 1
// Default constructor (does nothing)
BlackjackGame::BlackjackGame() {}
/* newGame() - Clear the two hands so we are ready for a new game */
void BlackjackGame::newGame()
{
mDealer.clear();
mPlayer.clear();
}
/* playGameCurses() - Play a single round of poker with one human player and one
* dealer following standard Vegas rules. Uses PDCurses for input and output to
* the console.
*
* You must implement this method but you do not need to worry about curses. Call
* 'promptPlayerCurses() to show the hands and prompt the human player to hit, stand
* or quit. This method will return the key they pressed. You can also use
* quitGameCurses() to exit properly if the user chose to 'quit'.
*/
void BlackjackGame::playGameCurses()
{
// TODO: Shuffle the deck and deal the cards (make sure dealer's first card is hidden).
mDeck.shuffle();
mPlayer.takeCard(mDeck.dealCard());
mDealer.takeCard(mDeck.dealCard());
mDealer.hideCard();
mPlayer.takeCard(mDeck.dealCard());
mDealer.takeCard(mDeck.dealCard());
// TODO: Check for a 'natural 21' (blackjack) before playing
if ( mDealer.hasBlackjack() || mPlayer.hasBlackjack())
quitGameCurses();
// TODO: Allower human player to hit, stand and quit as needed (repeat until player is done)
int flag = 0;
while(flag!=1)
{
char input = promptPlayerCurses(); // This line is an example only, a placeholder
switch(input)
{
case 'h': mPlayer.takeCard(mDeck.dealCard());
break;
case 's': flag =1;
break;
case 'q': quitGameCurses();
break;
default: break;
}
}
// TODO: Play the 'dealer' hand according to vegas rules
mDealer.showCards();
while(vegasDealerWouldHit())
{
mDealer.takeCard(mDeck.dealCard());
}
}
/* promptPlayerCurses() - Show the hands and prompt the human player to hit, stand or quit.
* output: returns the single character entered by the player at the prompt.
* - 'h' means hit, 's' means stand
* - 'q' means you should immediatly quit (call 'quitGameCurses()')
*/
char BlackjackGame::promptPlayerCurses()
{
// Show the hands
printHandsCurses();
// Hit or stand?
attron(COLOR_PAIR(NORM_TEXT));
mvprintw(3, 0, "Hit, stand or Quit ('h', 's', 'q'): ");
refresh();
// Read and return a single character
return getch();
}
/* quitGameCurses() - End curses output and exit the program immediately */
void BlackjackGame::quitGameCurses()
{
// End curses output, then pause and exit
endwin();
system("pause");
exit(0);
}
/* printDeckCurses() - A handy function that displays the content of the game deck
* using curses.
*
* This can be handy for debugging your deck and making sure it is getting properly
* shuffled. It is presently used for the fancy opening screen.
*/
void BlackjackGame::printDeckCurses()
{
// Start at the upper left corner of the screen
move(0, 0);
// For all 52 cards
for(int i=1; i<=52; i++)
{
// Get the next card and print it
PrintableCard lCard = mDeck.dealCard();
lCard.printCurses();
// If we've output 13 cards then move down a row
if(i%13 == 0)
{
move(2*(i/13), 0);
}
else
{
// Switch back to normal text color and output ' ' characters
attron(COLOR_PAIR(NORM_TEXT));
if(lCard.getFaceValue() == VALUE_TEN) printw(" ");
else printw(" ");
}
}
}
/* printHandsCurses() - A function to display the current scores and hands for this game
* using curses.
*
* This function is used in promptPlayerCurses() to show the hands before asking them if
* they want to hit or stand. Note that it alsways clears the window before drawing.
*/
void BlackjackGame::printHandsCurses(bool pShowDealerScore) const
{
// Clear window
erase();
// Show dealer and player hands
attron(COLOR_PAIR(NORM_TEXT));
mvprintw(0, 0, "Player: %d\n", mPlayer.score());
move(1, 0); mPlayer.printCurses();
attron(COLOR_PAIR(NORM_TEXT));
if(pShowDealerScore)
{
mvprintw(0, 50, "Dealer: %d\n", mDealer.score());
}
else
{
mvprintw(0, 50, "Dealer: ??\n");
}
move(1, 50); mDealer.printCurses();
refresh();
}
/* getGameStat() - Examine the hands of both players and return the current state of
* the game using the eGameState enum.
*
* You must examine the state of the game by comparing the two hands (their scores and their
* number of cards) and return the appropriate constant from the eGameState enum. Assume
* that the game is over (i.e. the player and dealer have both either gone bust or decided
* to stand).
*/
eGameState BlackjackGame::getGameState() const
{
if(mDealer.hasBlackjack() && mPlayer.hasBlackjack())
{
return GAME_BLACKJACK_PUSH;
}
else if(mDealer.hasBlackjack())
{
return GAME_DEALER_BLACKJACK;
}
else if(mPlayer.hasBlackJack())
{
return GAME_PLAYER_BLACKJACK;
}
else if(mPlayer.score() > 21)
{
return GAME_PLAYER_BUST;
}
else if(mDealer.score() > 21)
{
return GAME_DEALER_BUST;
}
else if(mDealer.score() > mPlayer.score())
{
return GAME_DEALER_WIN;
}
else if(mDealer.score() < mPlayer.score())
{
return GAME_PLAYER_WIN;
}
else {
return GAME_PUSH;
}
}
}
Main
#include <cstdlib>
#include <iostream> // Standard input and output
#include <string>
using namespace std;
#include <curses.h>
#include "BlackjackGame.h"
// Three different types of text colors used with PDCurses
#define NORM_TEXT 1
#define WIN_TEXT 2
#define LOSE_TEXT 3
int main()
{
// Setup 'PDcurses'
initscr();
start_color();
// Define our colors text colors (foreground, background)
init_pair(NORM_TEXT, COLOR_WHITE, COLOR_BLACK);
init_pair(WIN_TEXT, COLOR_YELLOW, COLOR_BLACK);
init_pair(LOSE_TEXT, COLOR_RED, COLOR_BLACK);
// Define our card colors (these are declared in PrintableCard.h)
init_pair(BLACK_CARD, COLOR_BLACK, COLOR_WHITE);
init_pair(RED_CARD, COLOR_RED, COLOR_WHITE);
// Input from the user and a game object
char ch = '\0';
BlackjackGame myGame;
// Output a 'fancy' welcome screen
myGame.printDeckCurses();
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(1, 13, "Welcome to Blackjack!");
mvprintw(5, 11, "Press any key to play ...");
refresh();
// Wait for input (the 'press any key to begin' thing)
ch = getch();
// Back to normal text to start the game
attron(COLOR_PAIR(NORM_TEXT));
do // Loop to play a new game until the user exits
{
// Restart and play a game (using curses)
myGame.newGame();
// Play a round of blackjack (most of the magic happens here!)
myGame.playGameCurses();
// Print the final status of the game
myGame.printHandsCurses(true);
// Print a game results message (use BOLD and the appropriate text color)
attron(A_BOLD);
switch(myGame.getGameState())
{
case GAME_BLACKJACK_PUSH:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "BLACKJACK TIE!!");
break;
case GAME_DEALER_BLACKJACK:
attron(COLOR_PAIR(LOSE_TEXT));
mvprintw(10, 25, "Dealer Blackjack. You lose.");
break;
case GAME_PLAYER_BLACKJACK:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "BLACKJACK! You win!");
break;
case GAME_DEALER_BUST:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "Dealer Bust. You Win!");
break;
case GAME_PLAYER_BUST:
attron(COLOR_PAIR(LOSE_TEXT));
mvprintw(10, 25, "BUST. You lose.");
break;
case GAME_DEALER_WIN:
attron(COLOR_PAIR(LOSE_TEXT));
mvprintw(10, 25, "You lose.");
break;
case GAME_PLAYER_WIN:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "You Win!");
break;
case GAME_PUSH:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "It's a tie!");
break;
}
// Turn off bold and return to normal text color
attroff(A_BOLD);
attron(COLOR_PAIR(NORM_TEXT));
// Prompt user to play again
mvprintw(20, 0, "Play again (y/n): ");
refresh();
ch = getch();
} while(ch != 'n');
// Close out 'PDCurses' and pause before exiting
endwin();
system("pause");
return 0;
}
In this program I am trying to create the game BlackJack, most of the game is in other files, but I do not receive errors in the other files. The errors come from the code inside of the Main. The errors are
undefined reference to `BlackjackGame::blackjackGame()
undefined reference to `BlackjackGame::printDeckCurses()
undefined reference to `BlackjackGame::newGame()
undefined reference to `BlackjackGame::playGameCurses()
undefined reference to `BlackjackGame::printedHandsCurses()
undefined reference to `BlackjackGame::getGameState()
With all of these errors coming from the main and these references in the header, that must be my problem, linking the two. Is that correct?
This problem is telling you that you are not linking in the BlackjackGame.o object file when you are building your executable or library.
You must fix your Makefile to link in this file in order to pass the linking stage.
Edit:
Seeing as how you are using Codeblocks for compilation, check these links out(as this error message means different things in different compilers):
undefined reference to function code blocks
Code::Blocks 10.05 Undefined reference to function
It looks like you need to add BlackJackGame.cpp to your project, based on the answer in link 2
Go to Project/Add files to add BlackJackGame source files to your Project, then re-build and that should work
Related
I have written some code with the help of Mbed framework, which is either supposed to take user input and then display sensor values or display the value after 15mins. When I try to execute this code, it is getting stuck at line 21 (display.printf("Inside loop\n");).
I am not able to understand why is it so and what is the fix for this problem so that the switch block gets executed. How to I solve this? FYI, although not important, the microcontroller I am using is STM32 bluepill (STM32F103C8T6).
#include "mbed.h"
#include "Sensor_input.h"
#include "Ticker.h"
#include "Dht11.h"
//#include "USBSerial.h"
Serial display(PA_2, PA_3, 9600);
char* a;
Dht11 DhtSensor(PA_4);
Ticker t;
Sensor_input Soil(PB_7, PB_6, 8);
float *SensorData;
void getSensorData();
int main ( void ){
uint8_t choice = 0;
display.printf("Enter 1 or 2:\n1.Greenhouse stats\n2.Return Control to System");
choice = display.putc(display.getc());
while(1){
display.printf("Inside loop\n");
wait_ms(15000);
switch(choice)
{
case 1:
display.printf("Inside case 1");
a = Soil.readTemp();
display.printf("Temperature: %f\n",DhtSensor.getCelsius());
display.printf("Humidity: %f\n",DhtSensor.getHumidity());
display.printf("Soil water content: %c\n ",*a);
break;
case 2:
/*<GreenHouse object>*/
/*Might have to proceed with timer*/
display.printf("Inside case 2");
t.attach(&getSensorData,4500);
display.printf("Temperature: %f\n",a[0]);
display.printf("Humidity: %f\n",a[1]);
display.printf("Soil water content: %c\n ",a[2]);
break;
default:
break;
}
}
}
void getSensorData(){
static float a[3];
a[0]=DhtSensor.getCelsius();
a[1]=DhtSensor.getHumidity();
a[2]=(int)Soil.readTemp();
}
Your switch statement is probably being executed, but always in the 'default' case. You can test this out by putting a print statement in the default.
When you request a char from the display, it will return the input as an ASCII-character. This means, if you enter '1' on the display, it will give you (as the ASCII table says) 0x31 (decimal 49) and not the value of 1. So you have to change your case to "case '1':" or "case 0x31:" and the equivalent for the second case.
I am using the MagStrip library (https://github.com/carlosefr/magstripelib). In my main loop I call the function which runs the code:
void magcardFunc()
{
static const byte DATA_BUFFER_LEN = 108;
static char magcard[DATA_BUFFER_LEN];
// Don't do anything if there isn't a card present
if (!card.available()) {
return;
}
// Read the card into the buffer "magcard" (as a null-terminated string)
short chars = card.read(magcard, DATA_BUFFER_LEN);
if (chars < 0) {
Serial.print("bad read");
return;
}
// Send the data to the computer if data was read
if (chars != 0) {
Serial.print(magcardstr);
}
}
The code successfully reads a magnetic card when inserted.
The problem is, I have other functions which are called from the main loop but they do not get called as the code pauses at:
if (!card.available()) {
return;
}
// Read the card into the buffer "magcard" (as a null-terminated string)
short chars = card.read(magcard, DATA_BUFFER_LEN);
If I insert a card into my mag reader (and leave it there), the program continues and the other functions are called. I want the main loop to continue and the other functions be called without having to leave a card inserted.
I have tried removing the IF statement:
if (!card.available()) {
return;
}
and I have tried reversing it
if (card.available()) {
return;
}
If I reverse it as above, the other functions run but when I do swipe a card, it is not read.
I get no compiler errors.
EDIT:
Here is an example sketch which replicates the problem. I noticed that this sketch actually did call the otherFunc when the mag card reader was unplugged. I am not sure why when it is plugged in it pauses. Maybe an interrupt is always on?
#include <MagStripe.h>
void magcardFunc();
void otherFunc();
MagStripe card;
static const byte DATA_BUFFER_LEN = 108;
static char data[DATA_BUFFER_LEN];
void setup()
{
// The card data will be sent over serial...
Serial.begin(9600);
// Initialize the library for reading track 2...
card.begin(2);
}
void loop()
{
magcardFunc();
otherFunc();
}
void magcardFunc()
{
// Don't do anything if there isn't a card present...
if (!card.available()) {
return;
}
Any help is appreciated.
What I want to do is sampling the keyboard input at a certain rate (e.g. 10-20 Hz). I use a while loop that read from stdin (I use read because I want to read asynchronously, e.i. I don't want to press enter every time) and then I have a pause before a new cycle starts to keep the sampling frequency stable.
The user press left/right arrow to give a command (to a robot). If nothing is pressed, the output is 0.
The problem is that, during the pause, the stdin buffer is written (I suppose), and so the read will return an old input. The final result is that the output is delayed. So if I press left the output immediately change to 1, but when I release it takes some seconds to return to 0. I want to remove this delay.
My aim is to sample just the more recent key pressed, in order to synchronize user input and output command without delays. Is there a way? Thank you in advance.
This is the method I'm using:
void key_reader::keyLoop()
{
char c;
bool dirty = false;
int read_flag;
// get the console in raw mode
tcgetattr(kfd, &cooked);
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
//FD_ZERO(&set); /* clear the set */
//FD_SET(kfd, &set); /* add our file descriptor to the set */
//timeout.tv_sec = 0;
//timeout.tv_usec = 10000;
if (fcntl(kfd, F_SETFL, O_NONBLOCK) == -1)
{
perror("fcntl:"); // an error accured
exit(-1);
}
puts("Reading from keyboard");
puts("---------------------------");
puts("Use arrow keys to move the turtle.");
ros::Rate r(10);
while (ros::ok())
{
read_flag = read(kfd, &c, 1);
switch (read_flag)
{
case -1:
// case -1: is empty and errono
// set EAGAIN
if (errno == EAGAIN)
{
//no input yet
direction = 0;
break;
}
else
{
perror("read:");
exit(2);
}
// case 0 means all bytes are read and EOF(end of conv.)
case 0:
//no input yet
direction = 0;
break;
default:
ROS_DEBUG("value: 0x%02X\n", c);
switch (c)
{
case KEYCODE_L:
ROS_DEBUG("LEFT");
direction = 1;
dirty = true;
break;
case KEYCODE_R:
ROS_DEBUG("RIGHT");
direction = -1;
dirty = true;
break;
}
}
continuos_input::input_command cmd;
cmd.type = "Keyboard";
cmd.command = direction;
cmd.stamp = ros::Time::now();
key_pub.publish(cmd);
r.sleep();
}
}
I feel that the issue is with your subscriber rather than publisher. I can see that you have used rate to limit the publishing rate to 10Hz. Do confirm the publishing rate using Topic Monitor in rqt. Also setting a lower queue size for the publisher might help. Can't give a more definitive answer without referring to your subscriber node.
I'm not sure exactly how to phrase what I'm trying to ask; in C++, using the stdio.h header instead of iostream, how would I make it so that if the escape key is pressed at any point, the program is terminated? Is there something I could add once at the top of the program, or would I have to add it to every loop/conditional individually? Below is my code (the sleep() function is just for a visual loading/calculating effect):
#include <stdio.h>
#include <math.h>
#include <windows.h>
void repeat();
void quadratic()
{
double a, b, c;
double ans[2];
printf("-Arrange your equation in the form aX^2+bX+c \n-Enter the value of a: ");
scanf("%lf", &a);
printf("-Enter the value of b: ");
scanf("%lf", &b);
printf("-Enter the value of c: ");
scanf("%lf", &c);
double radical=((b*b)-(4*a*c));
double root=sqrt(radical);
double negB=(-1)*b;
double denominator=2*a;
if(denominator==0)
{
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf("\nError: Denominator must be non-zero.\n \n \n");
}
else if(radical==0)
{
ans[0]=negB/denominator;
printf("Both roots are equal: both values are X=%lf\n \n \n", ans[0]);
}
else if(radical<0)
{
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
double r,i;
radical*=-1;
r=negB/(2*a);
i=sqrt(radical)/(2*a);
printf("\nBoth roots are imaginary numbers.\n");
printf("Non-real answer(s): X=%lf+%lfi X=%lf-%lfi\n \n \n",r,i,r,i);
}
else
{
ans[0]=(negB+root)/denominator;
ans[1]=(negB-root)/denominator;
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf("\nX=%lf, X=%lf\n \n", ans[0], ans[1]);
}
repeat();
}
void repeat()
{
quadratic();
}
int main(void)
{
quadratic();
return 0;
}
The terminal used in stdio is likely to be line buffered
(cooked). If it is, checking for the escape key through scanf will
not work.
SEE THESE URLS:
Capture characters from standard input without waiting for enter to be pressed
scanf not reading input
CURSES or NCURSES will detect the escape key (ASCII character 27), depending
on the terminal type.
This code can be used in WINDOWS to check for the ESCAPE
key.
#include <conio.h>
#include <ctype.h>
int ch;
_cputs( "Type 'Y' when finished typing keys: " );
do
{
ch = _getch();
ch = toupper( ch );
if (ch != 27) {
_cputs( "CHARACTER: " );
_putch( ch );
_putch( '\r' ); // Carriage return
_putch( '\n' ); // Line feed
}
} while( ch != 27 );
Is there something I could add once at the top of the program, or would I have to add it to every loop/conditional individually?
I think you can add something once, and use it to catch key stroke events easily throughout your program.
Following is a code snippet showing a function I have used to handle key stroke events in a console application. It uses GetAsyncKeyState(). Included is a section that shows how to capture a CTRL key, and how you can do something once you see it. (the snippet shown shows how I capture a <ctrl><shift><h> key sequence to display a help menu for using this particular routine.
Note: In the description, delay_x(float delay) is simply a custom, non-blocking sleep, or delay function that includes a call to the following snippet. It is called from within the main program loop while(1){...} . Exiting the program is provided in one of the keystroke combinations: <CTRL><SHIFT><K>
Code snippet:
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// SetAppState() is called continuously from within delay_x()
// to capture keystroke combinations as they occur asynchronously
// with this application, Keystroke combinations are listed below
//
// Note: GetAsyncKeyState() can maintian information regarding the
// state of a key instantaineously by use the MSB,
// and recently by using the LSB.
//
// For this application
// only instantaineous information will be kept, minimizing
// conflicts with other keyboard shortcut definitions
// defined by other applications that may be running
// simultaineously.
//
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
void SetAppState(void)
{
short state=0;
short state1=0;
state = GetAsyncKeyState(VK_CONTROL);
if (0x80000000 & state) //check instantaineous state of key
{
state = GetAsyncKeyState(VK_SHIFT);
if (0x80000000 & state) //check instantaineous state of key
{
state = GetAsyncKeyState('h');
state1 = GetAsyncKeyState('H');
if ((0x80000000 & state) ||
(0x80000000 & state1))
{ sprintf(gTempBuf, "Usage - keystrokes to access and control the PaAutoStartSlot application:\n\n"
"<CTRL><SHIFT> H (H)elp - \n"
"<CTRL><SHIFT> V o(V)erride - \n"
"<CTRL><SHIFT> S (S)tatus - \n"
"<CTRL><SHIFT> K (K)ill - \n"
"<CTRL><SHIFT> N (N)o - \n"
"<CTRL><SHIFT> I (I)Inside - \n"
"<CTRL><SHIFT> O (O)Outside- \n"
"\nSee log file at this location for runtime errors: \n\n%s", LOGFILE);
MessagePopup("Usage Menu",gTempBuf);
}
///// ... more code ...
End of snippet
Edit - Answer to questions in comments how to call GetAsyncKeyState()
There are many ways you could call GetAsyncKeyState() at the same time other stuff is going on. Threads are a good way. You can also do it all in line using a while()/switch(){} combination. Here is a very simple example of how you could do this (in pseudo code)
int gRunning = 1;
int state = 1;
int main(void)
{
//create variables, initialize stuff
while(gRunning)//this is your main program loop
{
delay_x(1.0);//check for keystrokes
switch(state) {
case 1:
//do some stuff here
//and experiment with values passed to delay_x(n)
delay_x(10000);//check for keystrokes
state++;
break;
case 2:
//do some different stuff here
delay_x(10000);//check for keystrokes
state++;
break;
... Add as many cases as you need for your program.
case n://last case, set execution flow to top
//do some more different stuff here
delay_x(10000);//check for keystrokes
state = 1;//loop back to top
break;
}
}
return 0;
}
void delay_x (float delay)
{
static clock_t time1;
static clock_t time2; clock();
time1 = clock();
time2 = clock();
while ((time2 - time1) < delay)
{
time2 = clock();
SetAppState(); //see partial definition in my original answer above.
}
}
Note: Using this method, you can have as many, or as few, cases as you need, the important thing is to keep a steady flow of calls to GetAsyncKeyState(). This does that via the call to delay_x().
Note2: Here is the segment (added to above definition) that will cause your program to exit:
state = GetAsyncKeyState('k');
state1 = GetAsyncKeyState('K');
if ((0x80000000 & state) ||
(0x80000000 & state1))
{
printf("Kill Program");
gRunning = FALSE;
}
In my project written in C++, I have FMOD currently working from my main.cpp. To help organize my engine I want to move my sound code to it's own translation unit. For some reason when I try to run my sound code from within my class, it doesn't play any sound. I'm not sure if it is because of incorrect assignment of the value or if there is a bigger issue that I don't know about. This is my class implementation:
//Sound.h
#ifndef SOUND_H
#define SOUND_H
#include <iostream>
#include "inc\fmod.hpp"
#include "inc\fmod_errors.h"
class Sound
{
public:
Sound(void);
~Sound(void);
void Init();
void FMODErrorCheck(FMOD_RESULT res);
void PlaySound();
void ResumeSound();
void PauseSound();
void Update();
private:
//sound
FMOD::System *sys;
FMOD_RESULT result;
size_t version; //this is just an unsigned int
FMOD_SPEAKERMODE speakerMode;
int numDrivers;
FMOD_CAPS caps;
char name[256];
FMOD::Sound *sound;
FMOD::Channel *channel;
bool quitFlag;
};
#endif
//Sound.cpp
#include "Sound.h"
Sound::Sound(void)
{
Init();
}
Sound::~Sound(void)
{
FMODErrorCheck(sound->release());
FMODErrorCheck(sys->release());
}
void Sound::Init()
{
// Create FMOD interface object
result = FMOD::System_Create(&sys);
FMODErrorCheck(result);
// Check version
result = sys->getVersion(&version);
FMODErrorCheck(result);
if(version < FMOD_VERSION)
{
std::cout << "Error! You are using an old version of FMOD " << version << ". This program requires " << FMOD_VERSION << std::endl;
exit(0);
}
// Get number of sound cards
result = sys->getNumDrivers(&numDrivers);
FMODErrorCheck(result);
// No sound cards (disable sound)
if(numDrivers == 0)
{
result = sys->setOutput(FMOD_OUTPUTTYPE_NOSOUND);
FMODErrorCheck(result);
}
// At least one sound card
else
{
// Get the capabilities of the default (0) sound card
result = sys->getDriverCaps(0, &caps, 0, &speakerMode);
FMODErrorCheck(result);
// Set the speaker mode to match that in Control Panel
result = sys->setSpeakerMode(speakerMode);
FMODErrorCheck(result);
// Increase buffer size if user has Acceleration slider set to off
if(caps & FMOD_CAPS_HARDWARE_EMULATED)
{
result = sys->setDSPBufferSize(1024, 10);
FMODErrorCheck(result);
}
// Get name of driver
result = sys->getDriverInfo(0, name, 256, 0);
FMODErrorCheck(result);
// SigmaTel sound devices crackle for some reason if the format is PCM 16-bit.
// PCM floating point output seems to solve it.
if(strstr(name, "SigmaTel"))
{
result = sys->setSoftwareFormat(48000, FMOD_SOUND_FORMAT_PCMFLOAT, 0, 0, FMOD_DSP_RESAMPLER_LINEAR);
FMODErrorCheck(result);
}
}
// Initialise FMOD
result = sys->init(100, FMOD_INIT_NORMAL, 0);
// If the selected speaker mode isn't supported by this sound card, switch it back to stereo
if(result == FMOD_ERR_OUTPUT_CREATEBUFFER)
{
result = sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
FMODErrorCheck(result);
result = sys->init(100, FMOD_INIT_NORMAL, 0);
}
FMODErrorCheck(result);
// Open music as a stream
//FMOD::Sound *song1, *song2, *effect;
//result = sys->createStream("Effect.mp3", FMOD_DEFAULT, 0, &sound);
//FMODErrorCheck(result);
result = sys->createSound("Effect.mp3", FMOD_DEFAULT, 0, &sound);
FMODErrorCheck(result);
// Assign each song to a channel and start them paused
//result = sys->playSound(FMOD_CHANNEL_FREE, sound, true, &channel);
//FMODErrorCheck(result);
// Songs should repeat forever
channel->setLoopCount(-1);
}
void Sound::FMODErrorCheck(FMOD_RESULT res)
{
if(res != FMOD_OK)
{
std::cout << "FMOD ERROR: (" << res << ") - " << FMOD_ErrorString(res) << std::endl;
//quitFlag = true;
}
}
void Sound::PlaySound()
{
sys->playSound(FMOD_CHANNEL_FREE, sound, false, 0);
}
void Sound::ResumeSound()
{
channel->setPaused(false);
}
void Sound::PauseSound()
{
channel->setPaused(true);
}
void Sound::Update()
{
sys->update();
}
//Main.cpp
Sound sound;
// Initialization routine.
void setup(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
sound = &Sound();
}
//------------------------------------------------------------ OnInit()
//
void OnIdle()
{
if(IsKeyPressed(KEY_ESCAPE))
{
exit(EXIT_SUCCESS);
}
if(IsKeyPressed('1'))
{
sound->PlaySound();
}
sound->Update();
// redraw the screen
glutPostRedisplay();
}
Currently it is giving me 2 errors:
Unhandled exception at 0x0F74465A (fmodex.dll) in TestOpenGL.exe: 0xC0000005: Access violation reading location 0x062C5040
and
FMOD error! (36) An invalid object handle was used
Any idea why it isn't working? Any idea how I solve these issues?
From your last comment and looking at your code I see a problem. You have created a pointer by FMOD::System *sys; but this pointer is not initialized to any instance of FMOD::System that is, there should be something like sys = new FMOD::System or sys = new FMOD::System(/* whatever argument you must supply to it's constructor */); somewhere in your code but right before you try to access anything related to FMOD::System object. This is most probably the reason for your program crash. Also since sys is a pointer to FMOD::System there's another problem at line containing result = FMOD::System_Create(&sys); you are passing a pointer by reference. I suggest you read a couple of articles about pointers in C and C++ and also some more about object creation and destruction in object oriented programming languages.
I was able to get help with the issue. I was initializing my sound variable incorrectly.
sound = &Sound();
Should actually be:
sound = new Sound();