CS 312 - Week 7 Class 1
CS 312 Audio Programming Winter 2020

Table of Contents

7.1 Class

Project 1 class presentations.

Homework 7.1

hw711_Qt_cmidiin_gui

Qt crash course (no pun intended)

Qt folder path
WCC /Users/labuser/Qt
CMC /Applications/Qt
Qt IDE application used to design, build, and run applications
"Qt Creator"

Setup
Execute in Mac terminal.

cd $HOME312/cs312/
mkdir hw71
mkdir hw71/build-hw711
mkdir hw71/build-hw712
mkdir hw71/build-hw713
# for WCC double quotes required because of space in filename
open "/Users/labuser/Qt/Qt Creator.app"

# for CMC double
# open "/Applications/Qt/Qt Creator.app"

Open the QtCreator application.

The above script should have opened Qt Creator for you. If not, navigate to the Qt folder and double click Qt Creator.

Choose New Project

hw711_qt00.png

When the New Project window appears use these settings and click the Choose button.

hw711_qt01.png

Name the project and set the project location.

hw711_qt02.png

Define the build system as qmake.

hw711_qt02a.png

Class Information can be left unchanged.

hw711_qt03.png

Translation File can be left as <none>

hw711_qt04.png

Kit Selection - Desktop Qt … clang 64bit

hw711_qt05.png

No version control

hw711_qt06.png

Click Done and the project explorer opens. At first it appears there are errors while QtCreator is checking the project. In a few seconds the errors should disappear.

Set the build folder

Project tool

Click the Project tool in the left toobar

hw711_qt06b.png

Make these settings.

  • Shadow build checked
  • Click the Choose button and navigate to the build-hw71 folder.

    hw711_qt06c.png

Build and Run

Click the green arrow on the left toolbar to build and run the application.

hw711_qt20.png

Note:
The icon directly below Run is the "Debug Run" button. Qt comes with an excellent debugger based on llvm and clang.

A blank window will appear with the title "MainWindow".

  • You can move it around and resize it.
  • The red, yellow, and green buttons in the window title bar buttons work as expected.
  • There is an hw711_cmidiin_gui menu that you can use to Quit the app.
  • You can also quit by clicking the red close button in the window title bar.

    hw711_qt06a.png

Edit tool

The Edit tool gives you access to all your project files. The main.cpp file has been selected. You'll notice how few lines of code it uses. The mainwindow.cpp file is where you code responses to GUI actions like mouse clicks, key presses, and menu choices. The mainwindow.ui file contians the GUI design you've create.

hw711_qt07.png

Design tool

Double click the mainwindow.ui file. The .ui extension is used for User Interface files that are built using the Design tool. A blank window representing the MainWindow will open. A list of widgets you can drag into the form are shown on the left.

hw711_qt08.png

Select the MainWindow form and name the window MIDI Monitor in the windowTitle field on the right. You'll have to scroll to find it.

hw711_qt12.png

Set the minimumSize and maximumSize to give the window a fixed dimension.

hw711_qt17.png

Drag a single Push Button widget into the ui window. Then create the other three by selecting the button you just placed and Option-dragging to create copies.

hw711_qt09.png

Add a plainTextEdit widget.

hw711_qt10.png

Arrange them like this.

hw711_qt11.png

Double click the push button text to change the button names as shown.

hw711_qt13.png

Or type the name in the object properties list.

hw711_qt14.png

Assign the the variable name pushButton_record to Record button. That name will be used to reference the Record button in your code.

hw711_qt15.png

Assign these variable names to the other three buttons.

  • pushButton_play
  • pushButton_stop
  • pushButton_clear

The variable names and types of the UI objects appear in the upper right panel. This panel can also be used to select objects on the main form.

hw711_qt16.png

Select the plainTextEdit widget. We'll accept the default name, plainTextEdit, because it's the only on on the form.

hw711_qt18.png

Add the ScrollBar policies.

hw711_qt19.png

Build and Run
Click the green arrow on the left toolbar to build and run the application.

hw711_qt20.png

The window will appear with the title "MIDI Monitor".

  • You can move it around but you cannot resize it.
  • You can type into the text area.
  • If you fill the text area a scroll bar will appear and you can scroll through the text.
  • Copy and paste works.
  • Undo works.
  • Select all works.
  • Text cursor movement using arrow keys works.
  • Command-UpArrow takes you to the top of the text.
  • Command-DownArrow takes you to the bottom of the text.
  • You can click the buttons and they highlight as long as the mouse is held down.
  • You can close the window using the red close button.
  • You can close the window using the Quit command in the hw711_Qt_cmidiin_gui menu.
  • All this comes for free.

    hw711_qt21.png

Close the project from the Qt Creator File menu.

hw711_qt21a.png

hw712 and hw713 requirements

  • See class 6.2 for detailed instructions.
  • IAC Driver must be enabled in Audio MIDI Setup app.
  • Au Lab must be open with the DLSMusicDevice setup correctly.
  • VMPK must be open.
  • You must have a working midiprobe command line tool in your courses bin folder

hw712_Qt_rtmidiin

In this assignment we'll display rtmidiin messages in CMidiPacket format inside the QEditPlainText widget.

Setup

cd $HOME312/Cs312/hw71
cp -R $HOME312/cs312/hw71/hw711_Qt_cmidiin_gui $HOME312/cs312/hw71/hw712_Qt_rtmidiin
cd $HOME312/cs312/hw71/hw712_Qt_rtmidiin
mv hw711_Qt_cmidiin_gui.pro hw712_Qt_rtmidiin.pro
mv hw711_Qt_cmidiin_gui.pro.user hw712_Qt_rtmidiin.pro.user
sed -i '' s/hw711_Qt_cmidiin_gui/hw712_Qt_rtmidiin/g hw711_Qt_cmidiin_gui.pro.user
cp $HOME312/common/RtMidi.h .
cp $HOME312/common/RtMidi.cpp .
cp $HOME312/common/hw332_CMidiPacket.h .
cp $HOME312/common/hw332_CMidiPacket.cpp .

Double click hw712_Qt_rtmidiin.pro to open it in QtCreator

hw712_qt01.png

qmake

Qt uses a build system called qmake and the .pro files are counterparts to cmake's CMakeLists.txt file. Qt is working towards importing and using a CMakeLists.txt file but it was not accurate enough for use in this class.

You can add files to your project by adding them to the qmake project file, hw711_Qt_cmidiin.pro.

Task 1 - Add RtMidi and hw331_CMidiPacket files

Open the hw712_Qt_cmidiin.pro file. You should see this.

hw712_qt01a.png

Make these changes. Copy/paste snippets are shown below the screenshot.

hw712_qt02.png

You can copy/paste sections from here. Be sure to match up with the picture above.

#CONFIG += c++11
CONFIG += c++17

## Mac OS specific qmake options
macx {
TARGET = hw711_Qt_cmidiin
TEMPLATE = app
    CONFIG += app_bundle
    INCLUDEPATH += /Users/je/cs312/common
    INCLUDEPATH += /Library/Frameworks/
    QMAKE_LFLAGS += -F/Library/Frameworks
    QMAKE_CXXFLAGS += -D__MACOSX_CORE__
    LIBS += -framework CoreFoundation \
        -framework CoreMIDI \
        -framework CoreAudio
    QMAKE_CXXFLAGS_WARN_ON += -Wno-unknown-pragmas
    QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.14
#    ICON = MIDIDisplay.icns
}

SOURCES += \
    main.cpp \
    mainwindow.cpp \
    RtMidi.cpp \
    hw332_CMidiPacket.cpp

HEADERS += \
    mainwindow.h \
    RtMidi.h \
    hw332_CMidiPacket.h
FORMS += \
    mainwindow.ui

After a short pause you should see the files appear in the Edit panel.

hw712_qt03.png

Change the project build folder to build-hw722
Click the Project tool and make these changes.

hw712_qt04.png

Build and run
You should have no errors at this point.

Task 2 - Add code to open RtMidiIn and RtMidiOut

mainwindow.h
Make these changes.

hw712_qt05.png

VMPK and midiprobe

  • If VMPK is not open, open it.
  • Run /midiprobe.
  • Make note of the VMPK Input and the DLSMusicDevice Output port numbers.
je$ midiprobe

Compiled APIs:
  OS-X CoreMIDI

Current input API: OS-X CoreMIDI

There are 2 MIDI input sources available.
  Input Port #0: IAC Driver Bus 1
  Input Port #1: VMPK Output

Current output API: OS-X CoreMIDI

There are 3 MIDI output ports available.
  Output Port #0: IAC Driver Bus 1
  Output Port #1: DLSMusicDevice
  Output Port #2: VMPK Input

It's slightly confusing the way MIDI is connected. This is true for both hardware and software.

  • The device Output (VMPK Output) connects to the computer input (RtMidiIn).
  • The computer output (DLSMusicDevice) connects to the device input (IAC Driver Bus 1).

mainwindow.cpp
Make these changes

hw712_qt06.png

Then copy and implement this code below the MIDI Utilities comment.

void mycallback( double deltatime, std::vector<unsigned char>* message, void* /*userData*/ )
{
    // This is the same callback you wrote for hw621_cmidiin_cmp33
    // to convert message into CMidiPacket data
    // up until you got to std::cout

    /*
      This is new
      You cannot use std::cout
      You have to use the QPlainTextEdit function insertPlainText.
      Click the Help icon in QtCreator and read about
      QPlainTextEdit
      QString
      Remember the variable pte is a pointer to a QPlainTextEdit widget
      You have to use this
      pte->insertPlainText(const QString &text)
      You've already got a CMidiPacket and you can use mp.to_string();
      Use Qt Help or google to find out how to convert a std::string into a QString.
    */

    // I did it in two lines of code
}

void MainWindow::init_midi_in( uint32_t num )
{
    // copy from c622_rt_cmidiin
    try
    {
       // gmidiin varaiable is in .h class
       // create gmidiin
    }

    catch ( RtMidiError& error )
    {
        error.printMessage();
        exit( EXIT_FAILURE );
    }

//    open midiin port( num ); // num is from result of midiprobe.
//    set midiin callback
//    maybe call ignoreTypes
}

void MainWindow::init_midi_out( uint32_t num )
{
    // copy from c623_rt_cmidiout
    try
    {
       // gmidiout varaiable is in .h class
       // create gmidiout
    }

    catch ( RtMidiError& error )
    {
        error.printMessage();
        exit( EXIT_FAILURE );
    }
//    open midiout port( num ); // num is from result of midiprobe.
}

Task 3 - Capture MIDI input data and display it

You can use most of the same code you used the hw621_cmidiin_cmp33 callback function.

Eliminate any warnings displayed in the code
Type conversion warning can be fixed using static_cast.

Build and Run the project
If there are no errors the MIDI Monitor window will appear.

Play notes and move controls on the VMPK software keyboard. You should see CMidiPacket messages appear with timestamps in the QPlainTextEdit widget.

hw712_qt07.png

hw713_rtmidiin_buttons

In this assignment we'll hook up the pushButton widgets.

Setup

cd $HOME312/Cs312/hw71
cp -R $HOME312/cs312/hw71/hw712_Qt_rtmidiin $HOME312/cs312/hw71/hw713_Qt_rtmidiin_buttons
cd $HOME312/cs312/hw71/hw713_Qt_rtmidiin_buttons
mv hw712_Qt_rtmidiin.pro hw713_Qt_rtmidiin_buttons.pro
mv hw712_Qt_rtmidiin.pro.user hw713_Qt_rtmidiin_buttons.pro.user
sed -i '' s/hw712_Qt_rtmidiin/hw713_Qt_rtmidiin_buttons/g hw713_Qt_rtmidiin_buttons.pro.user

Change the project build folder to build-hw723
Click the Project tool and make these changes.

hw713_qt01b.png

Build and run
You should have no errors at this point.

Variables needed

mainwindow.h
Add at bottom of the file outside of the mainwindow class. Use extern.

// hw711 variables
extern QPlainTextEdit* pte;
// hw713 variables
extern bool isRecording;
extern bool isFirstTimestampCaptured;
extern uint32_t firstRecordTimestamp;
extern std::vector<CMidiPacket> vplay;

#endif // MAINWINDOW_H

mainwindow.cpp
Add after the include section. Remove extern and initialize the variables.

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QString>

#ifndef HW332_CMIDIPACKET_H_
#include "hw332_CMidiPacket.h"
#endif

using namespace CMP33;

// hw711 variables
QPlainTextEdit* pte = nullptr;
// hw713 variables
bool isRecording = false;
bool isFirstTimestampCaptured = false;
uint32_t firstRecordTimestamp = 0;
std::vector<CMidiPacket> vplay;

Functions needed

Qt responds user actions in the GUI through a signal-slot mechanism. For the most part this is transparent to your code. We won't get into advanced uses of this mechanism here.

In the mainwindow.ui form right click the Record button and choose Go to slot.

hw713_qt01.png

The available signals are displayed. Choose clicked() and click OK.

hw713_qt02.png

You'll go the the implementation of the button click in mainwindow.cpp.

hw713_qt03.png

A new private slots: section has also been added to the /MainWindow class in mainwindow.h.

hw713_qt04.png

Repeat these steps for the Stop, Play, and Clear buttons

Record button
  • No MIDI messages are recorded or displayed if isRecording == false.
  • On click sets isRecording to true.
  • Messages received from rtmidiin are displayed in CMidiPacket format.
  • On each Record button click convert the first rtmidiin timestamp to CMidiPacket format and set firstRecordTimestamp to that value.
  • Set isFirstTimestampCaptured to true.
  • On future clicks the rtmidiin timestamp will not be zero but the CMidiPacket timestamp needs be reset to zero.
  • Use the firstRecordTimestamp to adjust the CMidiPacket timestamp back to zero
  • Clear std::vector<CMidiPacket> vplay.

Add this code to on_pushButton_record_clicked()

void MainWindow::on_pushButton_record_clicked()
{
    isRecording = true;
    isFirstTimestampCaptured = false;
    vplay.clear();
}
Stop button
  • Stop recording and displaying MIDI messages.
  • The RtMidi timestamp clock keeps running.

Add this code to on_pushButton_stop_clicked()

void MainWindow::on_pushButton_stop_clicked()
{
    isRecording = false;
    isFirstTimestampCaptured = true;
}
Play button
  • This is left as an exercise for the reader.

Add this code to on_pushButton_play_clicked().

void MainWindow::on_pushButton_play_clicked()
{
    isRecording = false;
    isFirstTimestampCaptured = true;
    // now make it play
}
Clear button
  • Clear the contents of QPlainTextEdit.
  • Turn off the isRecording flag.

Add this code to on_pushButton_stop_clicked()

void MainWindow::on_pushButton_clear_clicked()
{
    pte->clear();
    isRecording = false;
    isFirstTimestampCaptured = true;
    vplay.clear();
}
Button highlighting

We'll make each button remain highlighted until another button is clicked. Buttons act like toggles.

In Design view hold down the command key and select all 4 push buttons.

hw713_qt05.png

Then add the checkable property.

hw713_qt06.png

mainwindow.h
Add the allButtonsOff() function.

hw713_qt07.png

mainwindow.cpp
Add this code for void MainWindow::allButtonsOff()

void MainWindow::allButtonsOff()
{
    ui->pushButton_record->setChecked( false );
    ui->pushButton_stop->setChecked( false );
    ui->pushButton_play->setChecked( false );
    ui->pushButton_clear->setChecked( false );
}

Then turn on each pushButton in its on_pushButton_name_clicked() implementation. For example.

void MainWindow::on_pushButton_record_clicked()
{
    // highlighting
    allButtonsOff();
    ui->pushButton_record->setChecked( true );

    isRecording = true;
    isFirstTimestampCaptured = false;
    vplay.clear();
}

Repeat these steps for each button.

Enable MIDI Output
Make these connections in VMPK.
Choose MIDI connections from the Edit menu.

hw713_qt08.png

Choose these settings.

hw713_qt09.png

Build and run
You should hear the notes you play in VMPK and see the messages appear in the MIDI Monitor window.

Submission Format

Feel free to email jellinge@carleton.edu on any part of the homework that is unclear to you. Chances are if you have questions other students do too. I will answer those questions by email to everyone in the class. The original sender will remain anonymous. I have tried to provide clues to the homework in the web pages, reading assignments, and labs. Sometimes what seems obvious to me is not obvious to students just learning C++.

Create a folder named hwNN_LastnameFirstname_LastnameFirstname.
Substitute your name and your partner's name for LastnameFirstname_LastnameFirstname.

Remember

  • Boilerplate header at top of every file.
  • Make sure your program compiles before submitting.
  • Programs that compile and produce partially correct output will be graded.
  • You can send notes to me in your homework files by enclosing them in block comments.
  • Programs that do not compile get an automatic F (59%).
  • Submit the homework in only one of the partner's folders.
  • Empty your build or build-debug folders using the command emptyBuildFolder.sh
  • Only include .h, .cpp, Makefiles, CMakeLists.txt
  • Do not include these folders/files
    .git
    .vscode
  • Double check for the above two folders using this command

    ls -a hwNN_LastnameFirstname1_LastnameFirstname2
    # if either .git or .vscode exist in the folder you're submitting remove them with
    # rm -fR .git
    # rm -fR .vscode
    

Hand-in folder contents

$HOME312/common
├── hw332_CMidiPacket.cpp
├── hw332_CMidiPacket.h
├── hw411_rand_int.cpp
├── hw411_rand_int.h
├── hw421_CDelayMs.cpp
├── hw421_CDelayMs.h
├── hw422_CAppleMidiSynth.cpp
├── hw422_CAppleMidiSynth.h
├── hw423_CMidiTrack.cpp
├── hw423_CMidiTrack.h
├── hw511_CInstrument.cpp
├── hw511_CInstrument.h
└── hw511_CInstrument.h

$HOME312/cs312/hw71
├── hw711_Qt_cmidiin_gui
│   ├── hw711_Qt_cmidiin_gui.pro
│   ├── hw711_Qt_cmidiin_gui.pro.user
│   ├── main.cpp
│   ├── mainwindow.cpp
│   ├── mainwindow.h
│   └── mainwindow.ui
├── hw712_Qt_rtmidiin
│   ├── hw332_CMidiPacket.cpp
│   ├── hw332_CMidiPacket.h
│   ├── hw712_Qt_rtmidiin.pro
│   ├── hw712_Qt_rtmidiin.pro.user
│   ├── main.cpp
│   ├── mainwindow.cpp
│   ├── mainwindow.h
│   └── mainwindow.ui
└── hw713_Qt_rtmidiin_buttons
    ├── hw332_CMidiPacket.cpp
    ├── hw332_CMidiPacket.h
    ├── hw713_Qt_rtmidiin_buttons.pro
    ├── hw713_Qt_rtmidiin_buttons.pro.user
    ├── main.cpp
    ├── mainwindow.cpp
    ├── mainwindow.h
    └── mainwindow.ui

Author: John Ellinger

Created: 2020-02-16 Sun 20:26

Validate