화요일, 10월 12, 2021

[QT C++] Enum과 QString

 enum(ALIGNMENT){
   LEFT=0,
   CENTER,
   RIGHT
)
QEnum(ALIGNMENT)


위의 내용을 기준으로
   LEFT(0)으로부터 문자 "LEFT" 얻어오는 방법과 
   반대로 "LEFT"로부터 LEFT(0)을 얻어오는 방법에 대하여 고민해보자
구글링을 통하여 enum의 label을 문자열로 받는 방법은 있었는데 반대의 경우는 자세히 보지 못해서인지 못 찾았다.

그래서 고민 끝에 만들어 보았다.(내가 처음 만들었다는 소리는 아니다)

template<typename T>
QString enumToString(T value)
{
    int castValue = static_cast<int>(value);
    return QMetaEnum::fromType<T>().valueToKey(castValue);
}

Version#1
template<typename T>
T stringToEnum(QString keyStr, T defaultValue = (T)0)
{
    T retVal = defaultValue;
    QMetaEnum meta =  QMetaEnum::fromType<T>();
    for(int idx = 0; idx<meta.keyCount(); idx++){
        if(!QString(meta.valueToKey(meta.value(idx))).compare(keyStr,Qt::CaseInsensitive)){
            retVal = (T)meta.value(idx);
            break;
        }
    }
    return retVal;
}

Version#2
template<typename T>
T stringToEnum(QString keyStr, T defaultValue = (T)0)
{
    T retVal;
    bool bOK = false;
    QMetaEnum meta =  QMetaEnum::fromType<T>();
    T value = meta.keyToValue(keyStr.toLocal8Bit(),&bOK);
    if(bOK)
        retVal = value;
    else
        retVal = defaultValue;
    return retVal;
}

 사용법
 1. LEFT(0)으로부터 "LEFT" 얻어오기
  QString left = enumToString(ALIGNMENT::LEFT);
 
 2. "LEFT"로부터 LEFT(0) 얻어오기
 ALIGNMENT left = stringToEnum<ALIGMENT>("LEFT");
 ※ Default 값이 0(LEFT)이므로 오탈자의 경우 "LEFT"만 나온다. (Default 값 변경 가능)

수요일, 11월 20, 2019

QT wave file play


인터넷에서 찾으면 .. 나온다.. 라고 하면 글쓴 이유가 없지..

 일단 인터넷에서 찾아 볼 수 있는 흔한 예제...

QFile audio_file(strFullFilePath);
if(audio_file.open(QIDevice::ReadOnly)){
  audio_file.seek(44); // skip wav header
  QByteArray audio_data = audio_file.readAll();
  audio_file.close();

  QBuffer* audio_buffer = new QBuffer(&audio_data);
  
  QAudioFormat  format;

  format.setSampleSize(16);
  format.setSampleRate(44100);
  format.setChannelCount(2);
  format.setCodec("audio/pcm");
  format.setByteOrder(QAudioFormat::LittleEndian);
  format.setSampleType(QAudioFormat::UnsignedInt);

  QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
  if(!info.isFormatSupperted(format)) {      
      return; // Not support format
  }
  
  QAudioOutput* output = new QAudioOutput(info, format);
  output->start(audio_buffer);
}

간결하다. 근데 막상 테스트 해보면.. 안노온다.. 왜일까?

 우선.. output->start(audio_buffer)를 실행하는동안 내 Process는 살아 있어야 한다. 그렇기 때문에 간단히 맨 아래에 5줄 추가

 QEventLoop loop;
 QObject::connect(&output, SIGNAL(stateChanged(QAudio::State)), &loop, SLOT(quit()));
 do {
     loop.exec();
 } while(output.state() == QAudio::ActiveState);


일단 소리는 나온다. 하지만 이쁘게 나오는 경우도 있고 그렇지 않은 경우(도날드덕 목서리 내지는 테이프 빨리 감는소리)도 발생한다.  이 경우에는  약간의 대공사(?)를 진행하여 아래와 같은 코드가 필요하게 된다.

 QFile audio_file(FileName);
 QByteArray audio_Buffer;
 int Channels = 0;      // count of channel
 int SampleRate = 0;    // wave sampling rate
 int BPS = 0;           // Sample Size
 if(audio_file.open(QIODevice::ReadOnly)){
     char stream[4] = {0,};
     audio_file.read(stream, 4); //  Same with "RIFF"   not use
     audio_file.read(stream, 4); //  Chumk size         not use
     audio_file.read(stream, 4); //  Format             not use
     audio_file.read(stream, 4); //  Sub chunk1 ID      not use
     audio_file.read(stream, 4); //  Sub chunk1 size    not use
     audio_file.read(stream, 2); //  Audio Format       not use
     memset(stream,0, 4);
     audio_file.read(stream, 2); //  Channel number    use
     Channels = qFromLittleEndian<quint16>((uchar*)stream);
     memset(stream,0, 4);
     audio_file.read(stream, 4); //  Sample rate       use
     SampleRate = qFromLittleEndian<quint32>((uchar*)stream);
     audio_file.read(stream, 4); //  Byte rate         not use
     audio_file.read(stream, 2); //  Byte Allign       not use(아마도 엔디언 설정인듯)
     memset(stream,0, 4);
     audio_file.read(stream, 2); //  BPS               use
     BPS = qFromLittleEndian<quint16>((uchar*)stream);
     audio_file.read(stream, 4); //  Sub chunk2 ID      not use
     audio_file.read(stream, 4); //  Sub chunk2 size    not use
     audio_Buffer.clear();
     while(!file.atEnd())
     {
         char s[1];
         audio_file.read(s,1);
         audio_Buffer.append(s[0]);
     }
     audio_file.close();
     QBuffer audio_buffer(&audio_Buffer);
     audio_buffer.open(QIODevice::ReadOnly);
     QAudioFormat format;
     format.setSampleSize(BPS);
     format.setSampleRate(SampleRate);
     format.setChannelCount(Channels);
     format.setCodec("audio/pcm");
     format.setByteOrder(QAudioFormat::LittleEndian);
     format.setSampleType(QAudioFormat::UnSignedInt);
     QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
     if (!info.isFormatSupported(format)) {
         qWarning()<<"raw audio format not supported by backend, cannot play audio.";
         return;
     }
     // Create the device and start playing...
     QAudioOutput output(info, format);
     output.start(&audio_buffer);
     // ...then wait for the sound to finish
     QEventLoop loop;
     QObject::connect(&output, SIGNAL(stateChanged(QAudio::State)), &loop, SLOT(quit()));
     do {
         loop.exec();
     } while(output.state() == QAudio::ActiveState);
  }

완벽한 코드는 아니지만, 적어도 웨이브 파일 하나 정도는 잘 나오는 코드인듯 싶다.

수요일, 9월 04, 2019

OpenCV 4.1.1 install on Ubuntu 18.0.4

많은 블러그를 통해 정보를 모아보고 구글햄아를 통해 수정자료를 찾아 적용하여 본 자료를 남긴다.
■ 우선 참고한 블러그 및 사이트
  ● 설치
    nvidia 드라이버 : https://pythonkim.tistory.com/48
    OPENCV with contrib : https://webnautes.tistory.com/1186  기본 정보를 이곳에서 참고
 
  ● 수정정보
    OpenCV archive URL정보
    OpenCV : https://github.com/opencv/opencv/releases
    contrib  : https://github.com/opencv/opencv_contrib/releases
  ※ 해당 버전 우클릭 후 "링크주소 복사"를 통해 사용 (최신버전 4.1.1 사용)

■ cmake 중 옵션 수정 (처리 속도 향상을 위한 조치)
  ● TBB(Threading Building Blocks) : 그다지 성능향상 기대하기 힘듬(대체 되어 돌아가는 것들이 많으므로) => 패스
       -D WITH_TBB=OFF
  ● IPP : Intel core사용시 성능최적화지원 하는 함수들의 묶음(상용 이므로) => 패스
       -D WITH_IPP=OFF
  ● CUDA : OpenCV에서 GPU를 사용하기위함 사용
       -D WITH_CUDA=ON
   ※ 문제점 : cuda lib가 gcc 6이상에서는 compile이 안됨 gcc 5버전을 설치 후  해당 부분만 gcc5 버전으로 컴파일
      -D CUDA_GENERATION="Pascal"
      -D CUDA_HOST_COMPILER:FILEPATH=/usr/bin/gcc-5
  ● OpenCV 버전에 따른 contrib 경로 수정
      -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.1.1/modules

※ time make -j(코어수) : Target workstation 성능이 하이엔드 급 (Xeon silver core16)인데도 110분 걸림

금요일, 12월 23, 2016

awesomium c++ hide scroll bar

그냥 것 저것 찾아보다가.....

  보통

WebCore* pWeb = WebCore::Initialize(WebConfig());

  이런식으로 초기화를 하는데
  이놈을...

WebConfig Webcfg = WebConfig();
Webcfg.user_stylesheet.Append(Awesomium::WSLit("::-webkit-scrollbar {visibility:hidden;}"));

WebCore* pWeb = WebCore::Initialize(Webcfg);

  요렇기 초기화를 하면 스크롤을 없엘수 있다.  하지만!!!!
아마 버그일듯 싶은데
  스크롤이 사라진 만큼 화면(Surface나 Texture등)에 뿌릴때 유의 해야 할것이 소스이미지가 원래 의도했던 사이즈로 나오지 않는다는거다.  대략 스크롤바의 크기만큼 width와 height를 키워 줘야 원하는 그림이 나오지 싶다.
   키워주지 않으면 이미지가 약간 찌그러지거나 뭉그러지거나 그런 의미지나 나오더라.( 얇은 선등 을 주의해서 보면 그렇다.)

목요일, 12월 15, 2016

OpenCV 3.1 windows8.1 Visual Studio 2015 build 정리

요약)
   OpenCV 3.1을 Windows에서 build할때 Cuda Toolkit을 설치 하지 말자.
   cmake 과정에서 오류를 하나가득 쏟아 낸다.
   설치 과정은 생각보다 단순하다.

준비)
0. Visual Studio 2015와 CMake(최신버전)가 설치 되어있어야 한다.
1. github에서 openvc 를 내려 받는다.(폴더 꼭 기억)
2. CMake-gui 실행
   2-1. Source folder지정
   2-2. Build folder지정
   2-3. Configuraing 실행
   2-4. Generate 실행
3. Visual Stuido 2015 실행
  Compile

※ 추가 모듈의 경우 그냥 Github에서 통으로 download 받아서 위에서 opencv를 깔아 놓은 곳 하부의 modules 폴더에 복사 한 뒤( 통으로 복사 하지 말고 필요한 추가 모듈만 복사 하자... 개중에 오류를 밷어내는 놈들이 있다.) 2-3부터 다시~~!

※ 멍청하게 ALL_BUILD를 해놓고 오류 난다고 투덜되면 안된다...
    INSTALL을 해 놓고 LIB 및 Include등을 뽑아 놓고 예제파일을 컴파일 해야 드러운 꼴 안본다.

금요일, 11월 18, 2016

노트북 사운드(카드)이상으로 인한 문제

소니 VAIO노트북을 사용하고 있다.
몇개월간의 할부를 통해 구입했지만, 지난 1년여 부터 sound카드가 잡히지 않아 소리가 나지 않는 문제가 발생하였다.

 소니 A/S센터에가서 수리를 하면 windows를 세로 설치 하면 잠깐은 괜찮아 지더라. 하지만, 몇주가 지나지 않아 바로 같은 증상이 발견한다.
 내가 볼때 이건 100% 소니쪽에서 Driver의 Version관리를 제대로 하지 않아서 발생하는 것으로 보인다.(시스템 업데이트를 하면 발생하는것으로 미루어) 그렇다고 매번 드라이버를 다시 설치하고 업데이트를 수동으로 하고 할수 있는 문제는 아닌가 싶다.

  솔찍히 매우 게으른 나에게 문제도 있지만 이게 단지 사용자의 게으름의 문제는 아닌거 같다. 이런 귀찮이즘이 나에게 블루투스 스피커 구입으로 이어져 노트북의 사운드 카드 모듈은 무시하고 사용하게 되었다.

 그리고 소니 VAIO노트북의 이런 문제는 나만 그런게 아닌것같더라.(질문 글들이 생각보다 많다.) 바쁜데 매번 소니 수리센터에 노트북을 맞기고 하기도 그렇고(사실 수리보증기간의 문제도 있고)
 요즘 비교적 저렴한 블루투스 스피커도 많으니 이런 것도 하나의 해결 방법이 아닌가 싶다.
(블루투스 스피커로 소리를 듣는게 노트북 자체 스피커로 듣는거보다 솔찍히 음질도 좋다.)

수요일, 3월 09, 2016

라즈베리파이2 와 8채널 릴레이를 이용한 전등(형광등) 컨트롤(Lighting control with Raspberry Pi2 & 8ch Relay) 만들기 - Part3

 2)  스위치 입력 모듈 제작 (c 언어- gcc)
      이번 페이지에는 c 언어와 wiringpi를 이용하여 스위치의 입력을 받는 module에 대하여 쓰고자 한다. 이전 글에서 밝힌 바와 같이 약간의 트릭이 필요한데 이 글의 끝 즈음에 나올 것이다.
     (기본적으로 wiringPi를 이용한 개발은 다른 블로거의 페이지 등에 많이 나와 있고 또한 가능하다면 원 제작자의 페이지(여기)를 통하여 정보를 얻는 것이 더욱 확실할 것이라 생각 한다.)



제일 먼저 해야 할 일은 버튼(혹은 스위치-버튼으로 통일 하겠다. 동작이 그러하니)과 Raspberry Pi의 GPIO간의 Bin bind가 계획 되어야 할 것 같다 하여 나는 다음과 같이 Bind 하였다.

INPUT(Button)
Btn No.GPIOPin NumberWiringPi ID
1238
2359
3477
417110
527132
622153
7101912
792113
OUTPUT(Relay)
Relay No.GPIOPin NumberWiringPi ID
114815
2151016
318121
423164
524185
625226
782410
872611
다소 복잡해 보이는데 GPIO를 지원하는 H/W와 Library가 서로 Pin define이 틀려서 이런 작업을 해 놓지 않으면 오히려 내가 해깔려 버리는 상황이 오기때문에 이렇게 
미리 정해 놔야 혼란이 없다.

자그럼 본격적으로 Code를 살펴보자.

#include
#include
#include
#include

// Button's WiringPi ID
#define BTN1 8
#define BTN2 9
#define BTN3 7
#define BTN4 0
#define BTN5 2
#define BTN6 3
#define BTN7 12
#define BTN8 13

// constants
#define SwitchCount 8
#define PREVENT_REACT_TICK    250

// global variables
int Buttons[ButtonCount] = {BTN1,BTN2,BTN3,BTN4,BTN5,BTN6,BTN7,BTN8};
int ButtonStatus[ButtonCount] = {1,}; // pull-up 저항을 사용 하므로 1인 상태가 버튼이 눌러지지 않은상태 임.

// Function front declear
void init(void);
void initDevice(void);
unsigned long GetTickCount(void);

///////////////////////////////////////////////////////////////////////////
// main 함수


///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
int nLoop = 0;

if(init())
return 1;

while(1){
unsigned long curTick = GetTickCount();
for(nLoop = 0; nLoop < ButtonCount; nLoop++){
int CurButtonStauts = digitalRead(Buttons[nLoop]);
// Button click 검출
if(0 == ButtonStatus[nLoop] && 1 == CurButtonStauts && (0 == ulTick || (curTick-ulTick>PREVENT_REACT_TICK))){
printf("%d Button clicked\n", nLoop);
}// if
ButtonStatus[nLoop] = CurButtonStauts;
}// for
sleep(50); // Process가 CPU를 너무 만힝 잡아 먹지 않도록 적당히 동작해라.
}// while
return 0;
}


///////////////////////////////////////////////////////////////////////////
// wiringPi를 setup
///////////////////////////////////////////////////////////////////////////
int init(void)
{

if(wiringPiSetup() == -1){
printf("unable to setupwiringPi:%s\n", strerror(errno));
return 1;
}

initDevice();
return 0;
}

///////////////////////////////////////////////////////////////////////////
// 버튼에 할당된 GPIO들을 INPUT을 설정
///////////////////////////////////////////////////////////////////////////
void initDevice(void)
{
int nLoop =0;
for(nLoop = 0; nLoop < ButtonCount; nLoop++)
pinMode(Buttons[nLoop], INPUT);
}

///////////////////////////////////////////////////////////////////////////
// Linux에서 Win32의 getTickCount와 유사한 동작을 하도록 함.
// ※ 서핑을 통해 찾은 코드 조각 
///////////////////////////////////////////////////////////////////////////
unsigned long GetTickCount(void)
{
struct timeval gettick;
unsigned long tick;
int ret;
gettimeofday(&gettick, NULL);
tick = gettick.tv_sec*1000 + gettick.tv_usec/1000;
return tick;
}


시그널은 down edge /up edge 모두 들어오고 우리가 필요한 데이터는 버튼을 눌렀다 띄었을때의 데이터 이므로  down edge와 up edge의 모든 데이터가 필요하다.

 사람의 손이라는 것이 매우 투박하고 느리지만 PC(Raspberry Pi)의 신호 접수 속도는 사람 손의 속도에 비하면 번개와 같다. 또한 물리적인 운동이 회로상에서 발생되는 신호와 1:1로 매칭 되지 않는다. (실제 동작을 해보면 생각보다 많은 신호를 확인 할 수 있다.)

※ 정말로 더 명확하게 동작 하는 push button을 확인하고 싶으면 다음과 같은 로직이 적용되어야 할 것으로 보인다.
   - 아주 짧은 interval로 값을 확인 해당 값의 변화 추이에 따른 상태값의 변화
     ex) down 상태가 일정 시간 이상 되어야하거나 up 상태가 일정 시간 이상일 때문 입력으로 인식 하도록

본인이 사용한 방법은 단지 입력값이 무엇이 었든 회로로부터 한번에 한가지 신호만 들어 온다고 가정 하였기 때문에(회로도 그렇게 구성 했겠다.) 입력 변화의 인터벌 자체를 제한 하는 방법을 사용 한 것이다.
if(0 == ButtonStatus[nLoop] && 1 == CurButtonStauts && (0 == ulTick || (curTick-ulTick>PREVENT_REACT_TICK))){
이전 버튼의 상태가 0(pull-up 저항을 사용 했으므로 누른 상태)이고 현재 상태가 1(손을 땐 상태)이며 입력 변화가 0.25초이상의 간격이 존재할 경우 클릭으로 인식하라는 코드입니다.

3) 5V 8ch Relay 연동 및 control (c언어 -gcc)
  본인이 사용한 relay이다.
 생긴건 투박해도 동작 하는 모습을 보면 왠지 모르게 귀엽게 느껴지기도 한다.
 Pin assign은 사진에서 보는 중앙 부분을 볼때 왼쪽부터 GND/1/2/3/4/5/6/7/8/VCC으로 되어 있다.

인가 전압을 5V로 control은 Raspberry Pi에서 직접 넣었다. 라즈베리가 3.3V 동작인것을 감안하면 잘 안될것 같았는데 잘 되더라. 그리고 서핑한 결과 해외에서 사용해본 유저들의 리뷰를 보아서는 9개월 이상 이상없이 사용 중이다라는 리뷰가 있는 것을 보아서 크게 문제될 것은 없어 보인다. (상전 220v를 컨트롤 하는데 문제가 상기면 어쩌지 하는 걱정은 사실 지금도 있다.)

자 그럼 이놈을 어떻게 해볼까.
우선 계획은 순차적으로 켜지고 순차적으로 켜지고 다 키고 다 끄고 이정도의 동작이면 충분 할 것으로 생각 했다.

그럼 코드 조각을 살펴보자.
#include
#include
#include
#include

// reley's wiringPI ID
#define SW1 15
#define SW2 16
#define SW3 1
#define SW4 4
#define SW5 5
#define SW6 6
#define SW7 10
#define SW8 11

// constants
#define SwitchCount 8

// global variables
int Switchs[SwitchCount] = {SW1,SW2,SW3,SW4,SW5,SW6,SW7, SW8};
int SwitchStatus[SwitchCount]={0,};

// front function declare
void initDevice(void);
void SwitchON(int nIDX);
void SwitchOFF(int nIDX);
void SwitchToggle(int nIDX);
void TurnOffAllSwitch(void);
void TurnOnAllSwitch(void);

/////////////////////////////////////////////////////
// main function
/////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
int nLoop =0;

if(init())
return 1;

// 순서대로 켜기
for( nLoop = 0; nLoop
SwitchON(nLoop);
sleep(1000);
}
// 순서대로 끄기
for( nLoop = 0; nLoop
SwitchOFF(nLoop);
sleep(1000);
}
// 모두 켜기
TurnOnAllSwitch();
sleep(1000);
// 모두 끄기
TurnOffAllSwitch();
sleep(1000);

// 니 맘대로 한번 해봐라.(20번)
for( nLoop = 0; nLoop < SwitchCount; nLoop++){
int nIdx = (int)(rand()%SwitchCount);
SwitchToggle(inIdx);
sleep(1000):
}

return 0;
}

/////////////////////////////////////////////////////
// wiringPI setup
/////////////////////////////////////////////////////
int init(void)
{
printf("Start Init Device\n");

if(wiringPiSetup() == -1){
printf("unable to setupwiringPi:%s\n", strerror(errno));
return 1;
}

initDevice();

printf("End Init Device\n");

return 0;
}

/////////////////////////////////////////////////////
// Relay를 control 할 GPIO를 OUTPUT으로 설정
/////////////////////////////////////////////////////
void initDevice(void)
{
for(nLoop = 0; nLoop < SwitchCount; nLoop++){
pinMode(Switchs[nLoop], OUTPUT);
// 일단 모두 꺼라(만약 켜 있던놈이 있을지도 모르니
SwitchOFF(nLoop);
}
}

/////////////////////////////////////////////////////
// 켜기
/////////////////////////////////////////////////////
void SwitchON(int nIDX)
{
digitalWrite(Switchs[nIDX], 0); // ON
SwitchStatus[nIDX]=1;
printf("Turn On Switch %d\n", nIDX);
}

/////////////////////////////////////////////////////
// 끄기
/////////////////////////////////////////////////////
void SwitchOFF(int nIDX)
{
digitalWrite(Switchs[nIDX], 1); // OFF
SwitchStatus[nIDX]=0;
printf("Turn OFF Switch %d\n", nIDX);
}

/////////////////////////////////////////////////////
// 토글링
/////////////////////////////////////////////////////
void SwitchToggle(int nIDX)
{
if(SwitchStatus[nIDX]==0)
SwitchON(nIDX);
else
SwitchOFF(nIDX);

printf("Toggle Switch %d\n", nIDX);
}

/////////////////////////////////////////////////////
// 모두 끄기
/////////////////////////////////////////////////////
void TurnOffAllSwitch(void)
{
int nLoop=0;
log_message(szLogPath,"Turn Off All Switch\n");
for(nLoop = 0; nLoop < SwitchCount; nLoop++){
SwitchOFF(nLoop);
}
}

/////////////////////////////////////////////////////
// 모두 켜기
/////////////////////////////////////////////////////
void TurnOnAllSwitch(void)
{
int nLoop=0;
log_message(szLogPath,"Turn Off All Switch\n");
for(nLoop = 0; nLoop < SwitchCount; nLoop++){
SwitchON(nLoop);
}
}

코드가 다소 난잡해 보인다.
하지만 끌때 0 켤때 1을 해당 GPIO에 write 해주기만 하면 된다.
결국 핵심은
/////////////////////////////////////////////////////
// 켜기
/////////////////////////////////////////////////////
void SwitchON(int nIDX)
{
digitalWrite(Switchs[nIDX], 0); // ON
SwitchStatus[nIDX]=1;
printf("Turn On Switch %d\n", nIDX);
}

/////////////////////////////////////////////////////
// 끄기
/////////////////////////////////////////////////////
void SwitchOFF(int nIDX)
{
digitalWrite(Switchs[nIDX], 1); // OFF
SwitchStatus[nIDX]=0;
printf("Turn OFF Switch %d\n", nIDX);
}
해당 GPIO에 끌때는 0 킬때는 1을 써준다.

이번 글은 여기까지 하도록 하겠습니다.