I'm developing for the iPhone and am trying to get an initial timeStamp to sync my audioQueues.
I'm using AudioQueueDeviceGetCurrentTime for this. According to the documentation this function gives back a valid mHostTime whether the queue/device is running or not. But when I try this I get back a kAudioHardwareNotRunningError (1937010544). All queues have an timeLine associated and have been initialized before I call the function.
How can I retrieve a valid mHostTime to sync my AudioQueues (prior to running the queues)?
My code:
AudioSessionInitialize(NULL, NULL, interruptionListenerCallback, self);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
sizeof(sessionCategory), &sessionCategory);
// initialize all queues
// ....
AudioSessionSetActive(true);
OSStatus result;
AudioTimestamp currentTime;
result = AudioQueueDeviceGetCurrentTime(audioQueueRef, ¤tTimeStamp);
if (!result)
{
// rest of code
}
After some googling I found a post on the CoreAudio mailing list where they say that the hostTime is the same as mach_absolute_time().
Mach_absolute_time() is indeed giving me expected timestamp values.
I spent about a week stuck with this exact problem. As far as I can tell, the documentation is wrong - you must have a running audio queue to query the current device time.
My solution? It's really inelegant, but I just keep one audio queue running at all times playing silence so that I can time other queues off it.
Try this, the function AudioQueueGetCurrentTime() fills an AudioTimeStamp structure. If you get the mSampleTime property of this structure and divide by audio sample rate you will obtain the current seconds position. In code:
// AudioTimeStamp struct to store the value.
AudioTimeStamp timeStamp;
// Gets the current audio queue time.
AudioQueueGetCurrentTime(
mQueue, // The audio queue whose current time you want to get.
NULL,
&timeStamp, // On output, the current audio queue time.
NULL
);
// Return the value.
NSTimeInterval seconds = timeStamp.mSampleTime / mRecordFormat.mSampleRate;
If you doesn't known the current sample rate, this information is stored on the mSampleRate property if you are using the CAStreamBasicDescription structure to control this.
Hope it works.
you have to create a timeline before you can call AudioQueueDeviceGetCurrentTime
AudioSessionSetActive(true);
// initialize all queues
// ....
// initialize time line
AudioQueueTimelineRef audioTimeline;
status = AudioQueueCreateTimeline(audioQueueRef, &audioTimeline);
// now you can do what you want.
OSStatus result;
AudioTimestamp currentTime;
result = AudioQueueDeviceGetCurrentTime(audioQueueRef, ¤tTimeStamp);
if (!result)
{
// rest of code
}
Related
I am trying to test WiFi data transfer between cell phone and Esp32 (Arduino), when ESP32 reads file data via WiFi, even there is still data in, client.read() often return -1, I have to add other conditions to check reading finished or not.
My question is why there are so many failed reads, any ideas are highly appreciated.
void setup()
{
i=0;
Serial.begin(115200);
Serial.println("begin...");
// You can remove the password parameter if you want the AP to be open.
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
Serial.println("Server started");
}
// the loop function runs over and over again until power down or reset
void loop()
{
WiFiClient client = server.available(); // listen for incoming clients
if(client) // if you get a client,
{
Serial.println("New Client."); // print a message out the serial port
Serial.println(client.remoteIP().toString());
while(client.connected()) // loop while the client's connected
{
while(client.available()>0) // if there's bytes to read from the client,
{
char c = client.read(); // read a byte, then
if(DOWNLOADFILE ==c){
pretime=millis();
uint8_t filename[32]={0};
uint8_t bFilesize[8];
long filesize;
int segment=0;
int remainder=0;
uint8_t data[512];
int len=0;
int totallen=0;
delay(50);
len=client.read(filename,32);
delay(50);
len=client.read(bFilesize,8);
filesize=BytesToLong(bFilesize);
segment=(int)filesize/512;
delay(50);
i=0; //succeed times
j=0; //fail times
////////////////////////////////////////////////////////////////////
//problem occures here, to many "-1" return value
// total read 24941639 bytes, succeed 49725 times, failed 278348 times
// if there were no read problems, it should only read 48,715 times and finish.
//But it total read 328,073 times, including 278,348 falied times, wasted too much time
while(((len=client.read(data,512))!=-1) || (totallen<filesize))
{
if(len>-1) {
totallen+=len;
i++;
}
else{
j++;
}
}
///loop read end , too many times read fail//////////////////////////////////////////////////////////////////
sprintf(toClient, "\nfile name %s,size %d, total read %d, segment %d, succeed %d times, failed %d times\n",filename,filesize,totallen,segment,i,j);
Serial.write(toClient);
curtime=millis();
sprintf(toClient, "time splashed %d ms, speed %d Bps\n", curtime-pretime, filesize*1000/(curtime-pretime));
Serial.write(toClient);
client.write(RETSUCCESS);
}
else
{
Serial.write("Unknow command\n");
}
}
}
// close the connection:
client.stop();
Serial.println("Client Disconnected.");
}
When you call available() and check for > 0, you are checking to see if there is one or more characters available to read. It will be true if just one character has arrived. You read one character, which is fine, but then you start reading more without stopping to see if there are more available.
TCP doesn't guarantee that if you write 100 characters to a socket that they all arrive at once. They can arrive in arbitrary "chunks" with arbitrary delays. All that's guaranteed is that they will eventually arrive in order (or if that's not possible because of networking issues, the connection will fail.)
In the absence of a blocking read function (I don't know if those exist) you have to do something like what you are doing. You have to read one character at a time and append it to a buffer, gracefully handing the possibility of getting a -1 (the next character isn't here yet, or the connection broke). In general you never want to try to read multiple characters in a single read(buf, len) unless you've just used available() to make sure len characters are actually available. And even that can fail if your buffers are really large. Stick to one-character-at-a-time.
It's a reasonable idea to call delay(1) when available() returns 0. In the places where you try to guess at something like delay(20) before reading a buffer you are rolling the dice - there's no promise that any amount of delay will guarantee bytes get delivered. Example: Maybe a drop of water fell on the chip's antenna and it won't work until the drop evaporates. Data could be delayed for minutes.
I don't know how available() behaves if the connection fails. You might have to do a read() and get back a -1 to diagnose a failed connection. The Arduino documentation is absolutely horrible, so you'll have to experiment.
TCP is much simpler to handle on platforms that have threads, blocking read, select() and other tools to manage data. Having only non-blocking read makes things harder, but there it is.
In some situations UDP is actually a lot simpler - there are more guarantees about getting messages of certain sizes in a single chunk. But of course whole messages can go missing or show up out of order. It's a trade-off.
I need to play sounds upon certain events, and want to minimize
processor load, because some image processing is being done too, and
processor performance is limited.
For the present, I play only one sound at a time, and I do it as
follows:
At program startup, sounds are read from .wav files
and the raw pcm data are loaded into memory
a sound device is opened (snd_pcm_open() in mode SND_PCM_NONBLOCK)
a worker thread is started which continously calls snd_pcm_writei()
as long as it is fed with data (data->remaining > 0).
Somewhat resumed, the worker thread function is
static void *Thread_Func (void *arg)
{
thrdata_t *data = (thrdata_t *)arg;
snd_pcm_sframes_t res;
while (1)
{ pthread_mutex_lock (&lock);
if (data->shall_stop)
{ data->shall_stop = false;
snd_pcm_drop (data->pcm_device);
snd_pcm_prepare (data->pcm_device);
data->remaining = 0;
}
if (data->remaining > 0)
{ res = snd_pcm_writei (data->pcm_device, data->bufptr, data->remaining);
if (res == -EAGAIN) continue;
if (res < 0) // error
{ fprintf (stderr, "snd_pcm_writeX() error: %s\n", snd_strerror(result));
snd_pcm_recover (data->sub_device, res);
}
else // another chunk has been handed over to sound hw
{ data->bufptr += res * bytes_per_frame;
data->remaining -= res;
}
if (data->remaining == 0) snd_pcm_prepare (data->pcm_device);
}
pthread_mutex_unlock (&lock);
usleep (sleep_us); // processor relief
}
} // Thread_Func
Ok, so this works well for one sound at a time. How do I play various?
I found dmix, but it seems a tool on user level, to mix streams coming
from separate programs.
Furthermore, I found the Simple Mixer Interface in the ALSA Project C
Library Interface, without any hint or example or tutorial about how
to use all these function described by one line of text each.
As a last resort I could calculate the mean value of all the buffers
to be played synchronously. So long I've been avoiding that, hoping
that an ALSA solution might use sound hardware resources, thus
relieving the main processor.
I'd be thankful for any hint about how to continue.
In a simulation using RPR-FOM, if I get a reflectAttributeValues with a LogicalTime time stamp (simulation time) and the OrderType receive order in my FederateAmbassador. For dead reckoning algorithms do I use the time stamp supplied by the RTI or the time stamp encoded in the userSuppliedTag? Using the userSuppliedTag would be decoded value if absolute and system clock if relative.
To clarify, I get attributes reflected specified receive order from a time managed federate in this call in FederateAmbassador from the RTI:
void reflectAttributeValues(ObjectInstanceHandle theObject,
AttributeHandleValueMap theAttributes,
byte[] userSuppliedTag,
OrderType sentOrdering,
TransportationTypeHandle theTransport,
LogicalTime theTime,
OrderType receivedOrdering,
MessageRetractionHandle retractionHandle,
SupplementalReflectInfo reflectInfo)
For attributes that were updated Time Stamp Order, I used the time parameter to know when the attribute last had been updated and simulation time to dead reckon.
public void reflectAttributeValues(
ObjectInstanceHandle objectHandle,
AttributeHandleValueMap attributes,
byte[] userSuppliedTag,
OrderType sentOrdering,
TransportationTypeHandle theTransport,
LogicalTime time,
OrderType receivedOrdering,
MessageRetractionHandle retractionHandle,
SupplementalReflectInfo reflectInfo) {
attributes.forEach((attributeHandle, value) -> {
lastUpdated.put(attributeHandle, time));
timeManaged.add(attributeHandle);
// decode value into your object
...
}
}
For attributes that where updated Receive Order without time stamp, I used the userSuppliedTag to know when the attributed last had been updated (value in the tag for absolute and system clock at the time of receiving the attribute for relative) and then using the system clock to dead reckon.
public void reflectAttributeValues(
ObjectInstanceHandle objectHandle,
AttributeHandleValueMap attributes,
byte[] userSuppliedTag,
OrderType sentOrdering,
TransportationTypeHandle theTransport,
SupplementalReflectInfo reflectInfo) {
LogicalTime time;
if (isRelativeTag(userSuppliedTag)) {
time = factory.createSystemLogicalTime(System.currentTimeMillis());
} else {
time = decodeTag(userSuppliedTag);
}
attributes.forEach((attributeHandle, value)-> {
lastUpdated.put(attributeHandle, time);
timeManaged.remove(attributeHandle); // attributes might switch
// decode value into your objects
...
}
}
Then to dead reckon:
private Vector3D getDeadReckonedWorldLocation(LogicalTime time) {
LogicalTime lastUpdatedSpatial = lastUpdated.get(spatialAttributeHandle);
if (!timeManaged.contains(spatialAttributeHandle)) {
time = factory.createSystemLogicalTime(System.currentTimeMillis());
}
LogicalTimeInterval timeToDeadReckon = time.distance(lastUpdatedSpatial);
return deadReckon(timeToDeadReckon);
}
Code here are simplified examples and may not compile, but they capture the solution I managed to come up with.
Most users of the RPR FOM only use the time in the User Supplied Tag.
The HLA Time Management Services are usually not used any you would never receive a LogicalTime or messages in Time Stamp Order (TSO).
See the Federation Agreement for the RPR FOM, "SISO-STD-001-2015: Standard for Guidance, Rationale, and Interoperability Modalities (GRIM) for the Real-time Platform Reference Federation Object Model (RPR FOM)", for more details: https://www.sisostds.org/DigitalLibrary.aspx?Command=Core_Download&EntryId=30822
I'm using NSUbiquitousKeyValueStore to store some app settings. My logic is: when I save data locally, I save it to NSUbiquitousKeyValueStore also as a backup. When I need settings, I read locally and I only use iCloud key-value store if no data is found locally (after app is reinstalled, for example). If user has several devices sharing one icloud id, he can write settings on one device and download them to another (I warn him about rewrite).
I have a strange issue. Steps:
Installed an app and save its data to NSUbiquitousKeyValueStore. Made sure data is there.
Removed the app (assuming data is still persists in iCloud).
Waited several minutes just in case, then installed and launched the app from inside Xcode.
Tried to read a settings key using [[NSUbiquitousKeyValueStore defaultStore] dataForKey: #"mykeyname"] - sometimes it's ok, but sometimes key is not found!
Waited for 15 seconds, tried again. Success. Confused.
So it seems like ios needs some time to make remote key-value storage for my app available locally for dataForKey: call.
If I'd wrote such a system (actually I did - some time ago, in another life) there obviously must be a delay before asking and receiving a key-value data. So I'd like to have some notification saying: "we finished downloading/syncing key-value storage on first start" or something similar.
As far as I understand I can work with NSUbiquitousKeyValueStore in main thread synchronously (which is convenient for me). But [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] returns a valid url, and then I get "key isn't found". So I can't rely on it. Is there a way to be sure NSUbiquitousKeyValueStore works an is downloaded? It's important especially with slow internet.
UPDATE
Adding [[NSUbiquitousKeyValueStore defaultStore] synchronize] (as written in apple docs) to init and load was helped a little. Still there are many questions to iCloud.
Yesterday I've successfully saved data to the key-value store on phone 1 and restored on phone 2.
Today I've deleted app on phone 2 and tried to restore the data. But even [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] returned valid URL and I called [[NSUbiquitousKeyValueStore defaultStore] synchronize] I get nil when call dataForKey: MY_DATA_KEY.
When I tried to restore data from icloud on phone 1 (app is still installed) it succeeds, but when I reinstalled on this phone the app restore doesn't succeed any more.
Temporary solution is: "turn off iCloud->Documents&Data - turn off and on network - turn on Documents&Data", but also you should wait several minutes, and then it should work.
So, questions:
do you have such problems with iCloud?
Is there any way to find out is data not available or just not downloaded yet?
Is there any known "latency" of iCloud? I've heard about 7 seconds, but it's obviously not true.
It seems that when app isn't unistalled updates of iCloud data are pretty fast (seconds), but when you reinstall the app icloud needs several minutes to actualize key-value store. Is there any way to force this process?
P.S.
Below is my CloudHelper for your reference - pretty simple c++ class to write/read binary data to/from iCloud key-value store. It is not compilable, I've adapted it for SO somewhat to make more clear - removed my engine related code. Still if you remove MySystem::... calls it works pretty well. Except that I mentioned before.
class CloudHelper
{
public:
static bool init();
static void deInit();
//save our data to iCloud with
static int saveData(unsigned char* data, int from, int count);
//get our data from iCloud
static unsigned char * loadData(int *retsize, int * retint);
//does iCloud work for us
static bool isEnabled();
//do we have our key in iCloud
static int isAvailable();
static const int RESULT_OK = 0;
static const int RESULT_NO_CONNECTION = 1;
static const int RESULT_NOT_FOUND = 2;
static const int RESULT_SYNC_ERROR = 3;
private:
static bool enabled;
static NSURL *ubiq;
};
bool CloudHelper::enabled = false;
NSURL *CloudHelper::ubiq = NULL;
#define MY_DATA_KEY #"my_data_key"
int CloudHelper::saveData(unsigned char* data, int from, int count)
{
if ([NSUbiquitousKeyValueStore defaultStore])
{
NSData *d = [[[NSData alloc] initWithBytes:(data + from) length:count] autorelease];
[[NSUbiquitousKeyValueStore defaultStore] setData:d forKey: MY_DATA_KEY)];
if ([[NSUbiquitousKeyValueStore defaultStore] synchronize] != TRUE)
return RESULT_SYNC_ERROR;
return RESULT_OK;
}
return RESULT_NO_CONNECTION;
}
unsigned char * CloudHelper::loadData(int *retsize, int * retint)
{
if ([NSUbiquitousKeyValueStore defaultStore])
{
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY];
if (d != NULL)
{
if (retsize != NULL)
*retsize = d.length;
if (retint != NULL)
*retint = RESULT_OK;
return d.bytes;
}
else
{
if (retsize != NULL)
*retsize = -1;
if (retint != NULL)
*retint = RESULT_NOT_FOUND;
}
}
else
{
if (retsize != NULL)
*retsize = -1;
if (retint != NULL)
*retint = RESULT_NO_CONNECTION;
}
return NULL;
}
int CloudHelper::isAvailable()
{
int result = RESULT_NO_CONNECTION;
if ([NSUbiquitousKeyValueStore defaultStore])
{
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY];
if (d != NULL)
result = RESULT_OK;
else
result = RESULT_NOT_FOUND;
}
else
result = RESULT_NO_CONNECTION;
return result;
}
void CloudHelper::deInit()
{
enabled = false;
[ubiq release];
}
bool CloudHelper::init()
{
enabled = false;
NSURL *ubiq_ = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
if (ubiq)
{
enabled = true;
ubiq = [ubiq_ retain]; //save for further use
}
else
{
//is implemented elsewhere: this writes a local file with a counter, and if it is < REMINDER_COUNT allows us to show a warning to users
bool allow = MySystem::isAllowToShowDialog();
if (allow)
{
//determines network state with Apple's Reachability
if (!MySystem::isNetworkAvailable())
MySystem::showMessageBox(#"Network error"); //No network
else
MySystem::showMessageBox(#"You should log into your iCloud account to be able to backup your settings."); //No login
}
}
return enabled;
}
UPDATE 2
It's 2016. Android has become ios's evil twin, the humanity has discovered gravitational waves, Higgs have received his nobel, Microsoft has bought and killed Nokia. But iCloud is still as stupid as it was.
Finally I've made my own stack of network services on several VPS. I refused to use third-party services, because most of them are unstable and unpredictable. And yet I need iCloud. Because another die-born child of apple does not work. SecKeyChain. Its service dies when my game starts. So I decided to store random UUID in cloud to distinguish users (there is no device id anymore) even after reinstall. But what could go wrong? Everything! I've spend two days to make this stupid s*it to deploy without errors, and now it loses my data from time to time!
Thank you Apple, thank, thank, thank! La-la-la! Hip-hip hooray! (sounds of circus music, fading into weeping)
Conclusion
Temporary solution is:
- call synchronize before get data from key-value store
- to be sure it would work "turn off iCloud->Documents&Data - turn off and again on network - turn on Documents&Data", but also you should wait several minutes before iCloud downloads all needed data
Note: when app is installed and already worked (saved/loaded) with key-value store updates of iCloud data are pretty fast (7-15 sec), but when you reinstall the app it seems that icloud needs several minutes to actualize key-value store.
I'd be glad to hear your thoughts, because icloud looks like almost unusable feature. But I don't want to set up my own server to merely get the same functionality.
I am setting one dummy key to NSUbiquitousKeyValueStore and calling synchronize on app launch. The result is not 100% solution but somewhat better. You can try this.
Because obviously your app shouldn't hang while waiting for a slow netork. It's all in the iCloud Design Guide.
Register for NSUbiquitousKeyValueStoreDidChangeExternallyNotification, call -synchronize, and hopefully a notification should eventually arrive.
If the data is already up-to-date, I don't think you get a notification, and I don't think there's an wasy way to know how old the data is.
Here's the code:
ALint cProcessedBuffers = 0;
ALenum alError = AL_NO_ERROR;
alGetSourcei(m_OpenALSourceId, AL_BUFFERS_PROCESSED, &cProcessedBuffers);
if((alError = alGetError()) != AL_NO_ERROR)
{
throw "AudioClip::ProcessPlayedBuffers - error returned from alGetSroucei()";
}
alError = AL_NO_ERROR;
if (cProcessedBuffers > 0)
{
alSourceUnqueueBuffers(m_OpenALSourceId, cProcessedBuffers, arrBuffers);
if((alError = alGetError()) != AL_NO_ERROR)
{
throw "AudioClip::ProcessPlayedBuffers - error returned from alSourceUnqueueBuffers()";
}
}
The call to alGetSourcei returns with cProcessedBuffers > 0, but the following call to alSourceUnqueueBuffers fails with an INVALID_OPERATION. This in an erratic error that does not always occur. The program containing this sample code is a single-threaded app running in a tight loop (typically would be sync'ed with a display loop, but in this case I'm not using a timed callback of any sort).
Try alSourceStop(m_OpenALSourceId) first.
Then alUnqueueBuffers(), and after that, Restart playing by alSourcePlay(m_OpenALSourceId).
I solved the same problem by this way. But I don't know why have to do so in
Mentioned in this SO thread,
If you have AL_LOOPING enabled on a streaming source the unqueue operation will fail.
The looping flag has some sort of lock on the buffers when enabled. The answer by #MyMiracle hints at this as well, stopping the sound releases that hold, but it's not necessary..
AL_LOOPING is not meant to be set on a streaming source, as you manage the source data in the queue. Keep queuing, it will keep playing. Queue from the beginning of the data, it will loop.