{"_id":"5509437a368a5617004146da","project":"55093b151c38c50d00611894","user":"55093a63a4ae180d00c0eaf5","category":{"_id":"55093ef52ee8bf2b00491916","project":"55093b151c38c50d00611894","version":"55093b161c38c50d00611897","__v":9,"pages":["55094202961f17170070abbe","5509437a368a5617004146da","550963c0dd77250d007369c1","55096a172dd6a11900e6e774","5509811add77250d00736a1f","5509815f4ba6432d00bb7875","5509913ca2b4750d00a2341e","5509916add77250d00736a55","551923b0337285170047f861"],"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-03-18T09:01:41.604Z","from_sync":false,"order":3,"slug":"code-examples","title":"Code Examples"},"version":{"_id":"55093b161c38c50d00611897","project":"55093b151c38c50d00611894","__v":4,"createdAt":"2015-03-18T08:45:10.369Z","releaseDate":"2015-03-18T08:45:10.368Z","categories":["55093b161c38c50d00611898","55093ee2961f17170070abb9","55093ee9961f17170070abba","55093ef52ee8bf2b00491916"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.0.0","version":"1.0"},"__v":5,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2015-03-18T09:20:58.728Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"auth":"required","params":[],"url":""},"isReference":false,"order":1,"body":"Welcome to _the second tutorial_ !\n\nThis example presents the fundamental steps to master the ISKN API. \nYou will learn how to connect to the Slate, how to receive the slate events, \nand how to handle those events and get their parameters.\n\nThis is a simple tone generation application using the ISKN Slate. \nThe application detects the presence of a pen in the view field of the Slate, \nand generates a tone when the pen touches the surface. \nThe tone frequency depends on the position of the pen on the Slate.  \n\nNote that you can find __the full source code__ for this example in the ISKN_API examples folder.\n\n \n## 1.Set up your project\n\nBefore you start, be sure you have your development environment set up.\nIn this example you need to create an new `Qt Widgets Application` project.\nYou can specify SlateTheremin as a project name.\n\nLink the ISKN API to your project. To do so, you can see the `first tutorial` [here](getting-started-with-iskn-api-on-qt).\n\nAdd a new class named SlateTheremin. This generates a header file `slatetheremin.h` and a source file `slatetheremin.cpp`.\n\nNow your project solution should look like this:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/LQc7Z1q6RDjwhIXR65d0_project_files_slatetheremin.png\",\n        \"project_files_slatetheremin.png\",\n        \"232\",\n        \"196\",\n        \"#44538b\",\n        \"\"\n      ]\n    }\n  ]\n}\n[/block]\n\n\n## 2.Including the ISKN API main header file\n\n+ In order to use the API, include `ISKN_API.h` in the header file of the SlateTheremin:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#include \\\"ISKN_API.h\\\"\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.h\"\n    }\n  ]\n}\n[/block]\n\n\n## 3.Preparing the event listener class\n\n+ To receive the Slate events, you need to register a listener. \nIn this example, the SlateTheremin class extends the Listener class.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"class SlateTheremin : public QMainWindow, public Listener {\\n...\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.h\"\n    }\n  ]\n}\n[/block]\n\n\nThe Listener class is an interface providing the following methods:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"void processEvent(Event &e, unsigned int timecode);\\nvoid connectionStatusChanged(bool connected);\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.h\"\n    }\n  ]\n}\n[/block]\nThese methods should be implemented by the subclass in order to receive the Slate events.\n\nAt this point, the SlateTheremin listener class is ready to be registered. \n\n## 4.Preparing the communication tools\n\nBefore registering the listener, we need to create the Slate manager object, then get the device object.\nTo do so, add those attributes to your SlateTheremin class declaration:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"SlateManager *iskn_SlateManager;\\nDevice      *iskn_Device;\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.h\"\n    }\n  ]\n}\n[/block]\n\n\nIn the constructor method of the SlateTheremin class, create the slate manager object:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"iskn_SlateManager  = new SlateManager();\\niskn_Device     = &iskn_SlateManager->getDevice();\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\n## 5. Registering the events listener\n\nOnce the Slate manager is created, it is possible to register the SlateTheremin class as a listener. \nTo do so, use the registerListener method of the Slate manager:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"iskn_SlateManager->registerListener(this);\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\n## 6. Connect to the Slate and subscribe to the services\n\nBefore being able to receive Slate events, the application should connect to the Slate. \nUse the connect method of the Slate manager object to create a connection  and `connectionStatusChanged(bool)` to check if the connection was made successfully. \nOnce connected, request the Slate description in order to update the Device object information.\nThis gathers specific Slate information such as Slate name, the size of the Slate and the writing zone of the Slate.\nThis is performed by the request method of the Slate manager object.\nFinally, you need to subscribe to automatic events. This tells the Slate which events your listener should receive.\n\nIn this application, we request four types of events:\n+ AUTO_STATUS: Asks to receive Slate status changes (battery level change, SD card insertion/removal)\n+ AUTO_SOFTWARE_EVENTS: Asks to receive software events like object status change (Object recognized (SE_OBJECT_IN), object lost(SE_OBJECT_OUT), handshake event (a periodic event used to detect that the Slate is still connected)). \n+ AUTO_HARDWARE_EVENTS: Asks to receive hardware events (When a button is pressed (New Page button, New Layer button), SD card inserted or removed) \n+ AUTO_PEN_3D: Asks to receive 3D position of the Pen tip and contact status at a 140Hz frequency when the pen is in the view field of the Slate.  \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"void connectionStatusChanged(bool connected);\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.h\"\n    }\n  ]\n}\n[/block]\n\n\nAsk for a connection in the `SlateTheremin` constructor:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Connect to the Slate\\nif (iskn_SlateManager->connect())\\n{\\n\\tcout<<\\\"Ask for connection...\\\"<<endl;\\n}\\nelse\\n{\\n  QMessageBox::critical(this, tr(\\\"Error\\\"), tr(\\\"Slate is not connected. Check your USB connection.\\\"));\\n  cout<<\\\"Could not connect...\\\"<<endl;\\n  exit(0);\\n}\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\nSubscribe to the Slate events after you have verified that the connection was successful :\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"void SlateTheremin::connectionStatusChanged(bool connected)\\n{\\n  if(connected) {\\n    // Request Slate description\\n    iskn_SlateManager->request(REQ_DESCRIPTION);\\n\\n    // Subscribe to events (Status, Pen Status, Function Call and Pen_3D)\\n    iskn_SlateManager->subscribe(\\n        AUTO_STATUS |\\n        AUTO_SOFTWARE_EVENTS |\\n        AUTO_HARDWARE_EVENTS |\\n        AUTO_PEN_3D\\n    );\\n  }\\n}\\t\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\n## 7. Receive and extract events\n\nThe processEvent method is called whenever a new event is received from the Slate.\nUse the type attribute of the Event object in order to figure out the event name.\nYou can use a switch case statement as follows:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"void SlateTheremin::processEvent(Event &e, unsigned int timecode);\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.h\"\n    }\n  ]\n}\n[/block]\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"void SlateTheremin::processEvent(Event &e, unsigned int timecode)\\n{\\n  switch (e.Type)\\n  {\\n    case EVT_STATUS:\\n    cout<<\\\"Battery : \\\"<<iskn_Device->getBatteryCharge()<<endl;\\n    break ;\\n\\n    case EVT_DESCRIPTION:\\n    {\\n      wcout<<\\\"Device id: \\\"<<iskn_Device->getDeviceName().c_str()<<endl;\\n      Rect descr = e.Description.getActiveZone();\\n      this->ClientWidth = descr.Width;\\n      this->ClientHeight = descr.Height;\\n      this->ClientLeft = descr.Left;\\n      this->ClientTop = descr.Top;\\n    }\\n    break;\\n\\n    case EVT_SOFTWARE:\\n    switch (e.SoftwareEvent.getSoftwareEventType())\\n    {\\n      case SE_OBJECT_IN :\\n      break ;\\n\\n      case SE_OBJECT_OUT :\\n      if(ui->sound_when_no_pen->isChecked() == false) {\\n        this->pos_x = 0;\\n        this->pos_y = 0;\\n      }\\n      break ;\\n\\n      case SE_HANDSHAKE :\\n      break ;\\n    }\\n    break ;\\n\\n    case EVT_HARDWARE :\\n\\n\\n    break ;\\n\\n    case EVT_PEN_3D :\\n    {\\n      float rel_pos_x = 0, rel_pos_y = 0;\\n      rel_pos_x = ((e.Pen3D.getPosition().X - this->ClientLeft) / this->ClientWidth) *100;\\n      rel_pos_y = ((e.Pen3D.getPosition().Y - this->ClientTop) / this->ClientHeight) *100;\\n\\n      // Rejecting when outside of area\\n      if(rel_pos_x <= 0.0)    rel_pos_x = 0.0;\\n      if(rel_pos_x >= 100.0)  rel_pos_x = 100.0;\\n      if(rel_pos_y <= 0.0)    rel_pos_y = 0.0;\\n      if(rel_pos_y >= 100.0)  rel_pos_y = 100.0;\\n\\n      rel_pos_y = 100-rel_pos_y;\\n\\n      if((ui->sound_update_only_pen_touch->isChecked() && e.Pen3D.Touch() == true) ||\\n         ui->sound_update_pen_on_sight->isChecked()) {\\n        this->pos_x = rel_pos_x;\\n        this->pos_y = rel_pos_y;\\n      }\\n      else {\\n        if(ui->sound_no_maintain->isChecked() && ui->sound_update_only_pen_touch->isChecked()) {\\n          this->pos_x = 0;\\n          this->pos_y = 0;\\n        }\\n      }\\n    }\\n    break ;\\n\\n    default:\\n    if (e.Type>=EVT_ERROR)\\n      cout<<\\\"Event : \\\"<<(int)e.Type<<endl;\\n    break ;\\n  }\\n}\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\n## 8. Get the pen position\n\nIn order to get pen position in percentage you need to get informations about the Slate sensitive area.\n\nWhen the Slate is connected, ask for a description block: \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"case EVT_DESCRIPTION:\\n{\\n  wcout<<\\\"Device id: \\\"<<iskn_Device->getDeviceName().c_str()<<endl;\\n  Rect descr = e.Description.getClient();\\n  this->ClientWidth = descr.Width;\\n  this->ClientHeight = descr.Height;\\n  this->ClientLeft = descr.Left;\\n  this->ClientTop = descr.Top;\\n}\\nbreak;\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\nStill in the `processEvent()` you can calculate these percentages when receiving a `EVT_PEN_3D` event, and update `this->pos_x` and `this->pos_y`.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"case EVT_PEN_3D :\\n{\\n  float rel_pos_x = 0, rel_pos_y = 0;\\n  rel_pos_x = ((e.Pen3D.getPosition().X - this->ClientLeft) / this->ClientWidth) *100;\\n  rel_pos_y = ((e.Pen3D.getPosition().Y - this->ClientTop) / this->ClientHeight) *100;\\n\\n  // Rejecting when outside of area\\n  if(rel_pos_x <= 0.0)    rel_pos_x = 0.0;\\n  if(rel_pos_x >= 100.0)  rel_pos_x = 100.0;\\n  if(rel_pos_y <= 0.0)    rel_pos_y = 0.0;\\n  if(rel_pos_y >= 100.0)  rel_pos_y = 100.0;\\n\\n  rel_pos_y = 100-rel_pos_y;\\n\\n  if((ui->sound_update_only_pen_touch->isChecked() && e.Pen3D.Touch() == true) ||\\n     ui->sound_update_pen_on_sight->isChecked()) {\\n    this->pos_x = rel_pos_x;\\n    this->pos_y = rel_pos_y;\\n  }\\n  else {\\n    if(ui->sound_no_maintain->isChecked() && ui->sound_update_only_pen_touch->isChecked()) {\\n      this->pos_x = 0;\\n      this->pos_y = 0;\\n    }\\n  }\\n}\\nbreak ;\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\n## 9. Playing the tone\n\nTo play a frequency, you have to write on a QIODevice pointer returned by the QAudioOutput: `audio_outputDevice`.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"audio_outputStream = new QAudioOutput(format, this);\\naudio_outputDevice = audio_outputStream->start();\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\nYou will need to \"wake-up\" every `FREQ_UPDATE_MS` to:\n+ Generate the tone \n+ Write your buffer `short *audio_outputBuffer` to the QAudioOutput buffer `audio_outputDevice`.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"streaming_timer.setInterval(FREQ_UPDATE_MS);\\nconnect(&streaming_timer, SIGNAL(timeout()), this, SLOT(processAudio()));\\nstreaming_timer.start();\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\nIn the slot, check if the output device is still open and if the stream is not stopped before trying to write something.\n\n+ First, call the function that fills the buffer with the frequency you want to play\n+ Next, write that buffer to the QAudioOutput buffer: `audio_outputBuffer`.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"if(audio_outputDevice->isOpen() && audio_outputStream->state() != \\t\\tQAudio::StoppedState) {\\n  // Generate tone and \\\"keep\\\" the phase in \\\"last_phase\\\"\\n  last_phase = this->generate_tone(audio_outputBuffer, this->pos_x, this->pos_y, last_phase);\\n\\n  // Write our buffer to the QAudioOutput buffer\\n  audio_outputDevice->write((char *)audio_outputBuffer, BUF_TEST * sizeof(short));\\n}\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\nThe function that generates the tone is quite lengthy but pretty easy to understand.\n\nIn order to prevent any crackling sounds, the end phase of the buffer needs to match the first valued of the next buffer.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"double SlateTheremin::generate_tone(short *buf, float x, float y , double phase) {\\n  double deltaRad = 0;\\n  double nbPeriods,newPhase;\\n\\n  // Calcul frequency regard of the position of the pen\\n  double freq_min = ui->slider_freq_min->value();     // default is 200Hz\\n  double freq_max = ui->slider_freq_max->value();     // default is 2kHz\\n  double freq_hz = (((freq_max-freq_min)/100)*y) + freq_min;\\t// Wanted audio frequency\\n\\n  // Calcul amplitude regard of the position of the pen\\n  double amp_min = (((32767)/100)*ui->slider_sound_min->value());\\n  double amp_max = (((32767)/100)*ui->slider_sound_max->value());\\n  double amp_bit = (((amp_max-amp_min)/100)*x) + amp_min;\\t\\t// Wanted audio amplitude\\n\\n  deltaRad =  2.0*M_PI*freq_hz / SAMPLE_RATE;\\n\\n  for(unsigned long i=0 ; i < BUF_TEST ; i++)\\n  {\\n    // Sine waveform\\n    if(ui->waveform_sine->isChecked()) {\\n      buf[i] = amp_bit * sin((i)*deltaRad + phase);\\n    }\\n    // Sine distorted waveform\\n    else if(ui->waveform_distord_sine->isChecked()) {\\n      if(qAbs(amp_bit * sin((i)*deltaRad + phase)) < amp_bit/1.5)\\n        buf[i] = amp_bit * sin((i)*deltaRad + phase);\\n      else\\n        buf[i] = amp_bit;\\n    }\\n    // Square waveform\\n    else if(ui->waveform_square->isChecked()) {\\n      if(sin((i)*deltaRad + phase) < 0)\\n        buf[i] = -amp_bit;\\n      else\\n        buf[i] = amp_bit;\\n    }\\n  }\\n\\n  newPhase = (BUF_TEST)*deltaRad + phase;\\n  nbPeriods = floor(newPhase / (2.0*M_PI));\\n\\n  return newPhase - ( nbPeriods * 2.0 * M_PI );\\n}\",\n      \"language\": \"cplusplus\",\n      \"name\": \"SlateTheremin.cpp\"\n    }\n  ]\n}\n[/block]\n\n\n## 10. Run your project\n\nYour project is now ready to run.\n\n\n## 11. The ISKN Slate Theremin\n\nHere is the example created by the ISKN Team with explanations:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/5ZPy7lCTK26Md4hOUJTQ_SlateThereminMain.png\",\n        \"SlateThereminMain.png\",\n        \"800\",\n        \"806\",\n        \"#c36240\",\n        \"\"\n      ]\n    }\n  ]\n}\n[/block]","excerpt":"","slug":"slate-theremin-example-on-qt","type":"basic","title":"Slate Theremin example on Qt"}

Slate Theremin example on Qt


Welcome to _the second tutorial_ ! This example presents the fundamental steps to master the ISKN API. You will learn how to connect to the Slate, how to receive the slate events, and how to handle those events and get their parameters. This is a simple tone generation application using the ISKN Slate. The application detects the presence of a pen in the view field of the Slate, and generates a tone when the pen touches the surface. The tone frequency depends on the position of the pen on the Slate. Note that you can find __the full source code__ for this example in the ISKN_API examples folder. ## 1.Set up your project Before you start, be sure you have your development environment set up. In this example you need to create an new `Qt Widgets Application` project. You can specify SlateTheremin as a project name. Link the ISKN API to your project. To do so, you can see the `first tutorial` [here](getting-started-with-iskn-api-on-qt). Add a new class named SlateTheremin. This generates a header file `slatetheremin.h` and a source file `slatetheremin.cpp`. Now your project solution should look like this: [block:image] { "images": [ { "image": [ "https://files.readme.io/LQc7Z1q6RDjwhIXR65d0_project_files_slatetheremin.png", "project_files_slatetheremin.png", "232", "196", "#44538b", "" ] } ] } [/block] ## 2.Including the ISKN API main header file + In order to use the API, include `ISKN_API.h` in the header file of the SlateTheremin: [block:code] { "codes": [ { "code": "#include \"ISKN_API.h\"", "language": "cplusplus", "name": "SlateTheremin.h" } ] } [/block] ## 3.Preparing the event listener class + To receive the Slate events, you need to register a listener. In this example, the SlateTheremin class extends the Listener class. [block:code] { "codes": [ { "code": "class SlateTheremin : public QMainWindow, public Listener {\n...", "language": "cplusplus", "name": "SlateTheremin.h" } ] } [/block] The Listener class is an interface providing the following methods: [block:code] { "codes": [ { "code": "void processEvent(Event &e, unsigned int timecode);\nvoid connectionStatusChanged(bool connected);", "language": "cplusplus", "name": "SlateTheremin.h" } ] } [/block] These methods should be implemented by the subclass in order to receive the Slate events. At this point, the SlateTheremin listener class is ready to be registered. ## 4.Preparing the communication tools Before registering the listener, we need to create the Slate manager object, then get the device object. To do so, add those attributes to your SlateTheremin class declaration: [block:code] { "codes": [ { "code": "SlateManager *iskn_SlateManager;\nDevice *iskn_Device;", "language": "cplusplus", "name": "SlateTheremin.h" } ] } [/block] In the constructor method of the SlateTheremin class, create the slate manager object: [block:code] { "codes": [ { "code": "iskn_SlateManager = new SlateManager();\niskn_Device = &iskn_SlateManager->getDevice();", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] ## 5. Registering the events listener Once the Slate manager is created, it is possible to register the SlateTheremin class as a listener. To do so, use the registerListener method of the Slate manager: [block:code] { "codes": [ { "code": "iskn_SlateManager->registerListener(this);", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] ## 6. Connect to the Slate and subscribe to the services Before being able to receive Slate events, the application should connect to the Slate. Use the connect method of the Slate manager object to create a connection and `connectionStatusChanged(bool)` to check if the connection was made successfully. Once connected, request the Slate description in order to update the Device object information. This gathers specific Slate information such as Slate name, the size of the Slate and the writing zone of the Slate. This is performed by the request method of the Slate manager object. Finally, you need to subscribe to automatic events. This tells the Slate which events your listener should receive. In this application, we request four types of events: + AUTO_STATUS: Asks to receive Slate status changes (battery level change, SD card insertion/removal) + AUTO_SOFTWARE_EVENTS: Asks to receive software events like object status change (Object recognized (SE_OBJECT_IN), object lost(SE_OBJECT_OUT), handshake event (a periodic event used to detect that the Slate is still connected)). + AUTO_HARDWARE_EVENTS: Asks to receive hardware events (When a button is pressed (New Page button, New Layer button), SD card inserted or removed) + AUTO_PEN_3D: Asks to receive 3D position of the Pen tip and contact status at a 140Hz frequency when the pen is in the view field of the Slate. [block:code] { "codes": [ { "code": "void connectionStatusChanged(bool connected);", "language": "cplusplus", "name": "SlateTheremin.h" } ] } [/block] Ask for a connection in the `SlateTheremin` constructor: [block:code] { "codes": [ { "code": "// Connect to the Slate\nif (iskn_SlateManager->connect())\n{\n\tcout<<\"Ask for connection...\"<<endl;\n}\nelse\n{\n QMessageBox::critical(this, tr(\"Error\"), tr(\"Slate is not connected. Check your USB connection.\"));\n cout<<\"Could not connect...\"<<endl;\n exit(0);\n}", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] Subscribe to the Slate events after you have verified that the connection was successful : [block:code] { "codes": [ { "code": "void SlateTheremin::connectionStatusChanged(bool connected)\n{\n if(connected) {\n // Request Slate description\n iskn_SlateManager->request(REQ_DESCRIPTION);\n\n // Subscribe to events (Status, Pen Status, Function Call and Pen_3D)\n iskn_SlateManager->subscribe(\n AUTO_STATUS |\n AUTO_SOFTWARE_EVENTS |\n AUTO_HARDWARE_EVENTS |\n AUTO_PEN_3D\n );\n }\n}\t", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] ## 7. Receive and extract events The processEvent method is called whenever a new event is received from the Slate. Use the type attribute of the Event object in order to figure out the event name. You can use a switch case statement as follows: [block:code] { "codes": [ { "code": "void SlateTheremin::processEvent(Event &e, unsigned int timecode);", "language": "cplusplus", "name": "SlateTheremin.h" } ] } [/block] [block:code] { "codes": [ { "code": "void SlateTheremin::processEvent(Event &e, unsigned int timecode)\n{\n switch (e.Type)\n {\n case EVT_STATUS:\n cout<<\"Battery : \"<<iskn_Device->getBatteryCharge()<<endl;\n break ;\n\n case EVT_DESCRIPTION:\n {\n wcout<<\"Device id: \"<<iskn_Device->getDeviceName().c_str()<<endl;\n Rect descr = e.Description.getActiveZone();\n this->ClientWidth = descr.Width;\n this->ClientHeight = descr.Height;\n this->ClientLeft = descr.Left;\n this->ClientTop = descr.Top;\n }\n break;\n\n case EVT_SOFTWARE:\n switch (e.SoftwareEvent.getSoftwareEventType())\n {\n case SE_OBJECT_IN :\n break ;\n\n case SE_OBJECT_OUT :\n if(ui->sound_when_no_pen->isChecked() == false) {\n this->pos_x = 0;\n this->pos_y = 0;\n }\n break ;\n\n case SE_HANDSHAKE :\n break ;\n }\n break ;\n\n case EVT_HARDWARE :\n\n\n break ;\n\n case EVT_PEN_3D :\n {\n float rel_pos_x = 0, rel_pos_y = 0;\n rel_pos_x = ((e.Pen3D.getPosition().X - this->ClientLeft) / this->ClientWidth) *100;\n rel_pos_y = ((e.Pen3D.getPosition().Y - this->ClientTop) / this->ClientHeight) *100;\n\n // Rejecting when outside of area\n if(rel_pos_x <= 0.0) rel_pos_x = 0.0;\n if(rel_pos_x >= 100.0) rel_pos_x = 100.0;\n if(rel_pos_y <= 0.0) rel_pos_y = 0.0;\n if(rel_pos_y >= 100.0) rel_pos_y = 100.0;\n\n rel_pos_y = 100-rel_pos_y;\n\n if((ui->sound_update_only_pen_touch->isChecked() && e.Pen3D.Touch() == true) ||\n ui->sound_update_pen_on_sight->isChecked()) {\n this->pos_x = rel_pos_x;\n this->pos_y = rel_pos_y;\n }\n else {\n if(ui->sound_no_maintain->isChecked() && ui->sound_update_only_pen_touch->isChecked()) {\n this->pos_x = 0;\n this->pos_y = 0;\n }\n }\n }\n break ;\n\n default:\n if (e.Type>=EVT_ERROR)\n cout<<\"Event : \"<<(int)e.Type<<endl;\n break ;\n }\n}", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] ## 8. Get the pen position In order to get pen position in percentage you need to get informations about the Slate sensitive area. When the Slate is connected, ask for a description block: [block:code] { "codes": [ { "code": "case EVT_DESCRIPTION:\n{\n wcout<<\"Device id: \"<<iskn_Device->getDeviceName().c_str()<<endl;\n Rect descr = e.Description.getClient();\n this->ClientWidth = descr.Width;\n this->ClientHeight = descr.Height;\n this->ClientLeft = descr.Left;\n this->ClientTop = descr.Top;\n}\nbreak;", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] Still in the `processEvent()` you can calculate these percentages when receiving a `EVT_PEN_3D` event, and update `this->pos_x` and `this->pos_y`. [block:code] { "codes": [ { "code": "case EVT_PEN_3D :\n{\n float rel_pos_x = 0, rel_pos_y = 0;\n rel_pos_x = ((e.Pen3D.getPosition().X - this->ClientLeft) / this->ClientWidth) *100;\n rel_pos_y = ((e.Pen3D.getPosition().Y - this->ClientTop) / this->ClientHeight) *100;\n\n // Rejecting when outside of area\n if(rel_pos_x <= 0.0) rel_pos_x = 0.0;\n if(rel_pos_x >= 100.0) rel_pos_x = 100.0;\n if(rel_pos_y <= 0.0) rel_pos_y = 0.0;\n if(rel_pos_y >= 100.0) rel_pos_y = 100.0;\n\n rel_pos_y = 100-rel_pos_y;\n\n if((ui->sound_update_only_pen_touch->isChecked() && e.Pen3D.Touch() == true) ||\n ui->sound_update_pen_on_sight->isChecked()) {\n this->pos_x = rel_pos_x;\n this->pos_y = rel_pos_y;\n }\n else {\n if(ui->sound_no_maintain->isChecked() && ui->sound_update_only_pen_touch->isChecked()) {\n this->pos_x = 0;\n this->pos_y = 0;\n }\n }\n}\nbreak ;", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] ## 9. Playing the tone To play a frequency, you have to write on a QIODevice pointer returned by the QAudioOutput: `audio_outputDevice`. [block:code] { "codes": [ { "code": "audio_outputStream = new QAudioOutput(format, this);\naudio_outputDevice = audio_outputStream->start();", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] You will need to "wake-up" every `FREQ_UPDATE_MS` to: + Generate the tone + Write your buffer `short *audio_outputBuffer` to the QAudioOutput buffer `audio_outputDevice`. [block:code] { "codes": [ { "code": "streaming_timer.setInterval(FREQ_UPDATE_MS);\nconnect(&streaming_timer, SIGNAL(timeout()), this, SLOT(processAudio()));\nstreaming_timer.start();", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] In the slot, check if the output device is still open and if the stream is not stopped before trying to write something. + First, call the function that fills the buffer with the frequency you want to play + Next, write that buffer to the QAudioOutput buffer: `audio_outputBuffer`. [block:code] { "codes": [ { "code": "if(audio_outputDevice->isOpen() && audio_outputStream->state() != \t\tQAudio::StoppedState) {\n // Generate tone and \"keep\" the phase in \"last_phase\"\n last_phase = this->generate_tone(audio_outputBuffer, this->pos_x, this->pos_y, last_phase);\n\n // Write our buffer to the QAudioOutput buffer\n audio_outputDevice->write((char *)audio_outputBuffer, BUF_TEST * sizeof(short));\n}", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] The function that generates the tone is quite lengthy but pretty easy to understand. In order to prevent any crackling sounds, the end phase of the buffer needs to match the first valued of the next buffer. [block:code] { "codes": [ { "code": "double SlateTheremin::generate_tone(short *buf, float x, float y , double phase) {\n double deltaRad = 0;\n double nbPeriods,newPhase;\n\n // Calcul frequency regard of the position of the pen\n double freq_min = ui->slider_freq_min->value(); // default is 200Hz\n double freq_max = ui->slider_freq_max->value(); // default is 2kHz\n double freq_hz = (((freq_max-freq_min)/100)*y) + freq_min;\t// Wanted audio frequency\n\n // Calcul amplitude regard of the position of the pen\n double amp_min = (((32767)/100)*ui->slider_sound_min->value());\n double amp_max = (((32767)/100)*ui->slider_sound_max->value());\n double amp_bit = (((amp_max-amp_min)/100)*x) + amp_min;\t\t// Wanted audio amplitude\n\n deltaRad = 2.0*M_PI*freq_hz / SAMPLE_RATE;\n\n for(unsigned long i=0 ; i < BUF_TEST ; i++)\n {\n // Sine waveform\n if(ui->waveform_sine->isChecked()) {\n buf[i] = amp_bit * sin((i)*deltaRad + phase);\n }\n // Sine distorted waveform\n else if(ui->waveform_distord_sine->isChecked()) {\n if(qAbs(amp_bit * sin((i)*deltaRad + phase)) < amp_bit/1.5)\n buf[i] = amp_bit * sin((i)*deltaRad + phase);\n else\n buf[i] = amp_bit;\n }\n // Square waveform\n else if(ui->waveform_square->isChecked()) {\n if(sin((i)*deltaRad + phase) < 0)\n buf[i] = -amp_bit;\n else\n buf[i] = amp_bit;\n }\n }\n\n newPhase = (BUF_TEST)*deltaRad + phase;\n nbPeriods = floor(newPhase / (2.0*M_PI));\n\n return newPhase - ( nbPeriods * 2.0 * M_PI );\n}", "language": "cplusplus", "name": "SlateTheremin.cpp" } ] } [/block] ## 10. Run your project Your project is now ready to run. ## 11. The ISKN Slate Theremin Here is the example created by the ISKN Team with explanations: [block:image] { "images": [ { "image": [ "https://files.readme.io/5ZPy7lCTK26Md4hOUJTQ_SlateThereminMain.png", "SlateThereminMain.png", "800", "806", "#c36240", "" ] } ] } [/block]