I'm working on a simple Supercollider patch (my first), designed to swap samples in a file to get a stuttering, granular synthesis sort of sound.
What I'm trying to do is make a new audio file that's the length of the input file. It should run a loop that will swap the place of samples in a file with the sample at the scrambled indexes.
I haven't been able to find a way to get it to compile and write an audio file.
var indexa, indexb, frames, count, j, aa, ab;
j = 0;
s.boot;
b = Buffer.read(s, "/Users/admin/Desktop/10counter.aiff"); //my test file
"Frames: " + b.numFrames.postln;
"Channels: " + b.numChannels.postln;
count = b.numFrames * b.numChannels;
"Count: " + count.postln;
b.write("/Users/admin/Desktop/rbs.aiff", "aiff", "int16", 0, 0, true);
opCount.do ({
temp1 = Buffer.alloc(s, 1, 2);
temp2 = Buffer.alloc(s, 1, 2);
aa = Array.fill(frames, {arg i; i});
ab = a1.scramble;
})
//do the swaps
{j < count}.while ({
indexa = aa[j];
indexb = ab[j];
temp1 = b.get(indexa);
temp2 = b.get(indexb);
b.set(indexb, temp1);
b.set(indexa, temp2);
j.increment;
})
//write to file here?
b.close;
You really just need the Buffer.write method. Put it at the bottom of the code, like you suggest. I'm not sure what the existing b.write is supposed to be doing - you've presumably copied it from somewhere but it's not needed, you don't want to write the file before you've modified it, and also you don't want the leaveOpen argument to be true (because you're doing a single write, not continuously streaming to disk).
Related
I'm using node.js for a project im doing.
The project is to convert words into numbers and then to take those numbers and create an audio output.
The audio output should play the numbers as frequencies. for example, I have an array of numbers [913, 250,352] now I want to play those numbers as frequencies.
I know I can play them in the browser with audio API or any other third package that allows me to do so.
The thing is that I want to create some audio file, I tried to convert those numbers into notes and then save it as Midi file, I succeed but the problem is that the midi file takes the frequencies, convert them into the closest note (example: 913 will convert into 932.33HZ - which is note number 81),
// add a track
var array = gematriaArray
var count = 0
var track = midi.addTrack()
var note
for (var i = 0; i < array.length; i++) {
note = array[i]
track = track.addNote({
//here im converting the freq -> midi note.
midi: ftom(parseInt(note)),
time: count,
duration: 3
})
count++
}
// write the output
fs.writeFileSync('./public/sounds/' + name + random + '.mid', new Buffer.from(midi.toArray()))
I searched the internet but I couldn't find anything that can help.
I really want to have a file that the user can download with those numbers as frequencies, someone knows what can be done to get this result?
Thanks in advance for the helpers.
this function will populate a buffer with floating point values which represent the height of the raw audio curve for the given frequency
var pop_audio_buffer_custom = function (number_of_samples, given_freq, samples_per_second) {
var number_of_samples = Math.round(number_of_samples);
var audio_obj = {};
var source_buffer = new Float32Array(number_of_samples);
audio_obj.buffer = source_buffer;
var incr_theta = (2.0 * Math.PI * given_freq) / samples_per_second;
var theta = 0.0;
for (var curr_sample = 0; curr_sample < number_of_samples; curr_sample++) {
audio_obj.buffer[curr_sample] = Math.sin(theta);
console.log(audio_obj.buffer[curr_sample] , "theta ", theta);
theta += incr_theta;
}
return audio_obj;
}; // pop_audio_buffer_custom
var number_of_samples = 10000; // long enough to be audible
var given_freq = 300;
var samples_per_second = 44100; // CD quality sample rate
var wav_output_filename = "/tmp/wav_output_filename.wav"
var synthesized_obj = {};
synthesized_obj.buffer = pop_audio_buffer_custom(number_of_samples, given_freq, samples_per_second);
the world of digital audio is non trivial ... the next step once you have an audio buffer is to translate the floating point representation into something which can be stored in bytes ( typically 16 bit integers dependent on your choice of bit depth ) ... then that 16 bit integer buffer needs to get written out as a WAV file
audio is a wave sometimes called a time series ... when you pound your fist onto the table the table wobbles up and down which pushes tiny air molecules in unison with that wobble ... this wobbling of air propagates across the room and reaches a microphone diaphragm or maybe your eardrum which in turn wobbles in resonance with this wave ... if you glued a pencil onto the diaphragm so it wobbled along with the diaphragm and you slowly slid a strip of paper along the lead tip of the pencil you would see a curve being written onto that paper strip ... this is the audio curve ... an audio sample is just the height of that curve at an instant of time ... if you repeatedly wrote down this curve height value X times per second at a constant rate you will have a list of data points of raw audio ( this is what above function creates ) ... so a given audio sample is simply the value of the audio curve height at a given instant in time ... since computers are not continuous instead are discrete they cannot handle the entire pencil drawn curve so only care about this list of instantaneously measured curve height values ... those are audio samples
above 32 bit floating point buffer can be fed into following function to return a 16 bit integer buffer
var convert_32_bit_float_into_signed_16_bit_int_lossy = function(input_32_bit_buffer) {
// this method is LOSSY - intended as preliminary step when saving audio into WAV format files
// output is a byte array where the 16 bit output format
// is spread across two bytes in little endian ordering
var size_source_buffer = input_32_bit_buffer.length;
var buffer_byte_array = new Int16Array(size_source_buffer * 2); // Int8Array 8-bit twos complement signed integer
var value_16_bit_signed_int;
var index_byte = 0;
console.log("size_source_buffer", size_source_buffer);
for (var index = 0; index < size_source_buffer; index++) {
value_16_bit_signed_int = ~~((0 < input_32_bit_buffer[index]) ? input_32_bit_buffer[index] * 0x7FFF :
input_32_bit_buffer[index] * 0x8000);
buffer_byte_array[index_byte] = value_16_bit_signed_int & 0xFF; // bitwise AND operation to pluck out only the least significant byte
var byte_two_of_two = (value_16_bit_signed_int >> 8); // bit shift down to access the most significant byte
buffer_byte_array[index_byte + 1] = byte_two_of_two;
index_byte += 2;
};
// ---
return buffer_byte_array;
};
next step is to persist above 16 bit int buffer into a wav file ... I suggest you use one of the many nodejs libraries for that ( or even better write your own as its only two pages of code ;-)))
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.
On the webpage
http://services.runescape.com/m=itemdb_rs/Armadyl_chaps/viewitem.ws?obj=19463
It lists prices for a particular item in a game, I wanted to grab the "Current guide price:" of said item, and store it as a variable so I could output it in a google spreadsheet. I only want the number, currently it is "643.8k", but I am not sure how to grab specific text like that.
Since the number is in "k" form, that means I can't graph it, It would have to be something like 643,800 to make it graphable. I have a formula for it, and my second question would be to know if it's possible to use a formula on the number pulled, then store that as the final output?
-EDIT-
This is what I have so far and it's not working not sure why.
function pullRuneScape() {
var page = UrlFetchApp.fetch("http://services.runescape.com/m=itemdb_rs/Armadyl_chaps/viewitem.ws?obj=19463").getContentText();
var number = page.match(/Current guide price:<\/th>\n(\d*)/)[1];
SpreadsheetApp.getActive().getSheetByName('RuneScape').appendRow([new Date(), number]);
}
Your regex is wrong. I tested this one successfully:
var number = page.match(/Current guide price:<\/th>\s*<td>([^<]*)<\/td>/m)[1];
What it does:
Current guide price:<\/th> find Current guide price: and closing td tag
\s*<td> allow whitespace between tags, find opening td tag
([^<]*) build a group and match everything except this char <
<\/td> match the closing td tag
/m match multiline
Use UrlFetch to get the page [1]. That'll return an HTTPResponse that you can read with GetBlob [2]. Once you have the text you can use regular expressions. In this case just search for 'Current guide price:' and then read the next row. As to remove the 'k' you can just replace with reg ex like this:
'123k'.replace(/k/g,'')
Will return just '123'.
https://developers.google.com/apps-script/reference/url-fetch/
https://developers.google.com/apps-script/reference/url-fetch/http-response
Obviously, you are not getting anything because the regexp is wrong. I'm no regexp expert but I was able to extract the number using basic string manipulation
var page = UrlFetchApp.fetch("http://services.runescape.com/m=itemdb_rs/Armadyl_chaps/viewitem.ws?obj=19463").getContentText();
var TD = "<td>";
var start = page.indexOf('Current guide price');
start = page.indexOf(TD, start);
var end = page.indexOf('</td>',start);
var number = page.substring (start + TD.length , end);
Logger.log(number);
Then, I wrote a function to convert k,m etc. to the corresponding multiplying factors.
function getMultiplyingFactor(symbol){
switch(symbol){
case 'k':
case 'K':
return 1000;
case 'm':
case 'M':
return 1000 * 1000;
case 'g':
case 'G':
return 1000 * 1000 * 1000;
default:
return 1;
}
}
Finally, tie the two together
function pullRuneScape() {
var page = UrlFetchApp.fetch("http://services.runescape.com/m=itemdb_rs/Armadyl_chaps/viewitem.ws?obj=19463").getContentText();
var TD = "<td>";
var start = page.indexOf('Current guide price');
start = page.indexOf(TD, start);
var end = page.indexOf('</td>',start);
var number = page.substring (start + TD.length , end);
Logger.log(number);
var numericPart = number.substring(0, number.length -1);
var multiplierSymbol = number.substring(number.length -1 , number.length);
var multiplier = getMultiplyingFactor(multiplierSymbol);
var fullNumber = multiplier == 1 ? number : numericPart * multiplier;
Logger.log(fullNumber);
}
Certainly, not the optimal way of doing things but it works.
Basically I parse the html page as you did (with corrected regex) and split the string into number part and multiplicator (k = 1000). Finally I return the extracted number. This function can be used in Google Docs.
function pullRuneScape() {
var pageContent = UrlFetchApp.fetch("http://services.runescape.com/m=itemdb_rs/Armadyl_chaps/viewitem.ws?obj=19463").getContentText();
var matched = pageContent.match(/Current guide price:<.th>\n<td>(\d+\.*\d*)([k]{0,1})/);
var numberAsString = matched[1];
var multiplier = "";
if (matched.length == 3) {
multiplier = matched[2];
}
number = convertNumber(numberAsString, multiplier);
return number;
}
function convertNumber(numberAsString, multiplier) {
var number = Number(numberAsString);
if (multiplier == 'k') {
number *= 1000;
}
return number;
}
I am using a node.js server for a multiplayer synchronized dice, but i am having some strange problems with variables changing that are not referenced or used...
var origonalRolls = rolls;
//identify epic fails
var epicFails = [];
for(var r = 0; r < rolls.length; r++)
if(rolls[r] === 1)
epicFails.push(r);
console.log("TEST 1 :: " + JSON.stringify(rolls));
console.log("TEST 2 :: " + JSON.stringify(origonalRolls));
//remove epic fails and the corresponding heighest
if(epicFails.length > 0){
for(var i = epicFails.length-1; i >= 0; i--){
rolls.splice(epicFails[i], 1);
if(rolls[rolls.length-1] >= success)
rolls.splice(rolls.length-1, 1);
}
}
console.log("TEST 3 :: " + JSON.stringify(rolls));
console.log("TEST 4 :: " + JSON.stringify(origonalRolls));
the above should find any element in the rolls array which is 1 and then add it to epicFails. it should then remove it from rolls as well as the heighest remaining roll. (note, rolls is sorted numerically)
for some reason the output of this segment of code is as follows:
TEST 1 :: [1,1,2,3,3,6,7,7,9,9]
TEST 2 :: [1,1,2,3,3,6,7,7,9,9]
TEST 3 :: [2,3,3,6,7,7]
TEST 4 :: [2,3,3,6,7,7]
I am unsure why rolls and origonalRolls start the same and end the same. I am only using rolls.
Any help and/or explanation to this problem is welcome, it's been troubling me for a long time now...
In Javascript Arrays and Objects are only shallow copied - which means that an array (rolls) copied from another array (originalRolls) is only a reference to originalRolls - it is not an entirely new array, and modifying values in one will affect values in the other.
You will need to implement a deep copy function to create an entirely new array based off another. There are numerous imlementations of deep copying arrays/objects both here and elsewhere on the net - here is one of them from a quick Google.
Replace var origonalRolls = rolls; with:
var origonalRolls = [];
for (var i = 0, len = rolls.length; i < len; i++) {
origonalRolls[i] = rolls[i];
}
I have a tab-delimited text file of size of many GBs. Task here is to append header texts to each column. As of now, I use StreamReader to read line by line and append headers to each column. It takes a lot of time as of now. Is there a way to make it faster ? I was thinking if there is a way to process the file column-wise. One way would be to import the file in database table and then bcp out the data after appending the headers. Is there any other better way, probably by calling powershell, awk/sed in C# code ?
Code is as follows :
StreamReader sr = new StreamReader(#FilePath, System.Text.Encoding.Default);
string mainLine = sr.ReadLine();
string[] fileHeaders = mainLine.Split(new string[] { "\t" }, StringSplitOptions.None);
string newLine = "";
System.IO.StreamWriter outFileSw = new System.IO.StreamWriter(#outFile);
while (!sr.EndOfStream)
{
mainLine = sr.ReadLine();
string[] originalLine = mainLine.Split(new string[] { "\t" }, StringSplitOptions.None);
newLine = "";
for (int i = 0; i < fileHeaders.Length; i++)
{
if(fileHeaders[i].Trim() != "")
newLine = newLine + fileHeaders[i].Trim() + "=" + originalLine[i].Trim() + "&";
}
outFileSw.WriteLine(newLine.Remove(newLine.Length - 1));
}
Nothing else operating on just text files is going to be significantly faster - fundamentally you've got to read the whole of the input file, and you've got to create a whole new output file, as you can't "insert" text for each column.
Using a database would almost certainly be a better idea in general, but adding a column could still end up being a relatively slow business.
You can improve how you're dealing with each line, however. In this code:
for (int i = 0; i < fileHeaders.Length; i++)
{
if(fileHeaders[i].Trim() != "")
newLine = newLine + fileHeaders[i].Trim() + "=" + originalLine[i].Trim() + "&";
}
... you're using string concatenation in a loop, which will be slow if there's a large number of columns. Using a StringBuilder is very likely to be more efficient. Additionally, there's no need to call Trim() on every string in fileHeaders on every line. You can just work out which columns you want once, trim the header appropriately, and filter that way.