/***************************************************************************** * GetBufInfo: buffer status query ***************************************************************************** * This function returns the number of used byte in the queue. * It also deals with errors : indeed if the device comes to run out * of data to play, it switches to the "underrun" status. It has to * be flushed and re-prepared *****************************************************************************/ static int GetBufInfo( aout_instance_t *p_aout ) { int i_ret; snd_pcm_channel_status_t status; /* get current pcm status */ memset( &status, 0, sizeof(status) ); if( ( i_ret = snd_pcm_plugin_status( p_aout->output.p_sys->p_pcm_handle, &status ) ) < 0 ) { msg_Err( p_aout, "unable to get device status (%s)", snd_strerror( i_ret ) ); return( -1 ); } /* check for underrun */ switch( status.status ) { case SND_PCM_STATUS_READY: case SND_PCM_STATUS_UNDERRUN: if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle, SND_PCM_CHANNEL_PLAYBACK ) ) < 0 ) { msg_Err( p_aout, "unable to prepare channel (%s)", snd_strerror( i_ret ) ); } break; } return( status.count ); }
/* This function waits until it is possible to write a full sound buffer */ static void NTO_WaitAudio(_THIS) { int rval; int totalbytes,roomavail; /*we consider a full sound buffer to be of size pcm_len bytes */ #ifdef DEBUG_AUDIO fprintf(stderr,"NTO_WaitAudio\n"); #endif while(1) { memset(&cstatus, 0, sizeof(cstatus)); if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 ) { SDL_SetError("snd_pcm_plugin_status failed: %s\n", snd_strerror(rval)); return; } totalbytes = csetup.buf.block.frag_size *csetup.buf.block.frags; roomavail = totalbytes - cstatus.count; #ifdef DEBUG_AUDIO fprintf(stderr,"NTO_WaitAudio roomavail %d pcm_len %d\n",roomavail,pcm_len); #endif if ((roomavail >= pcm_len) || (roomavail < 0)) return; SDL_Delay(10); } }
static ALCuint qsa_available_samples(ALCdevice* device) { qsa_data* data=(qsa_data*)device->ExtraData; snd_pcm_channel_status_t status; ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); ALint free_size; int rstatus; memset(&status, 0, sizeof (status)); status.channel=SND_PCM_CHANNEL_CAPTURE; snd_pcm_plugin_status(data->pcmHandle, &status); if ((status.status==SND_PCM_STATUS_OVERRUN) || (status.status==SND_PCM_STATUS_READY)) { if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) { ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); aluHandleDisconnect(device); return 0; } snd_pcm_capture_go(data->pcmHandle); return 0; } free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags; free_size-=status.free; return free_size/frame_size; }
static int alsa_dsp_get_freespace(struct sysdep_dsp_struct *dsp) { struct alsa_dsp_priv_data *priv = dsp->_priv; snd_pcm_channel_status_t status={0}; snd_pcm_plugin_status(priv->audio_dev.m_AudioHandle, &status); return status.free / alsa_dsp_bytes_per_sample[dsp->hw_info.type]; }
/** * Set up capture based on the audio sample waverec except we use VoIP parameters */ static int capture(circular_buffer_t* circular_buffer) { // Re-usable buffer for capture char *record_buffer; record_buffer = (char*) malloc(g_frame_size_c); // Some diagnostic variables int failed = 0; int totalRead = 0; snd_pcm_channel_status_t status; status.channel = SND_PCM_CHANNEL_CAPTURE; // Loop until stopAudio() flags us while (g_execute_audio) { // This blocking read appears to take much longer than 20ms on the simulator // but it never fails and always returns 160 bytes int read = snd_pcm_plugin_read(g_pcm_handle_c, record_buffer, g_frame_size_c); if (read < 0 || read != g_frame_size_c) { failed++; fprintf(stderr,"CAPTURE FAILURE: snd_pcm_plugin_read: %d requested = %d\n",read,g_frame_size_c); if (snd_pcm_plugin_status(g_pcm_handle_c, &status) < 0) { fprintf(stderr, "Capture channel status error: %d\n",status.status); } else { if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_OVERRUN || status.status == SND_PCM_STATUS_CHANGE || status.status == SND_PCM_STATUS_ERROR) { fprintf(stderr, "CAPTURE FAILURE:snd_pcm_plugin_status: = %d \n",status.status); if (snd_pcm_plugin_prepare (g_pcm_handle_c, SND_PCM_CHANNEL_CAPTURE) < 0) { fprintf (stderr, "Capture channel prepare error 1 %d\n",status.status); exit (1); } } } } else { totalRead += read; // On simulator always room in the circular buffer if (!writeToCircularBuffer(circular_buffer, record_buffer, g_frame_size_c)) { failed++; } } capture_ready = true; } fprintf(stderr,"CAPTURE EXIT BEGIN\n"); (void) snd_pcm_plugin_flush(g_pcm_handle_c, SND_PCM_CHANNEL_CAPTURE); //(void)snd_mixer_close (mixer_handle); (void) snd_pcm_close(g_pcm_handle_c); audio_manager_free_handle(g_audio_manager_handle_c); // IMPORTANT NB: You only get failed on capture if the play loop has exited hence the circular buffer fills. This is with the simulator fprintf(stderr, "CAPTURE EXIT Total Bytes read = %d failed = %d\n",totalRead,failed); free(record_buffer); return 0; }
qint64 QnxAudioInput::read(char *data, qint64 len) { int errorCode = 0; QByteArray tempBuffer(m_periodSize, 0); const int actualRead = snd_pcm_plugin_read(m_pcmHandle, tempBuffer.data(), m_periodSize); if (actualRead < 1) { snd_pcm_channel_status_t status; memset(&status, 0, sizeof(status)); status.channel = SND_PCM_CHANNEL_CAPTURE; if ((errorCode = snd_pcm_plugin_status(m_pcmHandle, &status)) < 0) { qWarning("QnxAudioInput: read error, couldn't get plugin status (0x%x)", -errorCode); close(); setError(QAudio::FatalError); setState(QAudio::StoppedState); return -1; } if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_OVERRUN) { if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE)) < 0) { qWarning("QnxAudioInput: read error, couldn't prepare plugin (0x%x)", -errorCode); close(); setError(QAudio::FatalError); setState(QAudio::StoppedState); return -1; } } } else { setError(QAudio::NoError); setState(QAudio::ActiveState); } if (m_volume < 1.0f) QAudioHelperInternal::qMultiplySamples(m_volume, m_format, tempBuffer.data(), tempBuffer.data(), actualRead); m_bytesRead += actualRead; if (m_pullMode) { m_audioSource->write(tempBuffer.data(), actualRead); } else { memcpy(data, tempBuffer.data(), qMin(static_cast<qint64>(actualRead), len)); } m_bytesAvailable = 0; return actualRead; }
static void resetPlay() { snd_pcm_channel_status_t status; status.channel = SND_PCM_CHANNEL_PLAYBACK; if (snd_pcm_plugin_status(g_pcm_handle_p, &status) < 0) { fprintf(stderr, "FATAL Playback channel status error %d\n", status.status); exit (1); } if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN || status.status == SND_PCM_STATUS_CHANGE || status.status == SND_PCM_STATUS_ERROR) { fprintf(stderr, "PLAYBACK FAILURE:snd_pcm_plugin_status: = %d \n",status.status); if (snd_pcm_plugin_prepare (g_pcm_handle_p, SND_PCM_CHANNEL_PLAYBACK) < 0) { fprintf (stderr, "FATAL Playback channel prepare error %d\n", status.status); exit (1); } } }
static int write_qsa(audio_output_t* ao, unsigned char* buf, int bytes) { int written; int status; snd_pcm_channel_status_t cstatus; qsa_internal_t* userptr; userptr=ao->userptr; if (userptr!=NULL); { written=snd_pcm_plugin_write(userptr->audio_handle, buf, bytes); if (written!=bytes) { /* Check if samples playback got stuck somewhere in hardware or in */ /* the audio device driver */ if ((errno==EAGAIN) && (written==0)) { return 0; } if ((errno==EINVAL) || (errno==EIO)) { memset(&cstatus, 0, sizeof(cstatus)); cstatus.channel=SND_PCM_CHANNEL_PLAYBACK; status=snd_pcm_plugin_status(userptr->audio_handle, &cstatus); if (status>0) { return 0; } if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) { status=snd_pcm_plugin_prepare(userptr->audio_handle, SND_PCM_CHANNEL_PLAYBACK); if (status<0) { return 0; } } } } } return written; }
/** * Play audio received from PJMEDIA */ static int pb_thread_func (void *arg) { struct bb10_stream* stream = (struct bb10_stream *) arg; int size = stream->pb_buf_size; unsigned long nframes = stream->pb_frames; void *user_data = stream->user_data; char *buf = stream->pb_buf; pj_timestamp tstamp; int result = 0; int policy; struct sched_param param; TRACE_((THIS_FILE, "pb_thread_func: size = %d ", size)); if (pthread_getschedparam(pthread_self(), &policy, ¶m) == 0) { param.sched_priority = 18; pthread_setschedparam (pthread_self(), policy, ¶m); } pj_bzero (buf, size); tstamp.u64 = 0; /* Do the final initialization now the thread has started. */ if ((result = snd_pcm_plugin_prepare(stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) { TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result)); return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; /* pointer to buffer filled by PJMEDIA */ frame.buf = buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; /* Read the audio from pjmedia */ result = stream->pb_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero (buf, size); /* Write 640 to play unit */ result = snd_pcm_plugin_write(stream->pb_pcm,buf,size); if (result != size || result < 0) { /* either the write to output device has failed or not the * full amount of bytes have been written. This usually happens * when audio routing is being changed by another thread * Use a status variable for reading the error */ snd_pcm_channel_status_t status; status.channel = SND_PCM_CHANNEL_PLAYBACK; if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) { /* Call has failed nothing we can do except log and * continue */ PJ_LOG(4,(THIS_FILE, "underrun: playback channel status error")); } else { /* The status of the error has been read * RIM say these are expected so we can "re-prepare" the stream */ PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d", status.status)); if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN || status.status == SND_PCM_STATUS_ERROR ) { if (snd_pcm_plugin_prepare (stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK) < 0) { PJ_LOG(4,(THIS_FILE, "underrun: playback channel prepare error")); } } } } tstamp.u64 += nframes; } flush_play(stream); TRACE_((THIS_FILE, "pb_thread_func: Stopped")); return PJ_SUCCESS; }
static void QSA_PlayDevice(_THIS) { snd_pcm_channel_status_t cstatus; int written; int status; int towrite; void *pcmbuffer; if ((!this->enabled) || (!this->hidden)) { return; } towrite = this->spec.size; pcmbuffer = this->hidden->pcm_buf; /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ do { written = snd_pcm_plugin_write(this->hidden->audio_handle, pcmbuffer, towrite); if (written != towrite) { /* Check if samples playback got stuck somewhere in hardware or in */ /* the audio device driver */ if ((errno == EAGAIN) && (written == 0)) { if (this->hidden->timeout_on_wait != 0) { SDL_SetError("QSA: buffer playback timeout"); return; } } /* Check for errors or conditions */ if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { /* Let a little CPU time go by and try to write again */ SDL_Delay(1); /* if we wrote some data */ towrite -= written; pcmbuffer += written * this->spec.channels; continue; } else { if ((errno == EINVAL) || (errno == EIO)) { SDL_memset(&cstatus, 0, sizeof(cstatus)); if (!this->hidden->iscapture) { cstatus.channel = SND_PCM_CHANNEL_PLAYBACK; } else { cstatus.channel = SND_PCM_CHANNEL_CAPTURE; } status = snd_pcm_plugin_status(this->hidden->audio_handle, &cstatus); if (status < 0) { QSA_SetError("snd_pcm_plugin_status", status); return; } if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) { if (!this->hidden->iscapture) { status = snd_pcm_plugin_prepare(this->hidden-> audio_handle, SND_PCM_CHANNEL_PLAYBACK); } else { status = snd_pcm_plugin_prepare(this->hidden-> audio_handle, SND_PCM_CHANNEL_CAPTURE); } if (status < 0) { QSA_SetError("snd_pcm_plugin_prepare", status); return; } } continue; } else { return; } } } else { /* we wrote all remaining data */ towrite -= written; pcmbuffer += written * this->spec.channels; } } while ((towrite > 0) && (this->enabled)); /* If we couldn't write, assume fatal error for now */ if (towrite != 0) { this->enabled = 0; } }
static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) { qsa_data* data=(qsa_data*)device->ExtraData; char* read_ptr; snd_pcm_channel_status_t status; fd_set rfds; int selectret; struct timeval timeout; int bytes_read; ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); ALint len=samples*frame_size; int rstatus; read_ptr=buffer; while (len>0) { FD_ZERO(&rfds); FD_SET(data->audio_fd, &rfds); timeout.tv_sec=2; timeout.tv_usec=0; /* Select also works like time slice to OS */ bytes_read=0; selectret=select(data->audio_fd+1, &rfds, NULL, NULL, &timeout); switch (selectret) { case -1: aluHandleDisconnect(device); return ALC_INVALID_DEVICE; case 0: break; default: if (FD_ISSET(data->audio_fd, &rfds)) { bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len); break; } break; } if (bytes_read<=0) { if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) { continue; } memset(&status, 0, sizeof (status)); status.channel=SND_PCM_CHANNEL_CAPTURE; snd_pcm_plugin_status(data->pcmHandle, &status); /* we need to reinitialize the sound channel if we've overrun the buffer */ if ((status.status==SND_PCM_STATUS_OVERRUN) || (status.status==SND_PCM_STATUS_READY)) { if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) { ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); aluHandleDisconnect(device); return ALC_INVALID_DEVICE; } snd_pcm_capture_go(data->pcmHandle); } } else { read_ptr+=bytes_read; len-=bytes_read; } } return ALC_NO_ERROR; }
static void NTO_PlayAudio(_THIS) { int written, rval; int towrite; #ifdef DEBUG_AUDIO fprintf(stderr, "NTO_PlayAudio\n"); #endif if( !this->enabled) return; towrite = pcm_len; /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ do { written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite); #ifdef DEBUG_AUDIO fprintf(stderr, "NTO_PlayAudio: written = %d towrite = %d\n",written,towrite); #endif if (written != towrite) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { SDL_Delay(1); /* Let a little CPU time go by and try to write again */ #ifdef DEBUG_AUDIO fprintf(stderr, "errno == EAGAIN written %d\n", written); towrite -= written; //we wrote some data #endif continue; } else if((errno == EINVAL) || (errno == EIO)) { #ifdef DEBUG_AUDIO if(errno == EIO) fprintf(stderr,"snd_pcm_plugin_write failed EIO: %s\n", snd_strerror(written)); if(errno == EINVAL) fprintf(stderr,"snd_pcm_plugin_write failed EINVAL: %s\n", snd_strerror(written)); #endif memset(&cstatus, 0, sizeof(cstatus)); if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 ) { #ifdef DEBUG_AUDIO fprintf(stderr, "snd_pcm_plugin_status failed %s\n",snd_strerror(rval)); #endif SDL_SetError("snd_pcm_plugin_status failed: %s\n", snd_strerror(rval)); return; } if ( (cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY) ) { #ifdef DEBUG_AUDIO fprintf(stderr, "buffer underrun\n"); #endif if ( (rval = snd_pcm_plugin_prepare (audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0 ) { #ifdef DEBUG_AUDIO fprintf(stderr, "NTO_PlayAudio: prepare failed %s\n",snd_strerror(rval)); #endif SDL_SetError("snd_pcm_plugin_prepare failed: %s\n",snd_strerror(rval) ); return; } } continue; } else { #ifdef DEBUG_AUDIO fprintf(stderr, "NTO_PlayAudio: snd_pcm_plugin_write failed unknown errno %d %s\n",errno, snd_strerror(rval)); #endif return; } } else { towrite -= written; //we wrote all remaining data } } while ( (towrite > 0) && (this->enabled) ); /* If we couldn't write, assume fatal error for now */ if ( towrite != 0 ) { this->enabled = 0; } return; }
static int ca_thread_func (void *arg) { struct bb10_stream* stream = (struct bb10_stream *) arg; int size = stream->ca_buf_size; unsigned long nframes = stream->ca_frames; void *user_data = stream->user_data; /* Buffer to fill for PJMEDIA */ char *buf = stream->ca_buf; pj_timestamp tstamp; int result; struct sched_param param; pthread_t *thid; TRACE_((THIS_FILE, "ca_thread_func: size = %d ", size)); thid = (pthread_t*) pj_thread_get_os_handle (pj_thread_this()); param.sched_priority = sched_get_priority_max (SCHED_RR); result = pthread_setschedparam (*thid, SCHED_RR, ¶m); if (result) { if (result == EPERM) { PJ_LOG (4,(THIS_FILE, "Unable to increase thread priority, " "root access needed.")); } else { PJ_LOG (4,(THIS_FILE, "Unable to increase thread priority, " "error: %d", result)); } } pj_bzero (buf, size); tstamp.u64 = 0; /* Final init now the thread has started */ if ((result = snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE)) < 0) { close_capture_pcm(stream); TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result)); return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; pj_bzero (buf, size); result = snd_pcm_plugin_read(stream->ca_pcm, buf,size); if(result <0 || result != size) { snd_pcm_channel_status_t status; if (snd_pcm_plugin_status (stream->ca_pcm, &status) < 0) { PJ_LOG (4,(THIS_FILE, "overrun: capture channel status " "error")); continue; } if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_OVERRUN) { if (snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE) < 0) { PJ_LOG (4,(THIS_FILE, "overrun: capture channel prepare " "error")); continue; } } } if (stream->quit) break; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void *) buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; result = stream->ca_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; tstamp.u64 += nframes; } flush_capture(stream); close_capture_pcm(stream); TRACE_((THIS_FILE, "ca_thread_func: Stopped")); return PJ_SUCCESS; }
/** * Play audio received from PJMEDIA */ static int pb_thread_func (void *arg) { struct bb10_stream* stream = (struct bb10_stream *) arg; /* Handle from bb10_open_playback */ /* Will be 640 */ int size = stream->pb_buf_size; /* 160 frames for 20ms */ unsigned long nframes = stream->pb_frames; void *user_data = stream->user_data; char *buf = stream->pb_buf; pj_timestamp tstamp; int result = 0; pj_bzero (buf, size); tstamp.u64 = 0; TRACE_((THIS_FILE, "pb_thread_func: size = %d ", size)); /* Do the final initialization now the thread has started. */ if ((result = snd_pcm_plugin_prepare(stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) { close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result)); return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; /* pointer to buffer filled by PJMEDIA */ frame.buf = buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; result = stream->pb_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero (buf, size); /* Write 640 to play unit */ result = snd_pcm_plugin_write(stream->pb_pcm,buf,size); if (result != size || result < 0) { snd_pcm_channel_status_t status; if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) { PJ_LOG(4,(THIS_FILE, "underrun: playback channel status error")); continue; } if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN) { if (snd_pcm_plugin_prepare (stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK) < 0) { PJ_LOG(4,(THIS_FILE, "underrun: playback channel prepare error")); continue; } } TRACE_((THIS_FILE, "pb_thread_func failed write = %d", result)); } tstamp.u64 += nframes; } flush_play(stream); close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func: Stopped")); return PJ_SUCCESS; }
void AudioDriverBB10::thread_func(void *p_udata) { AudioDriverBB10 *ad = (AudioDriverBB10 *)p_udata; int channels = speaker_mode; int frame_count = ad->sample_buf_count / channels; int bytes_out = frame_count * channels * 2; while (!ad->exit_thread) { if (!ad->active) { for (int i = 0; i < ad->sample_buf_count; i++) { ad->samples_out[i] = 0; }; } else { ad->lock(); ad->audio_server_process(frame_count, ad->samples_in); ad->unlock(); for (int i = 0; i < frame_count * channels; i++) { ad->samples_out[i] = ad->samples_in[i] >> 16; } }; int todo = bytes_out; int total = 0; while (todo) { uint8_t *src = (uint8_t *)ad->samples_out; int wrote = snd_pcm_plugin_write(ad->pcm_handle, (void *)(src + total), todo); if (wrote < 0) { // error? break; }; total += wrote; todo -= wrote; if (wrote < todo) { if (ad->thread_exited) { break; }; printf("pcm_write underrun %i, errno %i\n", (int)ad->thread_exited, errno); snd_pcm_channel_status_t status; zeromem(&status, sizeof(status)); // put in non-blocking mode snd_pcm_nonblock_mode(ad->pcm_handle, 1); status.channel = SND_PCM_CHANNEL_PLAYBACK; int ret = snd_pcm_plugin_status(ad->pcm_handle, &status); //printf("status return %i, %i, %i, %i, %i\n", ret, errno, status.status, SND_PCM_STATUS_READY, SND_PCM_STATUS_UNDERRUN); snd_pcm_nonblock_mode(ad->pcm_handle, 0); if (ret < 0) { break; }; if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN) { snd_pcm_plugin_prepare(ad->pcm_handle, SND_PCM_CHANNEL_PLAYBACK); } else { break; }; }; }; }; snd_pcm_plugin_flush(ad->pcm_handle, SND_PCM_CHANNEL_PLAYBACK); ad->thread_exited = true; printf("**************** audio thread exit\n"); };
int main(int argc, char **argv) { FILE *file; int samples; char *sample_buffer; int rtn, final_return_code = -1, exit_application = 0; int bsize, bytes_read, total_written = 0; fd_set rfds, wfds; char input_file[PATH_MAX]; char cwd[PATH_MAX]; /* * Before we can listen for events from the BlackBerry Tablet OS platform * services, we need to initialize the BPS infrastructure */ bps_initialize(); if (setup_screen() != EXIT_SUCCESS) { printf("Unable to set up the screen. Exiting."); exit(-1); } /* * Once the BPS infrastructure has been initialized we can register for * events from the various BlackBerry Tablet OS platform services. The * Navigator service manages and delivers application life cycle and * visibility events. * * For this sample, we request Navigator events so we can track when * the system is terminating the application (NAVIGATOR_EXIT event). * This allows us to clean up application resources. * * We request Audio Device events because we want to make sure that * we properly handle changes in audio output. * * We request dialog events to properly initialize the dialog * subsystem in order to display status and error messages. */ if (BPS_SUCCESS != navigator_request_events(0)) { fprintf(stderr, "Error requesting navigator events: %s", strerror(errno)); exit(-1); } if (BPS_SUCCESS != dialog_request_events(0)) { fprintf(stderr, "Error requesting dialog events: %s", strerror(errno)); exit(-1); } if (BPS_SUCCESS != audiodevice_request_events(0)) { fprintf(stderr, "Error requesting audio device events: %s", strerror(errno)); exit(-1); } /* * Create and display the dialog. */ create_dialog(); /* * Open and check the input file. */ getcwd(cwd, PATH_MAX); rtn = snprintf(input_file, PATH_MAX, "%s/%s", cwd, WAV_RELATIVE_PATH); if (rtn > PATH_MAX - 1) { err("File name and path too long"); goto fail1; } if ((file = fopen(input_file, "r")) == 0) { err("File open failed"); goto fail1; } if (check_hdr(file) == -1) { err("check_hdr failed"); goto fail2; } /* * Parse the headers in the wav file to figure out what kind of data we * are dealing with in the file. */ samples = find_tag(file, "fmt "); fread(&wav_header, sizeof(wav_header), 1, file); fseek(file,(samples - sizeof(wave_hdr)), SEEK_CUR); sample_rate = ENDIAN_LE32(wav_header.samples_per_sec); sample_channels = ENDIAN_LE16(wav_header.channels); sample_bits = ENDIAN_LE16(wav_header.bits_per_sample); snprintf(msg, MSG_SIZE, "SampleRate = %d, channels = %d, SampleBits = %d\n", sample_rate, sample_channels, sample_bits); show_dialog_message(msg); if (setup_snd(NULL)) { goto fail2; } bsize = setup.buf.block.frag_size; samples = find_tag(file, "data"); sample_buffer = malloc(bsize); if (!sample_buffer) { goto fail3; } FD_ZERO(&rfds); FD_ZERO(&wfds); bytes_read = 1; while (total_written < samples && bytes_read > 0 ) { bps_event_t *event = NULL; while (BPS_SUCCESS == bps_get_event(&event, 0) && event) { /* * If it is a NAVIGATOR_EXIT event then we are done so stop * processing events, clean up and exit */ if (bps_event_get_domain(event) == navigator_get_domain()) { if (NAVIGATOR_EXIT == bps_event_get_code(event)) { exit_application = 1; goto success; } } if (bps_event_get_domain(event) == audiodevice_get_domain()) { /* * If it is a audio device event then it means a new audio device * has been enabled and a switch is required. We close the old, * open the new audio device using the path and get the card number so * that we can close and re-open the mixer with the new card * number. */ const char * audiodevice_path = audiodevice_event_get_path(event); if (NULL == audiodevice_path) { snprintf(msg, MSG_SIZE, "audiodevice_event_get_path failed: %s\n", snd_strerror(rtn)); show_dialog_message(msg); goto fail5; } if ((rtn = snd_mixer_close(mixer_handle)) < 0) { snprintf(msg, MSG_SIZE, "snd_mixer_close failed: %s\n", snd_strerror(rtn)); show_dialog_message(msg); goto fail4; } if ((rtn = snd_pcm_close(pcm_handle)) < 0) { snprintf(msg, MSG_SIZE, "snd_pcm_close failed: %s\n", snd_strerror(rtn)); show_dialog_message(msg); goto fail3; } if (setup_snd(audiodevice_path)) { /* * setup_snd() closes pcm and mixer handles in the case of error so we * skip clean up of the handles in the case of failure. */ goto fail3; } } } if (tcgetpgrp(0) == getpid()) FD_SET(STDIN_FILENO, &rfds); FD_SET(snd_mixer_file_descriptor(mixer_handle), &rfds); FD_SET(snd_pcm_file_descriptor(pcm_handle, SND_PCM_CHANNEL_PLAYBACK), &wfds); rtn = max(snd_mixer_file_descriptor(mixer_handle), snd_pcm_file_descriptor(pcm_handle, SND_PCM_CHANNEL_PLAYBACK)); if (select(rtn + 1, &rfds, &wfds, NULL, NULL) == -1) { err("select"); goto fail5; } if (FD_ISSET(snd_pcm_file_descriptor(pcm_handle, SND_PCM_CHANNEL_PLAYBACK), &wfds)) { snd_pcm_channel_status_t status; int written = 0; if ((bytes_read = fread(sample_buffer, 1, min(samples - total_written, bsize), file)) <= 0) continue; written = snd_pcm_plugin_write(pcm_handle, sample_buffer, bytes_read); if (written < bytes_read) { memset(&status, 0, sizeof(status)); status.channel = SND_PCM_CHANNEL_PLAYBACK; if (snd_pcm_plugin_status(pcm_handle, &status) < 0) { show_dialog_message("underrun: playback channel status error\n"); goto fail5; } if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN) { if (snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0) { show_dialog_message("underrun: playback channel prepare error\n"); goto fail5; } } if (written < 0) written = 0; written += snd_pcm_plugin_write(pcm_handle, sample_buffer + written, bytes_read - written); } total_written += written; } } success: bytes_read = snd_pcm_plugin_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK); final_return_code = 0; /* * there are return codes to these close calls, but we would do the same * thing regardless of error or success so we do not check the return codes. */ fail5: snd_mixer_close(mixer_handle); fail4: snd_pcm_close(pcm_handle); fail3: free(sample_buffer); sample_buffer = NULL; fail2: fclose(file); fail1: while (!exit_application) { /* * Something went wrong so there is probably an error message and * we don't want to exit right away because we want the user to see * the message in the dialog. * * Using a negative timeout (-1) in the call to bps_get_event(...) * ensures that we don't busy wait by blocking until an event is * available. */ bps_event_t *event = NULL; bps_get_event(&event, -1); if (event) { /* * If it is a NAVIGATOR_EXIT event then we are done so stop * processing events, clean up and exit */ if (bps_event_get_domain(event) == navigator_get_domain()) { if (NAVIGATOR_EXIT == bps_event_get_code(event)) { exit_application = 1; } } } } /* * Destroy the dialog, if it exists and cleanup screen resources. */ destroy_dialog(); cleanup_screen(); bps_shutdown(); return final_return_code; }
/* public methods (static but exported through the sysdep_dsp or plugin struct) */ static void *alsa_dsp_create(const void *flags) { int i, j; // audio_buf_info info; struct alsa_dsp_priv_data *priv = NULL; struct sysdep_dsp_struct *dsp = NULL; const struct sysdep_dsp_create_params *params = flags; const char *device = params->device; int err; int bytespersample; fprintf(stderr,"info: dsp_create called\n"); /* allocate the dsp struct */ if (!(dsp = calloc(1, sizeof(struct sysdep_dsp_struct)))) { fprintf(stderr, "error: malloc failed for struct sysdep_dsp_struct\n"); return NULL; } /* alloc private data */ if(!(priv = calloc(1, sizeof(struct alsa_dsp_priv_data)))) { fprintf(stderr, "error: malloc failed for struct dsp_priv_data\n"); alsa_dsp_destroy(dsp); return NULL; } /* fill in the functions and some data */ dsp->_priv = priv; dsp->get_freespace = alsa_dsp_get_freespace; dsp->write = alsa_dsp_write; dsp->destroy = alsa_dsp_destroy; dsp->hw_info.type = params->type; dsp->hw_info.samplerate = params->samplerate; priv->audio_dev.bMute = 0; priv->audio_dev.m_AudioHandle = NULL; priv->audio_dev.m_MixerHandle = NULL; priv->audio_dev.m_Acard = 0; priv->audio_dev.m_Adevice = 0; if (preferred_device) { if((err = snd_pcm_open_preferred(&(priv->audio_dev.m_AudioHandle), &priv->audio_dev.m_Acard, &priv->audio_dev.m_Adevice, SND_PCM_OPEN_PLAYBACK)) < 0) { fprintf(stderr,"info: snd_pcm_open_preferred failed: %s \n", snd_strerror(err)); alsa_dsp_destroy(dsp); return NULL; } } else { fprintf(stderr,"info: audio is using primary device\n"); if((err = snd_pcm_open(&(priv->audio_dev.m_AudioHandle), 0, 0, SND_PCM_OPEN_PLAYBACK)) < 0) { fprintf(stderr,"info: snd_pcm_open failed: %s \n", snd_strerror(err)); alsa_dsp_destroy(dsp); return NULL; } } memset (&(priv->audio_dev.m_Achaninfo), 0, sizeof (priv->audio_dev.m_Achaninfo)); priv->audio_dev.m_Achaninfo.channel = SND_PCM_CHANNEL_PLAYBACK; if ((err = snd_pcm_plugin_info (priv->audio_dev.m_AudioHandle, &(priv->audio_dev.m_Achaninfo))) < 0) { fprintf (stderr, "info: snd_pcm_plugin_info failed: %s\n", snd_strerror (err)); alsa_dsp_destroy(dsp); return NULL; } //needed to enable the count status parameter, mmap plugin disables this if((err = snd_plugin_set_disable(priv->audio_dev.m_AudioHandle, PLUGIN_DISABLE_MMAP)) < 0) { fprintf (stderr, "info: snd_plugin_set_disable failed: %s\n", snd_strerror (err)); alsa_dsp_destroy(dsp); return NULL; } /* calculate and set the fragsize & number of frags */ /* fragsize (as power of 2) */ i = 8; if (dsp->hw_info.type & SYSDEP_DSP_16BIT) i++; if (dsp->hw_info.type & SYSDEP_DSP_STEREO) i++; i += dsp->hw_info.samplerate / 22000; /* number of frags */ j = ((dsp->hw_info.samplerate * alsa_dsp_bytes_per_sample[dsp->hw_info.type] * params->bufsize) / (0x01 << i)) + 1; bytespersample=1; // dsp->hw_info.type &= ~SYSDEP_DSP_16BIT; // dsp->hw_info.type &= ~SYSDEP_DSP_STEREO; if (dsp->hw_info.type & SYSDEP_DSP_16BIT) bytespersample++; if (dsp->hw_info.type & SYSDEP_DSP_STEREO) bytespersample <<= 1; memset( &(priv->audio_dev.m_Aparams), 0, sizeof(priv->audio_dev.m_Aparams)); priv->audio_dev.m_Aparams.mode = SND_PCM_MODE_BLOCK; priv->audio_dev.m_Aparams.channel = SND_PCM_CHANNEL_PLAYBACK; priv->audio_dev.m_Aparams.start_mode = SND_PCM_START_FULL; priv->audio_dev.m_Aparams.stop_mode = SND_PCM_STOP_ROLLOVER; #if 0 priv->audio_dev.m_Aparams.buf.stream.queue_size = 512 * bytespersample; priv->audio_dev.m_Aparams.buf.stream.fill = SND_PCM_FILL_SILENCE; priv->audio_dev.m_Aparams.buf.stream.max_fill = 512 * bytespersample; #endif priv->audio_dev.m_Aparams.format.interleave = 1; priv->audio_dev.m_Aparams.format.rate = dsp->hw_info.samplerate; priv->audio_dev.m_Aparams.format.voices = (dsp->hw_info.type & SYSDEP_DSP_STEREO) ? 2 : 1; priv->audio_dev.m_Aparams.buf.block.frag_size = 1000; priv->audio_dev.m_Aparams.buf.block.frags_min = 1; priv->audio_dev.m_Aparams.buf.block.frags_max = 5; priv->audio_dev.m_BytesPerSample = bytespersample; priv->audio_dev.m_Aparams.format.format = #ifdef LSB_FIRST (dsp->hw_info.type & SYSDEP_DSP_16BIT) ? SND_PCM_SFMT_S16_LE : SND_PCM_SFMT_U8; #else (dsp->hw_info.type & SYSDEP_DSP_16BIT) ? SND_PCM_SFMT_S16_BE : SND_PCM_SFMT_U8; #endif if ((err = snd_pcm_plugin_params (priv->audio_dev.m_AudioHandle, &(priv->audio_dev.m_Aparams))) < 0) { fprintf (stderr, "info: snd_pcm_plugin_params failed: %s\n", snd_strerror (err)); alsa_dsp_destroy(dsp); return NULL; } if ((err = snd_pcm_plugin_prepare (priv->audio_dev.m_AudioHandle, SND_PCM_CHANNEL_PLAYBACK)) < 0) { fprintf (stderr, "warning: snd_pcm_plugin_prepare failed: %s\n", snd_strerror (err)); } memset (&(priv->audio_dev.m_Asetup), 0, sizeof (priv->audio_dev.m_Asetup)); priv->audio_dev.m_Asetup.channel = SND_PCM_CHANNEL_PLAYBACK; if ((err = snd_pcm_plugin_setup (priv->audio_dev.m_AudioHandle, &(priv->audio_dev.m_Asetup))) < 0) { fprintf (stderr, "warning: snd_pcm_plugin_setup failed: %s\n", snd_strerror (err)); alsa_dsp_destroy(dsp); return NULL; } memset (&(priv->audio_dev.m_Astatus), 0, sizeof (priv->audio_dev.m_Astatus)); priv->audio_dev.m_Astatus.channel = SND_PCM_CHANNEL_PLAYBACK; if ((err = snd_pcm_plugin_status (priv->audio_dev.m_AudioHandle, &(priv->audio_dev.m_Astatus))) < 0) { fprintf (stderr, "warning: snd_pcm_plugin_status failed: %s\n", snd_strerror (err)); } dsp->hw_info.bufsize = priv->audio_dev.m_Asetup.buf.stream.queue_size / alsa_dsp_bytes_per_sample[dsp->hw_info.type]; #if 0 if ((err=snd_pcm_nonblock_mode(priv->audio_dev.m_AudioHandle, 1))<0) { fprintf(stderr, "error: error with non block mode: %s\n", snd_strerror (err)); } #endif return dsp; }
static int ca_thread_func (void *arg) { struct bb10_stream* stream = (struct bb10_stream *) arg; int size = stream->ca_buf_size; unsigned long nframes = stream->ca_frames; void *user_data = stream->user_data; /* Buffer to fill for PJMEDIA */ char *buf = stream->ca_buf; pj_timestamp tstamp; int result; int policy; struct sched_param param; TRACE_((THIS_FILE, "ca_thread_func: size = %d ", size)); if (pthread_getschedparam(pthread_self(), &policy, ¶m) == 0) { param.sched_priority = 18; pthread_setschedparam (pthread_self(), policy, ¶m); } pj_bzero (buf, size); tstamp.u64 = 0; /* Final init now the thread has started */ if ((result = snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE)) < 0) { TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result)); return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; //pj_bzero (buf, size); /* read the input device */ result = snd_pcm_plugin_read(stream->ca_pcm, buf,size); if(result <0 || result != size) { /* We expect result to be size (640) * It's not so we have to read the status error and "prepare" * the channel. This usually happens when output audio routing * has been changed by another thread. * We won't "continue", instead just do what we can and leave * the end of the loop to write what's in the buffer. Not entirely * correct but saves a potential underrun in PJMEDIA */ PJ_LOG (4,(THIS_FILE, "snd_pcm_plugin_read ERROR read = %d required = %d", result,size)); snd_pcm_channel_status_t status; status.channel = SND_PCM_CHANNEL_CAPTURE; if ((result = snd_pcm_plugin_status (stream->ca_pcm, &status)) < 0) { /* Should not fail but all we can do is continue */ PJ_LOG(4,(THIS_FILE, "capture: snd_pcm_plugin_status ret = %d", result)); } else { /* RIM say these are the errors that we should "prepare" * after */ if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_OVERRUN || status.status == SND_PCM_STATUS_ERROR) { if (snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE) < 0) { PJ_LOG (4,(THIS_FILE, "overrun: capture channel prepare error")); } } } } if (stream->quit) break; /* Write the capture audio data to PJMEDIA */ frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void *) buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; result = stream->ca_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; tstamp.u64 += nframes; } flush_capture(stream); TRACE_((THIS_FILE, "ca_thread_func: Stopped")); return PJ_SUCCESS; }
FORCE_ALIGN static int qsa_proc_playback(void* ptr) { ALCdevice* device=(ALCdevice*)ptr; qsa_data* data=(qsa_data*)device->ExtraData; char* write_ptr; int avail; snd_pcm_channel_status_t status; struct sched_param param; fd_set wfds; int selectret; struct timeval timeout; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); /* Increase default 10 priority to 11 to avoid jerky sound */ SchedGet(0, 0, ¶m); param.sched_priority=param.sched_curpriority+1; SchedSet(0, 0, SCHED_NOCHANGE, ¶m); ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); while (!data->killNow) { ALint len=data->size; write_ptr=data->buffer; avail=len/frame_size; aluMixData(device, write_ptr, avail); while (len>0 && !data->killNow) { FD_ZERO(&wfds); FD_SET(data->audio_fd, &wfds); timeout.tv_sec=2; timeout.tv_usec=0; /* Select also works like time slice to OS */ selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout); switch (selectret) { case -1: aluHandleDisconnect(device); return 1; case 0: break; default: if (FD_ISSET(data->audio_fd, &wfds)) { break; } break; } int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len); if (wrote<=0) { if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) { continue; } memset(&status, 0, sizeof (status)); status.channel=SND_PCM_CHANNEL_PLAYBACK; snd_pcm_plugin_status(data->pcmHandle, &status); /* we need to reinitialize the sound channel if we've underrun the buffer */ if ((status.status==SND_PCM_STATUS_UNDERRUN) || (status.status==SND_PCM_STATUS_READY)) { if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0) { aluHandleDisconnect(device); break; } } } else { write_ptr+=wrote; len-=wrote; } } } return 0; }
static void NTO_PlayAudio(_THIS) { int written, rval; int towrite; void* pcmbuffer; if (!this->enabled) { return; } towrite = this->spec.size; pcmbuffer = pcm_buf; /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ do { written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite); if (written != towrite) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { /* Let a little CPU time go by and try to write again */ SDL_Delay(1); /* if we wrote some data */ towrite -= written; pcmbuffer += written * this->spec.channels; continue; } else { if ((errno == EINVAL) || (errno == EIO)) { SDL_memset(&cstatus, 0, sizeof(cstatus)); cstatus.channel = SND_PCM_CHANNEL_PLAYBACK; if ((rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0) { SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_status failed: %s\n", snd_strerror(rval)); return; } if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) { if ((rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) { SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval)); return; } } continue; } else { return; } } } else { /* we wrote all remaining data */ towrite -= written; pcmbuffer += written * this->spec.channels; } } while ((towrite > 0) && (this->enabled)); /* If we couldn't write, assume fatal error for now */ if (towrite != 0) { this->enabled = 0; } return; }
int main (int argc, char **argv) { int card = -1; int dev = 0; snd_pcm_t *pcm_handle; FILE *file; wave_hdr wav_header; int samples; int sample_rate; int sample_channels; int sample_bits; char *sample_buffer; int fragsize = -1; int verbose = 0; int rtn; int final_return_code = -1; snd_pcm_channel_info_t pi; snd_mixer_t *mixer_handle; snd_mixer_group_t group; snd_pcm_channel_params_t pp; snd_pcm_channel_setup_t setup; int bsize, bytes_read, total_written = 0; fd_set rfds, wfds; uint32_t voice_mask[] = { 0, 0, 0, 0 }; snd_pcm_voice_conversion_t voice_conversion; int voice_override = 0; int num_frags = -1; char input_file[PATH_MAX]; char cwd[PATH_MAX]; screen_context_t screen_cxt; screen_window_t screen_win; screen_buffer_t screen_buf; int screen_fill_attribs[] = { SCREEN_BLIT_COLOR, COLOR_PURPLE, SCREEN_BLIT_END }; int screen_dirty[4] = { 0, 0, 1024, 600 }; //start with sane default values int idle_mode = SCREEN_IDLE_MODE_KEEP_AWAKE; int usage = SCREEN_USAGE_NATIVE; if (screen_create_context(&screen_cxt, 0) != 0) { return err("failed to create context"); } if (screen_create_window(&screen_win, screen_cxt) != 0) { err("failed to create window"); goto fail1; } if (screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_USAGE, &usage) != 0) { err("failed to set native usage mode"); goto fail2; } if (screen_create_window_buffers(screen_win, 1) != 0) { err("failed to set native usage mode"); goto fail2; } if(screen_get_window_property_pv(screen_win, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&screen_buf) != 0) { err("failed to get screen buffer"); goto fail2; } if (screen_fill(screen_cxt, screen_buf, screen_fill_attribs) != 0) { err("failed to fill the screen"); goto fail3; } if (screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, screen_dirty+2) != 0) { err("failed to get window size"); goto fail3; } if (screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_IDLE_MODE, &idle_mode) != 0) { err("failed to set idle mode"); goto fail3; } if (screen_post_window(screen_win, screen_buf, 1, screen_dirty, 0) != 0) { err("failed to post the window"); goto fail3; } if ((rtn = snd_pcm_open_preferred (&pcm_handle, &card, &dev, SND_PCM_OPEN_PLAYBACK)) < 0) { err ("device open"); goto fail3; } getcwd(cwd, PATH_MAX); rtn = snprintf(input_file, PATH_MAX, "%s%s", cwd, WAV_RELATIVE_PATH); if (rtn > PATH_MAX - 1) { err ("File name and path too long"); goto fail4; } if ((file = fopen (input_file, "r")) == 0) { err ("File open failed"); goto fail4; } if (check_hdr (file) == -1) { err ("check_hdr failed"); goto fail5; } samples = find_tag (file, "fmt "); fread (&wav_header, sizeof (wav_header), 1, file); fseek (file, (samples - sizeof (wave_hdr)), SEEK_CUR); sample_rate = ENDIAN_LE32 (wav_header.samples_per_sec); sample_channels = ENDIAN_LE16 (wav_header.channels); sample_bits = ENDIAN_LE16 (wav_header.bits_per_sample); printf ("SampleRate = %d, channels = %d, SampleBits = %d\n", sample_rate, sample_channels, sample_bits); /* disabling mmap is not actually required in this example but it is included to * demonstrate how it is used when it is required. */ if ((rtn = snd_pcm_plugin_set_disable (pcm_handle, PLUGIN_DISABLE_MMAP)) < 0) { fprintf (stderr, "snd_pcm_plugin_set_disable failed: %s\n", snd_strerror (rtn)); goto fail5; } memset (&pi, 0, sizeof (pi)); pi.channel = SND_PCM_CHANNEL_PLAYBACK; if ((rtn = snd_pcm_plugin_info (pcm_handle, &pi)) < 0) { fprintf (stderr, "snd_pcm_plugin_info failed: %s\n", snd_strerror (rtn)); goto fail5; } memset (&pp, 0, sizeof (pp)); pp.mode = SND_PCM_MODE_BLOCK; pp.channel = SND_PCM_CHANNEL_PLAYBACK; pp.start_mode = SND_PCM_START_FULL; pp.stop_mode = SND_PCM_STOP_STOP; pp.buf.block.frag_size = pi.max_fragment_size; if (fragsize != -1) { pp.buf.block.frag_size = fragsize; } pp.buf.block.frags_max = num_frags; pp.buf.block.frags_min = 1; pp.format.interleave = 1; pp.format.rate = sample_rate; pp.format.voices = sample_channels; if (ENDIAN_LE16 (wav_header.format_tag) == 6) pp.format.format = SND_PCM_SFMT_A_LAW; else if (ENDIAN_LE16 (wav_header.format_tag) == 7) pp.format.format = SND_PCM_SFMT_MU_LAW; else if (sample_bits == 8) pp.format.format = SND_PCM_SFMT_U8; else if (sample_bits == 24) pp.format.format = SND_PCM_SFMT_S24; else pp.format.format = SND_PCM_SFMT_S16_LE; strcpy (pp.sw_mixer_subchn_name, "Wave playback channel"); if ((rtn = snd_pcm_plugin_params (pcm_handle, &pp)) < 0) { fprintf (stderr, "snd_pcm_plugin_params failed: %s\n", snd_strerror (rtn)); goto fail5; } if ((rtn = snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) { fprintf (stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rtn)); goto fail5; } if (voice_override) { snd_pcm_plugin_get_voice_conversion (pcm_handle, SND_PCM_CHANNEL_PLAYBACK, &voice_conversion); voice_conversion.matrix[0] = voice_mask[0]; voice_conversion.matrix[1] = voice_mask[1]; voice_conversion.matrix[2] = voice_mask[2]; voice_conversion.matrix[3] = voice_mask[3]; snd_pcm_plugin_set_voice_conversion (pcm_handle, SND_PCM_CHANNEL_PLAYBACK, &voice_conversion); } memset (&setup, 0, sizeof (setup)); memset (&group, 0, sizeof (group)); setup.channel = SND_PCM_CHANNEL_PLAYBACK; setup.mixer_gid = &group.gid; if ((rtn = snd_pcm_plugin_setup (pcm_handle, &setup)) < 0) { fprintf (stderr, "snd_pcm_plugin_setup failed: %s\n", snd_strerror (rtn)); goto fail5; } printf ("Format %s \n", snd_pcm_get_format_name (setup.format.format)); printf ("Frag Size %d \n", setup.buf.block.frag_size); printf ("Total Frags %d \n", setup.buf.block.frags); printf ("Rate %d \n", setup.format.rate); printf ("Voices %d \n", setup.format.voices); bsize = setup.buf.block.frag_size; if (group.gid.name[0] == 0) { fprintf (stderr, "Mixer Pcm Group [%s] Not Set \n", group.gid.name); goto fail5; } printf ("Mixer Pcm Group [%s]\n", group.gid.name); if ((rtn = snd_mixer_open (&mixer_handle, card, setup.mixer_device)) < 0) { fprintf (stderr, "snd_mixer_open failed: %s\n", snd_strerror (rtn)); goto fail5; } samples = find_tag (file, "data"); sample_buffer = malloc (bsize); FD_ZERO (&rfds); FD_ZERO (&wfds); bytes_read = 1; while (total_written < samples && bytes_read > 0) { if (tcgetpgrp (0) == getpid ()) FD_SET (STDIN_FILENO, &rfds); FD_SET (snd_mixer_file_descriptor (mixer_handle), &rfds); FD_SET (snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_PLAYBACK), &wfds); rtn = max (snd_mixer_file_descriptor (mixer_handle), snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_PLAYBACK)); if (select (rtn + 1, &rfds, &wfds, NULL, NULL) == -1) { err ("select"); goto fail6; } if (FD_ISSET (snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_PLAYBACK), &wfds)) { snd_pcm_channel_status_t status; int written = 0; if ((bytes_read = fread (sample_buffer, 1, min (samples - total_written, bsize), file)) <= 0) continue; written = snd_pcm_plugin_write (pcm_handle, sample_buffer, bytes_read); if (verbose) printf ("bytes written = %d \n", written); if (written < bytes_read) { memset (&status, 0, sizeof (status)); status.channel = SND_PCM_CHANNEL_PLAYBACK; if (snd_pcm_plugin_status (pcm_handle, &status) < 0) { fprintf (stderr, "underrun: playback channel status error\n"); goto fail6; } if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN) { if (snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0) { fprintf (stderr, "underrun: playback channel prepare error\n"); goto fail6; } } if (written < 0) written = 0; written += snd_pcm_plugin_write (pcm_handle, sample_buffer + written, bytes_read - written); } total_written += written; } } bytes_read = snd_pcm_plugin_flush (pcm_handle, SND_PCM_CHANNEL_PLAYBACK); final_return_code = 0; fail6: rtn = snd_mixer_close (mixer_handle); fail5: fclose (file); fail4: rtn = snd_pcm_close (pcm_handle); fail3: screen_destroy_buffer(screen_buf); fail2: screen_destroy_window(screen_win); fail1: screen_destroy_context(screen_cxt); return final_return_code; }
static void *processTones(void *dummy) { bool done; int error; int length; int index; Tone *tone, *nextTone; snd_pcm_channel_status_t status; //pthread_setname_np(pthread_self(), "tonegen"); // Main loop. Wait for audio driver to need more data while( live ) { pthread_mutex_lock( &toneMutex ); if( tone_count == 0 ) { // Wait for a tone command to come in snd_pcm_playback_flush( playback_handle ); snd_pcm_plugin_prepare( playback_handle, SND_PCM_CHANNEL_PLAYBACK ); pthread_cond_wait( &condvar_newtone, &toneMutex ); } if( tone_count == 0 ) { pthread_mutex_unlock( &toneMutex ); continue; } pthread_mutex_unlock( &toneMutex ); // Send tone data FD_ZERO( &write_handles ); FD_SET( sample_handle, &write_handles ); error = select(sample_handle + 1, NULL, &write_handles, NULL, NULL); if( error < 0 ) { // Select failed. slogf( _SLOG_SETCODE(_SLOGC_AUDIO, 0), _SLOG_CRITICAL, "processTones select failed %d (%d)\n", error, errno); pthread_mutex_unlock( &toneMutex ); live = DEAD; break; } length = 0; // This should always be true if( FD_ISSET( sample_handle, &write_handles ) ) { int active_count = 0; for(tone = tones; tone != NULL; tone = tone->next) { done = tone->killed || (tonepos > tone->end); //fprintf (stderr,"processTone::writeTone (before active check) tone %ld tone->end %ld active %d done %d tonepos %ld \n", tone, tone->end, tone->active, done, tonepos); if( !done ) { // Write the tone error = writeTone(tone, (tone != tones)); //error = tone->generator(tone->position, length, sample_frequency, frag_buffer, &tone->data, terminating); if( error != EOK ) { done = true; } active_count++; } tone->active &= !done; //fprintf (stderr,"processTone::writeTone tone %ld tone->end %ld active %d tonepos %ld \n", tone, tone->end, tone->active, tonepos); } for(index = 0; index < frag_samples; index++) { record_buffer[index*2 ] = stage_buffer[index] / stage_samples[index]; record_buffer[index*2+1] = stage_buffer[index] / stage_samples[index]; } pthread_mutex_lock(&fillMutex); if (render_buffer != NULL) { memcpy(render_buffer, record_buffer, frame_size); } pthread_mutex_unlock(&fillMutex); tonepos += frag_samples; if (active_count > 0) { fprintf (stderr,"processTone tonepos %ld \n", tonepos); error = snd_pcm_plugin_write (playback_handle, record_buffer, frame_size); if( error != frame_size ) { memset (&status, 0, sizeof (status)); status.channel = SND_PCM_CHANNEL_PLAYBACK; if ((error = snd_pcm_plugin_status (playback_handle, &status)) < 0) { slogf( _SLOG_SETCODE(_SLOGC_AUDIO, 0), _SLOG_CRITICAL, "retrieving audio interface status failed (%s)\n", snd_strerror (error)); } else if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN || status.status == SND_PCM_STATUS_CHANGE) { if ((error = snd_pcm_plugin_prepare (playback_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) { slogf( _SLOG_SETCODE(_SLOGC_AUDIO, 0), _SLOG_CRITICAL, "cannot prepare audio interface for use (%s)\n", snd_strerror (error)); } } else { slogf( _SLOG_SETCODE(_SLOGC_AUDIO, 0), _SLOG_CRITICAL, "non-underrun write failure (%s)\n", snd_strerror (error)); } // Retry now that we're prepared error = snd_pcm_plugin_write (playback_handle, record_buffer, frame_size); } if( error != frame_size ) { slogf( _SLOG_SETCODE(_SLOGC_AUDIO, 0), _SLOG_CRITICAL, "write to audio interface failed (%s) %d\n", snd_strerror (error), error); } } } else { slogf( _SLOG_SETCODE(_SLOGC_AUDIO, 0), _SLOG_CRITICAL, "Unknown file handle activated" ); } pthread_mutex_lock( &toneMutex ); //fprintf (stderr,"processTone::delete tones %lx count %d \n", tones, tone_count); int delete_count; do { delete_count = 0; for(tone = tones; tone != NULL; tone = tone->next) { done = tone->killed || (tonepos > tone->end); if( done || !tone->active ) { // Remove the tone from the list if (tone->prev != NULL) { tone->prev->next = tone->next; } else { tones = tone->next; } if (tone->next != NULL) { tone->next->prev = tone->prev; } tone_count--; free(tone); delete_count++; fprintf (stderr,"processTone::delete tone %lx tones %lx count %d \n", tone, tones, tone_count); break; } } } while (delete_count > 0); pthread_mutex_unlock( &toneMutex ); if (tone_count == 0) { memset(record_buffer, 0, frame_size); } } return NULL; }