/* * audio.c * * Audio handling routines and API for libcutlass. This * module (basically just audio.c) provides all the core functions * for handling audio in a calling application and cutlass internal, * cross-thread APIs. * * Copyright (c) 2004, 2005 nash e.f. * Todd MacDermid */ #include #include #include #include #include #include #include /* * cutlass_audio_req_pack packs a buffer intended to be the payload * of a CUT_AUDIO_REQ type packet with the contents of a * cutlass_audio_req_t structure. packet_buf must be at least * CUTLASS_AUDIO_REQ_LEN bytes long. * * Returns 0 if successful, -1 if unsuccessful. */ static int cutlass_audio_req_pack(cutlass_t *cut_handle, cutlass_audio_req_t *req, uint8_t *packet_buf) { if((NULL == cut_handle) || (NULL == req) || (NULL == packet_buf)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_pack: Passed a NULL pointer\n"); return(-1); } packet_buf[0] = req->type; packet_buf[1] = req->flags; packet_buf[2] = req->encoders; return(0); } /* * cutlass_audio_req_unpack unpacks a buffer packed by * cutlass_audio_req_pack. The cutlass_audio_req_t structure * will be filled in using the payload of packet_buf. * packet_buf must be at least CUTLASS_AUDIO_REQ_LEN bytes long. * * Returns 0 if successful, -1 if unsuccessful. */ static int cutlass_audio_req_unpack(cutlass_t *cut_handle, cutlass_audio_req_t *req, uint8_t *packet_buf) { if((NULL == cut_handle) || (NULL == req) || (NULL == packet_buf)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_unpack: Passed a NULL pointer\n"); return(-1); } req->type = packet_buf[0]; req->flags = packet_buf[1]; req->encoders = packet_buf[2]; return(0); } /* * cutlass_connection_audio_delete frees the memory and underlying * structures created within a connection, and decrements the audio * handle's refcount (Which will end with the audio system being * shutdown overall if it reaches zero). * * Users should not be calling this directly, they should instead be * calling cutlass_audio_disconnect(), which calls * cutlass_connection_audio_shutdown(), which calls this function * after appropriately locking various mutexes (mutexen? mutices?) * * Returns -1 on fatal error, 1 on nonfatal error, and 0 on success. */ int cutlass_connection_audio_delete(cutlass_t *cut_handle, conn_t *conn) { cutlass_conn_audio_t *conn_audio_handle; if((NULL == cut_handle) || (NULL == conn)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_delete: Passed a NULL pointer\n"); return(-1); } conn_audio_handle = conn->conn_audio_handle; if(NULL != conn_audio_handle) { switch(conn_audio_handle->encoder_type) { case CUTLASS_ENCODER_SPEEX: cutlass_speex_handle_destroy(cut_handle, conn_audio_handle->encoder_handle); cutlass_speex_handle_destroy(cut_handle, conn_audio_handle->decoder_handle); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_delete: " "Unknown encoder type (possible memory leak)\n"); break; } cutlass_ring_free(conn_audio_handle->out_ring); cutlass_ring_free(conn_audio_handle->in_ring); free(conn_audio_handle); conn->audio_state = CUTLASS_AUDIO_CLOSED; conn->conn_audio_handle = NULL; pthread_mutex_lock(&(cut_handle->audio_mutex)); cut_handle->audio_refcnt--; pthread_mutex_unlock(&(cut_handle->audio_mutex)); } else { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_connection_audio_delete: " "Shutting down already closed audio connection\n"); return(1); } return(0); } /* * cutlass_connection_audio_shutdown sends a close packet to the * remote host, and then calls cutlass_connection_audio_delete * to clean up its own side of things. Users should not call this * directly, instead use calling cutlass_audio_disconnect(). * * Returns 0 on success, 1 on nonfatal error, -1 on fatal error. */ static int cutlass_connection_audio_shutdown(cutlass_t *cut_handle, conn_t *conn) { cutlass_audio_req_t req; struct cutlass_packet_hdr packet_header; uint8_t packet_buf[CUTLASS_AUDIO_REQ_LEN]; if((NULL == cut_handle) || (NULL == conn)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_shutdown: " "Passed a NULL pointer\n"); return(-1); } req.type = CUTLASS_AUDIO_REQ_RESET; req.flags = CUTLASS_RESET_SESSIONEND; req.encoders = 0; cutlass_audio_req_pack(cut_handle, &req, packet_buf); packet_header.cut_type = CUT_AUDIO_REQ; packet_header.channel_id = 0; packet_header.length = CUTLASS_AUDIO_REQ_LEN; cutlass_send_process(cut_handle, conn, &packet_header, packet_buf, CUTLASS_AUDIO_REQ_LEN); conn->audio_state = CUTLASS_AUDIO_CLOSED; return(cutlass_connection_audio_delete(cut_handle, conn)); } /* * cutlass_audio_shutdown closes down all current audio activity. It * will notify remote sides that it is terminating audio, and * then clean up all data structures on its side. Use this when * quitting out, or when disabling all audio activity. * * No return value, it does its best to clean up, but if it fails, * well, you leaked. */ void cutlass_audio_shutdown(cutlass_t *cut_handle) { cutlass_audio_t *audio_handle; conn_t *conn; uint8_t fingerprints[CUT_HK_ARRAY_SZ * CUT_ID_LEN]; uint8_t *cur_fingerprint; int i; int num_conn; if(NULL == cut_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_shutdown: Passed a NULL pointer\n"); return; } num_conn = cutlass_conn_list(cut_handle, fingerprints, CUT_HK_ARRAY_SZ); cur_fingerprint = fingerprints; for (i = 0; i < num_conn; i++) { conn = hashtable_fingerprint_find(cut_handle, cur_fingerprint); if(NULL != conn) { if(CUTLASS_AUDIO_OPEN == conn->audio_state) { cutlass_connection_audio_shutdown(cut_handle, conn); } pthread_mutex_unlock(&(conn->conn_mutex)); } cur_fingerprint += CUT_ID_LEN; } pthread_mutex_lock(&(cut_handle->audio_mutex)); if(cut_handle->audio_refcnt > 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_shutdown: " "I thought I shut down all connections, but I still show" "%d connections open\n", cut_handle->audio_refcnt); pthread_mutex_unlock(&(cut_handle->audio_mutex)); return; } if(NULL != cut_handle->audio_handle) { audio_handle = cut_handle->audio_handle; /* Make sure the audio threads are both in a nondangerous state */ pthread_mutex_lock(&(audio_handle->write_mutex)); pthread_mutex_lock(&(audio_handle->read_mutex)); switch(audio_handle->driver_type) { case CUTLASS_AUDRIVER_OSS: oss_audio_close(audio_handle->driver); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_shutdown: Unknown driver type\n"); break; } pthread_mutex_unlock(&(audio_handle->write_mutex)); pthread_mutex_unlock(&(audio_handle->read_mutex)); pthread_mutex_destroy(&(audio_handle->write_mutex)); pthread_mutex_destroy(&(audio_handle->read_mutex)); free(audio_handle); } cut_handle->audio_handle = NULL; cut_handle->audio_refcnt = 0; pthread_mutex_unlock(&(cut_handle->audio_mutex)); return; } /* * audio_action_new initializes an already-allocated struct cut_action_obj * for audio purposes. * * This action should be one of CUT_SND_OFFER, CUT_SND_ACCEPT, CUT_SND_REJECT, * or CUT_SND_DONE * * Returns 0 if successful, or -1 on failure. */ static int audio_action_new(cutlass_t *cut_handle, conn_t *conn, struct cut_action_obj *action_obj, int action_type) { if((NULL == cut_handle) || (NULL == conn) || (NULL == action_obj)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "audio_action_new: Passed a NULL pointer\n"); return(-1); } memset(action_obj, 0, sizeof(struct cut_action_obj)); action_obj->action_type = action_type; action_obj->cut_handle = cut_handle; memcpy(action_obj->conn_fingerprint, conn->fingerprint, CUT_ID_LEN); action_obj->action_type = action_type; strncpy(action_obj->remote_nick, conn->conn_name, CUTLASS_NAME_LEN-1); memcpy(&(action_obj->remote_addr), &(conn->remote_addr), sizeof(struct sockaddr_in)); return(0); } /* * cutlass_req_accept_send sends a packet tot the remote side to let * them know the connection is open. This is separated out * from cutlass_audio_accept() so that we're not sending * spurious accept actions on missed open message retransmits. */ static int cutlass_req_accept_send(cutlass_t *cut_handle, conn_t *conn) { uint8_t packet_buf[CUTLASS_AUDIO_REQ_LEN]; cutlass_audio_req_t req; struct cutlass_packet_hdr packet_header; if((NULL == cut_handle) || (NULL==conn)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_accept: Passed a NULL pointer\n"); return(-1); } if(NULL != conn) { req.type = CUTLASS_AUDIO_REQ_ACCEPT; req.flags = CUTLASS_AUDIO_REQ_IWILLSEND | CUTLASS_AUDIO_REQ_IWILLRECV; req.encoders = CUTLASS_ENCODER_SPEEX; cutlass_audio_req_pack(cut_handle, &req, packet_buf); packet_header.cut_type = CUT_AUDIO_REQ; packet_header.channel_id = 0; packet_header.length = CUTLASS_AUDIO_REQ_LEN; cutlass_send_process(cut_handle, conn, &packet_header, packet_buf, CUTLASS_AUDIO_REQ_LEN); } /* HEREHEREHERE - Remove hardcoded encoder */ return(0); } /* * cutlass_audio_send puts unencoded data into the connection audio * handle's outbound ring buffer, then takes unencoded data out of * the outbound ring buffer, encodes it, and puts it on the * wire. The ring buffer stage is meant to smooth out differences between * buffer read sizes and encoder frame sizes. * * Returns 0 on success, -1 on failure. */ static int cutlass_audio_send(cutlass_t *cut_handle, conn_t *conn, float *audio_buf, int num_samples) { cutlass_conn_audio_t *conn_audio_handle; uint8_t out_encoded[CUT_AUDIO_PACKET_MAX] = {0}; uint32_t sequence_num; float out_unencoded[CUT_AUDIO_FRAMESIZE_MAX]; struct cutlass_packet_hdr header; int num_unencoded; int num_encoded = 0; if((NULL == cut_handle) || (NULL == conn) || (NULL == audio_buf)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_send: Passed a NULL pointer\n"); return(-1); } if(num_samples < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_send: Invalid number of samples: %d\n", num_samples); return(-1); } conn_audio_handle = conn->conn_audio_handle; cutlass_ring_write(conn_audio_handle->out_ring, audio_buf, num_samples); num_unencoded = cutlass_ring_read(conn_audio_handle->out_ring, out_unencoded, conn_audio_handle->out_framesize, conn_audio_handle->out_framesize); if(num_unencoded == conn_audio_handle->out_framesize) { switch(conn_audio_handle->encoder_type) { case CUTLASS_ENCODER_SPEEX: num_encoded = cutlass_speex_encode(cut_handle, conn_audio_handle->encoder_handle, out_unencoded, out_encoded + CUT_AUDIO_SEQUENCE_LEN, CUT_AUDIO_PACKET_MAX - CUT_AUDIO_SEQUENCE_LEN); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_send: Unknown encoder type\n"); break; } if(num_encoded > 0) { conn_audio_handle->out_seq_num++; sequence_num = htonl(conn_audio_handle->out_seq_num); memcpy(out_encoded, (uint8_t *)&sequence_num, CUT_AUDIO_SEQUENCE_LEN); memset(&header, 0, sizeof(header)); header.cut_type = CUT_AUDIO_DATA; header.length = num_encoded + CUT_AUDIO_SEQUENCE_LEN; if(cutlass_send_process(cut_handle, conn, &header, out_encoded, header.length) != 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_send: cutlass_send failed\n"); return(-1); } } } return(0); } /* * cutlass_audio_deliver sends the data in the buffer out to each of * the active audio connections. * * Returns 0 on success, -1 on failure. */ static int cutlass_audio_deliver(cutlass_t *cut_handle, float *audio_buf, int num_samples) { conn_t *conn; uint8_t fingerprints[CUT_HK_ARRAY_SZ * CUT_ID_LEN]; uint8_t *cur_fingerprint; int i; int num_conn; if((NULL == cut_handle) || (NULL == audio_buf)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_deliver: Passed a NULL pointer\n"); return(-1); } if(num_samples < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_deliver: Invalid number of samples: %d\n", num_samples); return(-1); } if(num_samples > 0) { num_conn = cutlass_conn_list(cut_handle, fingerprints, CUT_HK_ARRAY_SZ); cur_fingerprint = fingerprints; for (i = 0; i < num_conn; i++) { conn = hashtable_fingerprint_find(cut_handle, cur_fingerprint); if(NULL != conn) { if((CUTLASS_AUDIO_OPEN == conn->audio_state) && (conn->conn_audio_handle != NULL)) { cutlass_audio_send(cut_handle, conn, audio_buf, num_samples); } pthread_mutex_unlock(&(conn->conn_mutex)); } cur_fingerprint += CUT_ID_LEN; } } return(0); } /* * cutlass_audio_mix is reaaaal quick'n'dirty for right now. Its a way * to mix together multiple incoming audio streams. It's simple * addition right now, but that seems to work. * * Returns 0 on success, -1 on failure. */ static int cutlass_audio_mix(cutlass_t *cut_handle, float *audio_buf, float *mix_buf, int num_samples) { int i; if((NULL == cut_handle) || (NULL == audio_buf) || (NULL == mix_buf)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_mix: Passed a NULL pointer\n"); return(-1); } for(i = 0; i < num_samples; i++) { audio_buf[i] = audio_buf[i] + mix_buf[i]; } return(0); } /* * cutlass_audio_retrieve iterates through all the active audio connections, * and mixes their inbound contents together into an audio buffer to * produce a sum of all audio waveforms for a given timeframe. The integer * pointed to by total_samples will store the number of samples retrieved * by this operation. audio_buf must be at least CUTLASS_AUDIO_BLOCKSIZE * in length. * * Returns 0 on success, or -1 on failure. */ int cutlass_audio_retrieve(cutlass_t *cut_handle, float *audio_buf, int *total_samples) { conn_t *conn; cutlass_conn_audio_t *conn_audio_handle; float tmp_buf[CUTLASS_AUDIO_BLOCKSIZE]; int i; int num_conn; int num_samples; int max_samples = 0; uint8_t fingerprints[CUT_HK_ARRAY_SZ * CUT_ID_LEN]; uint8_t *cur_fingerprint; if((NULL == cut_handle) || (NULL == audio_buf) || (NULL == total_samples)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_retrieve: Passed a NULL pointer\n"); return(-1); } num_conn = cutlass_conn_list(cut_handle, fingerprints, CUT_HK_ARRAY_SZ); cur_fingerprint = fingerprints; for (i = 0; i < num_conn; i++) { conn = hashtable_fingerprint_find(cut_handle, cur_fingerprint); if(NULL != conn) { if(CUTLASS_AUDIO_OPEN == conn->audio_state) { conn_audio_handle = conn->conn_audio_handle; num_samples = cutlass_ring_read(conn_audio_handle->in_ring, tmp_buf, CUTLASS_AUDIO_BLOCKSIZE, CUTLASS_AUDIO_BLOCKSIZE); if(num_samples > max_samples) { max_samples = num_samples; } cutlass_audio_mix(cut_handle, audio_buf, tmp_buf, num_samples); } *total_samples = max_samples; pthread_mutex_unlock(&(conn->conn_mutex)); } cur_fingerprint += CUT_ID_LEN; } return(0); } /* * cutlass_audio_read_thread is the thread concerned with capturing * data from the audio driver, and playing it to remote audio connections * with a spin through the ring buffers to size it properly. It * terminates when no audio connections are open, or when the program * is terminated. */ void * cutlass_audio_read_thread(void *void_handle) { cutlass_t *cut_handle; cutlass_audio_t *audio_handle; float audio_buf[CUTLASS_AUDIO_BLOCKSIZE]; int continue_running = 1; int num_samples; int quiet_frames = 0; struct timespec sleep_time; pthread_detach(pthread_self()); cut_handle = void_handle; if(NULL == cut_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_read_thread: Passed a NULL pointer\n"); pthread_exit(NULL); } cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_audio_read_thread initialized\n"); sleep_time.tv_sec = 0; sleep_time.tv_nsec = CUTLASS_AUDIO_SLEEP_TV_NSEC; while(cut_handle->running_state && (continue_running)) { pthread_mutex_lock(&(cut_handle->audio_mutex)); if((cut_handle->audio_refcnt > 0) && (NULL != cut_handle->audio_handle)) { audio_handle = cut_handle->audio_handle; num_samples = 0; pthread_mutex_lock(&(audio_handle->read_mutex)); pthread_mutex_unlock(&(cut_handle->audio_mutex)); switch(audio_handle->driver_type) { case CUTLASS_AUDRIVER_OSS: num_samples = oss_audio_capture(cut_handle, audio_handle->driver, audio_buf, CUTLASS_AUDIO_BLOCKSIZE); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_read_thread: Unknown driver type\n"); break; } pthread_mutex_unlock(&(audio_handle->read_mutex)); if((audio_handle->out_threshold == 0.0) || (cutlass_audio_threshold_check(cut_handle, audio_handle->out_threshold, audio_buf, num_samples) > 0)) { quiet_frames = 0; } else { quiet_frames++; } if(quiet_frames <=CUTLASS_QUIET_FRAMES) { if(cutlass_audio_deliver(cut_handle, audio_buf, num_samples) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_read_thread: " "cutlass_audio_deliver had an unrecoverable error\n"); cutlass_audio_shutdown(cut_handle); } } } else { continue_running = 0; pthread_mutex_unlock(&(cut_handle->audio_mutex)); } nanosleep(&sleep_time, NULL); } cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_read_thread ending\n"); cutlass_audio_shutdown(cut_handle); pthread_exit(NULL); } /* * cutlass_audio_write_thread is the thread concerned with retrieving * inbound data from the ring buffers that the listener thread dumped * it off into, and playing it through the audio driver. It * terminates when no audio connections are open, or when the program * is terminated. */ void * cutlass_audio_write_thread(void *void_handle) { cutlass_t *cut_handle; cutlass_audio_t *audio_handle; int continue_running = 1; int num_samples; float audio_buf[CUTLASS_AUDIO_BLOCKSIZE]; struct timespec sleep_time; pthread_detach(pthread_self()); cut_handle = void_handle; if(NULL == cut_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_read_thread: Passed a NULL pointer\n"); pthread_exit(NULL); } cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_write_thread initialized\n"); sleep_time.tv_sec = 0; sleep_time.tv_nsec = CUTLASS_AUDIO_SLEEP_TV_NSEC; while(cut_handle->running_state && (continue_running)) { pthread_mutex_lock(&(cut_handle->audio_mutex)); if((cut_handle->audio_refcnt > 0) && (NULL != cut_handle->audio_handle)) { audio_handle = cut_handle->audio_handle; pthread_mutex_unlock(&(cut_handle->audio_mutex)); memset(audio_buf, 0, sizeof(audio_buf)); if(cutlass_audio_retrieve(cut_handle, audio_buf, &num_samples) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_write_thread: " "cutlass_audio_play had an unrecoverable error\n"); cutlass_audio_shutdown(cut_handle); } else { if(num_samples > 0) { pthread_mutex_lock(&(cut_handle->audio_mutex)); if((cut_handle->audio_refcnt > 0) && (NULL != cut_handle->audio_handle)) { audio_handle = cut_handle->audio_handle; pthread_mutex_lock(&(audio_handle->write_mutex)); pthread_mutex_unlock(&(cut_handle->audio_mutex)); switch(audio_handle->driver_type) { case CUTLASS_AUDRIVER_OSS: oss_audio_play(cut_handle, audio_handle->driver, audio_buf, num_samples); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_write_thread: " "Unknown driver type\n"); break; } } pthread_mutex_unlock(&(audio_handle->write_mutex)); } } } else { continue_running = 0; pthread_mutex_unlock(&(cut_handle->audio_mutex)); } nanosleep(&sleep_time, NULL); } cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_write_thread ending\n"); pthread_exit(NULL); } float cutlass_audio_get_threshold(cutlass_t *cut_handle) { float cur_threshold = -1.0; if(NULL == cut_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_get_threshold: Passed a NULL pointer\n"); return(-1.0); } pthread_mutex_lock(&(cut_handle->audio_mutex)); if(cut_handle->audio_handle != NULL) { cur_threshold = cut_handle->audio_handle->out_threshold; } pthread_mutex_unlock(&(cut_handle->audio_mutex)); return(cur_threshold); } int cutlass_audio_set_threshold(cutlass_t *cut_handle, float threshold) { if(NULL == cut_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_set_threshold: Passed a NULL pointer\n"); return(-1); } pthread_mutex_lock(&(cut_handle->audio_mutex)); if(cut_handle->audio_handle != NULL) { cut_handle->audio_handle->out_threshold = threshold; } pthread_mutex_unlock(&(cut_handle->audio_mutex)); return(0); } int cutlass_audio_threshold_check(cutlass_t *cut_handle, float threshold, float *audio_buf, int num_samples) { int i; if((NULL == cut_handle) || (NULL == audio_buf)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_set_threshold: Passed a NULL pointer\n"); return(-1); } for(i = 0; i < num_samples; i++) { if(audio_buf[i] < 0.0) { if(-audio_buf[i] > threshold) { return(1); } } else { if(audio_buf[i] > threshold) { return(1); } } } return(0); } /* * cutlass_audio startup gets called when a new audio connection * is made and the audio subsystem is currently inactive. This * activates the underlying driver code, allocates memory for * the audio handle, and kicks off the audio threads. * * Returns 0 on success, -1 on failure. */ static int cutlass_audio_startup(cutlass_t *cut_handle) { if(NULL == cut_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_startup: Passed a NULL pointer\n"); return(-1); } cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_audio_startup: " "starting up the audio sub-system...\n"); if(0 == cut_handle->audio_refcnt) { cut_handle->audio_handle = calloc(1, sizeof(cutlass_audio_t)); if(NULL == cut_handle->audio_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_startup: " "Error allocting memory\n"); goto fail; } cut_handle->audio_handle->out_threshold = 0.0; cut_handle->audio_handle->driver_type = cut_handle->audio_driver_type; switch(cut_handle->audio_driver_type) { case CUTLASS_AUDRIVER_OSS: cut_handle->audio_handle->driver = oss_audio_open(cut_handle, CUTLASS_AUDIO_SAMPLE_RATE); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_startup: Unknown driver type\n"); goto fail2; break; } if(NULL == cut_handle->audio_handle->driver) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_startup: Driver initialization failed\n"); goto fail2; } pthread_mutex_init(&(cut_handle->audio_handle->read_mutex), NULL); pthread_mutex_init(&(cut_handle->audio_handle->write_mutex), NULL); if(pthread_create(&(cut_handle->audio_read_tid), NULL, cutlass_audio_read_thread, cut_handle) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "Could not create audio read thread\n"); goto fail3; } if(pthread_create(&(cut_handle->audio_write_tid), NULL, cutlass_audio_write_thread, cut_handle) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "Could not create audio write thread\n"); goto fail3; } cut_handle->audio_refcnt = 1; } else { cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_audio_startup: " "Someone's already using the audio system\n"); } return(0); fail3: pthread_mutex_destroy(&(cut_handle->audio_handle->read_mutex)); pthread_mutex_destroy(&(cut_handle->audio_handle->write_mutex)); fail2: free(cut_handle->audio_handle); fail: return(-1); } /* * cutlass_connection_audio_startup initializes the audio handles * for a specific connection. This includes the ring buffers for * audio data and encoder state for the desired encoder type. * * cutlass_connection_audio_startup will also call cutlass_audio_startup * if the underlying audio system is not currently active. * * Returns 0 on success, -1 on failure. */ static int cutlass_connection_audio_startup(cutlass_t *cut_handle, conn_t *conn, int encoder_type) { cutlass_conn_audio_t *conn_audio_handle; if((NULL == cut_handle) || (NULL==conn)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_startup: " "Passed a NULL pointer\n"); return(-1); } if(NULL != conn->conn_audio_handle) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_connection_audio_startup: " "This connection already has an audio handle\n"); return(1); } conn_audio_handle = calloc(1, sizeof(cutlass_conn_audio_t)); if(NULL == conn_audio_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_startup: " "Error allocating memory"); goto fail; } conn_audio_handle->in_ring = cutlass_ring_new(CUTLASS_AUDIO_RING_MBRSZ, CUTLASS_AUDIO_RING_SIZE); if(NULL == conn_audio_handle->in_ring) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_startup: " "Error creating inbound ring buffer\n"); goto fail2; } conn_audio_handle->out_ring = cutlass_ring_new(CUTLASS_AUDIO_RING_MBRSZ, CUTLASS_AUDIO_RING_SIZE); if(NULL == conn_audio_handle->out_ring) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_startup: " "Error creating outbound ring buffer\n"); goto fail3; } conn_audio_handle->encoder_type = encoder_type; switch(encoder_type) { case CUTLASS_ENCODER_SPEEX: conn_audio_handle->encoder_handle = cutlass_speex_handle_init(cut_handle, CUTLASS_SPEEX_ENCODER); if(NULL == conn_audio_handle->encoder_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_startup: " "Error opening encoder handle\n"); goto fail4; } conn_audio_handle->decoder_handle = cutlass_speex_handle_init(cut_handle, CUTLASS_SPEEX_DECODER); if(NULL == conn_audio_handle->decoder_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_startup: " "Error opening decoder handle\n"); cutlass_speex_handle_destroy(cut_handle, conn_audio_handle->encoder_handle); goto fail4; } conn_audio_handle->out_framesize = CUTLASS_SPEEX_BLOCKSIZE; conn_audio_handle->in_framesize = CUTLASS_SPEEX_BLOCKSIZE; break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_connection_audio_startup: " "Unknown encoder type\n"); goto fail4; break; } conn->conn_audio_handle = conn_audio_handle; conn->audio_state = CUTLASS_AUDIO_OPEN; pthread_mutex_lock(&(cut_handle->audio_mutex)); if(0 == cut_handle->audio_refcnt) { cutlass_audio_startup(cut_handle); } else { cut_handle->audio_refcnt++; } pthread_mutex_unlock(&(cut_handle->audio_mutex)); return(0); fail4: cutlass_ring_free(conn_audio_handle->out_ring); fail3: cutlass_ring_free(conn_audio_handle->in_ring); fail2: free(conn_audio_handle); fail: return(-1); } /* * cutlass_audio_req_parse parses an incoming cutlass-audio message, * figures out how to deal with it, and then takes appropriate action. * This could be sending a reset packet, starting up an audio connection, * or kicking a query to the user about whether to accept or not. * * returns 0 on success, -1 on failure */ int cutlass_audio_req_parse( cutlass_t *cut_handle, conn_t *conn, uint8_t *msg_packet, struct cutlass_packet_hdr *packet_info, struct timespec *now) { cutlass_audio_req_t req; struct cut_action_obj action; if(packet_info->length < CUTLASS_AUDIO_REQ_LEN) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: Received undersized packet\n"); return (-1); } cutlass_audio_req_unpack(cut_handle, &req, msg_packet); switch(conn->audio_state) { case CUTLASS_AUDIO_CLOSED: if(audio_action_new(cut_handle, conn, &action, CUT_SND_OFFER) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: audio_action_new failed\n"); return(-1); } if(CUTLASS_AUDIO_REQ_OPEN == req.type) { switch((cut_handle->perms).permit_audio) { case NOT_ALLOWED: conn->audio_state = CUTLASS_AUDIO_CLOSED; cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Rejecting audio\n"); pthread_mutex_unlock(&(conn->conn_mutex)); cutlass_audio_reject(cut_handle, &action); break; case USER_ALLOW_CHECK: if (NULL != cut_handle->action_handlers[CUT_SND_OFFER]) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: " "Querying user...\n"); pthread_mutex_unlock(&(conn->conn_mutex)); cutlass_action(cut_handle, conn, &action); } else { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: " "Rejecting audio - (No action handler)\n"); pthread_mutex_unlock(&(conn->conn_mutex)); cutlass_audio_reject(cut_handle, &action); } break; case ALL_ALLOWED: cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Accepting audio\n"); pthread_mutex_unlock(&(conn->conn_mutex)); cutlass_audio_accept(cut_handle, &action); break; } } else if (CUTLASS_AUDIO_REQ_RESET == req.type) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Got reset\n"); /* Do nothing */ } else { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Rejecting audio 2\n"); pthread_mutex_unlock(&(conn->conn_mutex)); cutlass_audio_reject(cut_handle, &action); /* HEREHEREHERE - Flags are wrong. Works, but needs refinement */ } break; case CUTLASS_AUDIO_HALFOPEN: if(CUTLASS_AUDIO_REQ_OPEN == req.type) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Accepting from halfopen\n"); pthread_mutex_unlock(&(conn->conn_mutex)); if(audio_action_new(cut_handle, conn, &action, CUT_SND_OFFER) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: audio_action_new failed\n"); return(-1); } cutlass_audio_accept(cut_handle, &action); } else if(CUTLASS_AUDIO_REQ_RESET == req.type) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Got a reset-closing\n"); if(audio_action_new(cut_handle, conn, &action, CUT_SND_REJECT) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: audio_action_new failed\n"); return(-1); } cutlass_action(cut_handle, conn, &action); conn->audio_state = CUTLASS_AUDIO_CLOSED; } else if(CUTLASS_AUDIO_REQ_ACCEPT == req.type) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Accepted - starting\n"); cutlass_connection_audio_startup(cut_handle, conn, CUTLASS_ENCODER_SPEEX); /* HEREHEREHERE - Remove hardcoded encoder */ if(audio_action_new(cut_handle, conn, &action, CUT_SND_ACCEPT) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: audio_action_new failed\n"); return(-1); } cutlass_action(cut_handle, conn, &action); } break; case CUTLASS_AUDIO_OPEN: if(CUTLASS_AUDIO_REQ_OPEN == req.type) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Already open\n"); cutlass_req_accept_send(cut_handle, conn); } else if(CUTLASS_AUDIO_REQ_RESET == req.type) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: resetting open\n"); if(audio_action_new(cut_handle, conn, &action, CUT_SND_DONE) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: audio_action_new failed\n"); return(-1); } cutlass_action(cut_handle, conn, &action); cutlass_connection_audio_delete(cut_handle, conn); } else if(CUTLASS_AUDIO_REQ_ACCEPT == req.type) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_req_parse: Spurious accept\n"); /* Do nothing */ } break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: Unknown state\n"); break; } return (0); } /* * cutlass_audio_connect is the method that external callers use in * order to open a connection to a remote (but already connected) peer. * Note that just because the call is made, you don't necessarily have * working audio yet. You will be notified via an action handler * when audio initialization is completed. * * Returns 0 on success, -1 on failure. */ int cutlass_audio_connect(cutlass_t *cut_handle, uint8_t *fingerprint) { conn_t *conn; uint8_t packet_buf[CUTLASS_AUDIO_REQ_LEN]; cutlass_audio_req_t req; struct cutlass_packet_hdr packet_header; if((NULL == cut_handle) || (NULL==fingerprint)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_connect: Passed a NULL pointer\n"); return(-1); } conn = hashtable_fingerprint_find(cut_handle, fingerprint); if(!(conn->capabilities & CAN_RECV_AUDIO)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_file_offer: Remote host does not accept audio\n"); pthread_mutex_unlock(&(conn->conn_mutex)); return(-1); } cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_audio_connect: Attempting connection\n"); if(NULL != conn) { if(conn->audio_state != CUTLASS_AUDIO_OPEN) { req.type = CUTLASS_AUDIO_REQ_OPEN; req.flags = CUTLASS_AUDIO_REQ_IWILLSEND | CUTLASS_AUDIO_REQ_IWILLRECV; req.encoders = CUTLASS_ENCODER_SPEEX; cutlass_audio_req_pack(cut_handle, &req, packet_buf); packet_header.cut_type = CUT_AUDIO_REQ; packet_header.channel_id = 0; packet_header.length = CUTLASS_AUDIO_REQ_LEN; cutlass_send_process(cut_handle, conn, &packet_header, packet_buf, CUTLASS_AUDIO_REQ_LEN); conn->audio_state = CUTLASS_AUDIO_HALFOPEN; } else { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_connect: " "Attempting connection on already open audio channel\n"); } pthread_mutex_unlock(&(conn->conn_mutex)); } return(0); } /* * cutlass_audio_connect_all calls cutlass_audio_connect() on every * existing connection. You probably shouldn't do this in real life, * but it makes for easy (and lazy) debugging. * * Returns 0 on success, -1 on failure. */ int cutlass_audio_connect_all(cutlass_t *cut_handle) { uint8_t *array_loc; uint8_t conn_array[CUT_ID_LEN * CUT_HK_ARRAY_SZ]; int i; int num_conn; int retval; if(NULL == cut_handle) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_connect_all: Passed a NULL pointer\n"); return(-1); } num_conn = cutlass_conn_list(cut_handle, conn_array, CUT_HK_ARRAY_SZ); array_loc = conn_array; for (i = 0; i < num_conn; i++) { retval = cutlass_audio_connect(cut_handle, array_loc); if(retval < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_connect_all: Received fatal error\n"); return(-1); } array_loc += CUT_ID_LEN; } return(0); } /* * cutlass_audio_accept should be called in response to an incoming * cutlass-audio request message. It means that you * are ready to accept the connection and go to town. * * This will be getting called out of the CUT_AUDIO_REQ_ACTION * action handler, most of the time. Hence, the action_obj * argument. Just pass what was passed to you. * * Returns 0 on success, -1 on failure. */ int cutlass_audio_accept(cutlass_t *cut_handle, struct cut_action_obj *action) { conn_t *conn; struct cut_action_obj local_action; if((NULL == cut_handle) || (NULL == action)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_accept: Passed a NULL pointer\n"); return(-1); } cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_audio_accept: Accepting audio\n"); conn = hashtable_fingerprint_find(cut_handle, action->conn_fingerprint); if(NULL != conn) { cutlass_req_accept_send(cut_handle, conn); conn->audio_state = CUTLASS_AUDIO_OPEN; cutlass_connection_audio_startup(cut_handle, conn, CUTLASS_ENCODER_SPEEX); /* HEREHEREHERE - Remove hardcoded encoder */ if(audio_action_new(cut_handle, conn, &local_action, CUT_SND_ACCEPT) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: audio_action_new failed\n"); return(-1); } cutlass_action(cut_handle, conn, &local_action); pthread_mutex_unlock(&(conn->conn_mutex)); } return(0); } /* * cutlass_audio_reject should be called in response to an incoming * cutlass-audio request message. It means that you have refused * the connection. * * This will be getting called out of the CUT_AUDIO_REQ_ACTION * action handler, most of the time. Hence, the action_obj * argument. Just pass what was passed to you. * * Returns 0 on success, -1 on failure. */ int cutlass_audio_reject(cutlass_t *cut_handle, struct cut_action_obj *action) { conn_t *conn; struct cut_action_obj local_action; uint8_t packet_buf[CUTLASS_AUDIO_REQ_LEN]; cutlass_audio_req_t req; struct cutlass_packet_hdr packet_header; if((NULL == cut_handle) || (NULL == action)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_reject: Passed a NULL pointer\n"); return(-1); } cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_reject: Rejecting audio\n"); conn = hashtable_fingerprint_find(cut_handle, action->conn_fingerprint); if(NULL != conn) { req.type = CUTLASS_AUDIO_REQ_RESET; req.flags = CUTLASS_RESET_PERM; req.encoders = 0; cutlass_audio_req_pack(cut_handle, &req, packet_buf); packet_header.cut_type = CUT_AUDIO_REQ; packet_header.channel_id = 0; packet_header.length = CUTLASS_AUDIO_REQ_LEN; cutlass_send_process(cut_handle, conn, &packet_header, packet_buf, CUTLASS_AUDIO_REQ_LEN); conn->audio_state = CUTLASS_AUDIO_CLOSED; if(audio_action_new(cut_handle, conn, &local_action, CUT_SND_REJECT) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_req_parse: audio_action_new failed\n"); return(-1); } cutlass_action(cut_handle, conn, &local_action); pthread_mutex_unlock(&(conn->conn_mutex)); } return(0); } /* * cutlass_audio_disconnect disconnects the existing audio stream from * the connection specified by the fingerprint. Note that this is just * tearing down the audio, not deleting the underlying connection. Of * course, if you delete the underlying connection (with * cutlass_conn_shutdown()), the audio will go away automatically, too. * * returns 0 on success, -1 on failure. * */ int cutlass_audio_disconnect(cutlass_t *cut_handle, uint8_t *fingerprint) { conn_t *conn; if((NULL == cut_handle) || (NULL==fingerprint)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_disconnect: Passed a NULL pointer\n"); return(-1); } conn = hashtable_fingerprint_find(cut_handle, fingerprint); if(NULL != conn) { if(conn->audio_state != CUTLASS_AUDIO_CLOSED) { cutlass_connection_audio_shutdown(cut_handle, conn); } pthread_mutex_unlock(&(conn->conn_mutex)); } return (0); } /* * cutlass_audio_disconnect_all disconnects all audio connections * currently in existence. This is functionally very equivalent to * calling cutlass_audio_shutdown(), but it achieves the results in * a different way. This makes it useful for debugging * cutlass_audio_disconnect(). * * Returns -1 on failure, 0 on success. */ int cutlass_audio_disconnect_all(cutlass_t *cut_handle) { uint8_t *array_loc; uint8_t conn_array[CUT_ID_LEN * CUT_HK_ARRAY_SZ]; int i; int num_conn; int retval; num_conn = cutlass_conn_list(cut_handle, conn_array, CUT_HK_ARRAY_SZ); array_loc = conn_array; for (i = 0; i < num_conn; i++) { retval = cutlass_audio_disconnect(cut_handle, array_loc); if(retval < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_connect_all: Received fatal error\n"); return(-1); } array_loc += CUT_ID_LEN; } return(0); } /* * cutlass_audio_data_parse is called from the listener thread, * and handles incoming audio data, decoding it with an * appropriate decoder and stuffing it into the inbound ring buffer * where the audio playback thread can pick it up. * * Returns 0 on success, -1 on failure. */ int cutlass_audio_data_parse( cutlass_t *cut_handle, conn_t *conn, uint8_t *msg_packet, struct cutlass_packet_hdr *packet_info, struct timespec *now) { cutlass_conn_audio_t *conn_audio_handle; float in_decoded[CUT_AUDIO_FRAMESIZE_MAX]; uint8_t packet_buf[CUTLASS_AUDIO_REQ_LEN]; uint32_t sequence_num; cutlass_audio_req_t req; struct cutlass_packet_hdr packet_header; if((NULL == cut_handle) || (NULL==conn) || (NULL == msg_packet) || (NULL == packet_info) || (NULL == now)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_data_parse: Passed a NULL pointer\n"); return(-1); } if (conn->audio_state == CUTLASS_AUDIO_CLOSED) { req.type = CUTLASS_AUDIO_REQ_RESET; req.flags = CUTLASS_RESET_BADSESSION; req.encoders = 0; cutlass_audio_req_pack(cut_handle, &req, packet_buf); packet_header.cut_type = CUT_AUDIO_REQ; packet_header.channel_id = 0; packet_header.length = CUTLASS_AUDIO_REQ_LEN; cutlass_send_process(cut_handle, conn, &packet_header, packet_buf, CUTLASS_AUDIO_REQ_LEN); return(1); } if(conn->audio_state == CUTLASS_AUDIO_HALFOPEN) { cutlass_connection_audio_startup(cut_handle, conn, CUTLASS_ENCODER_SPEEX); } conn_audio_handle = conn->conn_audio_handle; memcpy((uint8_t *)&sequence_num, msg_packet, CUT_AUDIO_SEQUENCE_LEN); sequence_num = ntohl(sequence_num); /* * Note that since this is a 32-bit counter, it will wrap if we * leave an audio connection running for ~3 years. Hopefully * I will fix it before the 3 years are up. ;) */ if(sequence_num > conn_audio_handle->in_seq_num) { conn_audio_handle->in_seq_num = sequence_num; switch(conn_audio_handle->encoder_type) { case CUTLASS_ENCODER_SPEEX: cutlass_speex_decode(cut_handle, conn_audio_handle->decoder_handle, in_decoded, msg_packet + CUT_AUDIO_SEQUENCE_LEN, packet_info->length - CUT_AUDIO_SEQUENCE_LEN); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_data_parse: Unknown encoder type\n"); break; } cutlass_ring_write(conn_audio_handle->in_ring, in_decoded, conn_audio_handle->in_framesize); } else { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_data_parse: out of sequence packets\n"); } return(0); } int cutlass_audio_play_file(cutlass_t *cut_handle, char *filename) { int i; int fd; int numread; float float_buf[CUTLASS_AUDIO_BLOCKSIZE]; void *driver; uint8_t audio_buf[CUTLASS_AUDIO_BLOCKSIZE]; cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_audio_play_file: playing %s\n", filename); fd = open(filename, O_RDONLY); if(fd < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_play_file: Couldn't open %s: %s\n", filename, strerror(errno)); return(-1); } pthread_mutex_lock(&(cut_handle->audio_mutex)); if(cut_handle->audio_refcnt == 0) { switch(cut_handle->audio_driver_type) { case CUTLASS_AUDRIVER_OSS: driver = oss_audio_open(cut_handle, CUTLASS_AUDIO_SAMPLE_RATE); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_play_file: Unknown driver type\n"); pthread_mutex_unlock(&(cut_handle->audio_mutex)); close(fd); return(-1); } } else { driver = cut_handle->audio_handle->driver; pthread_mutex_lock(&(cut_handle->audio_handle->write_mutex)); pthread_mutex_lock(&(cut_handle->audio_handle->read_mutex)); } while((numread = read(fd, audio_buf, CUTLASS_AUDIO_BLOCKSIZE)) > 0) { for(i = 0; i < numread; i++) { float_buf[i] = (audio_buf[i] - 128) * 256.0; } switch(cut_handle->audio_driver_type) { case CUTLASS_AUDRIVER_OSS: oss_audio_play(cut_handle, driver, float_buf, numread); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_play_file: Unknown driver type\n"); goto fail; break; } } if(cut_handle->audio_refcnt == 0) { switch(cut_handle->audio_driver_type) { case CUTLASS_AUDRIVER_OSS: oss_audio_close(driver); break; default: cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_audio_play_file: Unknown driver type\n"); pthread_mutex_unlock(&(cut_handle->audio_mutex)); close(fd); return(-1); } } else { pthread_mutex_unlock(&(cut_handle->audio_handle->read_mutex)); pthread_mutex_unlock(&(cut_handle->audio_handle->write_mutex)); } close(fd); pthread_mutex_unlock(&(cut_handle->audio_mutex)); return(0); fail: close(fd); pthread_mutex_unlock(&(cut_handle->audio_handle->read_mutex)); pthread_mutex_unlock(&(cut_handle->audio_handle->write_mutex)); pthread_mutex_unlock(&(cut_handle->audio_mutex)); return(-1); } /*************************************************************************** * The below is commented out until we come up with a mid-flight parameter * * shifting strategy. * ***************************************************************************/ /** * @brief Handle an incoming information message. * * @detailed Handle an incoming information message. Typically this won't * require use interaction, but there may be special cases in the future * when this method will generate an action object that must be handled * somehow. For example, if the remote side wants to downgrade the compression * or encoding to a lower bit-rate. Users may want to be notified of this. * * @param cut_handle The current cutlass handle. * @param conn The current connection handle. * @param action_obj The action to be handled (typically comes from * cutlass_audio_req_parse). * * @return CUTLASS_OKAY (see cutlass/error.h) on success, anything else is a * failure. * * @note Not yet implemented. */ /* int */ /* cutlass_audio_info_parse( cutlass_t *cut_handle, */ /* conn_t *conn, */ /* uint8_t *msg_packet, */ /* struct cutlass_packet_hdr *packet_info, */ /* struct timespec *now) */ /* { */ /* return (CUTLASS_OKAY); */ /* } */ /** * @brief Build a cutlass info request message. * * @detailed No detailed description, yet. (Someone, write me!) * * @return CUTLASS_OKAY (see cutlass/error.h) on success, anything else is a * failure. * * @note There's at least one bug in here where we should be calling htons() * and related to convert types. So, this probably will only work between * systems of the same endianness, at the moment. */ /* int */ /* cutlass_audio_info_pack(cutlass_t *cut_handle, cutlass_audio_info_t *info) */ /* { */ /* /\* uint8_t *packet = NULL; *\/ */ /* uint8_t *ptr = NULL; */ /* if ((NULL == cut_handle) || (NULL == info)) */ /* { */ /* cutlass_sysmsg(NULL, CUT_ERROR, */ /* "cutlass_audio_info_pack: " */ /* "cut_handle or info ptr is NULL.\n"); */ /* return (CUTLASS_ERROR_NOPTR); */ /* } */ /* info->payload_len = CUTLASS_AUDIO_INFO_PSIZE(info->data_len); */ /* info->payload = (uint8_t*)calloc (info->payload_len, sizeof(uint8_t)); */ /* if (NULL == info->payload) */ /* { */ /* cutlass_sysmsg(cut_handle, CUT_ERROR, */ /* "cutlass_audio_info_pack: " */ /* "can't allocate memory for packet: %s\n", */ /* strerror(errno)); */ /* return (CUTLASS_ERROR_MALLOC); */ /* } */ /* /\* Pack each member into the buffer with memcpy(). *\/ */ /* ptr = info->payload; */ /* memcpy (ptr, &(info->type), sizeof(uint8_t)); */ /* ptr += sizeof(uint8_t); */ /* memcpy (ptr, &(info->flags), sizeof(uint8_t)); */ /* ptr += sizeof(uint8_t); */ /* memcpy (ptr, &(info->data_len), sizeof(uint8_t)); */ /* ptr += sizeof(uint8_t); */ /* memcpy (ptr, info->data, sizeof(uint8_t) * info->data_len); */ /* /\* FIX ME: am I missing anything? *\/ */ /* return (CUTLASS_OKAY); */ /* } */ /** * @brief Free memory associated with a cutlass audio info message. * * @detailed This method frees all memory associated with the cutlass audio * info message with the exception of the memory at info->data. See the note * below for why. Regardless, you need to free this memory yourself. * * @note We don't free whatever's in the data pointer because it might * come from a place where you need to retransmit... */ /* void */ /* cutlass_audio_info_free(cutlass_audio_info_t *info) */ /* { */ /* if (NULL == info) */ /* return; */ /* if (NULL != info->payload) */ /* free (info->payload); */ /* if (NULL != info->cutlass_hdr) */ /* free (info->cutlass_hdr); */ /* free (info); */ /* return; */ /* } */