using WebAudio AnalyserNode.getFloatFrequencyData() to shift pitch of a BufferSource - web-audio-api

I have a BufferSource, which I create thusly:
const proxyUrl = location.origin == 'file://' ? 'https://cors-anywhere.herokuapp.com/' : '';
const request = new XMLHttpRequest();
request.open('GET', proxyUrl + 'http://heliosophiclabs.com/~mad/projects/mad-music/non.mp3', true);
// request.open('GET', 'non.mp3', true);
request.responseType = 'arraybuffer';
request.onload = () => {
audioCtx.decodeAudioData(request.response, buffer => {
buff = buffer;
}, err => {
console.error(err);
});
}
request.send();
Yes, the CORS workaround is pathetic, but this is the way I found to be able to work locally without needing to run a HTTP server. Anyway...
I would like to shift the pitch of this buffer. I've tried various different forms of this:
const source = audioCtx.createBufferSource();
source.buffer = buff;
const analyser = audioCtx.createAnalyser();
analyser.connect(audioCtx.destination);
analyser.minDecibels = -140;
analyser.maxDecibels = 0;
analyser.smoothingTimeConstant = 0.8;
analyser.fftSize = 2048;
const dataArray = new Float32Array(analyser.frequencyBinCount);
source.connect(analyser);
analyser.connect(audioCtx.destination);
source.start(0);
analyser.getFloatFrequencyData(dataArray);
console.log('dataArray', dataArray);
All to no avail. dataArray is always filled with -Infinity values, no matter what I try.
My idea is to get this frequency domain data and then to move all the frequencies up/down by some amount and create a new Oscillator node out of these, like this:
const wave = audioCtx.createPeriodicWave(real, waveCompnents);
oscillator.setPeriodicWave(wave);
Anyway. If anyone has a better idea of how to shift pitch, I'd love to hear it. Sadly, detune and playbackRate both seem to do basically the same thing (why are there two ways of doing the same thing?), namely just to speed up or slow down the playback, so that's not it.

First, there's a small issue with the code: you connect the analyser to the destination twice. You don't actually need to connect it at all.
Second, I think the reason you're getting all -infinity values is because you call getFloatFrequencyData right after you start the source. There's a good chance that no samples have been played so the analyser only has buffers of all zeros.
You need to call getFloatFrequencyData after a bit of time to see non-zero values.
Third, I don't think this will work at all, even for shifting the pitch of an oscillator. getFloatFrequencyData only returns the magnitude information. You will need the phase information for the harmonics to get everything shifted correctly. Currently there's no way to get the phase information.
Fourth, if you have an AudioBuffer with the data you need, consider using the playbackRate to change the pitch. Not sure if this will produce the shift you want.

Related

When reading back asynchronously from compute shaders in Unity, can I reset buffer halfway?

Hope there will be nothing confusing in what I'm going to talk about, because my mother tongue is not English and my grammar is poor:p
I'm working on a mipmap analyzing tool which need to calculate with pixels from the render texture. Here's a part of the C# code:
private IEnumerator CSGroupColor(RenderTexture rt, GroupColor[] groupColors)
{
var outputBuffer = new ComputeBuffer(groupColors.Length, 8);
csKernelID = cs.FindKernel("CSGroupColor");
cs.SetTexture(csKernelID, "rt", rt);
cs.SetBuffer(csKernelID, "groupColorOut", outputBuffer);
cs.Dispatch(csKernelID, rt.width / 8, rt.height / 8, 1);
var req = AsyncGPUReadback.Request(outputBuffer);
yield return new WaitUntil(() => req.done);
req.GetData<GroupColor>().CopyTo(groupColors);
foreach (var color in groupColors)
{
if (!m_staticsDatas.TryGetValue(color.groupindex, out var vl))
continue;
if (color.value > 0)
vl.allColors.Add(color.value);
}
}
And what I want to implement next, is to make every buffer smaller(e.g.with a length of 4096), like we usually do in other asynchronous communications. Maybe I can pass the first buffer to CPU right away when it's full, and then replace it with the second buffer, and so on.
As I see it, using SetBuffer() again after req.done must be permitted to make that viable. I have been finding on Internet all day for a sample usage, but still found nothing.
Is there anyone who would give some help? Thx very much.

How can I loop AudioBufferSourceNode with overlapping (using the same AudioBuffer)

I need to loop my source with some cross parametr (in sec). It will be great to listen looping without interrupting on the sample border.AudioBufferSourceNode is audioNode in my code.
I faced with the problem of inability to reuse the buffer, is it possible to get around this?
playNoteOn: function(indexNote){
var attack = this.get('attack'),
release = this.get('release'),
volume = 1 + this.get('volume') / 100,
reverb = _.clone(this.get('reverb')),
loop = this.get('loop'), cross;
//peace for Loop process
if (loop) {
//milli sec
attack = this.get('startLoop')*1000;
release = this.get('endLoop')*1000;
//sec
cross = this.get('crossLoop');
}
//peace for ADSR process
var t0 = this.get('audioNode').context.currentTime,
spread = attack/1000 + release/1000,
attackSpread = t0 + attack/1000;
[this.get('schema').leftGain, this.get('schema').rightGain].forEach(function(gain, index){
gain.gain.cancelScheduledValues(0);
gain.gain.setValueAtTime(0, t0);
gain.gain.linearRampToValueAtTime(volume, attackSpread);
// gain.gain.setValueAtTime(volume, decaySpread);
// gain.gain.linearRampToValueAtTime(0, releaseSpread);
});
this.get('audioNode').connect(this.get('schema').splitter, 0, 0);
this.get('audioNode').connect(this.get('schema').leftGain);
this.get('audioNode').connect(this.get('schema').rightGain);
this.get('audioNode').connect(this.get('schema').reverb);
this.get('audioNode').connect(APP.Models.Synth.get('schema').reverb);
APP.Models.Synth.get('effects').where({active: false}).forEach(function(effect){
effect.get('node').disconnect();
});
APP.Models.Synth.get('effects').where({active: true}).forEach(function(effect){
effect.get('node').disconnect();
effect.get('node').setParams(effect.toJSON()).getNode(this.get('audioNode'), [this.get('schema').leftGain, this.get('schema').rightGain]);
}, this);
if(loop){
this.get('audioNode').loop = true;
this.get('audioNode').loopEnd = this.get('audioNode').buffer.duration - cross;
}
this.get('audioNode').start(t0);
},
You cannot reuse a buffer. Once stopped the sourcebuffer is gone forever. Where is your audio node object anyway? But that's no problem. When you decoded from a sound file you can use the buffer from the decoding again. Just create more buffer sources. You can create as much as you like. What language are you using anyway? Say Some backgrounds to what you are doing and which frameworks you use.
Beware the difference between buffer from decoding and buffer source. Your audionode is a buffer source which can be feeded by a buffer. You can reuse the buffer but not the buffer source. So create the buffer source in your playnote code.

How to play audio byte array (not file!) with JavaScript in a browser

For mostly security reasons, I'm not allowed to store a WAV file on the server to be accessed by a browser. What I have is a byte array contains audio data (the data portion of a WAV file I believe) on the sever, and I want it to be played on a browser through JavaScript (or Applet but JS preferred), I can use JSON-PRC to send the whole byte[] over, or I can open a socket to stream it over, but in either case I don't know who to play the byte[] within the browser?
The following code will play the sine wave at 0.5 and 2.0. Call the function play_buffersource() in your button or anywhere you want.
Tested using Chrome with Web Audio flag enabled. For your case, all that you need to do is just to shuffle your audio bytes to the buf.
<script type="text/javascript">
const kSampleRate = 44100; // Other sample rates might not work depending on the your browser's AudioContext
const kNumSamples = 16834;
const kFrequency = 440;
const kPI_2 = Math.PI * 2;
function play_buffersource() {
if (!window.AudioContext) {
if (!window.webkitAudioContext) {
alert("Your browser sucks because it does NOT support any AudioContext!");
return;
}
window.AudioContext = window.webkitAudioContext;
}
var ctx = new AudioContext();
var buffer = ctx.createBuffer(1, kNumSamples, kSampleRate);
var buf = buffer.getChannelData(0);
for (i = 0; i < kNumSamples; ++i) {
buf[i] = Math.sin(kFrequency * kPI_2 * i / kSampleRate);
}
var node = ctx.createBufferSource(0);
node.buffer = buffer;
node.connect(ctx.destination);
node.noteOn(ctx.currentTime + 0.5);
node = ctx.createBufferSource(0);
node.buffer = buffer;
node.connect(ctx.destination);
node.noteOn(ctx.currentTime + 2.0);
}
</script>
References:
http://epx.com.br/artigos/audioapi.php
https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
If you need to resample the audio, you can use a JavaScript resampler: https://github.com/grantgalitz/XAudioJS
If you need to decode the base64 data, there are a lot of JavaScript base64 decoder: https://github.com/carlo/jquery-base64
I accomplished this via the following code. I pass in a byte array containing the data from the wav file to the function playByteArray. My solution is similar to Peter Lee's, but I could not get his to work in my case (the output was garbled) whereas this solution works well for me. I verified that it works in Firefox and Chrome.
window.onload = init;
var context; // Audio context
var buf; // Audio buffer
function init() {
if (!window.AudioContext) {
if (!window.webkitAudioContext) {
alert("Your browser does not support any AudioContext and cannot play back this audio.");
return;
}
window.AudioContext = window.webkitAudioContext;
}
context = new AudioContext();
}
function playByteArray(byteArray) {
var arrayBuffer = new ArrayBuffer(byteArray.length);
var bufferView = new Uint8Array(arrayBuffer);
for (i = 0; i < byteArray.length; i++) {
bufferView[i] = byteArray[i];
}
context.decodeAudioData(arrayBuffer, function(buffer) {
buf = buffer;
play();
});
}
// Play the loaded file
function play() {
// Create a source node from the buffer
var source = context.createBufferSource();
source.buffer = buf;
// Connect to the final output node (the speakers)
source.connect(context.destination);
// Play immediately
source.start(0);
}
If you have the bytes on the server then I would suggest that you create some kind of handler on the server that will stream the bytes to the response as a wav file. This "file" would only be in memory on the server and not on disk. Then the browser can just handle it like a normal wav file.
More details on the server stack would be needed to give more information on how this could be done in your environment.
I suspect you can achieve this with HTML5 Audio API easily enough:
https://developer.mozilla.org/en/Introducing_the_Audio_API_Extension
This library might come in handy too, though I'm not sure if it reflects the latest browser behaviours:
https://github.com/jussi-kalliokoski/audiolib.js

DirectFB data from memory buffer

I need a very fast way of displaying a data buffer to screen. I first tried accessing the linux framebuffer and that proved to be quite good. Then I learned about directFB and I liked the extra features it provides (like fast memcpy, resizing the images on the fly, no need for extra code etc.). But then I hit a snag - all examples are for images that are loaded from files. As far as I can tell there are no examples/tutorials for its 'DataBuffer' type. After peering through the documentation and source code I've managed to compile something that goes like this:
DFBSurfaceDescription sdsc;
DFBDataBufferDescription ddsc;
DFBDataBufferDescriptionFlags ddscf = (DFBDataBufferDescriptionFlags)DBDESC_MEMORY;
IDirectFBDataBuffer *dbuffer;
IDirectFBImageProvider *provider;
ddsc.flags = ddscf;
ddsc.file = NULL;
ddsc.memory.data = m_z;
ddsc.memory.length = 640*480;
DFBCHECK (DirectFBInit (&argc, &argv));
DFBCHECK (DirectFBCreate (&dfb));
DFBCHECK (dfb->SetCooperativeLevel (dfb, DFSCL_FULLSCREEN));
sdsc.flags = DSDESC_CAPS;
sdsc.caps = (DFBSurfaceCapabilities)(DSCAPS_PRIMARY | DSCAPS_FLIPPING);
DFBCHECK (dfb->CreateSurface( dfb, &sdsc, &primary ));
DFBCHECK (primary->GetSize (primary, &screen_width, &screen_height));
DFBCHECK (dfb->CreateDataBuffer(dfb, &ddsc, &dbuffer));
DFBCHECK (dbuffer->CreateImageProvider(dbuffer, &provider));
DFBCHECK (provider->GetSurfaceDescription (provider, &sdsc));
DFBCHECK (dfb->CreateSurface( dfb, &sdsc, &fbwindow ));
DFBCHECK (provider->RenderTo (provider, fbwindow, NULL));
provider->Release (provider);
So basically I'm creating a DataBuffer from the DFB, then an ImageProvider from the DataBuffer and set it to render on a surface. When I run it however, it throws the error:
(#) DirectFBError [dbuffer->CreateImageProvider(dbuffer, &provider)]: No (suitable) implementation found!
Is the method really not implemented? I'm currently using DirectFB 1.4, from the API documentation the function should be there. That being said, does anyone know how to get a buffer (char*640*480*4 RGBA) from memory to render to the framebuffer using DirectFB?
Thanks.
Maybe a bit late to help you, but for the benefit of anyone else trying this, here is an answer.
It is actually more simple than you think (with one gotcha) - I am doing exactly what you want, using DirectFb 1.4.11.
Once you have the primary suface, don't bother with the DataBuffer. Create another surface, using the DSDESC_PREALLOCATED flasg and your buffer as the preallocated data. Then Blit() the data from your new surface onto the primary surface and Flip() the primary surface onto the screen. The one gotcha is that your data needs to be in a format that DirectFB understands: 32 bit RGBA is not one of them, but 32 bit ARGB is - I had to parse my buffer to swap the bytes around.
Example code:
dsc.width = screen_width;
dsc.height = screen_height;
dsc.flags = DSDESC_HEIGHT | DSDESC_WIDTH | DSDESC_PREALLOCATED | DSDESC_PIXELFORMAT;
dsc.caps = DSCAPS_NONE;
dsc.pixelformat = DSPF_ARGB;
dsc.preallocated[0].data = buffer; // Buffer is your data
dsc.preallocated[0].pitch = dsc.width*4;
dsc.preallocated[1].data = NULL;
dsc.preallocated[1].pitch = 0;
DFBCHECK (dfb->CreateSurface( dfb, &dsc, &imageSurface ));
DFBCHECK (primary->Blit(primary, imageSurface, NULL, 0, 0));
DFBCHECK (primary->Flip(primary, NULL, DSFLIP_ONSYNC));
If your buffer is not the same size/shape as your screen, you can use StretchBlit() instead to resize it.
I hope this helps.
The answer above from MartinP is good, but only works if the image is not compressed. I found this topic because I wanted to load and decode/uncompress a png/jpeg image directly from memory.
There is hardly any useful info on this and I have been struggling with it myself but found out how to do it. Although this is an old question, it might help others who try to accomplish the same:
// Variables
DFBDataBufferDescription ddsc;
DFBSurfaceDescription sdsc;
IDirectFBDataBuffer *buffer;
IDirectFBImageProvider *image_provider;
// The surface that will contain the rendered image
IDirectFBSurface *surface;
// create a data buffer for memory
ddsc.flags = DBDESC_MEMORY;
ddsc.memory.data = data;
ddsc.memory.length = dataLength;
DFBCHECK(directFB->CreateDataBuffer(directFB, &ddsc, &buffer));
// Create the image provider, surface description and surface itself
DFBCHECK(buffer->CreateImageProvider(buffer, &image_provider));
DFBCHECK(image_provider->GetSurfaceDescription(image_provider, &sdsc));
DFBCHECK(directFB->CreateSurface(directFB, &sdsc, &surface ));
// Now render the image onto the surface
DFBCHECK(image_provider->RenderTo(image_provider, surface, NULL));
// Release
image_provider->Release(image_provider);
buffer->Release(buffer);
The data variable is a pointer to an array of unsigned char containing the image (hint: you can create such an array with 'xxd -i image.jpg > image.h'). datalength is an unsigned int with the size of the array. The created surface can be blit to the screen, or perhaps you can 'renderto' the display-surface at once.

AudioQueue screws up output after modification

I am currently working on an audio DSP App development. The project requires direct access and modification of audio data. Right now I can successfully access and modify the raw audio data using AudioQueue but encounters error during playback. The output audio after any modification turns out be noise.
In short, the code is something like this:
(Modified from Speakhere sample code. The rest remains unchanged.)
void AQPlayer::AQBufferCallback(void * inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inCompleteAQBuffer)
{
AQPlayer *THIS = (AQPlayer *)inUserData;
if (THIS->mIsDone) return;
UInt32 numBytes;
UInt32 nPackets = THIS->GetNumPacketsToRead();
OSStatus result = AudioFileReadPackets(THIS->GetAudioFileID(),
false,
&numBytes,
inCompleteAQBuffer->mPacketDescriptions,
THIS->GetCurrentPacket(),
&nPackets,
inCompleteAQBuffer->mAudioData);
if (result)
printf("AudioFileReadPackets failed: %d", (int)result);
if (nPackets > 0) {
inCompleteAQBuffer->mAudioDataByteSize = numBytes;
inCompleteAQBuffer->mPacketDescriptionCount = nPackets;
//My modification starts from here
//Modifying audio data
SInt16 *testBuffer = (SInt16*)inCompleteAQBuffer->mAudioData;
for (int i = 0; i < (inCompleteAQBuffer->mAudioDataByteSize)/sizeof(SInt16); i++)
{
//printf("before modification %d", (int)*testBuffer);
*testBuffer = (SInt16) *testBuffer/2; //Say some simple modification
//printf("after modification %d", (int)*testBuffer);
testBuffer++;
}
AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, 0, NULL);
}
During debugging, the data in buffer is displayed as expected, but the actual output is nothing but noise.
Here are some other strange behaviors of the code that makes both the whole team crazy:
If there is no change to the data (add/sub by 0, multiply by 1) or the whole buffer is assigned to a constant (say 0, then the audio will be muted), the playback behaves normally (Of course!) But if I perform anything more than it, it still turns out to be noise.
In the case I hardcode a single tone as test audio, the output noise spreads into another channel also.
So where is the bug in this code? Or if I am on the wrong track, what is the correct approach to modify the audio data and perform playback CORRECTLY? Any insight will be sincerely appreciated.
Thank you very much :-)
Cheers,
Manca
are you SURE the sample format is SInt16? And how many channels are there? You seem to treat the audio as a single channel short stream, but suppose the format is actually dual channel Float32 or so, and you do the modifications there, than the effect would be exactly as you describe, including the noise on other channels.