| /************************************************************************************************** |
| Filename: ti_audio.c |
| |
| Description: audio over BLe, audio decoding app |
| |
| |
| Copyright 2013 Texas Instruments Incorporated. All rights reserved. |
| |
| IMPORTANT: Your use of this Software is limited to those specific rights |
| granted under the terms of a software license agreement between the user |
| who downloaded the software, his/her employer (which must be your employer) |
| and Texas Instruments Incorporated (the "License"). You may not use this |
| Software unless you agree to abide by the terms of the License. The License |
| limits your use, and you acknowledge, that the Software may not be modified, |
| copied or distributed unless embedded on a Texas Instruments microcontroller |
| or used solely and exclusively in conjunction with a Texas Instruments radio |
| frequency transceiver, which is integrated into your product. Other than for |
| the foregoing purpose, you may not use, reproduce, copy, prepare derivative |
| works of, modify, distribute, perform, display or sell this Software and/or |
| its documentation for any purpose. |
| |
| YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE |
| PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, |
| INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, |
| NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL |
| TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, |
| NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER |
| LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES |
| INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE |
| OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT |
| OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES |
| (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. |
| |
| Should you have any questions regarding your right to use this Software, |
| contact Texas Instruments Incorporated at www.TI.com. |
| **************************************************************************************************/ |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <poll.h> |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/time.h> |
| #include <unistd.h> |
| |
| #include "RAS_lib.h" |
| |
| #define FIFO_BTD2AUDIO_FILE "/tmp/Fifo_bdt_tiaudio" |
| |
| #define RAS_CMD_MASK 0x07 |
| #define RAS_START_CMD 0x04 |
| #define RAS_DATA_TIC1_CMD 0x01 |
| #define RAS_STOP_CMD 0x02 |
| #define RAS_DATA_RAW_CMD 0x03 |
| |
| #define TRUE 1 |
| #define FALSE 0 |
| |
| int fp; |
| |
| static pthread_mutex_t audio_mutex = PTHREAD_MUTEX_INITIALIZER; |
| |
| int frame_type; |
| int SeqNum, PrevSeqNum; |
| static float throughput, throughput_min = 10000000, throughput_max = 0, |
| throughput_ave = 0, throughput_raw_ave = 0; |
| static int cumulatedBytes = 0; |
| static int totalcumulatedBytes = 0; |
| static int totalcumulatedRawBytes = 0; |
| struct timeval prevTimeThrougput, prevTimeStarting, curTime, prevPacket; |
| long int elapsedTimeSinceLastPacket = 1; |
| char ras_pec_mode; |
| char previousFrameExist = 0; |
| unsigned char audioFrame[200]; |
| unsigned char audio_data_len; |
| unsigned char audioFrameIndex = 0; |
| FILE *fout_wav; |
| FILE *fout_tic1; |
| char outFile_wav[1024]; |
| char outFile_tic1[1024]; |
| char fileListIndex = 0; |
| char NewFilename[1024] = ""; |
| char NewOutputFilename[1024] = ""; |
| char outFile[1024]; |
| unsigned char Data_len; |
| int file_open = FALSE; |
| int streamStarted = TRUE; |
| int nbFrameLost; |
| int nbFrameReceived; |
| |
| static void audioRenameOutputFile(void) { |
| strcpy(NewFilename, ""); |
| strcpy(NewOutputFilename, ""); |
| strcat(NewOutputFilename, "gattToolAudio"); |
| |
| { |
| { |
| char temp[8]; |
| fileListIndex++; |
| strncat(NewOutputFilename, outFile, strlen(outFile)); |
| strcat(NewOutputFilename, "_"); |
| sprintf(temp, "%d", fileListIndex); |
| strcat(NewOutputFilename, temp); |
| } |
| } |
| |
| // Add the new suffix. |
| strcpy(outFile_wav, NewOutputFilename); |
| strcpy(outFile_tic1, NewOutputFilename); |
| strcat(outFile_wav, ".wav"); |
| strcat(outFile_tic1, ".tic1"); |
| |
| // Open the file |
| { |
| fout_wav = fopen(outFile_wav, "w"); |
| if (fout_wav != NULL) { |
| printf("===> Wav File Created %s <=== \n", outFile_wav); |
| |
| } else { |
| perror("fopen3:"); |
| exit(-1); |
| } |
| fout_tic1 = fopen(outFile_tic1, "w"); |
| if (fout_tic1 != NULL) { |
| printf("===> TIC1 File Created %s <=== \n", outFile_tic1); |
| } else { |
| perror("fopen4:"); |
| exit(-1); |
| } |
| file_open = TRUE; |
| } |
| } |
| |
| static void audioAddWaveHeader(FILE *fout_wav) { |
| // Initialize directly subchunksize1 |
| unsigned char Header[100] = {0x52, 0x49, 0x46, 0x46, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0x57, 0x41, 0x56, 0x45}; |
| |
| // subchunksize2 |
| Header[12] = 0x66; |
| Header[13] = 0x6d; |
| Header[14] = 0x74; |
| Header[15] = 0x20; |
| |
| Header[16] = 16; |
| Header[17] = 0; |
| Header[18] = 0; |
| Header[19] = 0; |
| |
| Header[20] = 1; |
| Header[21] = 0; |
| |
| Header[22] = 1; |
| Header[23] = 0; |
| |
| Header[24] = 0x80; |
| Header[25] = 0x3E; |
| Header[26] = 0; |
| Header[27] = 0; |
| |
| Header[28] = 0x00; |
| Header[29] = 0x7D; |
| Header[30] = 0; |
| Header[31] = 0; |
| |
| Header[32] = 2; |
| Header[33] = 0; |
| |
| Header[34] = 16; |
| Header[35] = 0; |
| |
| Header[36] = 0x64; |
| Header[37] = 0x61; |
| Header[38] = 0x74; |
| Header[39] = 0x61; |
| |
| Header[40] = 0xFF; |
| Header[41] = 0xFF; |
| Header[42] = 0xFF; |
| Header[43] = 0xFF; |
| |
| fwrite(Header, sizeof(char), 44, fout_wav); |
| printf("===> Add Wav Header <=== \n"); |
| } |
| |
| long int audioElapsedTime(struct timeval *timeStart, struct timeval *timeEnd) { |
| long int diffPrev; |
| long int decrementValue; |
| int t = 0; |
| |
| if (timeStart->tv_usec >= timeEnd->tv_usec) { |
| diffPrev = timeStart->tv_usec - timeEnd->tv_usec; |
| t = 0; |
| } else { |
| diffPrev = (timeStart->tv_usec + 1000000) - timeEnd->tv_usec; |
| t = 1; |
| } |
| |
| decrementValue = |
| (diffPrev + 1000000 * (timeStart->tv_sec - timeEnd->tv_sec - t)) / |
| 1000; // Res in ms |
| return decrementValue; |
| } |
| |
| void audioResetStream(void) { |
| gettimeofday(&prevTimeThrougput, NULL); |
| gettimeofday(&prevTimeStarting, NULL); |
| |
| totalcumulatedBytes = 0; |
| totalcumulatedRawBytes = 0; |
| cumulatedBytes = 0; |
| throughput = 0; |
| throughput_min = 10000000; |
| throughput_max = 0; |
| throughput_ave = 0; |
| throughput_raw_ave = 0; |
| PrevSeqNum = 0; |
| ras_pec_mode = 1; |
| streamStarted = FALSE; |
| nbFrameLost = 0; |
| nbFrameReceived = 0; |
| |
| if (RAS_Init(ras_pec_mode)) { |
| // printf("fail to initialize remoTI audio subsystem"); |
| // exit(-1); |
| } |
| previousFrameExist = 0; |
| audioFrameIndex = 0; |
| |
| elapsedTimeSinceLastPacket = audioElapsedTime(&curTime, &prevPacket); |
| |
| if (file_open && elapsedTimeSinceLastPacket > 700) { |
| // Equivalent to dead link timer |
| // Close current file and reopen a new one. |
| // Stop Frame |
| fflush(fout_wav); |
| fclose(fout_wav); |
| fout_wav = NULL; |
| fflush(fout_tic1); |
| fclose(fout_tic1); |
| fout_tic1 = NULL; |
| |
| // Need to add File size in the header of the wav file |
| { |
| int filesize; |
| unsigned char file[4]; |
| fout_wav = fopen(outFile_wav, "r+b"); |
| if (fout_wav == NULL) { |
| perror("fopen6:"); |
| exit(-1); |
| } |
| |
| fseek(fout_wav, 0L, SEEK_END); |
| filesize = ftell(fout_wav); |
| |
| file[0] = (unsigned char)(((filesize - 44) & 0xFF)); |
| file[1] = (unsigned char)(((filesize - 44) & 0xFF00) >> 8); |
| file[2] = (unsigned char)(((filesize - 44) & 0xFF0000) >> 16); |
| file[3] = (unsigned char)(((filesize - 44) & 0xFF000000) >> 24); |
| fseek(fout_wav, 40, SEEK_SET); |
| fwrite(file, 1, 4, fout_wav); |
| fclose(fout_wav); |
| fout_wav = NULL; |
| } |
| file_open = FALSE; |
| |
| printf("===> AUDIO STOP BEFORE START <=== \n"); |
| } |
| |
| if (!file_open) { |
| audioRenameOutputFile(); |
| // Add .Wav header to .wave output file |
| if (fout_wav) audioAddWaveHeader(fout_wav); |
| } |
| } |
| |
| static void audio_ParseData(unsigned char *pdu, unsigned short len) { |
| long int decrementValue, elapsed_time; |
| int i; |
| frame_type = pdu[3] & 0x7; |
| SeqNum = (pdu[3] >> 3) & 0x1F; |
| // s = g_string_new(NULL); |
| |
| Data_len = |
| (len - |
| 4); // This is audio data length only (audio frame header+ audio data) |
| // printf("FT = 0x%02x ,SeqNum: %d, len: %d\n", frame_type, SeqNum, len); |
| |
| cumulatedBytes += Data_len; |
| totalcumulatedBytes += Data_len; |
| totalcumulatedRawBytes += Data_len + 3 + 4 + 1; |
| |
| gettimeofday(&curTime, NULL); |
| decrementValue = audioElapsedTime(&curTime, &prevTimeThrougput); |
| elapsed_time = audioElapsedTime(&curTime, &prevTimeStarting); |
| |
| // check if 1s has elapse since last packet. |
| if (decrementValue > 1000) { |
| // Calculate and Display Throughput |
| throughput = |
| (float)(8 * (((float)cumulatedBytes) / ((float)decrementValue))); |
| (throughput_min > throughput) ? throughput_min = throughput |
| : throughput_min; |
| (throughput_max < throughput) ? throughput_max = throughput |
| : throughput_max; |
| gettimeofday(&prevTimeThrougput, NULL); |
| } |
| |
| if (elapsed_time > 500) // Not meaningful before |
| { |
| throughput_ave = |
| (float)(8 * ((float)totalcumulatedBytes) / ((float)elapsed_time)); |
| throughput_raw_ave = |
| (float)(8 * ((float)totalcumulatedRawBytes) / ((float)elapsed_time)); |
| } |
| |
| if (decrementValue > 1000) { |
| cumulatedBytes = 0; |
| printf( |
| "ElapsedTime(ms): %ld, throughput(kbps): %2.2f (%2.2f/%2.2f) " |
| "Ave:(%2.2f) Raw Ave: (%2.2f), AudioPER:%2.2f%%, ( %d lost over %d " |
| "sent) \n", |
| elapsed_time, throughput, throughput_min, throughput_max, |
| throughput_ave, throughput_raw_ave, |
| (double)(100 * (double)nbFrameLost / |
| (double)(nbFrameLost + nbFrameReceived)), |
| nbFrameLost, nbFrameReceived + nbFrameLost); |
| } |
| |
| // Throughput estimation |
| if (frame_type == RAS_START_CMD) { |
| printf("===> AUDIO START <=== \n"); |
| audioResetStream(); |
| streamStarted = TRUE; |
| } else if (frame_type == RAS_DATA_TIC1_CMD) { |
| if (!streamStarted) { |
| audioResetStream(); |
| streamStarted = TRUE; |
| } |
| nbFrameReceived++; |
| gettimeofday(&prevPacket, NULL); |
| if (PrevSeqNum + 1 != SeqNum) { |
| if (SeqNum < (PrevSeqNum + 1)) { |
| nbFrameLost += 32 + SeqNum - (PrevSeqNum + 1); |
| } else |
| nbFrameLost += SeqNum - (PrevSeqNum + 1); |
| printf("FRAME LOST !!!!(%d != %d) (%d nbFrameLost for %d sent\n", |
| PrevSeqNum + 1, SeqNum, nbFrameLost, |
| nbFrameLost + nbFrameReceived); |
| } |
| |
| if (SeqNum == 31) |
| PrevSeqNum = -1; |
| else |
| PrevSeqNum = SeqNum; |
| |
| // Decode Frame if any exist. |
| if (previousFrameExist) { |
| short temp[1024]; |
| unsigned short decLength; |
| |
| // printf("decode Frame %d\n", PrevSeqNum ); |
| RAS_Decode(RAS_DECODE_TI_TYPE1, audioFrame, audio_data_len, temp, |
| &decLength); |
| if (fout_wav != NULL) { |
| /*Write the decoded audio to file*/ |
| fwrite(temp, sizeof(short), decLength / 2, fout_wav); |
| } |
| |
| if (fout_tic1 != NULL) { |
| int8 temp2[512]; |
| temp2[0] = audio_data_len; |
| memcpy(&temp2[1], audioFrame, audio_data_len); |
| |
| /*Write the decoded audio to file*/ |
| fwrite(temp2, sizeof(char), audio_data_len + 1, fout_tic1); |
| } |
| } |
| |
| previousFrameExist = 1; |
| |
| // Store Data in audio frame: |
| audioFrameIndex = 0; |
| audio_data_len = Data_len; // Remove audio frame Header |
| for (i = 0; i < Data_len; i++) audioFrame[audioFrameIndex++] = pdu[i + 4]; |
| |
| } else if (frame_type == RAS_STOP_CMD) { |
| printf("===> AUDIO STOP <=== \n"); |
| // Stop Frame |
| fflush(fout_wav); |
| fclose(fout_wav); |
| fout_wav = NULL; |
| fflush(fout_tic1); |
| fclose(fout_tic1); |
| previousFrameExist = 0; |
| fout_tic1 = NULL; |
| |
| // Need to add File size in the header of the wav file |
| { |
| int filesize; |
| unsigned char file[4]; |
| fout_wav = fopen(outFile_wav, "r+b"); |
| if (fout_wav != NULL) { |
| printf("===> Wav File open for length update: %s <=== \n", |
| outFile_wav); |
| } else { |
| perror("fopen5:"); |
| exit(-1); |
| } |
| fseek(fout_wav, 0L, SEEK_END); |
| filesize = ftell(fout_wav); |
| |
| file[0] = (unsigned char)(((filesize - 44) & 0xFF)); |
| file[1] = (unsigned char)(((filesize - 44) & 0xFF00) >> 8); |
| file[2] = (unsigned char)(((filesize - 44) & 0xFF0000) >> 16); |
| file[3] = (unsigned char)(((filesize - 44) & 0xFF000000) >> 24); |
| fseek(fout_wav, 40, SEEK_SET); |
| fwrite(file, 1, 4, fout_wav); |
| fclose(fout_wav); |
| fout_wav = NULL; |
| } |
| file_open = FALSE; |
| } |
| } |
| |
| int main(int argc, char **argv) { |
| int attframecnt = 0; |
| int bytes_read = 0; |
| int num_bytes = 0; |
| int total_bytes = 0; |
| int read_state = 0; |
| unsigned char readbuf[256]; |
| unsigned char dummybuf[256]; |
| |
| while (1) { |
| pthread_mutex_lock(&audio_mutex); |
| { |
| // printf("Open reading pipe ...\n"); |
| fp = open(FIFO_BTD2AUDIO_FILE, O_RDONLY); |
| if (fp == -1) { |
| printf("can't open the pipe\n"); |
| exit(-1); |
| } |
| |
| // Read a single byte to know the length of the data |
| bytes_read = read(fp, &num_bytes, 1); |
| printf("%d bytes of Data (bytes to read further=%d): \n", bytes_read, |
| num_bytes); |
| if ((bytes_read) && (num_bytes == 4)) { |
| printf("Reading %d bytes of Audio Frame\n", num_bytes); |
| bytes_read = read(fp, &readbuf[0], num_bytes); |
| total_bytes += bytes_read; |
| |
| printf("Submitting Audio Frame. Size=%d\n", total_bytes); |
| |
| // Call Audio protocol Handler: |
| audio_ParseData(&readbuf[0], total_bytes); |
| total_bytes = 0; |
| |
| } else if ((bytes_read) && (num_bytes == 23)) { |
| if (read_state == 0) { |
| int rasFrame = 0; |
| bytes_read = read(fp, &readbuf[total_bytes], num_bytes); |
| total_bytes += bytes_read; |
| printf( |
| "Received %d bytes of Data (total_bytes=%d), retrieve data: \n", |
| bytes_read, total_bytes); |
| |
| rasFrame = readbuf[3] & 0x7; |
| if (rasFrame == 0x01) { |
| // Reading Complete Audio Frame |
| read_state = 1; |
| attframecnt = 0; |
| } |
| } |
| |
| if (read_state == 1) { |
| /* |
| //Read a single byte to know |
| the length of the data |
| bytes_read = read(fp, |
| &num_bytes, 1); |
| printf("%d bytes of Data |
| (bytes to read further=%d): \n", bytes_read, num_bytes); |
| // printf("Bytes to read = |
| %d\n", |
| num_bytes); |
| if( (bytes_read) && (num_bytes |
| == 23)) |
| { |
| */ |
| // Read the ATT Header data(3 Bytes) to Dummy file |
| bytes_read = read(fp, &dummybuf[0], 3); |
| // Read Actual data to readbuf |
| bytes_read = read(fp, &readbuf[total_bytes], (num_bytes - 3)); |
| total_bytes += bytes_read; |
| printf("Received %d bytes of Data (total_bytes=%d): \n", bytes_read, |
| total_bytes); |
| attframecnt += 1; |
| /* } |
| else |
| { |
| if(bytes_read) |
| { |
| //Read the ATT frame |
| to Dummy buffer |
| printf("Error while |
| reading Audio ATT Frame. Only %d bytes\n", num_bytes); |
| bytes_read = read(fp, |
| &dummybuf[0], num_bytes); |
| printf("Audio noti: |
| Len: %d, Data: (0) 0x%x, Data: (1) 0x%x, Data: (2) 0x%x, Data: (3) |
| 0x%x, (4) 0x%x, (5) 0x%x, (6) 0x%x, (7) 0x%x, (8) 0x%x, (9) 0x%x, |
| (10) 0x%x, (11) 0x%x, (12) 0x%x, (19) 0x%x, (20) 0x%x, (21) 0x%x, |
| (22) 0x%x ", num_bytes, dummybuf[0], dummybuf[1], dummybuf[2], |
| dummybuf[3], dummybuf[4], dummybuf[5], dummybuf[6], dummybuf[7], |
| dummybuf[8], dummybuf[9], dummybuf[10], dummybuf[11], dummybuf[12], |
| dummybuf[num_bytes-4], dummybuf[num_bytes-3], |
| dummybuf[num_bytes-2], dummybuf[num_bytes-1]); |
| |
| } |
| } |
| */ |
| } |
| |
| if (attframecnt > 4) { |
| printf("Submitting Audio Frame. Size=%d\n", total_bytes); |
| // Call Audio protocol Handler: |
| audio_ParseData(&readbuf[0], total_bytes); |
| read_state = 0; |
| total_bytes = 0; |
| } |
| |
| } else { |
| if (bytes_read) { |
| // Read the ATT frame to Dummy buffer |
| bytes_read = read(fp, &dummybuf[0], num_bytes); |
| printf("Ignoring ATT Frame\n"); |
| } else { |
| printf("End of Pipe Reached. Closing Pipe\n"); |
| } |
| } |
| |
| // Send Data Back to the Coordinator or End Device |
| // printf("\n"); |
| |
| bytes_read = 0; |
| num_bytes = 0; |
| |
| printf("Close reading pipe...\n"); |
| close(fp); |
| } |
| pthread_mutex_unlock(&audio_mutex); |
| |
| // msleep(500); |
| } |
| |
| printf("Exiting App...\n"); |
| } |