To achieve an optimal audio quality you will need to specify the physical audio equipment setup for each listener.
The Immersitech Engage SDK currently supports 2 categories of listening devices: headphones and speakers. The device type is set to headphones by default.
Please see the following documentation to change the device type:
When the device type for a listener is set to speakers you will also need to set the half-span angle property.
Half-span angle refers to the relative angle between the listener and the physical speakers that they are listening to. The angle can be from 1 to 90 degrees (integer values). For quick reference, the following devices roughly correspond to the half span angle listed below:
Speaker Type | Half-Span Angle (degrees) |
---|---|
Portable speakers | 7 |
Laptop speakers | 15 |
Large television sound bars | 25 |
Studio speakers | 30 – 45 |
To find the half span angle for your setup, you can measure the distance from your head to your speakers and also the distance between your speakers to calculate the angle as pictured below:
Please see the following documentation to change the half-span angle:
The Immersitech Engage SDK allows you to move participants around in a 3D virtual space and process the sound as 3D spatial audio. It is important to know what the x, y, and z coordinates in this space refer to.
The center point of a room is located at (0, 0, 0) which is x, y, and z respectively.
Moving along the x axis will move a participant left or right.
Moving along the y axis will move a participant up or down.
Moving along the z axis will move a participant forward or backward.
Also note that the unit describing these x, y, z coordinates is centimeters. Therefore, a participant at (15,-10,50) is 15 centimeters to the right of the center, ten centimeters down from the center, and 50 centimeters in front of the center.
To perform an accurate 3D rendering you’ll need to provide which direction a participant is facing. We refer to this measurement as the heading. The heading is composed of an azimuth angle and an elevation angle. The azimuth angle is with respect to the x and z plane. The elevation angle is with respect to the y and z plane.
Please see the following documentation to set position and heading:
The Immersitech library also has more advanced controls if you would like to fine tune the 3D rendering experience. Note that the following parameters only apply if 3D mixing is enabled.
As a source moves further from a listener, its volume will decrease. This parameter allows you to control precisely the drop in decibels that a source will undergo for each additional meter they move away from the source.
This parameter allows you to specify at which distance you would no longer like attenuation to occur. For example, you can prevent participants from no longer being audible if they move very far away from the listener.
By default, reverb is applied to more accurately localize the source in the room. However, you can use this setting to disable reverb if needed. Note that the source will still attenuate even without the reverb enabled.
Please see the following documentation to set these controls:
When input and output audio may have different sampling rates, it can be confusing to understand the size of the buffers required. The goal of this section is to explain and provide examples to clear this confusion.
When you initialize the Immersitech Library, part of the library configuration is the sampling rate and number of frames as parameters. These values dictate the format of the audio on the OUTPUT side.
When you initialize any participant using some variant of the add participant function, part of the participant configuration is the sampling rate and number of channels as parameters. These values dictate the format of the audio on the INPUT side.
You will submit audio into the input function with the input format you specified and the Immersitech Library will convert the output audio to the output format specified.
The table below will exercise some examples for 10 millisecond buffers:
Who | Sampling Rate | Number of Frames | Number of Channels | Number of Samples |
---|---|---|---|---|
Library Output | 48 kHz | 480 | 2 | 960 |
Library Output | 32 kHz | 320 | 1 | 320 |
Library Output | 24 kHz | 240 | 2 | 480 |
Library Output | 16 kHz | 160 | 2 | 320 |
Library Output | 8 kHz | 80 | 1 | 80 |
Participant 1 Input | 48 kHz | 480 | 2 | 960 |
Participant 2 Input | 48 kHz | 480 | 1 | 480 |
Participant 3 Input | 16 kHz | 160 | 2 | 320 |
Participant 4 Input | 16 kHz | 160 | 1 | 160 |
Participant 5 Input | 8 kHz | 80 | 2 | 160 |
Participant 6 Input | 8 kHz | 80 | 1 | 80 |
It is important to also establish here that the Immersitech library currently supports the output buffer size to either be 10 milliseconds or 20 milliseconds worth of data. For an output sampling rate of 48 kHz, this is either 480 frames or 960 frames while at an output sampling rate of 8 kHz this is 80 frames or 160 frames. We also support buffer sizes of 512 or 1024 if your output sampling rate is 48 kHz for some audio systems that only use power of two buffers.
The idea of this feature is that a participant can say something to another participant of the same conference without everyone in the conference hearing them. This concept is not critical for starting to use the library. Feel free to return to this concept when you need to have side conversations from the main conference.
How do these rooms work?
Please see the following documentation to add a participant to a sidebar:
In the Immersitech Libraries, we use the notation where one sample is a single value, one frame contains one sample per channel, and one buffer contains one sample period worth of frames. To learn more about this notation, visit this web resource.
A room is a space where participants can hear and speak to all other participants in the room.
Each room can have unique attributes that can change the audio experience for the participants in that room.
A room will have an associated list of seats that participants can occupy, changing their 3D perspective with respective to the other participants.
A room will also have a center point, towards which all participants will turn to face automatically if they are placed into a seat.
A seat is a pre-defined (x,y,z) position that a single participant can occupy.
The seat will also have an automatically generated heading that turns the participant in that seat towards the center point of the room.
If you want to move a participant to a seat that is already occupied, the behavior while be defined by the allowSeatStacking property of the room.
If seat stacking is allowed, then both participants will occupy the same position in (x,y,z) space, but have different seat IDs.
If seat stacking is not allowed, then the second participant will occupy the same position shifted in the z-axis by the stackDelta property.
If you would like to have complete manual control for the participant’s position and heading, use a room that is an Open Room.
In an Open Room, participants may be moved any where in the (x,y,z) coordinate system and face any direction.
If a participant is moved manually, they are no longer considered to be in a seat.
To achieve an optimal audio quality you will need to specify the physical audio equipment setup for each listener.
The Immersitech Engage SDK currently supports 2 categories of listening devices: headphones and speakers. The device type is set to headphones by default.
Please see the following documentation to change the device type:
When the device type for a listener is set to speakers you will also need to set the half-span angle property.
Half-span angle refers to the relative angle between the listener and the physical speakers that they are listening to. The angle can be from 1 to 90 degrees (integer values). For quick reference, the following devices roughly correspond to the half span angle listed below:
Speaker Type | Half-Span Angle (degrees) |
---|---|
Portable speakers | 7 |
Laptop speakers | 15 |
Large television sound bars | 25 |
Studio speakers | 30 – 45 |
To find the half span angle for your setup, you can measure the distance from your head to your speakers and also the distance between your speakers to calculate the angle as pictured below:
Please see the following documentation to change the half-span angle:
The Immersitech SDK provides a built-in way to override how Immersitech log messages are handled. By default, Immersitech log messages just print the error message to the standard output. But what if you want these messages to log to a file, or print to a user interface for easier debugging? Here’s how you can do just that.
Here we have an example C++ file, log_tutorial.cpp that simply enables Immersitech logging, sets up a room, and adds some participants.
#include <iostream>
#include "immersitech.h"
#include "immersitech_logger.h"
// some constants
#define OUTPUT_SAMPLING_RATE 48000
#define OUTPUT_NUM_FRAMES 480
#define OUTPUT_NUM_CHANNELS 1
#define INTERLEAVED true
#define SPATIAL_QUALITY 1
int main() {
// set up logging
imm_enable_logging(true);
imm_set_log_level(IMM_LOG_DEBUG);
// initialize the library
imm_error_code error_code;
imm_library_configuration output_config = {
OUTPUT_SAMPLING_RATE,
OUTPUT_NUM_FRAMES,
OUTPUT_NUM_CHANNELS,
INTERLEAVED,
SPATIAL_QUALITY
};
imm_handle imm = imm_initialize_library("Immersitech_Engineering_sound_manager_license_key.dat", NULL, NULL, output_config, &error_code);
// create a room and add some participants
int room_id = 0;
imm_create_room(imm, room_id);
imm_participant_configuration input_config = { 16000, 1, IMM_PARTICIPANT_REGULAR };
imm_add_participant(imm, room_id, 1, "participant_1", input_config);
imm_add_participant(imm, room_id, 2, "participant_2", input_config);
return 0;
}
We can compile this and run it on a Mac like so (assuming the Immersitech library, immersitech.h, and immersitech_logging.h are in /usr/local/lib/immersitech):
g++ ./log_tutorial.cpp -I/usr/local/lib/immersitech -L/usr/local/lib/immersitech -limmersitech -Xlinker -rpath -Xlinker /usr/local/lib/immersitech
./a.out
And the output from the default logger should look like this:
[Immersitech] [Tue, 18.04.2023 10:05:28] [ERROR]: The library will work properly except imm_enable_websockets and imm_send_custom_websocket_event.
[Immersitech] [Tue, 18.04.2023 10:05:29] [INFO]: A new room with id 0 was created
[Immersitech] [Tue, 18.04.2023 10:05:29] [DEBUG]: Noise cancellation model: DFN (created)
[Immersitech] [Tue, 18.04.2023 10:05:29] [INFO]: A new participant with id 1 has been added to room 0
[Immersitech] [Tue, 18.04.2023 10:05:29] [DEBUG]: Noise cancellation model: DFN (created)
[Immersitech] [Tue, 18.04.2023 10:05:29] [INFO]: A new participant with id 2 has been added to room 0
To override the default logger, we’ll need to create a class that inherits the public members of the imm_logger_handler. Then we’ll add a “handle” method that takes in an imm_log_level and a const char*, like so:
class custom_logger : public imm_logger_handler {
public:
custom_logger(){ };
~custom_logger(){ };
void handle(imm_log_level level, const char *message) {
std::cout << "---" << imm_logger::to_string(level) << "---" << message << std::endl;
}
};
For this example, we’re just changing the output formatting of the log messages, but you can do whatever you’d like with the message.
NOTE: You don’t need to handle only displaying messages of a certain log level in this function. The function imm_set_log_level() already does this for you before calling your custom logger handler.
Now, in our main function, we just need to create an instance of this class and initialize it as the logger using these two lines:
custom_logger log;
IMM_LOGGER->initialize(&log);
So the final version of the file will look like this:
#include <iostream>
#include "immersitech.h"
#include "immersitech_logger.h"
#define OUTPUT_SAMPLING_RATE 48000
#define OUTPUT_NUM_FRAMES 480
#define OUTPUT_NUM_CHANNELS 1
#define INTERLEAVED true
#define SPATIAL_QUALITY 1
class custom_logger : public imm_logger_handler {
public:
custom_logger(){ };
~custom_logger(){ };
void handle(imm_log_level level, const char *message) {
std::cout << "---" << imm_logger::to_string(level) << "---" << message << std::endl;
}
};
int main() {
imm_enable_logging(true);
imm_set_log_level(IMM_LOG_DEBUG);
custom_logger log;
IMM_LOGGER->initialize(&log);
imm_error_code error_code;
imm_library_configuration output_config = {
OUTPUT_SAMPLING_RATE,
OUTPUT_NUM_FRAMES,
OUTPUT_NUM_CHANNELS,
INTERLEAVED,
SPATIAL_QUALITY
};
imm_handle imm = imm_initialize_library("Immersitech_Engineering_sound_manager_license_key.dat", NULL, NULL, output_config, &error_code);
int room_id = 0;
imm_create_room(imm, room_id);
imm_participant_configuration input_config = { 16000, 1, IMM_PARTICIPANT_REGULAR };
imm_add_participant(imm, room_id, 1, "participant_1", input_config);
imm_add_participant(imm, room_id, 2, "participant_2", input_config);
return 0;
}
That’s it! Now, whenever the Immersitech library logs an error message, this new method will be called. When we run it, we’ll now see this output.
---ERROR---The library will work properly except imm_enable_websockets and imm_send_custom_websocket_event.
---INFO---A new room with id 0 was created
---DEBUG---Noise cancellation model: DFN (created)
---INFO---A new participant with id 1 has been added to room 0
---DEBUG---Noise cancellation model: DFN (created)
---INFO---A new participant with id 2 has been added to room 0
Please feel free to use this method to throw your debug messages wherever they’re most convenient for you. And as always, reach out to us if you have any further difficulties or questions. Happy debugging!
The Immersitech SDK provides a built-in way to override how Immersitech log messages are handled. By default, Immersitech log messages just print the error message to the standard output. But what if you want these messages to log to a file, or print to a user interface for easier debugging? Here’s how you can do just that.
Here we have an example C++ file, log_tutorial.cpp that simply enables Immersitech logging, sets up a room, and adds some participants.
#include <iostream>
#include "immersitech.h"
#include "immersitech_logger.h"
// some constants
#define OUTPUT_SAMPLING_RATE 48000
#define OUTPUT_NUM_FRAMES 480
#define OUTPUT_NUM_CHANNELS 1
#define INTERLEAVED true
#define SPATIAL_QUALITY 1
int main() {
// set up logging
imm_enable_logging(true);
imm_set_log_level(IMM_LOG_DEBUG);
// initialize the library
imm_error_code error_code;
imm_library_configuration output_config = {
OUTPUT_SAMPLING_RATE,
OUTPUT_NUM_FRAMES,
OUTPUT_NUM_CHANNELS,
INTERLEAVED,
SPATIAL_QUALITY
};
imm_handle imm = imm_initialize_library("Immersitech_Engineering_sound_manager_license_key.dat", NULL, NULL, output_config, &error_code);
// create a room and add some participants
int room_id = 0;
imm_create_room(imm, room_id);
imm_participant_configuration input_config = { 16000, 1, IMM_PARTICIPANT_REGULAR };
imm_add_participant(imm, room_id, 1, "participant_1", input_config);
imm_add_participant(imm, room_id, 2, "participant_2", input_config);
return 0;
}
We can compile this and run it on a Mac like so (assuming the Immersitech library, immersitech.h, and immersitech_logging.h are in /usr/local/lib/immersitech):
g++ ./log_tutorial.cpp -I/usr/local/lib/immersitech -L/usr/local/lib/immersitech -limmersitech -Xlinker -rpath -Xlinker /usr/local/lib/immersitech
./a.out
And the output from the default logger should look like this:
[Immersitech] [Tue, 18.04.2023 10:05:28] [ERROR]: The library will work properly except imm_enable_websockets and imm_send_custom_websocket_event.
[Immersitech] [Tue, 18.04.2023 10:05:29] [INFO]: A new room with id 0 was created
[Immersitech] [Tue, 18.04.2023 10:05:29] [DEBUG]: Noise cancellation model: DFN (created)
[Immersitech] [Tue, 18.04.2023 10:05:29] [INFO]: A new participant with id 1 has been added to room 0
[Immersitech] [Tue, 18.04.2023 10:05:29] [DEBUG]: Noise cancellation model: DFN (created)
[Immersitech] [Tue, 18.04.2023 10:05:29] [INFO]: A new participant with id 2 has been added to room 0
To override the default logger, we’ll need to create a class that inherits the public members of the imm_logger_handler. Then we’ll add a “handle” method that takes in an imm_log_level and a const char*, like so:
class custom_logger : public imm_logger_handler {
public:
custom_logger(){ };
~custom_logger(){ };
void handle(imm_log_level level, const char *message) {
std::cout << "---" << imm_logger::to_string(level) << "---" << message << std::endl;
}
};
For this example, we’re just changing the output formatting of the log messages, but you can do whatever you’d like with the message.
NOTE: You don’t need to handle only displaying messages of a certain log level in this function. The function imm_set_log_level() already does this for you before calling your custom logger handler.
Now, in our main function, we just need to create an instance of this class and initialize it as the logger using these two lines:
custom_logger log;
IMM_LOGGER->initialize(&log);
So the final version of the file will look like this:
#include <iostream>
#include "immersitech.h"
#include "immersitech_logger.h"
#define OUTPUT_SAMPLING_RATE 48000
#define OUTPUT_NUM_FRAMES 480
#define OUTPUT_NUM_CHANNELS 1
#define INTERLEAVED true
#define SPATIAL_QUALITY 1
class custom_logger : public imm_logger_handler {
public:
custom_logger(){ };
~custom_logger(){ };
void handle(imm_log_level level, const char *message) {
std::cout << "---" << imm_logger::to_string(level) << "---" << message << std::endl;
}
};
int main() {
imm_enable_logging(true);
imm_set_log_level(IMM_LOG_DEBUG);
custom_logger log;
IMM_LOGGER->initialize(&log);
imm_error_code error_code;
imm_library_configuration output_config = {
OUTPUT_SAMPLING_RATE,
OUTPUT_NUM_FRAMES,
OUTPUT_NUM_CHANNELS,
INTERLEAVED,
SPATIAL_QUALITY
};
imm_handle imm = imm_initialize_library("Immersitech_Engineering_sound_manager_license_key.dat", NULL, NULL, output_config, &error_code);
int room_id = 0;
imm_create_room(imm, room_id);
imm_participant_configuration input_config = { 16000, 1, IMM_PARTICIPANT_REGULAR };
imm_add_participant(imm, room_id, 1, "participant_1", input_config);
imm_add_participant(imm, room_id, 2, "participant_2", input_config);
return 0;
}
That’s it! Now, whenever the Immersitech library logs an error message, this new method will be called. When we run it, we’ll now see this output.
---ERROR---The library will work properly except imm_enable_websockets and imm_send_custom_websocket_event.
---INFO---A new room with id 0 was created
---DEBUG---Noise cancellation model: DFN (created)
---INFO---A new participant with id 1 has been added to room 0
---DEBUG---Noise cancellation model: DFN (created)
---INFO---A new participant with id 2 has been added to room 0
Please feel free to use this method to throw your debug messages wherever they’re most convenient for you. And as always, reach out to us if you have any further difficulties or questions. Happy debugging!
What you’ll learn
In this tutorial you’ll learn how to setup the Immersitech SDK. You’ll create a room, add participants to that room, and apply the noise cancellation effect. You’ll also learn how to simulate the audio for participants to speed up development.
If you’d rather just see the finished application you can find it in our Github examples repository.
Boilerplate
Let’s start by setting up some headers and constants for our project. We’ll add the standard C libraries, include the Immersitech header file, and define some constants that we can use for initializing the Immersitech library.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <windows.h>
#include "immersitech.h"
#define OUTPUT_SAMPLING_RATE 48000
#define OUTPUT_NUM_FRAMES 480
#define OUTPUT_NUM_CHANNELS 1
#define INTERLEAVED true
#define SPATIAL_QUALITY 1
Initialize the library
Next, let’s create our main function, enable logging, and initialize the Immersitech library.
int main(int argc, char **argv) {
imm_enable_logging(true);
imm_error_code error_code;
imm_library_configuration output_config = {
OUTPUT_SAMPLING_RATE,
OUTPUT_NUM_FRAMES,
OUTPUT_NUM_CHANNELS,
INTERLEAVED,
SPATIAL_QUALITY
};
imm_handle imm = imm_initialize_library("Immersitech_Engineering_sound_manager_license_key.dat", NULL, NULL, output_config, &error_code);
printf("\nUsing Immersitech version: %s", imm_get_version());
printf("\nLicense Key Info: %s", imm_get_license_info(imm));
}
Go ahead and build the project to make sure that the library accepts your license key. You should see something like the following in your console output:
Using Immersitech version: v1.0.9
License Key Info: { "valid": true, "name": "Immersitech_Engineering_sound_manager_license_key.dat", "department": "Engineering", "minimum_version": "v0.8.0", "maximum_version": "v1.9.999", "creation_date": "3/9/2022", "expiration_date": "8/21/2022" }
Assuming that your license key is valid we can move forward with using the library.
Simulating participant audio
The main use case of the Immersitech SDK is to apply audio effects and mixing to real-time conferencing. During development it may not be practical for you to gather your coworkers together each time you need to test changes to your code. That’s why we’ve provided a tutorial that uses audio files to simulate participants. The input file will simulate the user who is speaking and the output file will simulate what is being heard in the room.
Setting up the input and output buffers
Let’s start by creating a struct to store the header information for the .wav file. We will need to read in the header for the input file so that we can calculate the size of the buffers and determine the format for the output file. Then we’ll write the WAV header to the output file and create our input and output buffers.
If you need some audio files to test with you can download them from our examples repository.
typedef struct wav_header {
unsigned char chunk_id[4];
unsigned int chunk_size;
unsigned char format[4];
unsigned char subchunk1_id[4];
unsigned int subchunk1_size;
unsigned short audio_format;
unsigned short num_channels;
unsigned int sample_rate;
unsigned int byte_rate;
unsigned short block_align;
unsigned short bits_per_sample;
unsigned char subchunk2_id[4];
unsigned int subchunk2_size;
} wav_header;
// Input files will simulate input audio from each individual participant
FILE* input_file = fopen("input.wav", "rb");
// Output files let you review what each participant hears
FILE* output_file = fopen("output.wav", "wb");
// Read in data about input file
struct wav_header wav_meta_data;
fread(&wav_meta_data, 1, sizeof(wav_header), input_file);
int input_rate = wav_meta_data.sample_rate;
int input_channels = wav_meta_data.num_channels;
printf("Input Configuration: %s || %i Hz || %i Channel(s)\n\n", argv[1], input_rate, input_channels);
// Write WAV Header data to the output file
if (wav_meta_data.sample_rate != OUTPUT_SAMPLING_RATE) {
wav_meta_data.subchunk2_size *= (OUTPUT_SAMPLING_RATE / wav_meta_data.sample_rate);
wav_meta_data.sample_rate = OUTPUT_SAMPLING_RATE;
wav_meta_data.chunk_size = 36 + wav_meta_data.subchunk2_size;
}
fwrite(&wav_meta_data, 1, sizeof(wav_header), output_file);
// Calculate the number of frames we must receive from each input
int input_num_frames = (OUTPUT_NUM_FRAMES * input_rate) / OUTPUT_SAMPLING_RATE;
// Initialize buffers to read input files and write to output files
short* input_buffer = (short*)malloc(input_num_frames * input_channels * sizeof(short));
short* output_buffer = (short*)malloc(OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short));
Great! Now we’re set up to read from an audio file and write to an audio output file. One final step we need to take is to setup the library for processing our audio.
Creating a room
Let’s create a room and add two participants into the room. Then, we’ll enable noise cancellation for each of the participants.
// Create and initialize a room to put participants into
int room_id = 0;
imm_create_room(imm, room_id);
// Add Participants into our room
int ID_1 = 1;
int ID_2 = 2;
imm_participant_configuration input_config = { input_rate, input_channels, IMM_PARTICIPANT_REGULAR };
imm_add_participant(imm, room_id, ID_1, "participant_1", input_config);
imm_add_participant(imm, room_id, ID_2, "participant_2", input_config);
// Turn on the noise cancellation for all the participants
imm_set_all_participants_state(imm, room_id, IMM_CONTROL_ANC_ENABLE, 1);
Processing the Audio
At this point we have everything that we need set up for audio processing. We just need to actually process the audio files to simulate a real-time audio stream.
// Loop through a file and buffer it as you would see in a real-time application
while ( !feof(input_file) ) {
// Read in one buffer of audio from each file
// Input each buffer into its respective Immersitech Participant within the room
fread(input_buffer, 1, input_num_frames * input_channels * sizeof(short), input_file);
imm_input_audio_short(imm, room_id, ID_1, input_buffer, input_num_frames);
// Now that all the audio is entered into the room for this cycle, we are ready to generate the outputs
// Generate the output for each participant and save it to their output file
imm_output_audio_short(imm, room_id, ID_2, output_buffer);
fwrite(output_buffer, 1, OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short), output_file);
}
Cleanup
Finally, let’s free up memory by calling the destructors for the library and closing our input and output buffers.
// Remove all participants from the room
imm_remove_participant( imm, room_id, ID_1 );
imm_remove_participant( imm, room_id, ID_2 );
// Destroy the room
imm_destroy_room(imm, room_id);
// Close and free the library
imm_destroy_library(imm);
// Close input and output files and free input / output buffers
fclose(input_file);
fclose(output_file);
free(input_buffer);
free(output_buffer);
We should now be able to run the application that we’ve written. Just make sure that your input.wav file is included with the project. After running the application you should have a clean, noise-reduced output.wav file!
What you’ll learn
In this tutorial you’ll learn how to setup the Immersitech SDK. You’ll create a room, add participants to that room, and apply 3D mixing. You’ll also learn how to simulate the audio for participants to speed up development.
If you’d rather just see the finished application you can find it in our Github examples repository.
Boilerplate
Let’s set up some headers and constants for our project. We’ll add the standard C libraries, include the Immersitech header file, and define some constants to define our desired output from the library.
The number of channels MUST be set to stereo (2) to hear 3D Audio. Mono output will not allow for 3D listening experiences.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "immersitech.h"
#define OUTPUT_SAMPLING_RATE 48000
#define OUTPUT_NUM_FRAMES 960
#define OUTPUT_NUM_CHANNELS 2
#define INTERLEAVED true
#define SPATIAL_QUALITY 5
Initialize the library
Next, let’s create our main function, enable logging, and initialize the Immersitech library.
int main(int argc, char **argv) {
imm_enable_logging(true);
imm_error_code error_code;
imm_library_configuration output_config = {
OUTPUT_SAMPLING_RATE,
OUTPUT_NUM_FRAMES,
OUTPUT_NUM_CHANNELS,
INTERLEAVED,
SPATIAL_QUALITY
};
imm_handle imm = imm_initialize_library("Immersitech_Engineering_sound_manager_license_key.dat", NULL, NULL, output_config, &error_code);
printf("\nUsing Immersitech version: %s", imm_get_version());
printf("\nLicense Key Info: %s", imm_get_license_info(imm));
}
Go ahead and build the project to make sure that the library accepts your license key. You should see something like the following in your console output:
Using Immersitech version: v1.0.9
License Key Info: { "valid": true, "name": "Immersitech_Engineering_sound_manager_license_key.dat", "department": "Engineering", "minimum_version": "v0.8.0", "maximum_version": "v1.9.999", "creation_date": "3/9/2022", "expiration_date": "8/21/2022" }
Assuming that your license key is valid we can move forward with using the library.
Simulating participant audio
The main use case of the Immersitech SDK is to apply audio effects and mixing to real-time conferencing. During development it may not be practical for you to gather your coworkers together each time you need to test changes to your code. That’s why we’ve provided a tutorial that uses audio files to simulate participants. The input file will simulate the user who is speaking and the output file will simulate what is being heard in the room.
Setting up files and buffers for processing the audio
Let’s start by creating a struct to store the header information for the .wav file and a pointer that we can use for reading the file.
typedef struct header {
unsigned char chunk_id[4];
unsigned int chunk_size;
unsigned char format[4];
unsigned char subchunk1_id[4];
unsigned int subchunk1_size;
unsigned short audio_format;
unsigned short num_channels;
unsigned int sample_rate;
unsigned int byte_rate;
unsigned short block_align;
unsigned short bits_per_sample;
unsigned char subchunk2_id[4];
unsigned int subchunk2_size;
} header;
typedef struct header* header_p;
We will need to read in the header for the input file so that we can calculate the size of the buffers and determine the format for the output file. Then we’ll write the WAV header to the output file.
For the purposes of this tutorial we’ll just process the input for one participant. If you’d like to simulate audio input for all of the participants in the room then you’ll need to repeat this step for each participant.
// Input files will simulate input audio from each individual participant
FILE* input_file = fopen("input.wav", "rb");
// Output files let you review what each participant hears
FILE* output_file = fopen("output.wav", "wb");
header_p meta = (header_p)malloc(sizeof(header));
fread(meta, 1, sizeof(header), input_file);
int participant_1_sampling_rate = meta->sample_rate;
int participant_1_num_channels = meta->num_channels;
int participant_1_num_input_frames = (OUTPUT_NUM_FRAMES * participant_1_sampling_rate) / OUTPUT_SAMPLING_RATE;
meta->subchunk2_size *= 2;
meta->chunk_size = 36 + meta->subchunk2_size;
meta->num_channels = OUTPUT_NUM_CHANNELS;
meta->sample_rate = OUTPUT_SAMPLING_RATE;
fwrite(meta, 1, sizeof(header), output_file);
Next, we’ll setup our input and output buffers for processing the input audio from the first participant.
// Initialize buffers to read input files and write to output files
short* input_buffer = (short*)malloc(OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short));
short* output_buffer = (short*)malloc(OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short));
Great! Now we’re set up to read from an audio file and write to an audio output file. One final step we need to take is to setup the library for processing our audio.
Creating a room
Let’s create a room and add two participants into the room. The first participant will be the speaker in this scenario and the second participant will be hearing the first participant.
// Create and initialize a room to put participants into
int room_id = 0;
imm_create_room(imm, room_id);
// Add Participants into our room
int ID_1 = 1;
int ID_2 = 2;
imm_participant_configuration input_config = { participant_1_sampling_rate, participant_1_num_channels, IMM_PARTICIPANT_REGULAR };
imm_add_participant(imm, room_id, ID_1, "participant_1", input_config);
imm_add_participant(imm, room_id, ID_2, "participant_2", input_config);
Next, we’ll assign a position and heading to the first participant in the room. And enable 3D mixing for all participants.
imm_position position = { -60,0,10 };
imm_heading heading = { 0,0 };
imm_set_participant_position(imm, room_id, 1, position, heading );
imm_set_all_participants_state(imm, room_id, IMM_CONTROL_MIXING_3D_ENABLE, 1);
imm_set_all_participants_state(imm, room_id, IMM_CONTROL_DEVICE, IMM_DEVICE_HEADPHONE);
Processing the Audio
At this point we have everything that we need set up for audio processing. We just need to actually process the audio files to simulate a real-time audio application. The code below is an example of reading and writing audio to a single output file for a single participant. To process audio for all participants you’ll need to adjust this code to write to separate output files for each participant.
// Loop through a file and buffer it as you would see in a real-time application
while (!feof(input_file)) {
// Read in one buffer of audio from each file
fread(input_buffer, 1, participant_1_num_input_frames * participant_1_num_channels * sizeof(short), input_file);
imm_input_audio_short(imm, room_id, ID_1, input_buffer, participant_1_num_input_frames);
// Now that all the audio is entered into the room for this cycle, we are ready to generate the outputs
imm_output_audio_short(imm, room_id, ID_2, output_buffer);
fwrite(output_buffer, 1, OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short), output_file);
}
Cleanup
Finally, let’s free up memory by calling the destructors for the library and closing our input and output buffers for each participant.
// Remove all participants from the room
imm_remove_participant(imm, room_id, ID_1);
imm_remove_participant(imm, room_id, ID_2);
// Destroy the room
imm_destroy_room(imm, room_id);
// Close and free the library
imm_destroy_library(imm);
// Close input and output files and free input / output buffers
fclose(input_file);
fclose(output_file);
free(input_buffer);
free(output_buffer);
We should now be able to run the application that we’ve written. Just make sure that your input.wav file is included with the project. After running the application you should have a 3D spatialized output.wav file!
The Immersitech Engage SDK allows you to move participants around in a 3D virtual space and process the sound as 3D spatial audio. It is important to know what the x, y, and z coordinates in this space refer to.
The center point of a room is located at (0, 0, 0) which is x, y, and z respectively.
Moving along the x axis will move a participant left or right.
Moving along the y axis will move a participant up or down.
Moving along the z axis will move a participant forward or backward.
Also note that the unit describing these x, y, z coordinates is centimeters. Therefore, a participant at (15,-10,50) is 15 centimeters to the right of the center, ten centimeters down from the center, and 50 centimeters in front of the center.
To perform an accurate 3D rendering you’ll need to provide which direction a participant is facing. We refer to this measurement as the heading. The heading is composed of an azimuth angle and an elevation angle. The azimuth angle is with respect to the x and z plane. The elevation angle is with respect to the y and z plane.
Please see the following documentation to set position and heading:
The Immersitech library also has more advanced controls if you would like to fine tune the 3D rendering experience. Note that the following parameters only apply if 3D mixing is enabled.
As a source moves further from a listener, its volume will decrease. This parameter allows you to control precisely the drop in decibels that a source will undergo for each additional meter they move away from the source.
This parameter allows you to specify at which distance you would no longer like attenuation to occur. For example, you can prevent participants from no longer being audible if they move very far away from the listener.
By default, reverb is applied to more accurately localize the source in the room. However, you can use this setting to disable reverb if needed. Note that the source will still attenuate even without the reverb enabled.
Please see the following documentation to set these controls:
What you’ll learn
In this tutorial you’ll learn how to setup the Immersitech SDK. You’ll create a room, add participants to that room, and apply the noise cancellation effect. You’ll also learn how to simulate the audio for participants to speed up development.
If you’d rather just see the finished application you can find it in our Github examples repository.
Boilerplate
Let’s start by setting up some headers and constants for our project. We’ll add the standard C libraries, include the Immersitech header file, and define some constants that we can use for initializing the Immersitech library.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <windows.h>
#include "immersitech.h"
#define OUTPUT_SAMPLING_RATE 48000
#define OUTPUT_NUM_FRAMES 480
#define OUTPUT_NUM_CHANNELS 1
#define INTERLEAVED true
#define SPATIAL_QUALITY 1
Initialize the library
Next, let’s create our main function, enable logging, and initialize the Immersitech library.
int main(int argc, char **argv) {
imm_enable_logging(true);
imm_error_code error_code;
imm_library_configuration output_config = {
OUTPUT_SAMPLING_RATE,
OUTPUT_NUM_FRAMES,
OUTPUT_NUM_CHANNELS,
INTERLEAVED,
SPATIAL_QUALITY
};
imm_handle imm = imm_initialize_library("Immersitech_Engineering_sound_manager_license_key.dat", NULL, NULL, output_config, &error_code);
printf("\nUsing Immersitech version: %s", imm_get_version());
printf("\nLicense Key Info: %s", imm_get_license_info(imm));
}
Go ahead and build the project to make sure that the library accepts your license key. You should see something like the following in your console output:
Using Immersitech version: v1.0.9
License Key Info: { "valid": true, "name": "Immersitech_Engineering_sound_manager_license_key.dat", "department": "Engineering", "minimum_version": "v0.8.0", "maximum_version": "v1.9.999", "creation_date": "3/9/2022", "expiration_date": "8/21/2022" }
Assuming that your license key is valid we can move forward with using the library.
Simulating participant audio
The main use case of the Immersitech SDK is to apply audio effects and mixing to real-time conferencing. During development it may not be practical for you to gather your coworkers together each time you need to test changes to your code. That’s why we’ve provided a tutorial that uses audio files to simulate participants. The input file will simulate the user who is speaking and the output file will simulate what is being heard in the room.
Setting up the input and output buffers
Let’s start by creating a struct to store the header information for the .wav file. We will need to read in the header for the input file so that we can calculate the size of the buffers and determine the format for the output file. Then we’ll write the WAV header to the output file and create our input and output buffers.
If you need some audio files to test with you can download them from our examples repository.
typedef struct wav_header {
unsigned char chunk_id[4];
unsigned int chunk_size;
unsigned char format[4];
unsigned char subchunk1_id[4];
unsigned int subchunk1_size;
unsigned short audio_format;
unsigned short num_channels;
unsigned int sample_rate;
unsigned int byte_rate;
unsigned short block_align;
unsigned short bits_per_sample;
unsigned char subchunk2_id[4];
unsigned int subchunk2_size;
} wav_header;
// Input files will simulate input audio from each individual participant
FILE* input_file = fopen("input.wav", "rb");
// Output files let you review what each participant hears
FILE* output_file = fopen("output.wav", "wb");
// Read in data about input file
struct wav_header wav_meta_data;
fread(&wav_meta_data, 1, sizeof(wav_header), input_file);
int input_rate = wav_meta_data.sample_rate;
int input_channels = wav_meta_data.num_channels;
printf("Input Configuration: %s || %i Hz || %i Channel(s)\n\n", argv[1], input_rate, input_channels);
// Write WAV Header data to the output file
if (wav_meta_data.sample_rate != OUTPUT_SAMPLING_RATE) {
wav_meta_data.subchunk2_size *= (OUTPUT_SAMPLING_RATE / wav_meta_data.sample_rate);
wav_meta_data.sample_rate = OUTPUT_SAMPLING_RATE;
wav_meta_data.chunk_size = 36 + wav_meta_data.subchunk2_size;
}
fwrite(&wav_meta_data, 1, sizeof(wav_header), output_file);
// Calculate the number of frames we must receive from each input
int input_num_frames = (OUTPUT_NUM_FRAMES * input_rate) / OUTPUT_SAMPLING_RATE;
// Initialize buffers to read input files and write to output files
short* input_buffer = (short*)malloc(input_num_frames * input_channels * sizeof(short));
short* output_buffer = (short*)malloc(OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short));
Great! Now we’re set up to read from an audio file and write to an audio output file. One final step we need to take is to setup the library for processing our audio.
Creating a room
Let’s create a room and add two participants into the room. Then, we’ll enable noise cancellation for each of the participants.
// Create and initialize a room to put participants into
int room_id = 0;
imm_create_room(imm, room_id);
// Add Participants into our room
int ID_1 = 1;
int ID_2 = 2;
imm_participant_configuration input_config = { input_rate, input_channels, IMM_PARTICIPANT_REGULAR };
imm_add_participant(imm, room_id, ID_1, "participant_1", input_config);
imm_add_participant(imm, room_id, ID_2, "participant_2", input_config);
// Turn on the noise cancellation for all the participants
imm_set_all_participants_state(imm, room_id, IMM_CONTROL_ANC_ENABLE, 1);
Processing the Audio
At this point we have everything that we need set up for audio processing. We just need to actually process the audio files to simulate a real-time audio stream.
// Loop through a file and buffer it as you would see in a real-time application
while ( !feof(input_file) ) {
// Read in one buffer of audio from each file
// Input each buffer into its respective Immersitech Participant within the room
fread(input_buffer, 1, input_num_frames * input_channels * sizeof(short), input_file);
imm_input_audio_short(imm, room_id, ID_1, input_buffer, input_num_frames);
// Now that all the audio is entered into the room for this cycle, we are ready to generate the outputs
// Generate the output for each participant and save it to their output file
imm_output_audio_short(imm, room_id, ID_2, output_buffer);
fwrite(output_buffer, 1, OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short), output_file);
}
Cleanup
Finally, let’s free up memory by calling the destructors for the library and closing our input and output buffers.
// Remove all participants from the room
imm_remove_participant( imm, room_id, ID_1 );
imm_remove_participant( imm, room_id, ID_2 );
// Destroy the room
imm_destroy_room(imm, room_id);
// Close and free the library
imm_destroy_library(imm);
// Close input and output files and free input / output buffers
fclose(input_file);
fclose(output_file);
free(input_buffer);
free(output_buffer);
We should now be able to run the application that we’ve written. Just make sure that your input.wav file is included with the project. After running the application you should have a clean, noise-reduced output.wav file!
When input and output audio may have different sampling rates, it can be confusing to understand the size of the buffers required. The goal of this section is to explain and provide examples to clear this confusion.
When you initialize the Immersitech Library, part of the library configuration is the sampling rate and number of frames as parameters. These values dictate the format of the audio on the OUTPUT side.
When you initialize any participant using some variant of the add participant function, part of the participant configuration is the sampling rate and number of channels as parameters. These values dictate the format of the audio on the INPUT side.
You will submit audio into the input function with the input format you specified and the Immersitech Library will convert the output audio to the output format specified.
The table below will exercise some examples for 10 millisecond buffers:
Who | Sampling Rate | Number of Frames | Number of Channels | Number of Samples |
---|---|---|---|---|
Library Output | 48 kHz | 480 | 2 | 960 |
Library Output | 32 kHz | 320 | 1 | 320 |
Library Output | 24 kHz | 240 | 2 | 480 |
Library Output | 16 kHz | 160 | 2 | 320 |
Library Output | 8 kHz | 80 | 1 | 80 |
Participant 1 Input | 48 kHz | 480 | 2 | 960 |
Participant 2 Input | 48 kHz | 480 | 1 | 480 |
Participant 3 Input | 16 kHz | 160 | 2 | 320 |
Participant 4 Input | 16 kHz | 160 | 1 | 160 |
Participant 5 Input | 8 kHz | 80 | 2 | 160 |
Participant 6 Input | 8 kHz | 80 | 1 | 80 |
It is important to also establish here that the Immersitech library currently supports the output buffer size to either be 10 milliseconds or 20 milliseconds worth of data. For an output sampling rate of 48 kHz, this is either 480 frames or 960 frames while at an output sampling rate of 8 kHz this is 80 frames or 160 frames. We also support buffer sizes of 512 or 1024 if your output sampling rate is 48 kHz for some audio systems that only use power of two buffers.
What you’ll learn
In this tutorial you’ll learn how to setup the Immersitech SDK. You’ll create a room, add participants to that room, and apply 3D mixing. You’ll also learn how to simulate the audio for participants to speed up development.
If you’d rather just see the finished application you can find it in our Github examples repository.
Boilerplate
Let’s set up some headers and constants for our project. We’ll add the standard C libraries, include the Immersitech header file, and define some constants to define our desired output from the library.
The number of channels MUST be set to stereo (2) to hear 3D Audio. Mono output will not allow for 3D listening experiences.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "immersitech.h"
#define OUTPUT_SAMPLING_RATE 48000
#define OUTPUT_NUM_FRAMES 960
#define OUTPUT_NUM_CHANNELS 2
#define INTERLEAVED true
#define SPATIAL_QUALITY 5
Initialize the library
Next, let’s create our main function, enable logging, and initialize the Immersitech library.
int main(int argc, char **argv) {
imm_enable_logging(true);
imm_error_code error_code;
imm_library_configuration output_config = {
OUTPUT_SAMPLING_RATE,
OUTPUT_NUM_FRAMES,
OUTPUT_NUM_CHANNELS,
INTERLEAVED,
SPATIAL_QUALITY
};
imm_handle imm = imm_initialize_library("Immersitech_Engineering_sound_manager_license_key.dat", NULL, NULL, output_config, &error_code);
printf("\nUsing Immersitech version: %s", imm_get_version());
printf("\nLicense Key Info: %s", imm_get_license_info(imm));
}
Go ahead and build the project to make sure that the library accepts your license key. You should see something like the following in your console output:
Using Immersitech version: v1.0.9
License Key Info: { "valid": true, "name": "Immersitech_Engineering_sound_manager_license_key.dat", "department": "Engineering", "minimum_version": "v0.8.0", "maximum_version": "v1.9.999", "creation_date": "3/9/2022", "expiration_date": "8/21/2022" }
Assuming that your license key is valid we can move forward with using the library.
Simulating participant audio
The main use case of the Immersitech SDK is to apply audio effects and mixing to real-time conferencing. During development it may not be practical for you to gather your coworkers together each time you need to test changes to your code. That’s why we’ve provided a tutorial that uses audio files to simulate participants. The input file will simulate the user who is speaking and the output file will simulate what is being heard in the room.
Setting up files and buffers for processing the audio
Let’s start by creating a struct to store the header information for the .wav file and a pointer that we can use for reading the file.
typedef struct header {
unsigned char chunk_id[4];
unsigned int chunk_size;
unsigned char format[4];
unsigned char subchunk1_id[4];
unsigned int subchunk1_size;
unsigned short audio_format;
unsigned short num_channels;
unsigned int sample_rate;
unsigned int byte_rate;
unsigned short block_align;
unsigned short bits_per_sample;
unsigned char subchunk2_id[4];
unsigned int subchunk2_size;
} header;
typedef struct header* header_p;
We will need to read in the header for the input file so that we can calculate the size of the buffers and determine the format for the output file. Then we’ll write the WAV header to the output file.
For the purposes of this tutorial we’ll just process the input for one participant. If you’d like to simulate audio input for all of the participants in the room then you’ll need to repeat this step for each participant.
// Input files will simulate input audio from each individual participant
FILE* input_file = fopen("input.wav", "rb");
// Output files let you review what each participant hears
FILE* output_file = fopen("output.wav", "wb");
header_p meta = (header_p)malloc(sizeof(header));
fread(meta, 1, sizeof(header), input_file);
int participant_1_sampling_rate = meta->sample_rate;
int participant_1_num_channels = meta->num_channels;
int participant_1_num_input_frames = (OUTPUT_NUM_FRAMES * participant_1_sampling_rate) / OUTPUT_SAMPLING_RATE;
meta->subchunk2_size *= 2;
meta->chunk_size = 36 + meta->subchunk2_size;
meta->num_channels = OUTPUT_NUM_CHANNELS;
meta->sample_rate = OUTPUT_SAMPLING_RATE;
fwrite(meta, 1, sizeof(header), output_file);
Next, we’ll setup our input and output buffers for processing the input audio from the first participant.
// Initialize buffers to read input files and write to output files
short* input_buffer = (short*)malloc(OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short));
short* output_buffer = (short*)malloc(OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short));
Great! Now we’re set up to read from an audio file and write to an audio output file. One final step we need to take is to setup the library for processing our audio.
Creating a room
Let’s create a room and add two participants into the room. The first participant will be the speaker in this scenario and the second participant will be hearing the first participant.
// Create and initialize a room to put participants into
int room_id = 0;
imm_create_room(imm, room_id);
// Add Participants into our room
int ID_1 = 1;
int ID_2 = 2;
imm_participant_configuration input_config = { participant_1_sampling_rate, participant_1_num_channels, IMM_PARTICIPANT_REGULAR };
imm_add_participant(imm, room_id, ID_1, "participant_1", input_config);
imm_add_participant(imm, room_id, ID_2, "participant_2", input_config);
Next, we’ll assign a position and heading to the first participant in the room. And enable 3D mixing for all participants.
imm_position position = { -60,0,10 };
imm_heading heading = { 0,0 };
imm_set_participant_position(imm, room_id, 1, position, heading );
imm_set_all_participants_state(imm, room_id, IMM_CONTROL_MIXING_3D_ENABLE, 1);
imm_set_all_participants_state(imm, room_id, IMM_CONTROL_DEVICE, IMM_DEVICE_HEADPHONE);
Processing the Audio
At this point we have everything that we need set up for audio processing. We just need to actually process the audio files to simulate a real-time audio application. The code below is an example of reading and writing audio to a single output file for a single participant. To process audio for all participants you’ll need to adjust this code to write to separate output files for each participant.
// Loop through a file and buffer it as you would see in a real-time application
while (!feof(input_file)) {
// Read in one buffer of audio from each file
fread(input_buffer, 1, participant_1_num_input_frames * participant_1_num_channels * sizeof(short), input_file);
imm_input_audio_short(imm, room_id, ID_1, input_buffer, participant_1_num_input_frames);
// Now that all the audio is entered into the room for this cycle, we are ready to generate the outputs
imm_output_audio_short(imm, room_id, ID_2, output_buffer);
fwrite(output_buffer, 1, OUTPUT_NUM_FRAMES * OUTPUT_NUM_CHANNELS * sizeof(short), output_file);
}
Cleanup
Finally, let’s free up memory by calling the destructors for the library and closing our input and output buffers for each participant.
// Remove all participants from the room
imm_remove_participant(imm, room_id, ID_1);
imm_remove_participant(imm, room_id, ID_2);
// Destroy the room
imm_destroy_room(imm, room_id);
// Close and free the library
imm_destroy_library(imm);
// Close input and output files and free input / output buffers
fclose(input_file);
fclose(output_file);
free(input_buffer);
free(output_buffer);
We should now be able to run the application that we’ve written. Just make sure that your input.wav file is included with the project. After running the application you should have a 3D spatialized output.wav file!
The idea of this feature is that a participant can say something to another participant of the same conference without everyone in the conference hearing them. This concept is not critical for starting to use the library. Feel free to return to this concept when you need to have side conversations from the main conference.
How do these rooms work?
Please see the following documentation to add a participant to a sidebar:
In the Immersitech Libraries, we use the notation where one sample is a single value, one frame contains one sample per channel, and one buffer contains one sample period worth of frames. To learn more about this notation, visit this web resource.
A room is a space where participants can hear and speak to all other participants in the room.
Each room can have unique attributes that can change the audio experience for the participants in that room.
A room will have an associated list of seats that participants can occupy, changing their 3D perspective with respective to the other participants.
A room will also have a center point, towards which all participants will turn to face automatically if they are placed into a seat.
A seat is a pre-defined (x,y,z) position that a single participant can occupy.
The seat will also have an automatically generated heading that turns the participant in that seat towards the center point of the room.
If you want to move a participant to a seat that is already occupied, the behavior while be defined by the allowSeatStacking property of the room.
If seat stacking is allowed, then both participants will occupy the same position in (x,y,z) space, but have different seat IDs.
If seat stacking is not allowed, then the second participant will occupy the same position shifted in the z-axis by the stackDelta property.
If you would like to have complete manual control for the participant’s position and heading, use a room that is an Open Room.
In an Open Room, participants may be moved any where in the (x,y,z) coordinate system and face any direction.
If a participant is moved manually, they are no longer considered to be in a seat.