void AudioThruEngine::ApplyLoad(double load) { double loadNanos = (load * mBufferSize / mSampleRate) /* seconds */ * 1000000000.; UInt64 now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); UInt64 waitUntil = UInt64(now + loadNanos); while (now < waitUntil) { now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); } }
static int chronos_nanotime(lua_State * L) { //TODO All the apple stuff is untested because, like, I don't even got Apple. //If like, you have a mac, let me know whether this works. //see https://stackoverflow.com/questions/675626/coreaudio-audiotimestamp-mhosttime-clock-frequency //for info. #ifdef CHRONOS_USE_COREAUDIO //Apparently this is just a wrapper around mach_absolute_time() anyway. lua_pushnumber( L, (lua_Number)AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) * 1.e9 ); return 1; #else static int init = 1; static double resolution; static double multiplier; mach_timebase_info_data_t res_info; if(init){ mach_timebase_info(&res_info); resolution = (double)res_info.numer / res_info.denom; multiplier = 1. / 1.e9; init = 0; } lua_pushnumber(L, (lua_Number)(mach_absolute_time() * resolution) * multiplier); return 1; #endif }
void o2_clock_initialize() { if (clock_initialized) { o2_clock_finish(); } #ifdef __APPLE__ start_time = AudioGetCurrentHostTime(); #elif __linux__ struct timeval tv; gettimeofday(&tv, NULL); start_time = tv.tv_sec; #elif WIN32 timeBeginPeriod(1); // get 1ms resolution on Windows start_time = timeGetTime(); #else #error o2_clock has no implementation for this system #endif // until local clock is synchronized, LOCAL_TO_GLOBAL will return -1: local_time_base = 0; global_time_base = -1; clock_rate = 0; is_master = FALSE; o2_clock_is_synchronized = FALSE; time_callback = NULL; time_callback_data = NULL; found_clock_service = FALSE; ping_reply_count = 0; time_offset = 0; o2_method_new("/_o2/ps", "", &o2_ping_send_handler, NULL, FALSE, TRUE); o2_method_new("/_o2/cu", "i", &catch_up_handler, NULL, FALSE, TRUE); }
PtTimestamp Pt_Time() { UInt64 clock_time, nsec_time; clock_time = AudioGetCurrentHostTime() - start_time; nsec_time = AudioConvertHostTimeToNanos(clock_time); return (PtTimestamp)(nsec_time / NSEC_PER_MSEC); }
void PsychGetPrecisionTimerSeconds(double *secs) { UInt64 ticks; ticks=AudioGetCurrentHostTime(); *secs=(double)(AudioConvertHostTimeToNanos(ticks) / (double)1000000000.0); }
double bootSeconds() { #ifdef SC_DARWIN return 1e-9 * (double)AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); #else return GetTimeOfDay(); #endif }
double elapsedTime() { #ifdef SC_DARWIN return 1e-9 * (double)(AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) - gHostStartNanos); #else return GetTimeOfDay(); #endif }
void OverlaodListenerProc( void * inRefCon, AudioUnit ci, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement) { didOverload++; overloadTime = AudioGetCurrentHostTime(); }
static int64_t audiotimestamp_to_latency(AudioTimeStamp const * tstamp, cubeb_stream * stream) { if (!(tstamp->mFlags & kAudioTimeStampHostTimeValid)) { return 0; } uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime); uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); return ((pres - now) * stream->sample_spec.mSampleRate) / 1000000000LL; }
SC_DLLEXPORT_C void schedInit() { pthread_cond_init (&gSchedCond, NULL); pthread_mutex_init (&gLangMutex, NULL); #ifdef SC_DARWIN syncOSCOffsetWithTimeOfDay(); pthread_create (&gResyncThread, NULL, resyncThread, (void*)0); gHostStartNanos = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); gElapsedOSCoffset = (int64)(gHostStartNanos * kNanosToOSC) + gHostOSCoffset; #else gElapsedOSCoffset = (int64)kSECONDS_FROM_1900_to_1970 << 32; #endif }
void syncOSCOffsetWithTimeOfDay() { // generate a value gHostOSCoffset such that // (gHostOSCoffset + systemTimeInOSCunits) // is equal to gettimeofday time in OSCunits. // Then if this machine is synced via NTP, we are synced with the world. // more accurate way to do this?? struct timeval tv; int64 systemTimeBefore, systemTimeAfter, diff; int64 minDiff = 0x7fffFFFFffffFFFFLL; // take best of several tries const int numberOfTries = 8; int64 newOffset = gHostOSCoffset; for (int i=0; i<numberOfTries; ++i) { systemTimeBefore = AudioGetCurrentHostTime(); gettimeofday(&tv, 0); systemTimeAfter = AudioGetCurrentHostTime(); diff = systemTimeAfter - systemTimeBefore; if (diff < minDiff) { minDiff = diff; // assume that gettimeofday happens halfway between AudioGetCurrentHostTime calls int64 systemTimeBetween = systemTimeBefore + diff/2; int64 systemTimeInOSCunits = (int64)((double)AudioConvertHostTimeToNanos(systemTimeBetween) * kNanosToOSC); int64 timeOfDayInOSCunits = ((int64)(tv.tv_sec + kSECONDS_FROM_1900_to_1970) << 32) + (int64)(tv.tv_usec * kMicrosToOSC); newOffset = timeOfDayInOSCunits - systemTimeInOSCunits; } } gHostOSCoffset = newOffset; //postfl("gHostOSCoffset %016llX\n", gHostOSCoffset); }
static void _macosx_send(struct midi_port *p, const unsigned char *data, unsigned int len, unsigned int delay) { struct macosx_midi *m; m = (struct macosx_midi *)p->userdata; if (!m->x) { m->x = MIDIPacketListInit(m->pl); } /* msec to nsec? */ m->x = MIDIPacketListAdd(m->pl, sizeof(m->packet), m->x, (MIDITimeStamp)AudioConvertNanosToHostTime( AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) + (1000000*delay)), len, data); }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_GetStatus +---------------------------------------------------------------------*/ BLT_METHOD OsxAudioUnitsOutput_GetStatus(BLT_OutputNode* _self, BLT_OutputNodeStatus* status) { OsxAudioUnitsOutput* self = ATX_SELF(OsxAudioUnitsOutput, BLT_OutputNode); /* default values */ status->flags = 0; pthread_mutex_lock(&self->lock); /* check if the queue is full */ if (ATX_List_GetItemCount(self->packet_queue) >= self->max_packets_in_queue) { ATX_LOG_FINER("packet queue is full"); status->flags |= BLT_OUTPUT_NODE_STATUS_QUEUE_FULL; } /* compute the media time */ BLT_TimeStamp_Set(status->media_time, 0, 0); if (self->media_time_snapshot.rendered_host_time) { UInt64 host_time = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); UInt64 media_time = BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_packet_ts); ATX_LOG_FINER_3("host time = %lld, last rendered packet = %lld, rendered ts = %lld", host_time, self->media_time_snapshot.rendered_host_time, media_time); if (host_time > self->media_time_snapshot.rendered_host_time) { media_time += host_time-self->media_time_snapshot.rendered_host_time; } UInt64 max_media_time; max_media_time = BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_packet_ts) + BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_duration); ATX_LOG_FINER_2("computed media time = %lld, max media time = %lld", media_time, max_media_time); if (media_time > max_media_time) { ATX_LOG_FINER("media time clamped to max"); media_time = max_media_time; } status->media_time = BLT_TimeStamp_FromNanos(media_time); ATX_LOG_FINER_1("media time = %lld", media_time); } pthread_mutex_unlock(&self->lock); return BLT_SUCCESS; }
static void *Pt_CallbackProc(void *p) { pt_callback_parameters *parameters = (pt_callback_parameters *) p; int mytime = 1; kern_return_t error; thread_extended_policy_data_t extendedPolicy; thread_precedence_policy_data_t precedencePolicy; extendedPolicy.timeshare = 0; error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY, (thread_policy_t)&extendedPolicy, THREAD_EXTENDED_POLICY_COUNT); if (error != KERN_SUCCESS) { mach_error("Couldn't set thread timeshare policy", error); } precedencePolicy.importance = THREAD_IMPORTANCE; error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&precedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT); if (error != KERN_SUCCESS) { mach_error("Couldn't set thread precedence policy", error); } /* to kill a process, just increment the pt_callback_proc_id */ /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); */ while (pt_callback_proc_id == parameters->id) { /* wait for a multiple of resolution ms */ UInt64 wait_time; int delay = mytime++ * parameters->resolution - Pt_Time(); PtTimestamp timestamp; if (delay < 0) delay = 0; wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC); wait_time += AudioGetCurrentHostTime(); error = mach_wait_until(wait_time); timestamp = Pt_Time(); (*(parameters->callback))(timestamp, parameters->userData); } free(parameters); return NULL; }
void CoreMidiManager::SendMIDI(char actionType, int noteNum, int value) { uint8_t buffer[PACKET_BUF_SIZE]; uint8_t msg[3]; MIDIPacketList *packetList = (MIDIPacketList*) buffer; MIDIPacket *curPacket = MIDIPacketListInit(packetList); curPacket = MIDIPacketListAdd(packetList, PACKET_BUF_SIZE, curPacket, AudioGetCurrentHostTime(), actionType == 'C' ? 2 : 3, msg); if (!curPacket) midimsg_die("packet list allocation failed"); midimsg_attempt(MIDIReceived(m_midiendpoint, packetList), "error sending midi"); }
o2_time o2_local_time() { if (time_callback) { return (*time_callback)(time_callback_data) - time_offset; } #ifdef __APPLE__ uint64_t clock_time, nsec_time; clock_time = AudioGetCurrentHostTime() - start_time; nsec_time = AudioConvertHostTimeToNanos(clock_time); return ((o2_time) (nsec_time * 1.0E-9)) - time_offset; #elif __linux__ struct timeval tv; gettimeofday(&tv, NULL); return ((tv.tv_sec - start_time) + (tv.tv_usec * 0.000001)) - time_offset; #elif WIN32 return ((timeGetTime() - start_time) * 0.001) - time_offset; #else #error o2_clock has no implementation for this system #endif }
PtError Pt_Start(int resolution, PtCallback *callback, void *userData) { if (time_started_flag) return ptAlreadyStarted; start_time = AudioGetCurrentHostTime(); if (callback) { int res; pt_callback_parameters *parms; parms = (pt_callback_parameters *) malloc(sizeof(pt_callback_parameters)); if (!parms) return ptInsufficientMemory; parms->id = pt_callback_proc_id; parms->resolution = resolution; parms->callback = callback; parms->userData = userData; res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms); if (res != 0) return ptHostError; } time_started_flag = TRUE; return ptNoError; }
void PsychGetPrecisionTimerTicks(psych_uint64 *ticks) { *ticks=AudioGetCurrentHostTime(); }
// A simple utility which encapsulates CoreAudio's HostTime APIs. // It returns the current host time in nanoseconds which when subtracted from // a previous getCurrentTimeInNanos() result produces the delta in nanos. static UInt64 getCurrentTimeInNanos() { UInt64 hostTime = AudioGetCurrentHostTime(); UInt64 nanos = AudioConvertHostTimeToNanos(hostTime); return nanos; }
static MIDITimeStamp midiTime(float latencySeconds) { // add the latency expressed in seconds, to the current host time base. UInt64 latencyNanos = 1000000000 * latencySeconds ; //secs to nano return (MIDITimeStamp)AudioGetCurrentHostTime() + AudioConvertNanosToHostTime(latencyNanos); }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_RenderCallback +---------------------------------------------------------------------*/ static OSStatus OsxAudioUnitsOutput_RenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { OsxAudioUnitsOutput* self = (OsxAudioUnitsOutput*)inRefCon; ATX_ListItem* item; unsigned int requested; unsigned char* out; ATX_Boolean timestamp_measured = ATX_FALSE; BLT_COMPILER_UNUSED(ioActionFlags); BLT_COMPILER_UNUSED(inTimeStamp); BLT_COMPILER_UNUSED(inBusNumber); BLT_COMPILER_UNUSED(inNumberFrames); /* sanity check on the parameters */ if (ioData == NULL || ioData->mNumberBuffers == 0) return 0; /* in case we have a strange request with more than one buffer, just return silence */ if (ioData->mNumberBuffers != 1) { unsigned int i; ATX_LOG_FINEST_1("strange request with %d buffers", (int)ioData->mNumberBuffers); for (i=0; i<ioData->mNumberBuffers; i++) { ATX_SetMemory(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); } return 0; } /* init local variables */ requested = ioData->mBuffers[0].mDataByteSize; out = (unsigned char*)ioData->mBuffers[0].mData; ATX_LOG_FINEST_2("request for %d bytes, %d frames", (int)requested, (int)inNumberFrames); /* lock the packet queue */ pthread_mutex_lock(&self->lock); /* return now if we're paused */ //if (self->paused) goto end; /* abort early if we have no packets */ if (ATX_List_GetItemCount(self->packet_queue) == 0) goto end; /* fill as much as we can */ while (requested && (item = ATX_List_GetFirstItem(self->packet_queue))) { BLT_MediaPacket* packet = ATX_ListItem_GetData(item); const BLT_PcmMediaType* media_type; BLT_Size payload_size; BLT_Size chunk_size; BLT_TimeStamp chunk_duration; BLT_TimeStamp packet_ts; unsigned int bytes_per_frame; unsigned int sample_rate; /* get the packet info */ BLT_MediaPacket_GetMediaType(packet, (const BLT_MediaType**)&media_type); packet_ts = BLT_MediaPacket_GetTimeStamp(packet); bytes_per_frame = media_type->channel_count*media_type->bits_per_sample/8; sample_rate = media_type->sample_rate; /* record the timestamp if we have not already done so */ if (!timestamp_measured) { self->media_time_snapshot.rendered_packet_ts = packet_ts; self->media_time_snapshot.rendered_host_time = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); BLT_TimeStamp_Set(self->media_time_snapshot.rendered_duration, 0, 0); timestamp_measured = ATX_TRUE; ATX_LOG_FINEST_2("rendered TS: packet ts=%lld, host ts=%lld", BLT_TimeStamp_ToNanos(packet_ts), self->media_time_snapshot.rendered_host_time); } /* compute how much to copy from this packet */ payload_size = BLT_MediaPacket_GetPayloadSize(packet); if (payload_size <= requested) { /* copy the entire payload and remove the packet from the queue */ chunk_size = payload_size; ATX_CopyMemory(out, BLT_MediaPacket_GetPayloadBuffer(packet), chunk_size); ATX_List_RemoveItem(self->packet_queue, item); packet = NULL; media_type = NULL; ATX_LOG_FINER_1("media packet fully consumed, %d left in queue", ATX_List_GetItemCount(self->packet_queue)); } else { /* only copy a portion of the payload */ chunk_size = requested; ATX_CopyMemory(out, BLT_MediaPacket_GetPayloadBuffer(packet), chunk_size); } /* update the counters */ requested -= chunk_size; out += chunk_size; /* update the media time snapshot */ if (bytes_per_frame) { unsigned int frames_in_chunk = chunk_size/bytes_per_frame; chunk_duration = BLT_TimeStamp_FromSamples(frames_in_chunk, sample_rate); } else { BLT_TimeStamp_Set(chunk_duration, 0, 0); } self->media_time_snapshot.rendered_duration = BLT_TimeStamp_Add(self->media_time_snapshot.rendered_duration, chunk_duration); /* update the packet unless we're done with it */ if (packet) { /* update the packet offset and timestamp */ BLT_MediaPacket_SetPayloadOffset(packet, BLT_MediaPacket_GetPayloadOffset(packet)+chunk_size); BLT_MediaPacket_SetTimeStamp(packet, BLT_TimeStamp_Add(packet_ts, chunk_duration)); } } end: /* fill whatever is left with silence */ if (requested) { ATX_LOG_FINEST_1("filling with %d bytes of silence", requested); ATX_SetMemory(out, 0, requested); } pthread_mutex_unlock(&self->lock); return 0; }
JNIEXPORT jlong JNICALL Java_com_apple_audio_util_HostTime_AudioGetCurrentHostTime (JNIEnv *, jclass) { return (jlong)AudioGetCurrentHostTime(); }
int main (int argc, const char * argv[]) { if (argc == 1) { fprintf (stderr, "%s\n", usageStr); exit(0); } char* filePath = 0; bool shouldPlay = false; bool shouldSetBank = false; bool shouldUseMIDIEndpoint = false; bool shouldPrint = true; bool waitAtEnd = false; bool diskStream = false; OSType dataFormat = 0; Float64 srate = 0; const char* outputFilePath = 0; MusicSequenceLoadFlags loadFlags = 0; char* bankPath = 0; Float32 startTime = 0; UInt32 numFrames = 512; for (int i = 1; i < argc; ++i) { if (!strcmp ("-p", argv[i])) { shouldPlay = true; } else if (!strcmp ("-w", argv[i])) { waitAtEnd = true; } else if (!strcmp ("-d", argv[i])) { diskStream = true; } else if (!strcmp ("-b", argv[i])) { shouldSetBank = true; if (++i == argc) goto malformedInput; bankPath = const_cast<char*>(argv[i]); } else if (!strcmp ("-n", argv[i])) { shouldPrint = false; } else if ((filePath == 0) && (argv[i][0] == '/' || argv[i][0] == '~')) { filePath = const_cast<char*>(argv[i]); } else if (!strcmp ("-t", argv[i])) { if (++i == argc) goto malformedInput; sscanf (argv[i], "%f", &startTime); } else if (!strcmp("-e", argv[i])) { shouldUseMIDIEndpoint = true; } else if (!strcmp("-c", argv[i])) { loadFlags = kMusicSequenceLoadSMF_ChannelsToTracks; } else if (!strcmp ("-i", argv[i])) { if (++i == argc) goto malformedInput; sscanf (argv[i], "%ld", &numFrames); } else if (!strcmp ("-f", argv[i])) { if (i + 3 >= argc) goto malformedInput; outputFilePath = argv[++i]; str2OSType (argv[++i], dataFormat); sscanf (argv[++i], "%lf", &srate); } else { malformedInput: fprintf (stderr, "%s\n", usageStr); exit (1); } } if (filePath == 0) { fprintf (stderr, "You have to specify a MIDI file to print or play\n"); fprintf (stderr, "%s\n", usageStr); exit (1); } if (shouldUseMIDIEndpoint && outputFilePath) { printf ("can't write a file when you try to play out to a MIDI Endpoint\n"); exit (1); } MusicSequence sequence; OSStatus result; require_noerr (result = LoadSMF (filePath, sequence, loadFlags), fail); if (shouldPrint) CAShow (sequence); if (shouldPlay) { AUGraph graph = 0; AudioUnit theSynth = 0; require_noerr (result = MusicSequenceGetAUGraph (sequence, &graph), fail); require_noerr (result = AUGraphOpen (graph), fail); require_noerr (result = GetSynthFromGraph (graph, theSynth), fail); require_noerr (result = AudioUnitSetProperty (theSynth, kAudioUnitProperty_CPULoad, kAudioUnitScope_Global, 0, &maxCPULoad, sizeof(maxCPULoad)), fail); if (shouldUseMIDIEndpoint) { MIDIClientRef theMidiClient; MIDIClientCreate(CFSTR("Play Sequence"), NULL, NULL, &theMidiClient); ItemCount destCount = MIDIGetNumberOfDestinations(); if (destCount == 0) { fprintf (stderr, "No MIDI Endpoints to play to.\n"); exit(1); } require_noerr (result = MusicSequenceSetMIDIEndpoint (sequence, MIDIGetDestination(0)), fail); } else { if (shouldSetBank) { FSRef soundBankRef; require_noerr (result = FSPathMakeRef ((const UInt8*)bankPath, &soundBankRef, 0), fail); printf ("Setting Sound Bank:%s\n", bankPath); require_noerr (result = AudioUnitSetProperty (theSynth, kMusicDeviceProperty_SoundBankFSRef, kAudioUnitScope_Global, 0, &soundBankRef, sizeof(soundBankRef)), fail); } if (diskStream) { UInt32 value = diskStream; require_noerr (result = AudioUnitSetProperty (theSynth, kMusicDeviceProperty_StreamFromDisk, kAudioUnitScope_Global, 0, &value, sizeof(value)), fail); } if (outputFilePath) { // need to tell synth that is going to render a file. UInt32 value = 1; require_noerr (result = AudioUnitSetProperty (theSynth, kAudioUnitProperty_OfflineRender, kAudioUnitScope_Global, 0, &value, sizeof(value)), fail); } require_noerr (result = SetUpGraph (graph, numFrames, srate, (outputFilePath != NULL)), fail); if (shouldPrint) { printf ("Sample Rate: %.1f \n", srate); printf ("Disk Streaming is enabled: %c\n", (diskStream ? 'T' : 'F')); } require_noerr (result = AUGraphInitialize (graph), fail); if (shouldPrint) CAShow (graph); } MusicPlayer player; require_noerr (result = NewMusicPlayer (&player), fail); require_noerr (result = MusicPlayerSetSequence (player, sequence), fail); // figure out sequence length UInt32 ntracks; require_noerr(MusicSequenceGetTrackCount (sequence, &ntracks), fail); MusicTimeStamp sequenceLength = 0; for (UInt32 i = 0; i < ntracks; ++i) { MusicTrack track; MusicTimeStamp trackLength; UInt32 propsize = sizeof(MusicTimeStamp); require_noerr (result = MusicSequenceGetIndTrack(sequence, i, &track), fail); require_noerr (result = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &trackLength, &propsize), fail); if (trackLength > sequenceLength) sequenceLength = trackLength; } // now I'm going to add 8 beats on the end for the reverb/long releases to tail off... sequenceLength += 8; require_noerr (result = MusicPlayerSetTime (player, startTime), fail); require_noerr (result = MusicPlayerPreroll (player), fail); if (shouldPrint) { printf ("Ready to play: %s, %.2f beats long\n\t<Enter> to continue: ", filePath, sequenceLength); getc(stdin); } startRunningTime = AudioGetCurrentHostTime (); require_noerr (result = MusicPlayerStart (player), fail); if (outputFilePath) WriteOutputFile (outputFilePath, dataFormat, srate, sequenceLength, shouldPrint, graph, numFrames, player); else PlayLoop (player, graph, sequenceLength, shouldPrint, waitAtEnd); require_noerr (result = MusicPlayerStop (player), fail); if (shouldPrint) printf ("finished playing\n"); // this shows you how you should dispose of everything require_noerr (result = DisposeMusicPlayer (player), fail); require_noerr (result = DisposeMusicSequence(sequence), fail); // don't own the graph so don't dispose it (the seq owns it as we never set it ourselves, we just got it....) } else { require_noerr (result = DisposeMusicSequence(sequence), fail); } while (waitAtEnd) CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, false); return 0; fail: if (shouldPrint) printf ("Error = %ld\n", result); return result; }