stop the video in mfc with thread - c++

I read a program with MFC visual studio 2013 that load a video from file and the we can track a object. I set a stop button. it work for loading video and stop the loading. but I can't stop the running video in tracking. it's my gui program:
bool Cgui2Dlg::getImageFilePath()
{
m_inputVideo.GetWindowTextW(file_path_);
if (file_path_.GetLength() > 0)
return true;
else
{
AfxMessageBox(L"Please, select one video file or image sequence folder!");
//return false;
}
}
Size Cgui2Dlg::getWindowSizeByID(int IDC) {
return getWindowSize(GetDlgItem(IDC)->m_hWnd);
}
void Cgui2Dlg::openfile()
{
getImageFilePath();
CT2CA filePath2(file_path_);
string fp(filePath2);
VideoCapture capture(fp);
Mat frame;
if (!capture.isOpened())
throw "Error when reading steam_avi";
run_ = true;
///// show on the window frame
while (run_) {
capture >> frame;
if (frame.empty())
{
run_ = false;
break;
}
auto input_size = getWindowSizeByID(IDC_Video_Original);
view1_.create(input_size, CV_8UC3);
rect1_ = ZoomWithSpect(frame, view1_, cofi_, Scalar::all(0));
matToWinControl(view1_, &m_original_frame);
///
SendMessage(WM_MSG, 0, 0);
waitKey(400); // waits to display frame
this_thread::sleep_for(std::chrono::milliseconds(interval_));
}
if (run_) {
run_ = false;
OnBnClickedStop();
}
}
void Cgui2Dlg::OnBnClickedButton1()
{
//CFileDialog dlg(TRUE, _T("*.avi"), NULL,
//OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY, _T("video files (*.bmp; *.jpg; *.avi) |*.bmp;*.jpg;|All Files (*.*)|*.*||"), NULL);
//dlg.m_ofn.lpstrTitle = _T("Open Video");
//CString file_path_ = dlg.GetPathName(); // the selected filename // This is image pointer
setButtonsState(EcvButtonState::BUTTON1);
UpdateData(TRUE);
interval_ = 25;
std::thread t(&Cgui2Dlg::openfile, this);
t.detach();
}
void Cgui2Dlg::OnBnClickedButton3()
{
setButtonsState(EcvButtonState::BUTTON3);
UpdateData(TRUE);
interval_ = 200;
std::thread tt(&Cgui2Dlg::trackingfile, this);
tt.detach();
}
void Cgui2Dlg::trackingfile()
{
getImageFilePath();
CT2CA filePath2(file_path_);
string fp(filePath2);
run_ = true;
//VideoCapture capture(fp);
//Mat frame;
//capture >> frame;
setButtonsState(EcvButtonState::BUTTON3);
//check radio botton for choosing one algorithm
int checkradio = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO2);
switch (checkradio)
{
case IDC_RADIO1:
{
auto input_size = getWindowSizeByID(IDC_Video_Track);
view2_.create(input_size, CV_8UC3);
//rect1_ = ZoomWithSpect(frame, view1_, cofi_, Scalar::all(0));
//matToWinControl(view2_, &m_Track_frame);
//FCAMshift CAM(fp);//,run_);
//CAM.track(fp); //, run_);
FRunTracker CAM(fp);//,run_);
CAM.track(fp); //, run_);
//CAM.showresult();
///
//waitKey(40); // waits to display frame
//this_thread::sleep_for(std::chrono::milliseconds(interval_));
}
if (run_) {
run_ = false;
OnBnClickedStop();
}
break;
case IDC_RADIO2:
{
auto input_size = getWindowSizeByID(IDC_Video_Track);
view2_.create(input_size, CV_8UC3);
FCAMshift CAM(fp);//,run_);
CAM.track(fp); //, run_);
///
//waitKey(40); // waits to display frame
this_thread::sleep_for(std::chrono::milliseconds(interval_));
}
if (run_) {
run_ = false;
OnBnClickedStop();
}
break;
}
}
void Cgui2Dlg::setButtonsState(const EcvButtonState& state) {
switch (state)
{
case EcvButtonState::BUTTON1:
m_stop_btn.EnableWindow(TRUE);
m_Track_btn.EnableWindow(TRUE);
m_load_btn.EnableWindow(TRUE);
break;
case EcvButtonState::BUTTON3:
m_stop_btn.EnableWindow(TRUE);
m_Track_btn.EnableWindow(TRUE);
m_load_btn.EnableWindow(TRUE);
break;
case EcvButtonState::stop:
m_stop_btn.EnableWindow(TRUE);
m_Track_btn.EnableWindow(TRUE);
m_load_btn.EnableWindow(TRUE);
break;
}
//UpdateData(FALSE);
}
void Cgui2Dlg::OnBnClickedRadio1()
{
// TODO: Add your control notification handler code here
}
void Cgui2Dlg::OnBnClickedButton2()
{
run_ = false;
setButtonsState(EcvButtonState::stop);
}
void Cgui2Dlg::OnBnClickedRadio2()
{
// TODO: Add your control notification handler code here
}
void Cgui2Dlg::OnStnClickedVideoTrack()
{
}
void Cgui2Dlg::OnStnClickedVideoOriginal()
{
// TODO: Add your control notification handler code here
}
LRESULT Cgui2Dlg::onUpdateUI(WPARAM wparam, LPARAM lparam) {
matToWinControl(view1_, &m_original_frame);
//matToWinControl(view2_, &m_result_frame);
return 0;
}
void Cgui2Dlg::OnBnClickedStop()
{
run_ = false;
interval_ = 2000;
UpdateData(TRUE);
setButtonsState(EcvButtonState::stop);
}
and this is my tracking code:
/************************************************************************
* File: RunTracker.cpp
************************************************************************/
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "CompressiveTracker.h"
#include "RunTracker.h"
using namespace cv;
using namespace std;
FRunTracker::FRunTracker(string x) //,bool y)
{
fp = x;
//run_= y;
}
Mat src2, img2, ROI2, image2;
Rect box(0, 0, 0, 0);
Point Pi1(0, 0);
Point Pi2(0, 0);
const char* winName2 = "compressive tracking demo";
bool click = false;
void showimage22() {
img2 = src2.clone();
//checkBoundary();
if (box.width>0 && box.height>0) {
ROI2 = src2(box);
//imshow("cropped", ROI2);
}
rectangle(img2, box, Scalar(0, 255, 0), 1, 8, 0);
imshow(winName2, img2);
}
void onMouse2(int event, int x, int y, int f, void*) {
switch (event) {
case CV_EVENT_LBUTTONDOWN:
click = true;
Pi1.x = x;
Pi1.y = y;
Pi2.x = x;
Pi2.y = y;
break;
case CV_EVENT_LBUTTONUP:
Pi2.x = x;
Pi2.y = y;
click = false;
//trackObject = -1;
break;
case CV_EVENT_MOUSEMOVE:
if (click) {
Pi2.x = x;
Pi2.y = y;
}
break;
default: break;
}
if (click) {
if (Pi1.x>Pi2.x) {
box.x = Pi2.x;
box.width = Pi1.x - Pi2.x;
}
else {
box.x = Pi1.x;
box.width = Pi2.x - Pi1.x;
}
if (Pi1.y>Pi2.y) {
box.y = Pi2.y;
box.height = Pi1.y - Pi2.y;
}
else {
box.y = Pi1.y;
box.height = Pi2.y - Pi1.y;
}
}
showimage22();
}
void FRunTracker::track(string fp)
{
VideoCapture input_video(fp);
input_video >> src2;
namedWindow(winName2, WINDOW_NORMAL);
setMouseCallback(winName2, onMouse2, NULL);
imshow(winName2, src2);
waitKey(0);
// CT framework
CompressiveTracker ct;
Mat grayimg2;
cvtColor(src2, grayimg2, CV_RGB2GRAY);
ct.init(grayimg2, box);
char strFrame[20];
//FILE* resultStream;
//resultStream = fopen("TrackingResults.txt", "w");
//fprintf(resultStream, "%i %i %i %i\n", (int)box.x, (int)box.y, (int)box.width, (int)box.height);
while (1) {
input_video >> src2;
if (src2.empty())
break;
cvtColor(src2, grayimg2, CV_RGB2GRAY);
ct.processFrame(grayimg2, box);// Process frame
rectangle(src2, box, Scalar(200, 0, 0), 2);// Draw rectangle
//fprintf(resultStream, "%i %i %i %i\n", (int)box.x, (int)box.y, (int)box.width, (int)box.height);
//sprintf(strFrame, "#%d ", i);
//putText(src2, strFrame, cvPoint(0, 20), 2, 1, CV_RGB(25, 200, 25));
imshow("Compressive Tracking", src2);// Display
waitKey(1);
}
//fclose(resultStream);
}
FRunTracker::~FRunTracker()
{
}

Your track function in FRunTracker class has a forever loop. Instead, put a boolean condition to check (like while(isRunning), etc) in the loop, and make the functions of your Cgui2Dlg class to change this condition. Don't forget to protect it with mutexes.

Related

Speed up Serial Communication to Arduino c++

First time posting so I apologize.
I am trying to make a ball and beam project using a camera as the sensor and trying to get the serial communication to work.
The code currently works if I set the waitKey to (1000) but that is not fast enough for the project to work and was wanting some help speeding it up.
the output tends to only read the first string that I send then ignores anything else until the program is paused by holding the slider on the output screen for a couple of seconds.
I have tried to use a faster baud rate but the problem still persists.
This is the C++ code
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <vector>
#include "SerialPort.h"
#include <string.h>
#include <stdlib.h>
#include <string>
using namespace cv;
using namespace std;
//global for the image processing
Mat imgGray, imgCanny, imgBlur, imgDil;
Mat imgHSV, mask;
//int hmin = 53, smin = 102, vmin = 35; //green
//int hmax = 93, smax = 253, vmax = 169;
/* //green ball
int hmin = 41, smin = 93, vmin = 49;
int hmax = 93, smax = 243, vmax = 192; //p
*/
//purple ball
int hmin = 117, smin = 26, vmin = 63;
int hmax = 179, smax = 113, vmax = 169;
//global for the serial
char output[MAX_DATA_LENGTH];
char incomingData[MAX_DATA_LENGTH];
char port[] = " ////.//COM3";
int PosX;
// this will convert the output into the correct string because the "stoi" function doesn't work as intended
String intToString(int I)
{
String J;
if (I ==80 )
{
J ="80" ;
}
else if (I ==81 )
{
J = "81";
}
else if (I ==82 )
{
J = "82";
}
else if (I == 83)
{
J = "83";
}
else if (I ==84 )
{
J = "84";
}
else if (I == 85)
{
J = "85";
}
else if (I == 86)
{
J = "86";
}
else if (I ==87 )
{
J = "87";
}
else if (I ==88 )
{
J = "88";
}
else if (I ==89 )
{
J = "89";
}
else if (I ==90 )
{
J = "90";
}
else if (I ==91 )
{
J = "91";
}
else if (I == 92)
{
J = "92";
}
else if (I == 93)
{
J = "93";
}
else if (I == 94)
{
J = "94";
}
else if (I == 95)
{
J = "95";
}
else if (I == 96)
{
J = "96";
}
else if (I == 97)
{
J = "97";
}
else if (I == 98)
{
J = "98";
}
else if (I == 99)
{
J = "99";
}
else if (I == 100)
{
J = "100";
}
else if (I > 100)
{
J = "100";
}
else if (I < 80)
{
J = "80";
}
return (J);
}
// calculation to make the output between outMin and outMax taken from arduino website and transformed into calculation
int map(int x,int in_min, int in_max, int out_min, int out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// just the P of the PID controller
int PCONTROL(int xPos)
{
int P, Kp = 1, Proportional, error;
error = 240 - xPos; //240 is the middle of the camera and xPos is the current position
Proportional = Kp * error;
P = map(Proportional,-240,240,80,100); // calculation to make the output between 80&100 taken from arduino website and transformed into calculation
return (P);
}
// this is the function made to be the PID contol loop
int PIDCONTROL(int xPos)
{
int error_prior = 0, error, integral_prior = 0, Kp, Ki,
Kd, integral, derivative, proportional, outPut, PID_total, period = 50;
// I have set the Kp and Ki to zero when testing the P contoller
Kp = 1;
Ki = 0;
Kd = 0;
//error = (img.rows/2) - xPos; // 640 is the desired Value
error = (480/2) - xPos; // this is if I got the wrong part of the resolution
proportional = Kp * error;
integral = integral_prior + error;
derivative = (error - error_prior)/period;
PID_total = ((Kp * proportional) + (Ki * integral) + (Kd * derivative)); //this is the calculation for the PID controller
error_prior = error;
integral_prior = integral;
outPut = map(PID_total,-240,240,80,100);
cout << "output = " << outPut << endl;
return (outPut);
}
// this code will get the x and y values of the ball, draw a box around it and lable it
// this is what i need for the project
void getContours(Mat imgDil, Mat img)
{
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
string objectType;
string x, y;
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//drawContours(img, contours, -1, Scalar(255, 0, 255), 2); //draws everything
for (int i = 0; i < contours.size(); i++)
{
int area = contourArea(contours[i]);
//cout << area << endl; //displays the area of the ball
vector<vector<Point>>conPoly(contours.size());
vector<Rect>boundRect(contours.size());
if (area > 1000 && area < 5000) //filters out smaller and larger objects
{
float peri = arcLength(contours[i], true);
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
drawContours(img, contours, i, Scalar(255, 0, 255), 2); // with a filter applyed
boundRect[i] = boundingRect(conPoly[i]); //draw a box around the ball
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
putText(img, "Ball", { boundRect[i].x,boundRect[i].y - 5 }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 2); //display text above the box saying ball
//cout << "X = " << boundRect[i].x << endl;// << "Y = " << boundRect[i].y << endl; //print the x and y values
PosX = boundRect[i].x;
}
}
}
void main()
{
VideoCapture cap(0);
Mat img;
string xposSrg;
//int TempData;
Mat Reimg, Reimg2;
string data;
//char TrackbarName[50];
const int alpha_slider_max = 100;
int xPos, preData = 0;
createTrackbar("Hue Min", "Trackbars", &hmin, 179);
SerialPort arduino(port);
if (arduino.isConnected()) {
cout << "Connection" << endl << endl;
}
else {
cout << "Error Connecting" << endl << endl;
}
while (arduino.isConnected()) // no point do it if not connected
{
cap.read(img);
//prepocessing
Mat imgGray, imgCanny, imgBlur,Reimg,Reimg2;
//cvtColor(img, imgGray, COLOR_BGR2GRAY);
//pyrDown(img, Reimg, Size(img.cols / 2, img.rows / 2)); // downsample for faster processing
cvtColor(img, imgHSV, COLOR_BGR2HSV); // get the big wierd colours
Scalar lower(hmin, smin, vmin);
Scalar upper(hmax, smax, vmax);
inRange(imgHSV, lower, upper, mask); // make only the green ball visible
GaussianBlur(mask, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDil, kernel);
getContours(imgDil, img); // use only the visible green to the contours fuction
//displaying processes used in debuging
/*
imshow("Ball", img);
imshow("Gray", imgGray);
imshow("Blur", imgBlur);
imshow("Canny", imgCanny);
imshow("Dil", imgDil);
*/
//cout << img.rows << endl;;
//display the orginal img with the contors drawn on to it
imshow("img", img);
// do the PID controller calculations
preData = PIDCONTROL(PosX);
//send the data to the arduino
data = intToString(preData);
cout << "Data =" << data << "." << endl;
char* charArray = new char[data.size() +1];
copy(data.begin(), data.end(), charArray);
charArray[data.size()] = '\n';
arduino.writeSerialPort(charArray, MAX_DATA_LENGTH);
arduino.readSerialPort(output, MAX_DATA_LENGTH);
cout << " Arduino >> " << output << "." << endl;
delete[] charArray;
waitKey(1); //work as fast as possible
// turns out this library can only output every second
}
}
Here is the Arduino code
#include <Servo.h>
Servo myservo;
String info;
void setup() {
Serial.begin(9600);
//pinMode(led,OUTPUT);
myservo.attach(9); // attaches the servo on pin 9 to the servo object
myservo.write(90); // set the starting position to 90 degrees(flat)
}
// this is the one for on using 10 degrees
void loop()
{
if (Serial.available() >0)
{
info = Serial.readStringUntil('\n');
}
Serial.flush();
//check what the imput is then change the servo level (every 10 degrees) and output the level
// checking the string other ways caused errors dunno why
if (info.equals("0"))
{
Serial.println("Not Receiving Data");
myservo.write(90);
}
else if (info.equals("80"))
{
Serial.println("80");
myservo.write(80);
}
else if (info.equals("81"))
{
Serial.println("81");
myservo.write(81);
}
else if (info.equals("82"))
{
Serial.println("82");
myservo.write(82);
}
else if (info.equals("83"))
{
Serial.println("83");
myservo.write(83);
}
else if (info.equals("84"))
{
Serial.println("84");
myservo.write(84);
}
else if (info.equals("85"))
{
Serial.println("85");
myservo.write(85);
}
else if (info.equals("86"))
{
Serial.println("86");
myservo.write(86);
}
else if (info.equals("87"))
{
Serial.println("87");
myservo.write(87);
}
else if (info.equals("88"))
{
Serial.println("88");
myservo.write(88);
}
else if (info.equals("89"))
{
Serial.println("89");
myservo.write(89);
}
else if (info.equals("90"))
{
Serial.println("90");
myservo.write(90);
}
else if (info.equals("91"))
{
Serial.println("91");
myservo.write(91);
}
else if (info.equals("92"))
{
Serial.println("92");
myservo.write(92);
}
else if (info.equals("93"))
{
Serial.println("93");
myservo.write(93);
}
else if (info.equals("94"))
{
Serial.println("94");
myservo.write(94);
}
else if (info.equals("95"))
{
Serial.println("95");
myservo.write(95);
}
else if (info.equals("96"))
{
Serial.println("96");
myservo.write(96);
}
else if (info.equals("97"))
{
Serial.println("97");
myservo.write(97);
}
else if (info.equals("98"))
{
Serial.println("98");
myservo.write(98);
}
else if (info.equals("99"))
{
Serial.println("99");
myservo.write(99);
}
else if (info.equals("100"))
{
Serial.println("100");
myservo.write(100);
}
}
Here is the headers file taken from https://www.youtube.com/watch?v=8BWjyZxGr5o
#ifndef SERIALPORT_H
#define SERIALPORT_H
#define ARDUINO_WAIT_TIME 2000
#define MAX_DATA_LENGTH 256
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
class SerialPort
{
private:
HANDLE handler;
bool connected;
COMSTAT status;
DWORD errors;
public:
SerialPort(char* portName);
~SerialPort();
int readSerialPort(char* buffer, unsigned int buf_size);
bool writeSerialPort(char* buffer, unsigned int buf_size);
bool isConnected();
};
#endif // SERIALPORT_H
#include "SerialPort.h"
SerialPort::SerialPort(char* portName)
{
this->connected = false;
this->handler = CreateFileA(static_cast<LPCSTR>(portName),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (this->handler == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
printf("ERROR: Handle was not attached. Reason: %s not available\n", portName);
}
else
{
printf("ERROR!!!");
}
}
else {
DCB dcbSerialParameters = { 0 };
if (!GetCommState(this->handler, &dcbSerialParameters)) {
printf("failed to get current serial parameters");
}
else {
dcbSerialParameters.BaudRate = CBR_9600;
dcbSerialParameters.ByteSize = 8;
dcbSerialParameters.StopBits = ONESTOPBIT;
dcbSerialParameters.Parity = NOPARITY;
dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;
if (!SetCommState(handler, &dcbSerialParameters))
{
printf("ALERT: could not set Serial port parameters\n");
}
else {
this->connected = true;
PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
Sleep(ARDUINO_WAIT_TIME);
}
}
}
}
SerialPort::~SerialPort()
{
if (this->connected) {
this->connected = false;
CloseHandle(this->handler);
}
}
int SerialPort::readSerialPort(char* buffer, unsigned int buf_size)
{
DWORD bytesRead;
unsigned int toRead = 0;
ClearCommError(this->handler, &this->errors, &this->status);
if (this->status.cbInQue > 0) {
if (this->status.cbInQue > buf_size) {
toRead = buf_size;
}
else toRead = this->status.cbInQue;
}
if (ReadFile(this->handler, buffer, toRead, &bytesRead, NULL)) return bytesRead;
return 0;
}
bool SerialPort::writeSerialPort(char* buffer, unsigned int buf_size)
{
DWORD bytesSend;
if (!WriteFile(this->handler, (void*)buffer, buf_size, &bytesSend, 0)) {
ClearCommError(this->handler, &this->errors, &this->status);
return false;
}
else return true;
}
bool SerialPort::isConnected()
{
return this->connected;
}

do i have to lock a vectors that i just enumerate or i only do it when i change it?

I change and get of vector elements.
So as the thread is running, i use a mutex to change the vector's elements.
but if i just want to enumerate the vector's elements do i still have to lock my vector?
Here is the code:
#include <napi.h>
#include "facedetect.h"
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <thread>
using namespace std;
using namespace cv;
std::mutex facesMutex;
string cascadeName = "/usr/local/share/opencv4/haarcascades/haarcascade_frontalface_alt.xml";;
bool running = true;
vector<Rect> faces;
class FaceDetectWorker : public Napi::AsyncWorker {
public:
FaceDetectWorker(Napi::Function& callback, string url)
: Napi::AsyncWorker(callback), url(url) {
}
~FaceDetectWorker() {
}
vector<Rect> detectAndDraw( Mat& img, CascadeClassifier& cascade)
{
double t = 0;
vector<Rect> faces;
Mat gray, smallImg;
cvtColor( img, gray, COLOR_BGR2GRAY );
//double scale = 1;
// scale = 1, fx = 1 / scale
double fx = 1;
resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
equalizeHist( smallImg, smallImg );
t = (double)getTickCount();
cascade.detectMultiScale( smallImg, faces,
1.1, 2, 0
//|CASCADE_FIND_BIGGEST_OBJECT
//|CASCADE_DO_ROUGH_SEARCH
|CASCADE_SCALE_IMAGE,
Size(30, 30) );
t = (double)getTickCount() - t;
printf( "detection time = %g ms\n", t*1000/getTickFrequency());
return faces;
}
// Executed inside the worker-thread.
// It is not safe to access JS engine data structure
// here, so everything we need for input and output
// should go on `this`.
void Execute () {
Mat frame, image;
VideoCapture capture;
CascadeClassifier cascade;
if (!cascade.load(samples::findFile(cascadeName)))
{
Napi::AsyncWorker::SetError("ERROR: Could not load classifier cascade");
return;
}
if (!capture.open(url))
{
Napi::AsyncWorker::SetError("ERROR: Error opening video stream " + url);
return;
}
if( capture.isOpened() )
{
cout << "Video capturing has been started ..." << endl;
try {
while(running) {
capture >> frame;
if( frame.empty()) {
continue;
}
Mat frame1 = frame.clone();
vector<Rect> facesResult = detectAndDraw( frame1, cascade);
facesMutex.lock();
faces = facesResult;
facesMutex.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(30));
}
} catch (std::exception &e) {
facesMutex.unlock();
Napi::AsyncWorker::SetError(e.what());
}
}
}
// Executed when the async work is complete
// this function will be run inside the main event loop
// so it is safe to use JS engine data again
void OnOK() {
Napi::HandleScope scope(Env());
Callback().Call({Env().Undefined(), Env().Undefined()});
}
private:
string url;
};
// Asynchronous access to the `Estimate()` function
Napi::Value FaceDetect(const Napi::CallbackInfo& info) {
Napi::String url = info[0].As<Napi::String>().ToString();
Napi::Function callback = info[1].As<Napi::Function>();
FaceDetectWorker* faceDetectWorker = new FaceDetectWorker(callback, url);
faceDetectWorker->Queue();
return info.Env().Undefined();
}
Napi::Array FaceDetectGet(const Napi::CallbackInfo &info) {
Napi::Array faceArray = Napi::Array::New(info.Env(), faces.size());
facesMutex.lock();
for(int i = 0; i < faces.size(); i++) {
Rect rect = faces[i];
Napi::Object obj = Napi::Object::New(info.Env());
obj.Set("x", rect.x);
obj.Set("y", rect.y);
obj.Set("width", rect.width);
obj.Set("height", rect.height);
faceArray[i] = obj;
}
facesMutex.unlock();
return faceArray;
}
So the questionsis, if i use the FaceDetectGet, which only enumerating the vector, should I lock and unlock it?
Actually the solution was to use a shared mutex.
The code looks like this:
#include <napi.h>
#include "facedetect.h"
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <thread>
#include <mutex> // For std::unique_lock
#include <shared_mutex>
// https://stackoverflow.com/questions/55313194/do-i-have-to-lock-a-vectors-that-i-just-enumerate-or-i-only-do-it-when-i-change?noredirect=1#comment97357425_55313194
using namespace std;
using namespace cv;
std::shared_mutex _facesMutex;
string cascadeName = "/usr/local/share/opencv4/haarcascades/haarcascade_frontalface_alt.xml";;
bool running = true;
vector<Rect> faces;
class FaceDetectWorker : public Napi::AsyncWorker {
public:
FaceDetectWorker(Napi::Function& callback, string url, int skip, int sleep)
: Napi::AsyncWorker(callback), url(url), skip(skip), sleep(sleep) {
}
~FaceDetectWorker() {
}
vector<Rect> detectFaces(Mat &img, CascadeClassifier &cascade)
{
double t = 0;
vector<Rect> faces;
Mat gray, smallImg;
cvtColor( img, gray, COLOR_BGR2GRAY );
//double scale = 1;
// scale = 1, fx = 1 / scale
double fx = 1;
//resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
//equalizeHist( gray, smallImg );
//t = (double)getTickCount();
cascade.detectMultiScale( gray, faces,
1.1, 2, 0
//|CASCADE_FIND_BIGGEST_OBJECT
//|CASCADE_DO_ROUGH_SEARCH
|CASCADE_SCALE_IMAGE,
Size(30, 30) );
//t = (double)getTickCount() - t;
//printf( "detection time = %g ms\n", t*1000/getTickFrequency());
return faces;
}
// Executed inside the worker-thread.
// It is not safe to access JS engine data structure
// here, so everything we need for input and output
// should go on `this`.
void Execute () {
running = true;
Mat frame, image;
VideoCapture capture;
CascadeClassifier cascade;
if (!cascade.load(samples::findFile(cascadeName)))
{
Napi::AsyncWorker::SetError("ERROR: Could not load classifier cascade");
return;
}
if (!capture.open(url))
{
Napi::AsyncWorker::SetError("ERROR: Error opening video stream " + url);
return;
}
if( capture.isOpened() )
{
cout << "Video capturing has been started ..." << endl;
try {
int skipCount = 0;
while(running) {
//capture.read(frame);
capture >> frame;
if( frame.empty()) {
continue;
}
skipCount++;
//cout<< "sleep " << sleep << " skip " << skip << endl;
if (skipCount >= skip) {
//cout<< "calculation " << skipCount << endl;
skipCount = 0;
Mat frame1 = frame.clone();
vector<Rect> facesResult = detectFaces(frame1, cascade);
std::unique_lock lock(_facesMutex);
faces = facesResult;
lock.unlock();
}
//waitKey(250);
std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
}
} catch (std::exception &e) {
Napi::AsyncWorker::SetError(e.what());
}
} else {
Napi::AsyncWorker::SetError("ERROR: Could not open video camera " + url);
}
}
// Executed when the async work is complete
// this function will be run inside the main event loop
// so it is safe to use JS engine data again
void OnOK() {
Napi::HandleScope scope(Env());
Callback().Call({Env().Undefined(), Env().Undefined()});
}
private:
string url;
int skip = 3;
int sleep = 30;
};
// Asynchronous access to the `Estimate()` function
Napi::Value FaceDetect(const Napi::CallbackInfo& info) {
Napi::String url = info[0].As<Napi::String>();
Napi::Number skip = info[1].As<Napi::Number>();
Napi::Number sleep = info[2].As<Napi::Number>();
Napi::Function callback = info[3].As<Napi::Function>();
FaceDetectWorker* faceDetectWorker = new FaceDetectWorker(callback, url, skip, sleep);
faceDetectWorker->Queue();
return info.Env().Undefined();
}
Napi::Array FaceDetectGet(const Napi::CallbackInfo &info) {
Napi::Array faceArray = Napi::Array::New(info.Env(), faces.size());
std::shared_lock lock(_facesMutex);
vector<Rect> faces2 = faces;
lock.unlock();
for(int i = 0; i < faces2.size(); i++) {
Rect rect = faces[i];
Napi::Object obj = Napi::Object::New(info.Env());
obj.Set("x", rect.x);
obj.Set("y", rect.y);
obj.Set("width", rect.width);
obj.Set("height", rect.height);
faceArray[i] = obj;
}
return faceArray;
}
void FaceDetectCancel(const Napi::CallbackInfo &info) {
running = false;
}

Playing a Sine Wave with changable parameters - how to do the phase shifting?

I am generating a Sine wave and send it to the SDL Audio Buffer to generate sound. All the parameter like the amplitude and frequency can be changed with the arrow keys of the keyboard.
Now, the problem is that when I change the frequency, I hear a "scratch". I understand why this is happening: I'm getting a totally wrong value when I just continue iterating my x in f(x) when the function itself has changed. But I fail to see or understand how I can fix this problem by phase shifting.
Any tips how to start?
#include "WaveGenerator.h"
#include <thread>
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm> // std::min
int main(int argc, char* argv[]){
WaveGenerator* wg = new WaveGenerator();
int i;
std::cin >> i;
return 0;
}
int graphThreadFunc(void *pointer){
WaveGenerator* wg = (WaveGenerator*)pointer;
wg->init();
return 0;
}
// SDL calls this function whenever it wants its buffer to be filled with samples
// length = 2048
void SDLAudioCallback(void *data, Uint8 *buffer, int length){
uint8_t *stream = (uint8_t*)buffer;
WaveGenerator* wg = (WaveGenerator*)data; // pointer to our WaveGenerator object where the voice data is stored
for (int i = 0; i < length; i++){
if (wg->voice.audioLength <= 0)
stream[i] = wg->getSpec()->silence; // 128 is silence in a uint8 stream
else
{
stream[i] = wg->voice.getSample(); // calculate the current sample value
}
wg->voice.audioPosition++;
}
}
WaveGenerator::WaveGenerator()
{
// spawn thread
SDL_Thread *refresh_thread = SDL_CreateThread(graphThreadFunc, NULL, this);
}
SDL_AudioSpec* WaveGenerator::getSpec(){
return &this->spec;
}
void WaveGenerator::init()
{
// Init SDL & SDL_ttf
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_zero(desiredDeviceSpec);
desiredDeviceSpec.freq = SAMPLING_RATE; // Sample Rate
desiredDeviceSpec.format = AUDIO_U8; // Unsigned 8-Bit Samples
desiredDeviceSpec.channels = 1; // Mono
desiredDeviceSpec.samples = 2048; // The size of the Audio Buffer (in number of samples, eg: 2048 * 1 Byte (AUDIO_U8)
desiredDeviceSpec.callback = SDLAudioCallback;
desiredDeviceSpec.userdata = this;
dev = SDL_OpenAudioDevice(NULL, 0, &desiredDeviceSpec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (dev == 0) {
printf("\nFailed to open audio: %s\n", SDL_GetError());
}
else {
SDL_PauseAudioDevice(dev, 1); /* pause! */
SDL_PauseAudio(1);
}
//Create an application window with the following settings:
window = SDL_CreateWindow(
WINDOW_TITLE.c_str(), // window title
SDL_WINDOWPOS_UNDEFINED, // initial x position
SDL_WINDOWPOS_UNDEFINED, // initial y position
WINDOW_WIDTH, // width, in pixels
WINDOW_HEIGHT, // height, in pixels
SDL_WINDOW_SHOWN // flags - see below
);
// Check if the window was successfully created
if (window == NULL) {
// In case the window could not be created...
printf("Could not create window: %s\n", SDL_GetError());
return;
}
else{
// Initial wave parameters
voice.waveForm = WaveGenerator::Voice::WaveForm::SINE;
voice.amp = 120;
voice.frequency = 440;
SDL_PauseAudioDevice(dev, 1); // pause
voice.audioLength = SAMPLING_RATE;
voice.audioPosition = 0;
SDL_PauseAudioDevice(dev, 0); // play
SDL_Delay(SAMPLING_RATE / voice.audioLength * 1000); // 44100 / length of the audio * 1000 (to get milliseconds)
mainLoop();
}
return;
}
void WaveGenerator::mainLoop()
{
bool waveHasChanged = false;
// poll SDL events until we terminate the thread
while (thread_exit == 0){
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type)
{
case SDL_KEYDOWN:
{
if (event.key.keysym.scancode == SDL_SCANCODE_SPACE){
switch (voice.waveForm){
case Voice::SINE:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::TRIANGLE;
break;
}
case Voice::TRIANGLE:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::RECT;
break;
}
case Voice::RECT:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::SAWTOOTH;
break;
}
case Voice::SAWTOOTH:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::NOISE;
break;
}
case Voice::NOISE:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::SINE;
break;
}
default:
break;
}
waveHasChanged = true;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE){
exit();
}
else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT){
voice.frequency -= 10;
waveHasChanged = true;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT){
voice.frequency += 10;
waveHasChanged = true;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_UP){
voice.amp += 2;
waveHasChanged = true;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_DOWN){
voice.amp -= 2;
waveHasChanged = true;
}
else{
}
break;
}
case SDL_QUIT:
{
exit();
return;
break;
}
default: /* unhandled event */
break;
}
}
if (!pause_thread && waveHasChanged)
{
// calculate phase shifting?
}
SDL_Delay(50);
}
return;
}
void WaveGenerator::exit(){
thread_exit = 1;
// Clean up
SDL_Quit();
}
WaveGenerator::Voice::Voice(){
}
uint8_t WaveGenerator::Voice::getSample(){
switch (waveForm){
case SINE:
{
return (amp * sin(2 * M_PI * audioPosition * frequency / SAMPLING_RATE)) + 128;
break;
}
// .....
default:
return 0;
}
}
and the header file:
#ifndef WAVEGENERATOR_H
#define WAVEGENERATOR_H
#include "SDL.h"
#include "SDL_audio.h"
#include <stdio.h>
#include <cmath>
#include <string>
#include <stack>
#include <io.h> // unistd.h for mac/linux, io.h for windows
#include <vector>
#include <fstream>
/* Window Constants */
const std::string WINDOW_TITLE = "Wave Graph";
const int WINDOW_WIDTH = 1980;
const int WINDOW_HEIGHT = 255;
/* Audio Constants */
const int SAMPLING_RATE = 44100; // number of samples per second
class WaveGenerator
{
private:
SDL_Window *window;
// SDL Audio
SDL_AudioSpec desiredDeviceSpec;
SDL_AudioSpec spec;
SDL_AudioDeviceID dev;
int thread_exit = 0;
bool pause_thread = false;
public:
WaveGenerator();
void init();
void mainLoop();
void exit();
SDL_AudioSpec* getSpec();
// SDL audio members
struct Voice{
Voice();
// WaveForm parameters
enum WaveForm{
SINE = 0, RECT = 1, SAWTOOTH = 2, TRIANGLE = 3, NOISE = 4
} waveForm;
int frequency; // the frequency of the voice
int amp; // the amplitude of the voice
// SDL buffer handling members
int audioLength; // number of samples to be played, eg: 1.2 seconds * 44100 samples per second
int audioPosition = 0; // counter
uint8_t getSample();
} voice;
};
#endif
The easiest way to change frequencies without a jump in phase by removing audioPosition from the equation:
class WaveGenerator
{
private:
double m_sinePhase;
double m_sinePhaseInc;
uint8_t WaveGenerator::Voice::getSample(){
switch (waveForm){
case SINE:
{
uint8_t sample = (amp * sin(2 * M_PI * m_sinePhase)) + 128;
m_sinePhase += m_sinePhaseInc;
return sample;
}
}
And then when you change the frequency just recompute the phase increment
m_sinePhaseInc = freq/sampleRate;

Playing sine soundwave with SDL2 - noise / scratch issue

My goal is to create an SDL window plotting different waveforms and playing an indefinite sound of this wave. By pressing specific keys, the parameters of the wave, like the amplitude, frequency or waveform can be modified.
The problem is that even a simple sine wave which looks nice when plotted, sounds noisy. I don't understand why.
Code:
#include "Graph.h"
#include <thread>
#include <iostream>
#include <sstream>
#include <string>
int main(int argc, char* argv[]){
Graph* g = new Graph();
int i;
std::cin >> i;
return 0;
}
int graphThreadFunc(void *pointer){
Graph* grid = (Graph*)pointer;
grid->init();
return 0;
}
// SDL calls this function whenever it wants its buffer to be filled with samples
void SDLAudioCallback(void *data, Uint8 *buffer, int length){
uint8_t *stream = (uint8_t*)buffer;
Graph* graph = (Graph*)data;
for (int i = 0; i <= length; i++){
if (graph->voice.audioLength <= 0)
stream[i] = graph->getSpec()->silence; // 128 is silence in a uint8 stream
else
{
stream[i] = graph->voice.getSample();
graph->voice.audioPosition++;
// Fill the graphBuffer with the first 1000 bytes of the wave for plotting
if (graph->graphPointer < 999)
graph->graphBuffer[graph->graphPointer++] = stream[i];
}
}
}
Graph::Graph()
{
// spawn thread
SDL_Thread *refresh_thread = SDL_CreateThread(graphThreadFunc, NULL, this);
}
SDL_AudioSpec* Graph::getSpec(){
return &this->spec;
}
void Graph::init()
{
// Init SDL & SDL_ttf
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_zero(desiredDeviceSpec);
desiredDeviceSpec.freq = 44100; // Sample Rate
desiredDeviceSpec.format = AUDIO_U8; // Unsigned 8-Bit Samples
desiredDeviceSpec.channels = 1; // Mono
desiredDeviceSpec.samples = 2048; // The size of the Audio Buffer (in number of samples, eg: 2048 * 1 Byte (AUDIO_U8)
desiredDeviceSpec.callback = SDLAudioCallback;
desiredDeviceSpec.userdata = this;
dev = SDL_OpenAudioDevice(NULL, 0, &desiredDeviceSpec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (dev == 0) {
printf("\nFailed to open audio: %s\n", SDL_GetError());
}
else {
SDL_PauseAudioDevice(dev, 1); /* pause! */
SDL_PauseAudio(1);
}
// Create an application window with the following settings:
window = SDL_CreateWindow(
WINDOW_TITLE.c_str(), // window title
SDL_WINDOWPOS_UNDEFINED, // initial x position
SDL_WINDOWPOS_UNDEFINED, // initial y position
WINDOW_WIDTH, // width, in pixels
WINDOW_HEIGHT, // height, in pixels
SDL_WINDOW_SHOWN // flags - see below
);
// Check if the window was successfully created
if (window == NULL) {
// In case the window could not be created...
printf("Could not create window: %s\n", SDL_GetError());
return;
}
else{
voice.waveForm = Graph::Voice::WaveForm::SINE;
voice.amp = 120;
voice.frequency = 440;
SDL_PauseAudioDevice(dev, 1); // play
graphPointer = 0;
voice.audioLength = 44100;
voice.audioPosition = 0;
SDL_PauseAudioDevice(dev, 0); // play
SDL_Delay(200);
drawGraph();
mainLoop();
return;
}
}
void Graph::mainLoop()
{
while (thread_exit == 0){
SDL_Event event;
bool hasChanged = false;
while (SDL_PollEvent(&event)) {
switch (event.type)
{
case SDL_KEYDOWN:
{
hasChanged = true;
if (event.key.keysym.scancode == SDL_SCANCODE_SPACE){
//pause_thread = !pause_thread;
switch (voice.waveForm){
case Voice::SINE:
{
voice.waveForm = Graph::Voice::WaveForm::TRIANGLE;
break;
}
case Voice::TRIANGLE:
{
voice.waveForm = Graph::Voice::WaveForm::RECT;
break;
}
case Voice::RECT:
{
voice.waveForm = Graph::Voice::WaveForm::SAWTOOTH;
break;
}
case Voice::SAWTOOTH:
{
voice.waveForm = Graph::Voice::WaveForm::SINE;
break;
}
default:
break;
}
}
else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE){
exit();
}
else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN){
}
else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT){
voice.frequency -= 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT){
voice.frequency += 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_UP){
voice.amp += 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_DOWN){
voice.amp -= 2;
}
else{
}
break;
}
case SDL_QUIT:
{
exit();
return;
break;
}
default: /* unhandled event */
break;
}
}
if (!pause_thread && hasChanged)
{
//SDL_PauseAudioDevice(dev, 1); // play
graphPointer = 0;
voice.audioLength = 44100;
voice.audioPosition = 0;
SDL_PauseAudioDevice(dev, 0); // play
SDL_Delay(200);
drawGraph();
}
//voice.waveForm = Voice::WaveForm::TRIANGLE;
//SDL_Delay(n); // delay the program to prevent the voice to be overridden before it has been played to the end
//SDL_PauseAudioDevice(dev, 1); // pause
SDL_Delay(REFRESH_INTERVAL);
//SDL_PauseAudioDevice(dev, 1); // pause
}
return;
}
void Graph::drawGraph()
{
SDL_Renderer *renderer = SDL_GetRenderer(window);
if (renderer == nullptr)
renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED);
// Set background color
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// Clear winow
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 22, 22, 22, 255);
for (int x = 0; x < WINDOW_WIDTH; x++){
uint8_t y = graphBuffer[x];
SDL_RenderDrawPoint(renderer, x, WINDOW_HEIGHT - y);
}
SDL_RenderPresent(renderer);
return;
}
void Graph::exit(){
thread_exit = 1;
// Close and destroy the window
SDL_DestroyWindow(window);
// Clean up
SDL_Quit();
}
uint8_t Graph::Voice::getSample(){
switch (waveForm){
case SINE:
{
float sineStep = 2 * M_PI * audioPosition * frequency / 44100;
return (amp * sin(sineStep)) + 128;
break;
}
case RECT:
break;
case SAWTOOTH:
break;
case TRIANGLE:
break;
default:
return 0;
}
}
And the header file:
#ifndef GRAPH_H
#define GRAPH_H
#include "SDL.h"
#include "SDL_audio.h"
#include <stdio.h>
#include <cmath>
#include <string>
#include <stack>
/* Constants */
const int REFRESH_INTERVAL = 50; // mseconds
const int WINDOW_WIDTH = 1000;
const int WINDOW_HEIGHT = 255;
const std::string WINDOW_TITLE = "Wave Graph";
class Graph
{
private:
SDL_Window *window; // Declare a pointer
// SDL audio stuff
SDL_AudioSpec desiredDeviceSpec;
SDL_AudioSpec spec;
SDL_AudioDeviceID dev;
int thread_exit = 0;
bool pause_thread = false;
public:
Graph();
void init();
void mainLoop();
void drawGraph();
void exit();
SDL_AudioSpec* getSpec();
struct Voice{
int frequency; // the frequency of the voice
int amp; // the amplitude of the voice
int audioLength; // number of samples to be played, eg: 1.2 seconds * 44100 samples per second
int audioPosition = 0; // counter
enum WaveForm{
SINE = 0, RECT = 1, SAWTOOTH = 2, TRIANGLE = 3
} waveForm;
uint8_t getSample();
} voice;
int graphPointer = 0;
uint8_t graphBuffer[1000];
};
#endif
Your SDLAudioCallback() is writing an extra byte off the end of buffer:
void SDLAudioCallback(void *data, Uint8 *buffer, int length)
{
...
for (int i = 0; i <= length; i++)
// ^^ huh?
{
...
}
}
Changing the <= to just < fixes the crackles on my system.
Generally C-style "byte pointer + length" APIs expect a left-closed, right-open interval: [0, length). I.e., you can access buffer[length - 1] but not buffer[length].

SDL2 'Bullet' movement not working?

I am attempting to make a simple scrolling shooter game with SDL2. I have a moving player on a screen, and I am trying to make the player shoot a bullet using an array (so they can shoot multiple bullets) however, when I press the space bar, nothing happens, and instead the bullet image sort of flashes in the top left corner.
Heres the same code in codepad: http://codepad.org/rOhE1AqY
#include <SDL.h>
#include <stdio.h> //use for things like printf, same as cout
#include <iostream>
#include <string>
#include <time.h>
using namespace std;
//screend dimensions& sprtie dimensions
const int SCREEN_HEIGHT = 600;
const int SCREEN_WIDTH = 400;
const int SPRITE_WIDTH = 60;
const int SPRITE_HEIGHT = 80;
const int MAX_BULLETS = 50;
SDL_Window* Window = NULL;//the window rendering to
SDL_Surface* ScreenSurface = NULL;//surface contained by window
SDL_Surface* Background = NULL;
SDL_Surface* Player = NULL;
SDL_Surface* Enemy = NULL;
SDL_Surface* Bullet = NULL;
SDL_Surface* newBullet = NULL;
SDL_Rect posPlayer, posEnemy, posBullet, posnewBullet;
const Uint8* keystate = SDL_GetKeyboardState(NULL);
SDL_Event event;
class thePlayer
{
public:
thePlayer();
void player_movement();
void show_player();
private:
};
class theBullet
{
public:
theBullet();
bool isActive;
int x_position;
int y_position;
void bullet_movement();
void add_new_bullet();
void show_bullet();
private:
};
theBullet arrayofBullets[MAX_BULLETS];
class theEnemy
{
public:
theEnemy();
void enemy_movement();
void show_enemy();
private:
};
thePlayer::thePlayer()
{
posPlayer.x = 170;
posPlayer.y = SCREEN_HEIGHT;
posPlayer.w = 20;
posPlayer.h = 30;
}
void thePlayer::player_movement()
{
if(keystate[SDL_SCANCODE_LEFT])
{
posPlayer.x -= 2;
}
if(keystate[SDL_SCANCODE_RIGHT])
{
posPlayer.x += 2;
}
if(keystate[SDL_SCANCODE_UP])
{
posPlayer.y -= 2;
}
if(keystate[SDL_SCANCODE_DOWN])
{
posPlayer.y += 2;
}
if ((posPlayer.x + SPRITE_WIDTH) > SCREEN_WIDTH)
{
posPlayer.x = (SCREEN_WIDTH - SPRITE_WIDTH);
}
if ((posPlayer.y + SPRITE_HEIGHT) > SCREEN_HEIGHT)
{
posPlayer.y = (SCREEN_HEIGHT - SPRITE_HEIGHT);
}
}
void thePlayer::show_player()
{
SDL_BlitSurface(Player, NULL, ScreenSurface, &posPlayer);
SDL_SetColorKey(Player, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));
}
theBullet::theBullet()
{
/*posBullet.x;
posBullet.y;
posBullet.w = 10;
posBullet.h = 15;*/
}
void theBullet::bullet_movement()
{
/*if(keystate[SDL_SCANCODE_SPACE])
{
posBullet.x = posPlayer.x + 25;
posBullet.y = posPlayer.y + 10;
}
posBullet.y -= 2;
if(posBullet.y < 0)
{
posBullet.y = -50;
}*/
}
void theBullet::show_bullet()
{
//SDL_BlitSurface(Bullet, NULL, ScreenSurface, &posBullet);
//SDL_SetColorKey(Bullet, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));//removes white background
}
theEnemy::theEnemy()
{
srand (time(NULL));
posEnemy.x = rand() % 300 + 50;
posEnemy.y =0;
posEnemy.w = 35;
posEnemy.h = 60;
}
void theEnemy::enemy_movement()
{
posEnemy.y += 1;
if(posEnemy.y > SCREEN_HEIGHT)
{
posEnemy.y = SCREEN_HEIGHT +50;
}
}
void theEnemy::show_enemy()
{
SDL_BlitSurface(Enemy, NULL, ScreenSurface, &posEnemy);
SDL_SetColorKey(Enemy, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));
}
bool initialise()
{
bool success = true;
if (SDL_Init(SDL_INIT_EVERYTHING) !=0)
{
cout<<"SDL_Init Error."<<SDL_GetError()<<endl;
success = false;
}
else
{
//create the window for game
Window = SDL_CreateWindow("Scrolling Shooter Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (Window == NULL)
{
cout<<"Window Error"<<SDL_GetError()<<endl;
success = false;
}
else
{
//get window surface
ScreenSurface = SDL_GetWindowSurface(Window);
}
}
return success;
}
bool LoadingMedia()
{
bool success = true;
Background = SDL_LoadBMP("background.bmp");
if (Background == NULL)
{
cout<<"Error in loading background."<<SDL_GetError()<<endl;
success = false;
}
Player = SDL_LoadBMP("spaceship.bmp");
if (Player == NULL)
{
cout<<"Error in loading player."<<SDL_GetError()<<endl;
success = false;
}
Enemy = SDL_LoadBMP("enemy.bmp");
if (Enemy == NULL)
{
cout<<"Error in loading enemy."<<SDL_GetError()<<endl;
success = false;
}
Bullet = SDL_LoadBMP("bullet.bmp");
if (Bullet == NULL)
{
cout<<"Error in loading bullet."<<SDL_GetError()<<endl;
success = false;
}
return success;
}
void closedown()
{
SDL_FreeSurface(Background);
Background = NULL;
SDL_FreeSurface(Player);
Player = NULL;
SDL_FreeSurface(Enemy);
Enemy = NULL;
SDL_DestroyWindow(Window);
Window = NULL;
SDL_Quit();
}
int main(int argc, char** argv)
{
bool quit = false;
thePlayer myPlayer;
theEnemy myEnemy;
theBullet myBullet;
if (!initialise())
{
cout<<"Failed to initialise"<<SDL_GetError()<<endl;
}
else
{
if (!LoadingMedia())
{
cout<<"Error loading media"<<SDL_GetError()<<endl;
}
}
//makes all bullets false
for (int i=0; i<MAX_BULLETS; i++)
{
arrayofBullets[i].isActive = false;
}
//GAME LOOP
while (quit == false)
{
SDL_BlitSurface(Background, NULL, ScreenSurface, NULL);
myPlayer.show_player();
myPlayer.player_movement();
while (SDL_PollEvent(&event))
{
if( event.type == SDL_QUIT )
{
quit = true;
break;
}
if(keystate[SDL_SCANCODE_SPACE])
{
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == false)
{
arrayofBullets[i].x_position = posPlayer.x + 25;
arrayofBullets[i].y_position = posPlayer.y + 10;
arrayofBullets[i].isActive = true;
break;
}
}
}
//update game objects
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == true)
{
arrayofBullets[i].y_position -= 2;
if (arrayofBullets[i].y_position < 0)
{
arrayofBullets[i].isActive = false;
}
}
}
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == true)
{
SDL_BlitSurface(Bullet, NULL, ScreenSurface, NULL);
}
}
}
//myPlayer.show_player();
//myBullet.show_bullet();
//myEnemy.show_enemy();
//myPlayer.player_movement();
//myBullet.bullet_movement();
//myEnemy.enemy_movement();
SDL_UpdateWindowSurface(Window); //updates screen
}
closedown();
return 0;
}
SDL_BlitSurface(Bullet, NULL, ScreenSurface, NULL);
You haven't specified destination rect, so it will blit on left-top corner.
It should be
SDL_Rect dstrect;
dstrect.x = arrayofBullets[i].x_position;
dstrect.y = arrayofBullets[i].y_position;
dstrect.w = Bullet->w;
dstrect.h = Bullet->h;
SDL_BlitSurface(Bullet, NULL, ScreenSurface, &dstrect);