web audio - merge 3 audiostreams into 1 - web-audio-api

Pretty confident that it should be possible.
Currently i can only get the stream from one:
let mediaElement = document.createElement("img");
mediaElement.className = "fileMedia";
mediaElement.style.maxWidth = "280px";
mediaElement.crossOrigin = "anonymous";
mediaElement.controls = true;
mediaElement.src = URL.createObjectURL(selectedLocalMedia); // a file from type="file" input
let audioCtx = new AudioContext();
// create placeholder stream from our AudioContext
let dest = audioCtx.createMediaStreamDestination();
this.audioStream[pluginHandleIndex] = dest.stream;
// connect our video element's output to the stream
let sourceNode = audioCtx.createMediaElementSource(mediaElement);
// if (pluginHandleIndex > 0) {
// let testvideo = document.getElementById("testvideo");
// let sourceNodeTwo = audioCtx.createMediaElementSource(testvideo);
// sourceNodeTwo.connect(dest);
// }
sourceNode.connect(dest);
sourceNode.connect(audioCtx.destination);
How can i add more audio streams to this.audioStream[pluginHandleIndex]( audioCtx.createMediaStreamDestination() ) ?
If you see commented lines
// if (pluginHandleIndex > 0) {
// let testvideo = document.getElementById("testvideo");
// let sourceNodeTwo = audioCtx.createMediaElementSource(testvideo);
// sourceNodeTwo.connect(dest);
// }
I try to attach tome like the first time, but this doesn't seem to work :/
Thanks!

Related

Cannot set optional value, prints nil

I am working on project that writes to google sheets. I am trying to Unmerge cells. This function works however it unmerges everything in the sheet. This is because it is not setting the .range value. When I print (as seen below) the "test" value all range values are shown accordingly however when I print the "request.unmergeCells?.range" it says nil. I am further confused as I use this exact code elsewhere for a merge command and it loads the values fine (see second snippet of code.)
I have tried for days to resolve this issue with no avail. Any thoughts?
func unmergecell1() {
let request = GTLRSheets_Request.init()
let test = GTLRSheets_GridRange.init()
rowstart = 4
rowend = 100
columnstart = 0
columnend = 100
test.startRowIndex = rowstart
test.endRowIndex = rowend
test.startColumnIndex = columnstart
test.endColumnIndex = columnend
request.unmergeCells?.range = test
request.unmergeCells = GTLRSheets_UnmergeCellsRequest.init()
print("=========unmerge==============")
print(test)
print(request.unmergeCells?.range)
let batchUpdate = GTLRSheets_BatchUpdateSpreadsheetRequest.init()
batchUpdate.requests = [request]
let createQuery = GTLRSheetsQuery_SpreadsheetsBatchUpdate.query(withObject: batchUpdate, spreadsheetId: spreadsheetId)
service.executeQuery(createQuery) { (ticket, result, NSError) in
}
}
func mergecell() {
let request = GTLRSheets_Request.init()
request.mergeCells = GTLRSheets_MergeCellsRequest.init()
let test = GTLRSheets_GridRange.init()
test.startRowIndex = rowstart
test.endRowIndex = rowend
test.startColumnIndex = columnstart
test.endColumnIndex = columnend
request.mergeCells?.range = test
request.mergeCells?.mergeType = kGTLRSheets_MergeCellsRequest_MergeType_MergeRows
let batchUpdate = GTLRSheets_BatchUpdateSpreadsheetRequest.init()
batchUpdate.requests = [request]
let createQuery = GTLRSheetsQuery_SpreadsheetsBatchUpdate.query(withObject: batchUpdate, spreadsheetId: spreadsheetId)
service.executeQuery(createQuery) { (ticket, result, NSError) in
}
}
I think you need to switch these 2 lines:
request.unmergeCells?.range = test
request.unmergeCells = GTLRSheets_UnmergeCellsRequest.init()
The 2nd line initates the unmerge request, and only after you initate it can you add the range to it. So by making it the other way round, as below, it should work.
request.unmergeCells = GTLRSheets_UnmergeCellsRequest.init()
request.unmergeCells?.range = test

AudioKit, exporting AVAudioPCMBuffer array to audio file with fade in/out

I'm capturing audio from AKLazyTap and rendering the accumulated [AVAudioPCMBuffer] to an audio file, in the background, while my app's audio is running. This works great, but I want to add fade in/out to clean up the result. I see the convenience extension for adding fades to a single AVAudioPCMBuffer, but I'm not sure how I'd do it on an array. I'd thought to concatenate the buffers, but there doesn't appear to be support for that. Does anyone know if that's currently possible? Basically it would require something similar to copy(from:readOffset:frames), but would need to have a write offset as well...
Or maybe there's an easier way?
UPDATE
Okay, after studying some related AK code, I tried directly copying buffer data over to a single, long buffer, then applying the fade convenience function. But this gives me an empty (well, 4k) file. Is there some obvious error here that I'm just not seeing?
func renderBufferedAudioToFile(_ audioBuffers: [AVAudioPCMBuffer], withStartOffset startOffset: Int, endOffset: Int, fadeIn: Float64, fadeOut: Float64, atURL url: URL) {
// strip off the file name
let name = String(url.lastPathComponent.split(separator: ".")[0])
var url = self.module.stateManager.audioCacheDirectory
// UNCOMPRESSED
url = url.appendingPathComponent("\(name).caf")
let format = Conductor.sharedInstance.sourceMixer.avAudioNode.outputFormat(forBus: 0)
var settings = format.settings
settings["AVLinearPCMIsNonInterleaved"] = false
// temp buffer for fades
let totalFrameCapacity = audioBuffers.reduce(0) { $0 + $1.frameLength }
guard let tempAudioBufferForFades = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: totalFrameCapacity) else {
print("Failed to create fade buffer!")
return
}
// write ring buffer to file.
let file = try! AVAudioFile(forWriting: url, settings: settings)
var writeOffset: AVAudioFrameCount = 0
for i in 0 ..< audioBuffers.count {
var buffer = audioBuffers[i]
let channelCount = Int(buffer.format.channelCount)
if i == 0 && startOffset != 0 {
// copy a subset of samples in the buffer
if let subset = buffer.copyFrom(startSample: AVAudioFrameCount(startOffset)) {
buffer = subset
}
} else if i == audioBuffers.count - 1 && endOffset != 0 {
if let subset = buffer.copyTo(count: AVAudioFrameCount(endOffset)) {
buffer = subset
}
}
// write samples into single, long buffer
for i in 0 ..< buffer.frameLength {
for n in 0 ..< channelCount {
tempAudioBufferForFades.floatChannelData?[n][Int(i + writeOffset)] = (buffer.floatChannelData?[n][Int(i)])!
}
}
print("buffer \(i), writeOffset = \(writeOffset)")
writeOffset = writeOffset + buffer.frameLength
}
// update!
tempAudioBufferForFades.frameLength = totalFrameCapacity
if let bufferWithFades = tempAudioBufferForFades.fade(inTime: fadeIn, outTime: fadeOut) {
try! file.write(from: bufferWithFades)
}
}

Swift Get Next Page from header of NSHTTPURLResponse

I am consuming an API that gives me the next page in the Header inside a field called Link. (For example Github does the same, so it isn't weird.Github Doc)
The service that I am consuming retrieve me the pagination data in the following way:
As we can see in the "Link" gives me the next page,
With $0.response?.allHeaderFields["Link"]: I get </api/games?page=1&size=20>; rel="next",</api/games?page=25&size=20>; rel="last",</api/games?page=0&size=20>; rel="first".
I have found the following code to read the page, but it is very dirty... And I would like if anyone has dealt with the same problem or if there is a standard way of face with it. (I have also searched if alamofire supports any kind of feature for this but I haven't found it)
// MARK: - Pagination
private func getNextPageFromHeaders(response: NSHTTPURLResponse?) -> String? {
if let linkHeader = response?.allHeaderFields["Link"] as? String {
/* looks like:
<https://api.github.com/user/20267/gists?page=2>; rel="next", <https://api.github.com/user/20267/gists?page=6>; rel="last"
*/
// so split on "," the on ";"
let components = linkHeader.characters.split {$0 == ","}.map { String($0) }
// now we have 2 lines like '<https://api.github.com/user/20267/gists?page=2>; rel="next"'
// So let's get the URL out of there:
for item in components {
// see if it's "next"
let rangeOfNext = item.rangeOfString("rel=\"next\"", options: [])
if rangeOfNext != nil {
let rangeOfPaddedURL = item.rangeOfString("<(.*)>;", options: .RegularExpressionSearch)
if let range = rangeOfPaddedURL {
let nextURL = item.substringWithRange(range)
// strip off the < and >;
let startIndex = nextURL.startIndex.advancedBy(1) //advance as much as you like
let endIndex = nextURL.endIndex.advancedBy(-2)
let urlRange = startIndex..<endIndex
return nextURL.substringWithRange(urlRange)
}
}
}
}
return nil
}
I think that the forEach() could have a better solution, but here is what I got:
let linkHeader = "</api/games?page=1&size=20>; rel=\"next\",</api/games?page=25&size=20>; rel=\"last\",</api/games?page=0&size=20>; rel=\"first\""
let links = linkHeader.components(separatedBy: ",")
var dictionary: [String: String] = [:]
links.forEach({
let components = $0.components(separatedBy:"; ")
let cleanPath = components[0].trimmingCharacters(in: CharacterSet(charactersIn: "<>"))
dictionary[components[1]] = cleanPath
})
if let nextPagePath = dictionary["rel=\"next\""] {
print("nextPagePath: \(nextPagePath)")
}
//Bonus
if let lastPagePath = dictionary["rel=\"last\""] {
print("lastPagePath: \(lastPagePath)")
}
if let firstPagePath = dictionary["rel=\"first\""] {
print("firstPagePath: \(firstPagePath)")
}
Console output:
$> nextPagePath: /api/games?page=1&size=20
$> lastPagePath: /api/games?page=25&size=20
$> firstPagePath: /api/games?page=0&size=20
I used components(separatedBy:) instead of split() to avoid the String() conversion at the end.
I created a Dictionary for the values to hold and removed the < and > with a trim.

Swift, dictionary parse error

I'm using an API to get weather condition and the retrieved dict is
dict = {
base = stations;
clouds = {
all = 92;
};
cod = 200;
coord = {
lat = "31.23";
lon = "121.47";
};
dt = 1476853699;
id = 1796231;
main = {
"grnd_level" = "1028.63";
humidity = 93;
pressure = "1028.63";
"sea_level" = "1029.5";
temp = "73.38";
"temp_max" = "73.38";
"temp_min" = "73.38";
};
name = "Shanghai Shi";
rain = {
3h = "0.665";
};
sys = {
country = CN;
message = "0.0125";
sunrise = 1476827992;
sunset = 1476868662;
};
weather = (
{
description = "light rain";
icon = 10d;
id = 500;
main = Rain;
}
);
wind = {
deg = "84.50239999999999";
speed = "5.97";
};
}
If I want the value of humidity, I just use
let humidityValue = dict["main"]["humidity"] and it works.
But the problem is I also want to get the value of description in weather
when I used let dscptValue = dict["weather"]["description"]
it retrieved nil.
How's that? and I notice there are two brackets around weather .I'm not sure whether it is the same with the statement without brackets.
weather = (
{
description = "light rain";
icon = 10d;
id = 500;
main = Rain;
}
);
How to get the value of description?
weather keys contains Array of Dictionary not directly Dictionary, so you need to access the first object of it.
if let weather = dict["weather"] as? [[String: AnyObject]], let weatherDict = weather.first {
let dscptValue = weatherDict["description"]
}
Note: I have used optional wrapping with if let for preventing crash with forced wrapping.
Weather is an array of dictionaries.
dict["weather"][0]["description"]
may give you the expected result.

Flex 4 Send and load variables to a new window

I want to post some variables to a new window.
The receiving c# will then generate a CSV which will stream as a download.
In flex this used to be achieved using loadVars and specifying _blank as the target.
I currently use the following:
var myRequest:URLRequest = new URLRequest(url);
var myLoader:URLLoader = new URLLoader();
var myVariables:URLVariables = new URLVariables();
myVariables.CurrentActiveUserID = currentUserID
myVariables.ReportRuleListID = SingleChartID
myRequest.method = URLRequestMethod.POST;
myRequest.data = myVariables;
myLoader.load(myRequest);
But it does not seem to support targeting of new windows.
Any ideas.
Please and thank you.
I finally sorted it with:
private function sendAndLoadCSVData():void {
var swfURL:String = this.loaderInfo.url;
swfURL = swfURL.substr(0,swfURL.lastIndexOf("/") + 1);
var tempDom:Array = swfURL.split("/");
var domURL:String = tempDom.slice(0,3).join("/") + "/";
var url:String = swfURL + "../Reporting/ExportChartCSV.aspx"
// var post_variable:LoadVars = new LoadVars();
var myRequest:URLRequest = new URLRequest(url);
var myLoader:URLLoader = new URLLoader();
var myVariables:URLVariables = new URLVariables();
myVariables.CurrentActiveUserID = currentUserID
myVariables.ReportRuleListID = SingleChartID
myRequest.method = URLRequestMethod.POST;
myRequest.data = myVariables;
navigateToURL(myRequest, '_blank')
//myLoader.load(myRequest);
// Alert.show(url);
}