CS 312 - Week 7 Class 3
CS 312 Audio Programming Winter 2020
Table of Contents
7.3 Class
Install RtAudio
RtAudio is and open source cross platform audio library. It's written in C++ by the same authors that wrote the RtMidi library. The libsnd file library was designed to read and write sound files. The hw811 homework projects wrote samples one at a time to a file and then saved that file to disk. Any audio processing on that file was done post writing. RtAudio is designed to read and write buffers of samples that are processed in close to real time before being sent to the audio output. The size of the buffers determine the latency involved between the time input is received and output is heard. Latency increases with larger block sizes. The audio processing is done in a callback routine that must be written as efficiently as possible. That means allocating memory, or writing output to disk, and updating the GUI are not recommended except for debugging purposes.
Setup
Open the RtAudio home page.
https://www.music.mcgill.ca/~gary/rtaudio/
Look for the Download section and download Version 5.1.0.
The download file will be named rtaudio-5.1.0.tar.gz
Copy rtaudio-5.1.0.tar.gz to your $HOME312 directory at the same level as your bin folder.
Double click to extract.
cd to your common folder
cd $HOME312/common mkdir RtAudio
Copy the RtAudio.h and RtAudio.cpp files from the download into the common/RtAudio folder.
cd to your cs312 folder
cd $HOME312/cs312 mkdir hw73 mkdir hw73/hw731_playsaw
Copy the playsaw.cpp file from the downloaded rtaudio/tests folder into the hw73/hw731_playsaw folder.
Rename playsaw.cpp to hw731_playsaw.cpp
mv playsaw.cpp hw731_playsaw.cpp
Reading
Equal Temperament
The Equal Temperament tuning system we use today was established during the 18th and early 19th centuries. Equal Temperament divides the octave into twelve equal half steps. On the piano the only intervals that are perfectly in tune are octaves, everything else is equally out of tune. Octaves have a frequency ratio of 1:2 and are divided into 12 equal steps. The frequency ratio between each half step is
\[{2^{\frac{1}{{12}}}}\]
In order to calculate the pitch of any note you need a reference frequency and you need to know how many half steps distant it is from that reference. The standard reference pitch is A 440 Hz (called A440) and is MIDI note number 69.
MIDI Note Number To Frequency
The pitch standard in music is 440Hz, the note A above middle C on the piano. Middle C is MIDI note number 60 and A440 is MIDI Note number 69. The frequency of any midiNote is found using the formula below.
\[frequency = 440*{2^{\frac{{midiNote - 69}}{{12}}}}\]
For example Middle C has a frequency of
\[Middle C = 440*{2^{\frac{{60 - 69}}{{12}}}} = 440*{2^{\frac{{ - 9}}{{12}}}} = 261.63Hz\]
Half steps, whole steps, and scales
In musical terms the 12 steps in one octave are called half steps. Two consecutive half steps are called a whole step. Many musical scales are constructed solely out of whole step and half step patterns with the last note of the scale being an octave higher that the first note.
In hw735_qtplaysine_midi2freq you'll create buttons to play the C Major scale whose pattern of whole steps and half steps is WWHWWWH. In MIDI note terms it's the pattern {0,2,4,5,7,9,11,12} plus a starting offset. Instead of using MIDI output you'll have to calculate the frequencies in Hz and generate sine wave tones at that frequency.
Homework 7.3
hw731_playsaw
This is a VScode CMake project.
Create the CMakeLists.txt file
I modified the one from hw732_gensin_phasor.
cmake_minimum_required(VERSION 3.5) set(PROJECT_NAME hw731_playsaw) project(${PROJECT_NAME}) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Like RtMidi, RtAudio needs this compiler define -D__MACOSX_CORE__ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall -D__MACOSX_CORE__") # Change this path to your common folder set(COMMON "/Users/je/cs312/_cs312/common") set(LSF "${COMMON}/libsndfile") set(ULL "/usr/local/lib") set (RTA "/Users/je/cs312/_cs312/common/RtAudio") set(SOURCE_FILES ${LSF}/sndfile.hh ${RTA}/RtAudio.cpp ${PROJECT_NAME}.cpp ) add_executable( ${PROJECT_NAME} ${SOURCE_FILES}) target_include_directories(${PROJECT_NAME} PRIVATE ${COMMON} ${LSF} ${RTA}) target_link_libraries(${PROJECT_NAME} ${ULL}/libsndfile.1.dylib ${ULL}/librtaudio.6.1.0.dylib "-framework CoreAudio" "-framework CoreFoundation")
Build and run hw731_playsaw
cd build
cmake .. && make && ./hw73_playsaw
Read the usage comments and try again …
./hw731_playsaw 1 44100
and again
./hw731_playsaw 2 44100
Here's what the single channel saw wave looks like in Audacity.
In the two channel version the frequencies (periods) of each channel are different.
hw732_playsine
This is a VScode CMake project.
We're going to refactor hw73_playsaw.cpp leaving only essentials necessary to generate and play a mono audio file.
Close any open hw73_playsaw files or projects.
Setup
cd $HOME312/cs312/hw73 cp -r hw731_playsaw hw732_playsine cd hw732_playsine mv hw731_playsaw.cpp hw732_playsine.cpp sed -i '' s/saw/sine/g hw732_playsine.cpp sed -i '' s/hw731_playsaw/hw732_playsine/g CMakeLists.txt
hw732_playsine.cpp
Open this pdf in new tab.
hw732_playsaw.pdf
Then make these changes. I've colored the text.
- Black
- leave alone
- Red
- delete
- Blue
- replacement text for red text immediately above
CMakeLists.txt
Copy/paste
Build using cmake
Open hw732_playsine folder in VScode.
hw733_playsine_rta
This is a VScode CMake project.
We're going to refactor hw732_playsine.cpp and move all RtAudio related calls to their own rtaudioutils.h and rtaudioutils.cpp files. This will make hw733_playsine_rta.cpp much easier to read.
Setup
cd $HOME312/cs312/hw73 cp -r hw732_playsine hw733_playsine_rta cd hw733_playsine_rta touch scratch.cpp touch rtaudioutils.h touch rtaudioutils.cpp mv hw732_playsine.cpp hw733_playsine_rta.cpp sed -i '' s/hw732_playsine/hw733_playsine_rta/g CMakeLists.txt
hw733_playsine_rta.cpp
Open this pdf in new tab.
hw733_playsine.pdf
Then make these changes. It's different than above. I've colored the text.
- Black
- leave alone
- Red
- delete and replace as stated
- Blue
- Move to scratch.cpp and delete from hw733_playsine_rta.cpp
You might find it easier to move the text to scratch.cpp if you split the VScode window into two panels.
Save both.
rtaudioutils.h
Copy/paste. This was all stuff that you removed from hw733_playsine_rta.cpp copied to scratch.cpp
#ifndef RTAUDIOUTILS_H_ #define RTAUDIOUTILS_H_ #endif #ifndef __RTAUDIO_H #include "RtAudio.h" #endif #include <cmath> // for M_PI // EITHER/OR typedef float MY_TYPE; #define FORMAT RTAUDIO_FLOAT32 // #define FORMAT RTAUDIO_FLOAT64 // #define SCALE 1.0 // EITHER/OR END const int FS = 44100; const MY_TYPE k2PI = 2 * M_PI; const MY_TYPE T = 1.0 / FS; // sample period const MY_TYPE k2PIT = k2PI * T; const unsigned int kRTABUFFER_SZ = 512; const unsigned int callbackReturnValue = 1; extern RtAudio::StreamOptions options; extern RtAudio::StreamParameters oParams; struct RTA { // RtAudio stuff unsigned int channels; unsigned int frameCounter; bool checkCount; unsigned int nFrames; unsigned int bufferFrames; unsigned int fs; unsigned int device; unsigned int offset; // constructor RTA(); }; extern RTA rta; // declared here, instantiated in globals.cpp extern void errorCallback(RtAudioError::Type type, const std::string &errorText);
hw733_playsine_rta.cpp
Open hw733_playsine_rta.cpp. Select the Problems tab in the Terminal panel and you'll see eight problems.
They're related to the RTA struct in rtaudioutils.h and can be fixed by adding the the rta. prefix. When fixed you should be free of errors.
rtaudioutils.cpp
Copy/paste. Most of the hw733_playsine_rta.cpp variables are now part of a class (a struct is a class).
#ifndef RTAUDIOUTILS_H_ #include "rtaudioutils.h" #endif RtAudio::StreamOptions options; RtAudio::StreamParameters oParams; RTA rta; // constructor RTA::RTA() : channels{1}, frameCounter{0}, checkCount{false}, nFrames{0}, bufferFrames{kBUFFER_SZ}, fs{FS}, device{0}, offset{0} { oParams.deviceId = device; oParams.nChannels = channels; oParams.firstChannel = offset; options.flags = RTAUDIO_HOG_DEVICE; options.flags |= RTAUDIO_SCHEDULE_REALTIME; options.flags |= RTAUDIO_NONINTERLEAVED; } void errorCallback( RtAudioError::Type type, const std::string& errorText ) { // This example error handling function does exactly the same thing // as the embedded RtAudio::error() function. std::cout << "in errorCallback" << std::endl; if ( type == RtAudioError::WARNING ) std::cerr << '\n' << errorText << "\n\n"; else if ( type != RtAudioError::WARNING ) throw( RtAudioError( errorText, type ) ); }
hw733_playsine_rta.cpp
Should be ok.
CMakeLists.txt
You'll need to add the rtaudioutils.cpp source file.
Build and run using cmake
You should hear a sine wave at 440Hz.
Once you get the program running add this callback function to hw733_playsine_rta.cpp.
int sineSweep(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *userData) { MY_TYPE *buffer = (MY_TYPE *)outputBuffer; if (status) std::cout << "Stream underflow detected!" << std::endl; MY_TYPE freq = 440.0; MY_TYPE amp = 1.0; static MY_TYPE phz = 0; // must be static MY_TYPE phzinc = k2PIT * freq; // add a new static variable that retains its value across multiple callbacks static MY_TYPE tmpfreq{freq}; // //phase increment formula for (uint32_t i = 0; i < nBufferFrames; i++) { phzinc = k2PIT * (freq + tmpfreq); tmpfreq += 0.01; if (tmpfreq > freq * 3) tmpfreq = freq; *buffer++ = amp * sin(phz); phz += phzinc; if (phz >= k2PI) phz -= k2PI; } rta.frameCounter += nBufferFrames; if (rta.checkCount && (rta.frameCounter >= rta.nFrames)) return callbackReturnValue; return 0; }
Then in main() change this
// dac.openStream(&oParams, NULL, FORMAT, rta.fs, &rta.bufferFrames, &sine, (void *)data, &options, &errorCallback); dac.openStream(&oParams, NULL, FORMAT, rta.fs, &rta.bufferFrames, &sineSweep, (void *)data, &options, &errorCallback);
Build and run again
Experiment
Experiment with freq, tmpfreq, phz variables in the callback function and you can get some pretty weird sounds
hw734_qtplaysin
This is a QtCreator qmake project.
Setup
Create a new QT Widgets Application project.
Name it hw734_qtplaysin and save it in ~/cs312/hw73
Use qmake build
Accept MainWindow defaults
Use Desktop…clang 64bit Kit
Select <None> for version control and then click Done.
Open Mac Terminal and execute these commands
cd $HOME312/cs312/hw73/hw734_qtplaysine mkdir build-debug
Open the Projects view and add the build-debug folder to $HOME312/cs312/hw73/hw734_qtplaysin.
Folder layout
Create this form
Back to Edit view. Open Forms mainwindow.ui and create this layout.
Hint:
Use the arrow keys to align widgets to the grid.
Assign these variable names to the widgets
Set the min/max amplitude range and value for horizontalSlider_freq.
These values will used in code and be displayed 20-4000 the label_freqValue.
Set the min/max amplitude range and value for horizontalSlider_amp.
These values will converted to 0-1.0 in your code and displayed as 0-1.0 in the label_ampValue.
Use the default settings for the Play and Stop buttons.
Build and run
Click the Run button in Qt Creator.
It should compile, run and display then MainWindow.
Next step is to make it do something.
Add hw732_playsine code
cd $HOME312/cs312/hw73/hw734_qtplaysine cp $HOME312/cs312/hw73/hw733_playsine_rta/rtaudioutils.cpp . cp $HOME312/cs312/hw73/hw733_playsine_rta/rtaudioutils.h .
Modify hw734_qtplaysine.pro
Open hw734_qtplaysine.pro in a new tab.
The lines in red are needed. Copy/replace.
This is the makefile used by qmake.
Clean and run qmake
This is recommended after you modify a .pro file.
Choose Clean All from the Build menu.
Choose Run qmake from the Build menu.
Build and run
It should still compile and display MainWindow.
Add/move code to mainwindow.cpp
mainwindow.cpp
Add this code below #include "ui_mainwindow.h". It's code copied directly from hw733_playsine_rta.cpp.
#ifndef RTAUDIOUTILS_H_ #include "rtaudioutils.h" #endif #include <iostream> #include <cmath> // for M_PI // One-channel sine wave generator replaces saw callback function int sine(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *userData) { MY_TYPE *buffer = (MY_TYPE *)outputBuffer; if (status) std::cout << "Stream underflow detected!" << std::endl; static MY_TYPE phz = 0; MY_TYPE freq = 440.0; MY_TYPE amp = 1.0; // //phase increment formula const MY_TYPE phzinc = k2PIT * freq; for (uint32_t i = 0; i < nBufferFrames; i++) { *buffer++ = amp * sin(phz); phz += phzinc; if (phz >= k2PI) phz -= k2PI; } rta.frameCounter += nBufferFrames; if (rta.checkCount && (rta.frameCounter >= rta.nFrames)) return callbackReturnValue; return 0; }
Add this code into main(…) directly below ui->setupUi( this );. It's code copied directly from hw733_playsine_rta.cpp.
RtAudio dac; if (dac.getDeviceCount() < 1) { std::cout << "\nNo audio devices found!\n"; exit(1); } MY_TYPE *data = (MY_TYPE *)calloc(rta.channels, sizeof(MY_TYPE)); // Let RtAudio print messages to stderr. dac.showWarnings(true); if (rta.device == 0) oParams.deviceId = dac.getDefaultOutputDevice(); try { dac.openStream(&oParams, NULL, FORMAT, rta.fs, &rta.bufferFrames, &sine, (void *)data, &options, &errorCallback); // dac.openStream(&oParams, NULL, FORMAT, rta.fs, &rta.bufferFrames, &sineSweep, (void *)data, &options, &errorCallback); dac.startStream(); } catch (RtAudioError &e) { e.printMessage(); goto cleanup; } char input; //std::cout << "Stream latency = " << dac.getStreamLatency() << "\n" << std::endl; std::cout << "\nPlaying ... press <enter> to quit (buffer size = " << rta.bufferFrames << ").\n"; std::cin.get(input); try { // Stop the stream dac.stopStream(); } catch (RtAudioError &e) { e.printMessage(); } cleanup: if (dac.isStreamOpen()) dac.closeStream(); free(data);
Build and Run
You should hear the sine wave.
But there's two big problems.
- You cannot see the GUI window
- You cannot type return to quit.
The only way to quit is by clicking this button in QtCreator.
The fix for problem 1 is to comment out these lines in main()
// char input; // //std::cout << "Stream latency = " << dac.getStreamLatency() << "\n" << std::endl; // std::cout << "\nPlaying ... press <enter> to quit (buffer size = " << rta.bufferFrames << ").\n"; // std::cin.get( input );
The window appeared but the sound disappeared. The reason for no sound can be found by tracing through the code in the Debugger. You'll discover that dac.startStream() is called and immediately afterwards dac.stopStream() is called. There's no time to process the callback function.
The fix involves refactoring the RtAudio code in main() into functions that can be called as needed outside of main(). These are the RtAudio functions you'll need.
- openStream(…)
- closeStream()
- startStream()
- stopStream()
- isStreamOpen()
- getDefaultOutputDevice()
Here's how I'd organize the calls.
- Call open_dac_stream() (you implement it) once in the MainWindow constructor but don't call startStream().
- Call closeStream() in the MainWindow destructor but first check isStreamOpen().
- Call startStream() only when the Play button is clicked.
- Call stopStream() only when the Stop button is clicked.
Then you'll still need to deal with the frequency and amplitude sliders and value labels.
FYI
If you're using a third party USB audio interface you might need to find it's ID. This code came from one of the RtAudio examples. You can easily examine the DeviceInfo struct in the Debugger.
unsigned int numDev = dac.getDeviceCount(); RtAudio::DeviceInfo di; for ( unsigned int i = 0; i < numDev; ++i ) { // use the Debugger if you need to know deviceID di = dac.getDeviceInfo( i ); } if ( !isPlaying ) return;
mainwindow.h
Copy/paste
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #ifndef RTAUDIOUTILS_H_ #include "rtaudioutils.h" #endif QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow( QWidget* parent = nullptr ); ~MainWindow(); private: Ui::MainWindow* ui; RtAudio dac; void init_controls(); void open_dac_stream(); }; #endif // MAINWINDOW_H
mainwindow.cpp
Here's some skeleton code to get you started.
Copy/replace/paste.
#include "mainwindow.h" #include "ui_mainwindow.h" #ifndef RTAUDIOUTILS_H_ #include "rtaudioutils.h" #endif #include <cmath> // for M_PI #include <iostream> /*------------------------------------------------- two new variables -------------------------------------------------*/ RtAudio dac; bool isPlaying = true; // One-channel sine wave generator replaces saw callback function // unchanged int sine( void* outputBuffer, void* inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void* userData ) { MY_TYPE* buffer = ( MY_TYPE* )outputBuffer; if ( status ) std::cout << "Stream underflow detected!" << std::endl; static MY_TYPE phz = 0; MY_TYPE freq = 440.0; MY_TYPE amp = 1.0; // //phase increment formula const MY_TYPE phzinc = k2PIT * freq; for ( uint32_t i = 0; i < nBufferFrames; i++ ) { *buffer++ = amp * sin( phz ); phz += phzinc; if ( phz >= k2PI ) phz -= k2PI; } rta.frameCounter += nBufferFrames; if ( rta.checkCount && ( rta.frameCounter >= rta.nFrames ) ) return callbackReturnValue; return 0; } MainWindow::MainWindow( QWidget* parent ) : QMainWindow( parent ), ui( new Ui::MainWindow ) { ui->setupUi( this ); /*------------------------------------------------- init_controls() open_dac_stream() but don't call startStream() -------------------------------------------------*/ } MainWindow::~MainWindow() { delete ui; /*------------------------------------------------- if dac.isStreamOpen() then close it -------------------------------------------------*/ } void MainWindow::init_controls() { /*------------------------------------------------- set these controls to their default values horizontalSlider_freq horizontalSlider_amp label_freqValue label_ampValue -------------------------------------------------*/ } void MainWindow::open_dac_stream() { /*------------------------------------------------- figure it out don't forget about QApplication::processEvents(); -------------------------------------------------*/ } void MainWindow::close_dac_stream() { /*------------------------------------------------- figure it out -------------------------------------------------*/ } // Use right-click "Go to Slot" on the control in Design view void MainWindow::on_pushButton_play_clicked() { /*------------------------------------------------- figure it out -------------------------------------------------*/ } // Use right-click "Go to Slot" on the control in Design view void MainWindow::on_pushButton_stop_clicked() { /*------------------------------------------------- figure it out -------------------------------------------------*/ } // Use right-click "Go to Slot" on the control in Design view void MainWindow::on_horizontalSlider_freq_valueChanged( int value ) { /*------------------------------------------------- figure it out remember to update label_freqValue whenever the slider moves -------------------------------------------------*/ } // Use right-click "Go to Slot" on the control in Design view void MainWindow::on_horizontalSlider_amp_valueChanged( int value ) { /*------------------------------------------------- figure it out remember to update label_ampValue whenever the slider moves remember code and label_ampValue use/display values as 0.0-1.0 -------------------------------------------------*/ }
When you're finished the frequency and amplitude sliders should work smoothly without any static, pops, or clicks in the sound. The value labels should display the value of the sliders as they are moved.
Last step
Fix as many yellow warnings as you can. Ideally you'll end up with none.
Word of advice. Fix things one at a time and test.
hw735_qtplaysine_midi2freq
Setup
cd $HOME312/cs312/hw73 cp -R hw734_qtplaysine hw735_qtplaysine_midi2freq cd $HOME312/cs312/hw73/hw735_qtplaysine_midi2freq mv hw734_qtplaysine.pro hw735_qtplaysine_midi2freq.pro rm -f hw734_qtplaysine.pro.user emptyBuildFolder.sh cd build-debug rm -f .qmake.stash cd ..
Open the Project view and add the build-debug folder to ~/cs312/hw73/hw735qtplaysin_midi2freq.
Create this form
Open Forms mainwindow.ui.
Delete all widgets.
Create this new layout.
The "piano keys" were created using the Tool Button widget.
Assign these variable names to the Widgets
Settings for verticalSlider_amp.
Use the default settings for the Quit button.
Build
Add/remove functions, rename variables, create new files, whatever it takes to get the program to compile and run.
Program requirements
- Audio begins at the correct frequency when you press and hold the mouse button down on a "piano key" button.
- Audio stops when you release the mouse button or move out of the button.
- Amplitude slider works as before.
- Quit button exits the application. Hint, use the Help tool or Google to look up QApplication functions.
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
$HOME312/common ├── RtAudio │ ├── RtAudio.cpp │ └── RtAudio.h ├── RtMidi │ ├── RtMidi.cpp │ └── RtMidi.h ├── 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 └── libsndfile ├── sndfile.h └── sndfile.hh $HOME312/cs312/hw73 ├── hw731_playsaw │ ├── CMakeLists.txt │ ├── build │ └── hw731_playsaw.cpp ├── hw732_playsine │ ├── CMakeLists.txt │ ├── build │ └── hw732_playsine.cpp ├── hw733_playsine_rta │ ├── CMakeLists.txt │ ├── build │ ├── hw733_playsine_rta.cpp │ ├── rtaudioutils.cpp │ ├── rtaudioutils.h │ └── scratch.cpp ├── hw734_qtplaysine │ ├── build-debug │ ├── hw734_qtplaysine.pro │ ├── hw734_qtplaysine.pro.user │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── mainwindow.ui │ ├── rtaudioutils.cpp │ └── rtaudioutils.h └── hw735_qtplaysine_midi2freqJE ├── build-debug ├── hw735_qtplaysine_midi2freq.pro ├── hw735_qtplaysine_midi2freq.pro.user ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── rtaudioutils.cpp └── rtaudioutils.h