ASIO SDK how to process audio buffers - audio

So I am working on some audio processing software and I'm kinda confused on some of the ASIO audio processing. From what I'm reading, It seems like when the buffer switch callback is made, I need to process the "input" into the "output". But what if I have a different number of input and output channels? And I'm guessing I need to do the format conversions if my input and output format do not match right?

What you have to do is access to asioDriverInfo.inputsChannels and asioDriverInfo.inputBuffers. I used this code snippet to figure out my device input/ output enumeration. I Hope it helps.
string cad;
if (ASIOStart() == ASE_OK)
{
for (int i = 0; i < asioDriverInfo.inputChannels + asioDriverInfo.outputChannels; i++)
{
cad = asioDriverInfo.channelInfos[i].isInput? "input" : "output ";
cout<<"Chanel "<<i<<" : "<<asioDriverInfo.channelInfos[i].name<<" "<<cad<<endl;
}
for (int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++)
{
cad = asioDriverInfo.bufferInfos[i].isInput? "input" : "output ";
cout<<"Buffer "<<i<<asioDriverInfo.bufferInfos[i].buffers<<" : " << cad <<" chanel "<<asioDriverInfo.bufferInfos[i].channelNum<<endl;
}
...and rest of stuff

Related

String with the same content but comparison always returns false

I have the following code, in which using the WiFi library I perform a scan of the available WiFi networks and want to detect if a specific network is available. I am using ESP32 and Arduino IDE. EEPROM memory handling seems to work, but I don't understand why the comparison always returns zero.
SSID = EEPROM.readString(500); // I read from eeprom the string stored in pos 500
WiFi.mode(WIFI_STA);
delay(100);
Serial.println(logg + "SCAN!");
int n = WiFi.scanNetworks();
Serial.println(logg + "SE DETECTARON: " + String(n) + " REDES WIFI!");
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.println("'" + WiFi.SSID(i) + "' vs '" + SSID + "' SizeOf: " + String(WiFi.SSID(i).length()) + " - " + String(SSID.length()) + " Comparacion " + String(WiFi.SSID(i) == SSID );
}
delay(10);
What I get on the serial monitor is the following:
'WRT1900AC 2.4GHz' vs 'WRT1900AC 2.4GHz' SizeOf: 16 - 16 Comparacion 0
The two strings look the same, they are the same size. I already tried replacing comparator "==" with strcmp and I get -244.
I also tried using .c_str as follows:
WiFi.SSID(i).c_str() == SSID.c_str()
but with the same results. If someone comes up with something I would be very grateful.
The two strings look the same, they are the same size.
Although the WiFI.SSID() return a String object, however it does not necessary to be ASCII-encoded. The string encoding is depend on the locale setting of the router, and the reason it looks the same is because the Serial.print() cast it into ASCII. This can be proof by the following sketch by using both Serial.print() and Serial.printf() in ESP32 which shown what is actually received (Serial.printf() however does not support Unicode formatting in ESP32 implementation, so it will produce some garbage characters).
int n = WiFi.scanNetworks();
for (int i=0; i<n; i++) {
// Serial.print() will cast the WiFi.SSID() to ASCII
Serial.print(WiFi.SSID(i));
// this shown what WiFi.SSID() truly return
Serial.printf(" --- %s\n", WiFi.SSID(i));
}
This will produce results in show in this picture. As you can see some SSIDs produce the correct results but some shown up as garbage.
So String comparison operator does do the job correctly when you compare WiFi.SSID(i) == SSID and the result indeed is not necessary equal for some SSID, even though it "looks" the same to human.
So how to solve it? If you want to treat them "as the same", ironically, converting String object to char array with .c_str() does do the job because it convert each char to an ASCII. I guess you just didn't use the char array comparison strcmp() correctly.
if(strcmp(WiFi.SSID(i).c_str(), SSID.c_str()) == 0) {
// match
}
else {
// not match
}
If you are saying that this c.str() comparison return -244, then edit your question and do a Serial.printf() on both String or better off to loop through the String character by charter and print out the HEX code to see what's going on.

Using pa_simple api efficiently

I'm playing around with pa_simple api to get a better understanding of digital audio. I've written some code to feed custom generated audio data to the pulseaudio server:
char buff[2];
while (playing) {
timePassed += periodMicros;
int val = userFunc(timePassed/1000000.0);
buff[0] = (val >> 0) & 0xff;
buff[1] = (val >> 1) & 0xff;
int error;
pa_simple_write(s, buff, 2, &error);
}
func generates a simple sine wave like this:
uint16_t func(double time)
{
double op = sin(2*3.14*freq * time);
return op * 100;
}
It produces a note as expected, but eats up 50% of cpu on my machine, because of the unrestricted while loop in the first codeblock. So I tried fixing it like this:
void _mainLoop() {
char buff[2*bufSize];
while (playing) {
for ( int i = 0; i < bufSize; i++ ) {
uint8_t word = userFunc(timePassed/1000000.0);
timePassed += periodMicros;
buff[i*2+0] = (word >> 0) & 0xff;
buff[i*2+1] = (word >> 1) & 0xff;
}
pa_simple_write(s, buff, 2*bufSize, &error);
this_thread::sleep_for(chrono::microseconds(bufSize*periodMicros));
}
}
It fixes the cpu issue but the audio generated by it is no longer a perfect note. It appears to be a bit higher pitched with a retro feel to it.
I wanted to know the correct way to use the pa_simple api without my code eating up the cpu.
Note that the sound generated by second code isn't fixed even if I change bufSize to 1 and remove the sleep line.
Edit:
This is how I've initialized the pulseaudio connection:
pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.channels = 1,
.rate = 44100
};
s = pa_simple_new(
NULL,
"SYNTH",
PA_STREAM_PLAYBACK,
NULL,
"Music",
&ss,
NULL,
NULL,
NULL
);
It was a bug in the program. I was storing the output of userFunc in a uint, hence the negative values were being cutoff. Changing that to int and removing the sleep call fixed the problem, since the pa_simple api in synchronous.

How to get direct access to polygons in VTK?

I found this post online (dates back to 2013) when I had trouble getting direct access to a specific cell in a vtkPolyData. I am using the latest version: VTK 8.1.1 and it seems like the newer version of VTK still has this issue.
polys->InitTraversal();
for(int i = 0; i < polys->GetNumberOfCells(); i++)
{
polys->GetNextCell(idList); // This sequential method gets the point IDs correctly
int a = idList->GetId(0);
int b = idList->GetId(1);
int c = idList->GetId(2);
}
However, the direct access method seems to have issues
polys->InitTraversal();
for(int i = 0; i < polys->GetNumberOfCells(); i++)
{
polys->GetCell(i, idList); // This method returns wrong ids
int a = idList->GetId(0);
int b = idList->GetId(1);
int c = idList->GetId(2);
}
How can I get the point IDs in a specific cell without looping through all the cell? Isn't polys->GetCell(i, idList) meant to give you direct access to a specific cell?
For direct access, we can use vtkPolyData::GetCellPoints() method. For example we can do
vtkNew<vtkIdList> idL; // or auto idL = vtkSmartPointer<vtkIdList>::New();
poly->GetCellPoints( 13, idL ); // Assuming you want the points for 13th cell
for(auto i = 0; i < idL->GetNumberOfIds(); ++i)
std::cout<< idL->GetId(i) << std::endl;
For looping over all cells I prefer a while loop:
vtkNew<vtkIdList> idL;
poly->GetPolys()->InitTraversal();
while(poly->GetPolys()->GetNextCell(idL)){
for(auto i = 0; i < idL->GetNumberOfIds(); ++i)
std::cout<< idL->GetId(i) << std::endl;
}

QT. Is any way to multiplicate string several times?

In python i can write
s = "dad" * 3
Result will be: s = "daddaddad"
I want to append "tabs" to my string. Something like:
QString tabs = "\t" * count;
What would be a simple, idiomatic way to do it?
You can do it quite simply with a loop:
QString mystring("somestring");
QString output;
for (int i = 0; i < 3; ++i)
output.append(mystring);
//'output' will contain the result string
Please note that the code I provide is in C++, not Python, but the concept still applies (and should be easily ported).
EDIT:
If you need to concatenate single characters, you could do it more easily like this:
int size = 5;
QString output(size, QChar('\t'));
//'output' contains 5 tab characters
Or, if you need to assign to another string (output is already created):
int size = 5;
output.fill(QChar('\t'), size);
//'output' contains 5 tab characters
#include <QString>
QString s;
for(int i = 0; i < 3; ++i)
{
s << "dad";
}

How do I swap stereo channels in raw PCM audio data on OS X?

I'm writing audio from an external decoding library on OS X to an AIFF file, and I am able to swap the endianness of the data with OSSwapInt32().
The resulting AIFF file (16-bit PCM stereo) does play, but the left and right channels are swapped.
Would there be any way to swap the channels as I am writing each buffer?
Here is the relevant loop:
do
{
xmp_get_frame_info(writer_context, &writer_info);
if (writer_info.loop_count > 0)
break;
writeModBuffer.mBuffers[0].mDataByteSize = writer_info.buffer_size;
writeModBuffer.mBuffers[0].mNumberChannels = inputFormat.mChannelsPerFrame;
// Set up our buffer to do the endianness swap
void *new_buffer;
new_buffer = malloc((writer_info.buffer_size) * inputFormat.mBytesPerFrame);
int *ourBuffer = writer_info.buffer;
int *ourNewBuffer = new_buffer;
memset(new_buffer, 0, writer_info.buffer_size);
int i;
for (i = 0; i <= writer_info.buffer_size; i++)
{
ourNewBuffer[i] = OSSwapInt32(ourBuffer[i]);
};
writeModBuffer.mBuffers[0].mData = ourNewBuffer;
frame_size = writer_info.buffer_size / inputFormat.mBytesPerFrame;
err = ExtAudioFileWrite(writeModRef, frame_size, &writeModBuffer);
} while (xmp_play_frame(writer_context) == 0);
This solution is very specific to 2 channel audio. I chose to do it at the same time you're looping to change the byte ordering to avoid an extra loop. I'm going through the loop 1/2 the number and processing two samples per iteration. The samples are interleaved so I copy from odd sample indexes into even sample indexes and vis-a-versa.
for (i = 0; i <= writer_info.buffer_size/2; i++)
{
ourNewBuffer[i*2] = OSSwapInt32(ourBuffer[i*2 + 1]);
ourNewBuffer[i*2 + 1] = OSSwapInt32(ourBuffer[i*2]);
};
An alternative is to use a table lookup for channel mapping.

Resources