ReMAS  1.5
Real-time Musical Accompaniment System
MacOSSoundFunctions.h
Go to the documentation of this file.
1 /**************************************************************************
2  * Copyright (C) 2017 by "Information Retrieval and Parallel Computing" *
3  * group (University of Oviedo, Spain), "Interdisciplinary Computation *
4  * and Communication" group (Polytechnic University of Valencia, Spain) *
5  * and "Signal Processing and Telecommunication Systems Research" group *
6  * (University of Jaen, Spain) *
7  * Contact: remaspack@gmail.com *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the *
21  * Free Software Foundation, Inc., *
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
23  **************************************************************************
24  */
35 #pragma once
36 
37 #ifdef CAUDIO
38 #ifndef MacOSSoundFunctions_h
39 #define MacOSSoundFunctions_h
40 
41 #include <AudioToolbox/AudioToolbox.h>
42 #include "defines.h"
43 #include "ctype.h"
44 #include <unistd.h>
45 
46 
47 #define _POSIX_SOURCE
48 #include <signal.h>
49 #include <sys/time.h>
50 
51 #pragma mark user data struct
52 #pragma mark user info struct
53 typedef struct{
54  Boolean running; //Boolean to keep track of whether the queue is running
55  AudioQueueBufferRef buffer; //Buffer containing the last frame read (Async)
56  short *middleBuffer; //Buffer that contain the 10 las frames readed
57  int idReadMB; //ID of last frame readed in middleBuffer
58  short idWriteMB; //ID of las frame writed in middleBuffer
59  short numLastRead; //Number of shorts readed in idWriteMB
60  short toursReaded; //Number of reading tours in te middlebuffer circular buffer
61  short toursWrited; //Number of writing tours in te middlebuffer circular buffer
62  bool bufferChange; //Bollean that identify if the buffer read a new frame
63 } RecorderStruct;
64 
65 void timer_handler (int signum);
72 void timer_handler (int signum)
73 {
74  //NOT implemented method
75 }
76 
77 void timer_launch();
83 void timer_launch(){
84  struct sigaction sa;
85 
86  sa.sa_handler = timer_handler;
87  sa.sa_flags = SA_RESETHAND;
88  sigaction(SIGALRM, &sa, 0);
89 }
90 
91 #pragma mark record callback function
92 static void AQInputCallback(void *inUserData, AudioQueueRef inQueue, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription *inPacketDesc);
104 static void AQInputCallback(void *inUserData, AudioQueueRef inQueue, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription *inPacketDesc)
105 {
106  int err = 0;
107  RecorderStruct *recorder = (RecorderStruct *)inUserData;
108 
109  if (inNumPackets > 0) {
110  recorder->buffer = inBuffer;
111  short *goodBuffer = inBuffer->mAudioData;
112 
113  //Actualize recorder variable
114  recorder->idWriteMB = (recorder->idWriteMB + recorder->numLastRead) % MiddleBufferSize;
115  if(recorder->idWriteMB == 0) {
116  recorder->toursWrited++;
117  }
118 
119  //Compute the middlebuffer to determine if it's filled or no and copy in it the recibed sound
120  if(recorder->idWriteMB + (inBuffer->mAudioDataByteSize/sizeof(short)) > MiddleBufferSize-1)
121  {
122  int toTT = MiddleBufferSize - recorder->idWriteMB;
123  while((recorder->idWriteMB > (recorder->idReadMB + MiddleBufferSize*0.5) && recorder->toursReaded == recorder->toursWrited) || (recorder->toursWrited > recorder->toursReaded && (recorder->idWriteMB + MiddleBufferSize*0.5) > recorder->idReadMB) || (recorder->toursWrited < recorder->toursReaded && MiddleBufferSize*0.5 <= recorder->idWriteMB - recorder->idReadMB)){
124  timer_launch();
125  }
126  memcpy(&recorder->middleBuffer[recorder->idWriteMB],goodBuffer, toTT*sizeof(short));
127  recorder->numLastRead = (inBuffer->mAudioDataByteSize/sizeof(short)) - toTT;
128  recorder->idWriteMB = 0;
129  recorder->toursWrited++;
130  memcpy(recorder->middleBuffer,&goodBuffer[toTT], recorder->numLastRead*sizeof(short));
131  }
132  else
133  {
134  while((recorder->idWriteMB > (recorder->idReadMB + MiddleBufferSize*0.5) && recorder->toursReaded == recorder->toursWrited) || (recorder->toursWrited > recorder->toursReaded && (recorder->idWriteMB + MiddleBufferSize*0.5) > recorder->idReadMB) || (recorder->toursWrited < recorder->toursReaded && MiddleBufferSize*0.5 <= recorder->idWriteMB - recorder->idReadMB)){
135  timer_launch();
136  }
137  memcpy(&recorder->middleBuffer[recorder->idWriteMB],goodBuffer, inBuffer->mAudioDataByteSize);
138  recorder->numLastRead = inBuffer->mAudioDataByteSize/sizeof(short);
139  }
140  }
141  if (recorder->running)
142  {
143  err = AudioQueueEnqueueBuffer(inQueue,inBuffer, 0, NULL);
144  recorder->bufferChange = true;
145  }
146  if (err != noErr)
147  {
148  fprintf(stderr, "Error: %s (%s)\n", "AQInputCallback failed", "(Enqueue or Write Output file?)");
149  exit(1);
150  }
151 }
152 
153 #pragma mark utility functions
154 void CheckError(OSStatus error, const char *operation);
163 void CheckError(OSStatus error, const char *operation)
164 {
165  if (error == noErr) return;
166 
167  char errorString[20];
168 
169  *(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(error);
170  if (isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4]))
171  {
172  errorString[0] = errorString[5] = '\'';
173  errorString[6] = '\0';
174  }
175  else
176  {
177  sprintf(errorString, "%d", (int)error);
178  }
179 
180  fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
181  exit(1);
182 }
183 
184 
185 static int ComputeRecordBufferSize( const AudioStreamBasicDescription *format, AudioQueueRef queue, float frames);
194 static int ComputeRecordBufferSize( const AudioStreamBasicDescription *format, AudioQueueRef queue, float frames)
195 {
196  int packets, bytes;
197  if (format->mBytesPerFrame > 0)
198  bytes = frames * format->mBytesPerFrame;
199  else
200  {
201  UInt32 maxPacketSize;
202  if (format->mBytesPerPacket > 0)
203  // Constant packet size
204  maxPacketSize = format->mBytesPerPacket;
205  else
206  {
207  // Get the largest single packet size possible
208  UInt32 propertySize = sizeof(maxPacketSize);
209  CheckError(AudioQueueGetProperty(queue, kAudioConverterPropertyMaximumOutputPacketSize,
210  &maxPacketSize, &propertySize), "Couldn't get queue's maximum output packet size");
211  }
212  if (format->mFramesPerPacket > 0)
213  packets = frames / format->mFramesPerPacket;
214  else
215  // Worst-case scenario: 1 frame in a packet
216  packets = frames;
217  // Sanity check
218  if (packets == 0)
219  packets = 1;
220  bytes = packets * maxPacketSize;
221  }
222  return bytes;
223 }
224 
225 void ConfigureAndAllocAudioQueues(RecorderStruct* recorder, AudioQueueRef* queue);
233 void ConfigureAndAllocAudioQueues(RecorderStruct* recorder, AudioQueueRef* queue)
234 {
235  //Create and configure the Audio Stream
236  AudioStreamBasicDescription recordFormat;
237  memset(&recordFormat, 0, sizeof(recordFormat));
238  recordFormat.mFormatID |= kAudioFormatLinearPCM;
239 
240  recordFormat.mFormatFlags &= ~kLinearPCMFormatFlagsAreAllClear;
241  recordFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsFloat;
242  recordFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
243  recordFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
244  recordFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
245  recordFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonMixable;
246  recordFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
247 
248  recordFormat.mChannelsPerFrame = AQChannels;
249  recordFormat.mBitsPerChannel = AQBitsPerChannel;
250  recordFormat.mSampleRate = AQRate;
251 
252  int bytes = (recordFormat.mBitsPerChannel / 8) * recordFormat.mChannelsPerFrame;
253  recordFormat.mBytesPerFrame = bytes;
254  recordFormat.mBytesPerPacket = bytes;
255 
256  UInt32 propSize = sizeof(recordFormat);
257  CheckError(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &propSize, &recordFormat), "AudioFormatGetProperty failed");
258 
259  //Allocate recorder struct info
260  recorder->middleBuffer = calloc(MiddleBufferSize, sizeof(short));
261  recorder->idReadMB = -1;
262  recorder->idWriteMB = 0;
263  recorder->numLastRead = 0;
264  recorder->toursWrited = 0;
265  recorder->toursReaded = 1;
266  recorder->bufferChange = false;
267 
268  //Set up queue
269  CheckError(AudioQueueNewInput(&recordFormat, AQInputCallback, recorder, NULL, NULL, 0, queue), "AudioQueueNewInput failed");
270  UInt32 size = sizeof(recordFormat);
271  CheckError(AudioQueueGetProperty(*queue, kAudioConverterCurrentOutputStreamDescription, &recordFormat, &size), "Couldn't get queue's format");
272 
273  // Other setup as needed
274  int bufferByteSize = ComputeRecordBufferSize(&recordFormat, *queue, AQBufferSize);
275 
276  int bufferIndex;
277  for (bufferIndex = 0; bufferIndex < kNumberRecordBuffers; ++bufferIndex)
278  {
279  AudioQueueBufferRef buffer;
280  CheckError(AudioQueueAllocateBuffer(*queue, bufferByteSize, &buffer), "AudioQueueAllocateBuffer failed");
281  CheckError(AudioQueueEnqueueBuffer(*queue, buffer, 0, NULL), "AudioQueueEnqueueBuffer failed");
282  }
283  CheckError(AudioQueueAllocateBuffer(*queue, bufferByteSize, &recorder->buffer), "AudioQueueAllocateBuffer failed");
284 
285  printf("Rate after config: %f \nBitsPerChannel: %d\nChanels: %d\nBytes Per Frames and Packets: %d\n",recordFormat.mSampleRate, recordFormat.mBitsPerChannel, recordFormat.mChannelsPerFrame, recordFormat.mBytesPerPacket);
286 }
287 
288 int ReadAudioQueue1st(short *frame, RecorderStruct* recorder, FILE *fpdump);
297 int ReadAudioQueue1st(short *frame, RecorderStruct* recorder, FILE *fpdump)
298 {
299  int i;
300  for (i=0; i<=(TAMTRAMA/TAMMUESTRA) ; i++)
301  {
302  if (recorder->idReadMB < 0)
303  {
304  recorder->idReadMB = 0;
305  while((recorder->idWriteMB < (recorder->idReadMB + MiddleBufferSize*0.5) && recorder->toursReaded == recorder->toursWrited) || (recorder->toursWrited < recorder->toursReaded && recorder->idWriteMB < (recorder->idReadMB + MiddleBufferSize*0.5)) || (recorder->toursWrited > recorder->toursReaded && MiddleBufferSize*0.5 <= recorder->idReadMB - recorder->idWriteMB)){
306  timer_launch();
307  }
308  memcpy(&frame[TAMMUESTRA*i],recorder->middleBuffer,TAMMUESTRA*sizeof(short));
309  recorder->bufferChange = false;
310  }
311  else
312  {
313  recorder->idReadMB = (recorder->idReadMB + TAMMUESTRA) % MiddleBufferSize;
314  if(recorder->idReadMB == 0){
315  recorder->toursReaded++;
316  }
317  while((recorder->idWriteMB < (recorder->idReadMB + MiddleBufferSize*0.5) && recorder->toursReaded == recorder->toursWrited) || (recorder->toursWrited < recorder->toursReaded && recorder->idWriteMB < (recorder->idReadMB + MiddleBufferSize*0.5)) || (recorder->toursWrited > recorder->toursReaded && MiddleBufferSize*0.5 <= recorder->idReadMB - recorder->idWriteMB)){
318  timer_launch();
319  }
320  memcpy(&frame[TAMMUESTRA*i],&recorder->middleBuffer[recorder->idReadMB],TAMMUESTRA*sizeof(short));
321  recorder->bufferChange = false;
322  }
323  }
324 
325  #ifdef DUMP
326  if (fwrite(&frame[TAMMUESTRA], sizeof(short), TTminusTM, fpdump) != TTminusTM) return ErrWriteFile;
327  #endif
328 
329  return OK;
330 }
331 
332 int ReadAudioQueue(short *frame, RecorderStruct* recorder, FILE *fpdump);
341 int ReadAudioQueue(short *frame, RecorderStruct* recorder, FILE *fpdump)
342 {
343  memmove(frame, &frame[TAMMUESTRA], sizeof(short)*TTminusTM);
344  recorder->idReadMB = (recorder->idReadMB + TAMMUESTRA) % MiddleBufferSize;
345 
346  if(recorder->idReadMB == 0) {
347  recorder->toursReaded++;
348  }
349  while((recorder->idWriteMB < (recorder->idReadMB + MiddleBufferSize*0.5) && recorder->toursReaded == recorder->toursWrited) || (recorder->toursWrited < recorder->toursReaded && recorder->idWriteMB < (recorder->idReadMB + MiddleBufferSize*0.5)) || (recorder->toursWrited > recorder->toursReaded && MiddleBufferSize*0.5 <= recorder->idReadMB - recorder->idWriteMB)){
350  timer_launch();
351  }
352  memcpy(&frame[TTminusTM],&recorder->middleBuffer[recorder->idReadMB],TAMMUESTRA*sizeof(short));
353  recorder->bufferChange = false;
354  #ifdef DUMP
355  if (fwrite(&frame[TTminusTM], sizeof(short), TAMMUESTRA, fpdump) != TAMMUESTRA) return ErrWriteFile;
356  #endif
357 
358 
359  return OK;
360 }
361 
362 
363 #endif /* MacOSSoundFunctions_h */
364 #endif /* CAUDIO: Core Audio in DARWIN */
General header file with constants, defines, structs, etc. using by ReMAS, both CPU and GPU...