qint64 QAlsaAudioInput::read(char* data, qint64 len) { // Read in some audio data and write it to QIODevice, pull mode if ( !handle ) return 0; int bytesRead = 0; int bytesInRingbufferBeforeRead = ringBuffer.bytesOfDataInBuffer(); if (ringBuffer.bytesOfDataInBuffer() < len) { // bytesAvaiable is saved as a side effect of checkBytesReady(). int bytesToRead = checkBytesReady(); if (bytesToRead < 0) { // bytesAvailable as negative is error code, try to recover from it. xrun_recovery(bytesToRead); bytesToRead = checkBytesReady(); if (bytesToRead < 0) { // recovery failed must stop and set error. close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return 0; } } bytesToRead = qMin<qint64>(len, bytesToRead); bytesToRead = qMin<qint64>(ringBuffer.freeBytes(), bytesToRead); bytesToRead -= bytesToRead % period_size; int count=0; int err = 0; while(count < 5 && bytesToRead > 0) { char buffer[bytesToRead]; int chunks = bytesToRead / period_size; int frames = chunks * period_frames; if (frames > (int)buffer_frames) frames = buffer_frames; int readFrames = snd_pcm_readi(handle, buffer, frames); bytesRead = snd_pcm_frames_to_bytes(handle, readFrames); if (m_volume < 1.0f) QAudioHelperInternal::qMultiplySamples(m_volume, settings, buffer, buffer, bytesRead); if (readFrames >= 0) { ringBuffer.write(buffer, bytesRead); #ifdef DEBUG_AUDIO qDebug() << QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(bytesRead).arg(readFrames).toLatin1().constData(); #endif break; } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { errorState = QAudio::IOError; err = 0; break; } else { if(readFrames == -EPIPE) { errorState = QAudio::UnderrunError; err = snd_pcm_prepare(handle); } else if(readFrames == -ESTRPIPE) { err = snd_pcm_prepare(handle); } if(err != 0) break; } count++; } } bytesRead += bytesInRingbufferBeforeRead; if (bytesRead > 0) { // got some send it onward #ifdef DEBUG_AUDIO qDebug() << "frames to write to QIODevice = " << snd_pcm_bytes_to_frames( handle, (int)bytesRead ) << " (" << bytesRead << ") bytes"; #endif if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) return 0; if (pullMode) { qint64 l = 0; qint64 bytesWritten = 0; while (ringBuffer.bytesOfDataInBuffer() > 0) { l = audioSource->write(ringBuffer.availableData(), ringBuffer.availableDataBlockSize()); if (l > 0) { ringBuffer.readBytes(l); bytesWritten += l; } else { break; } } if (l < 0) { close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); } else if (l == 0 && bytesWritten == 0) { if (deviceState != QAudio::IdleState) { errorState = QAudio::NoError; deviceState = QAudio::IdleState; emit stateChanged(deviceState); } } else { bytesAvailable -= bytesWritten; totalTimeValue += bytesWritten; resuming = false; if (deviceState != QAudio::ActiveState) { errorState = QAudio::NoError; deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } } return bytesWritten; } else { while (ringBuffer.bytesOfDataInBuffer() > 0) { int size = ringBuffer.availableDataBlockSize(); memcpy(data, ringBuffer.availableData(), size); data += size; ringBuffer.readBytes(size); } bytesAvailable -= bytesRead; totalTimeValue += bytesRead; resuming = false; if (deviceState != QAudio::ActiveState) { errorState = QAudio::NoError; deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } return bytesRead; } } return 0; }
bool play_pcm_buffer (playable_pcm_buffer_t *buffer) { int status; snd_pcm_t *handle; snd_pcm_format_t format; snd_pcm_sframes_t frames_wrote; int frames_count; size_t bytes_handled; size_t bytes_to_write; format = determine_pcm_format (&(buffer->info)); if (format == SND_PCM_FORMAT_UNKNOWN) { fprintf (stderr, "%s: Unable to determine the beep's PCM data format.\n", progname); return false; } status = snd_pcm_open (&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); if (status < 0) { fprintf (stderr, "%s: Failed to open the playback device: %s\n", progname, snd_strerror (status)); return false; } status = snd_pcm_set_params (handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, buffer->info.channels, buffer->info.sample_rate, 1, /* soft_resample */ 0); /* latency (us).*/ if (status < 0) { fprintf (stderr, "%s: Failed to configure the playback device: %s.\n", progname, snd_strerror (status)); snd_pcm_close (handle); return false; } bytes_handled = 0; while (bytes_handled < buffer->data_len) { if (buffer->data_len - bytes_handled < BUFSIZ) bytes_to_write = buffer->data_len - bytes_handled; else bytes_to_write = BUFSIZ; frames_count = snd_pcm_bytes_to_frames (handle, bytes_to_write); frames_wrote = snd_pcm_writei (handle, buffer->data + bytes_handled, frames_count); if (frames_wrote < 0) frames_wrote = snd_pcm_recover (handle, frames_wrote, 0); if (frames_wrote < 0) { fprintf (stderr, "%s: Writing to the playback device failed: %s.\n", progname, snd_strerror (frames_wrote)); snd_pcm_close (handle); return false; } if (frames_wrote > 0 && frames_wrote < frames_count) { fprintf (stderr, "%s: Warning: Wrote only %ld frames instead of the " "expected %d to the playback device.\n", progname, frames_wrote, frames_count); } bytes_handled += bytes_to_write; } snd_pcm_drain (handle); snd_pcm_close (handle); return true; }
ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes) { AutoMutex lock(mLock); if (!mPowerLock) { acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock"); mPowerLock = true; usleep(35000); //201104011 [email protected], fix sound tick noise from global B } /* check if handle is still valid, otherwise we are coming out of standby */ if(mHandle->handle == NULL) { nsecs_t previously = systemTime(); mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode, mHandle->curChannels); nsecs_t delta = systemTime() - previously; LOGE("RE-OPEN AFTER STANDBY:: took %llu msecs\n", ns2ms(delta)); } acoustic_device_t *aDev = acoustics(); // For output, we will pass the data on to the acoustics module, but the actual // data is expected to be sent to the audio device directly as well. if (aDev && aDev->write) aDev->write(aDev, buffer, bytes); snd_pcm_sframes_t n; size_t sent = 0; status_t err; while (mHandle->handle && sent < bytes) { n = snd_pcm_writei(mHandle->handle, (char *)buffer + sent, snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent)); if (n == -EBADFD) { // Somehow the stream is in a bad state. The driver probably // has a bug and snd_pcm_recover() doesn't seem to handle this. mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode, mHandle->curChannels); if (aDev && aDev->recover) aDev->recover(aDev, n); } else if (n < 0) { if (mHandle->handle) { // snd_pcm_recover() will return 0 if successful in recovering from // an error, or -errno if the error was unrecoverable. n = snd_pcm_recover(mHandle->handle, n, 1); if (aDev && aDev->recover) aDev->recover(aDev, n); if (n) return static_cast<ssize_t>(n); } } else { if (mHandle->handle) { mFrameCount += n; sent += static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n)); } } } return sent; }
/************************************************************************** * widRecorder [internal] */ static DWORD CALLBACK widRecorder(LPVOID pmt) { WORD uDevID = (DWORD_PTR)pmt; WINE_WAVEDEV* wwi = &WInDev[uDevID]; WAVEHDR* lpWaveHdr; DWORD dwSleepTime; DWORD bytesRead; enum win_wm_message msg; DWORD_PTR param; HANDLE ev; DWORD frames_per_period; wwi->state = WINE_WS_STOPPED; InterlockedExchange((LONG*)&wwi->dwTotalRecorded, 0); wwi->lpQueuePtr = NULL; SetEvent(wwi->hStartUpEvent); /* make sleep time to be # of ms to output a period */ dwSleepTime = (wwi->dwPeriodSize * 1000) / wwi->format.Format.nAvgBytesPerSec; frames_per_period = snd_pcm_bytes_to_frames(wwi->pcm, wwi->dwPeriodSize); TRACE("sleeptime=%d ms, total buffer length=%d ms (%d bytes)\n", dwSleepTime, wwi->dwBufferSize * 1000 / wwi->format.Format.nAvgBytesPerSec, wwi->dwBufferSize); for (;;) { /* wait for dwSleepTime or an event in thread's queue */ if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING) { DWORD frames; DWORD bytes; DWORD read; lpWaveHdr = wwi->lpQueuePtr; /* read all the fragments accumulated so far */ frames = snd_pcm_avail_update(wwi->pcm); bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames); TRACE("frames = %d bytes = %d state=%d\n", frames, bytes, snd_pcm_state(wwi->pcm)); if (snd_pcm_state(wwi->pcm) == SND_PCM_STATE_XRUN) { FIXME("Recovering from XRUN!\n"); snd_pcm_prepare(wwi->pcm); frames = snd_pcm_avail_update(wwi->pcm); bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames); snd_pcm_start(wwi->pcm); snd_pcm_forward(wwi->pcm, frames - snd_pcm_bytes_to_frames(wwi->pcm, wwi->dwPeriodSize)); continue; } while (frames > 0 && wwi->lpQueuePtr) { TRACE("bytes = %d\n", bytes); if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded < bytes) { bytes = lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded; frames = snd_pcm_bytes_to_frames(wwi->pcm, bytes); } /* directly read fragment in wavehdr */ read = wwi->read(wwi->pcm, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames); bytesRead = snd_pcm_frames_to_bytes(wwi->pcm, read); TRACE("bytesRead=(%d(%d)/(%d)) -> (%d/%d)\n", bytesRead, read, frames, lpWaveHdr->dwBufferLength, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded); if (read != (DWORD) -1) { /* update number of bytes recorded in current buffer and by this device */ lpWaveHdr->dwBytesRecorded += bytesRead; InterlockedExchangeAdd((LONG*)&wwi->dwTotalRecorded, bytesRead); frames -= read; bytes -= bytesRead; /* buffer is full. notify client */ if (!snd_pcm_bytes_to_frames(wwi->pcm, lpWaveHdr->dwBytesRecorded - lpWaveHdr->dwBufferLength)) { /* must copy the value of next waveHdr, because we have no idea of what * will be done with the content of lpWaveHdr in callback */ LPWAVEHDR lpNext = lpWaveHdr->lpNext; lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; wwi->lpQueuePtr = lpNext; widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0); lpWaveHdr = lpNext; } } else { WARN("read(%s, %p, %d) failed (%d/%s)\n", wwi->pcmname, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames, frames, snd_strerror(read)); } } } ALSA_WaitRingMessage(&wwi->msgRing, dwSleepTime); while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev)) { TRACE("msg=%s param=0x%lx\n", ALSA_getCmdString(msg), param); switch (msg) { case WINE_WM_PAUSING: wwi->state = WINE_WS_PAUSED; /*FIXME("Device should stop recording\n");*/ SetEvent(ev); break; case WINE_WM_STARTING: wwi->state = WINE_WS_PLAYING; snd_pcm_start(wwi->pcm); SetEvent(ev); break; case WINE_WM_HEADER: lpWaveHdr = (LPWAVEHDR)param; lpWaveHdr->lpNext = 0; /* insert buffer at the end of queue */ { LPWAVEHDR* wh; for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); *wh = lpWaveHdr; } break; case WINE_WM_STOPPING: if (wwi->state != WINE_WS_STOPPED) { snd_pcm_drain(wwi->pcm); /* read any headers in queue */ widRecorder_ReadHeaders(wwi); /* return current buffer to app */ lpWaveHdr = wwi->lpQueuePtr; if (lpWaveHdr) { LPWAVEHDR lpNext = lpWaveHdr->lpNext; TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext); lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; wwi->lpQueuePtr = lpNext; widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0); } } wwi->state = WINE_WS_STOPPED; SetEvent(ev); break; case WINE_WM_RESETTING: if (wwi->state != WINE_WS_STOPPED) { snd_pcm_drain(wwi->pcm); } wwi->state = WINE_WS_STOPPED; wwi->dwTotalRecorded = 0; /* read any headers in queue */ widRecorder_ReadHeaders(wwi); /* return all buffers to the app */ while (wwi->lpQueuePtr) { lpWaveHdr = wwi->lpQueuePtr; TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext); wwi->lpQueuePtr = lpWaveHdr->lpNext; lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0); } SetEvent(ev); break; case WINE_WM_CLOSING: wwi->hThread = 0; wwi->state = WINE_WS_CLOSED; SetEvent(ev); ExitThread(0); /* shouldn't go here */ default: FIXME("unknown message %d\n", msg); break; } } } ExitThread(0); /* just for not generating compilation warnings... should never be executed */ return 0; }
bool play_pcm_file (playable_pcm_file_t *file) { int status; snd_pcm_t *handle; snd_pcm_format_t format; snd_pcm_sframes_t frames_wrote; uint8_t playback_buf[BUFSIZ]; int frames_count; size_t read_bytes; format = determine_pcm_format (&(file->info)); if (format == SND_PCM_FORMAT_UNKNOWN) { fprintf (stderr, "%s: Unable to determine the beep's PCM data format.\n", progname); return false; } if (fsetpos (file->stream, &(file->pcm_start_pos)) != 0) { fprintf (stderr, "%s: Failed to seek to the PCM data of `%s': %s.\n", progname, file->name, strerror (errno)); return false; } status = snd_pcm_open (&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); if (status < 0) { fprintf (stderr, "%s: Failed to open the playback device: %s\n", progname, snd_strerror (status)); return false; } status = snd_pcm_set_params (handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, file->info.channels, file->info.sample_rate, 1, /* soft_resample */ 0); /* latency (us).*/ if (status < 0) { fprintf (stderr, "%s: Failed to configure the playback device: %s.\n", progname, snd_strerror (status)); snd_pcm_close (handle); return false; } while (true) { read_bytes = fread (playback_buf, 1, BUFSIZ, file->stream); if (read_bytes == 0) { if (ferror (file->stream)) { fprintf (stderr, "%s: An error occured while reading from `%s': %s.\n", progname, file->name, strerror (errno)); snd_pcm_close (handle); return false; } break; } frames_count = snd_pcm_bytes_to_frames (handle, read_bytes); frames_wrote = snd_pcm_writei (handle, playback_buf, frames_count); if (frames_wrote < 0) frames_wrote = snd_pcm_recover (handle, frames_wrote, 0); if (frames_wrote < 0) { fprintf (stderr, "%s: Writing to the playback device failed: %s.\n", progname, snd_strerror (frames_wrote)); snd_pcm_close (handle); return false; } if (frames_wrote > 0 && frames_wrote < frames_count) { fprintf (stderr, "%s: Warning: Wrote only %ld frames instead of the " "expected %d to the playback device.\n", progname, frames_wrote, frames_count); } } snd_pcm_drain (handle); snd_pcm_close (handle); return true; }
static ALCenum alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples) { alsa_data *data = (alsa_data*)Device->ExtraData; if(data->ring) { ReadRingBuffer(data->ring, Buffer, Samples); return ALC_NO_ERROR; } data->last_avail -= Samples; while(Device->Connected && Samples > 0) { snd_pcm_sframes_t amt = 0; if(data->size > 0) { /* First get any data stored from the last stop */ amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); if((snd_pcm_uframes_t)amt > Samples) amt = Samples; amt = snd_pcm_frames_to_bytes(data->pcmHandle, amt); memmove(Buffer, data->buffer, amt); if(data->size > amt) { memmove(data->buffer, data->buffer+amt, data->size - amt); data->size -= amt; } else { free(data->buffer); data->buffer = NULL; data->size = 0; } amt = snd_pcm_bytes_to_frames(data->pcmHandle, amt); } else if(data->doCapture) amt = snd_pcm_readi(data->pcmHandle, Buffer, Samples); if(amt < 0) { ERR("read error: %s\n", snd_strerror(amt)); if(amt == -EAGAIN) continue; if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0) { amt = snd_pcm_start(data->pcmHandle); if(amt >= 0) amt = snd_pcm_avail_update(data->pcmHandle); } if(amt < 0) { ERR("restore error: %s\n", snd_strerror(amt)); aluHandleDisconnect(Device); break; } /* If the amount available is less than what's asked, we lost it * during recovery. So just give silence instead. */ if((snd_pcm_uframes_t)amt < Samples) break; continue; } Buffer = (ALbyte*)Buffer + amt; Samples -= amt; } if(Samples > 0) memset(Buffer, ((Device->FmtType == DevFmtUByte) ? 0x80 : 0), snd_pcm_frames_to_bytes(data->pcmHandle, Samples)); return ALC_NO_ERROR; }
static ALCuint alsa_available_samples(ALCdevice *Device) { alsa_data *data = (alsa_data*)Device->ExtraData; snd_pcm_sframes_t avail = 0; if(Device->Connected && data->doCapture) avail = snd_pcm_avail_update(data->pcmHandle); if(avail < 0) { ERR("avail update failed: %s\n", snd_strerror(avail)); if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0) { if(data->doCapture) avail = snd_pcm_start(data->pcmHandle); if(avail >= 0) avail = snd_pcm_avail_update(data->pcmHandle); } if(avail < 0) { ERR("restore error: %s\n", snd_strerror(avail)); aluHandleDisconnect(Device); } } if(!data->ring) { if(avail < 0) avail = 0; avail += snd_pcm_bytes_to_frames(data->pcmHandle, data->size); if(avail > data->last_avail) data->last_avail = avail; return data->last_avail; } while(avail > 0) { snd_pcm_sframes_t amt; amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); if(avail < amt) amt = avail; amt = snd_pcm_readi(data->pcmHandle, data->buffer, amt); if(amt < 0) { ERR("read error: %s\n", snd_strerror(amt)); if(amt == -EAGAIN) continue; if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0) { if(data->doCapture) amt = snd_pcm_start(data->pcmHandle); if(amt >= 0) amt = snd_pcm_avail_update(data->pcmHandle); } if(amt < 0) { ERR("restore error: %s\n", snd_strerror(amt)); aluHandleDisconnect(Device); break; } avail = amt; continue; } WriteRingBuffer(data->ring, data->buffer, amt); avail -= amt; } return RingBufferSize(data->ring); }
qint64 QAudioInputPrivate::read(char* data, qint64 len) { // Read in some audio data and write it to QIODevice, pull mode if ( !handle ) return 0; // bytesAvaiable is saved as a side effect of checkBytesReady(). int bytesToRead = checkBytesReady(); if (bytesToRead < 0) { // bytesAvailable as negative is error code, try to recover from it. xrun_recovery(bytesToRead); bytesToRead = checkBytesReady(); if (bytesToRead < 0) { // recovery failed must stop and set error. close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return 0; } } bytesToRead = qMin<qint64>(len, bytesToRead); bytesToRead -= bytesToRead % period_size; int count=0, err = 0; while(count < 5) { int chunks = bytesToRead/period_size; int frames = chunks*period_frames; if(frames > (int)buffer_frames) frames = buffer_frames; int readFrames = snd_pcm_readi(handle, audioBuffer, frames); if (readFrames >= 0) { err = snd_pcm_frames_to_bytes(handle, readFrames); #ifdef DEBUG_AUDIO qDebug()<<QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(err).arg(readFrames).toLatin1().constData(); #endif break; } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { errorState = QAudio::IOError; err = 0; break; } else { if(readFrames == -EPIPE) { errorState = QAudio::UnderrunError; err = snd_pcm_prepare(handle); } else if(readFrames == -ESTRPIPE) { err = snd_pcm_prepare(handle); } if(err != 0) break; } count++; } if(err > 0) { // got some send it onward #ifdef DEBUG_AUDIO qDebug()<<"frames to write to QIODevice = "<< snd_pcm_bytes_to_frames( handle, (int)err )<<" ("<<err<<") bytes"; #endif if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) return 0; if (pullMode) { qint64 l = audioSource->write(audioBuffer,err); if(l < 0) { close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); } else if(l == 0) { if (deviceState != QAudio::IdleState) { errorState = QAudio::NoError; deviceState = QAudio::IdleState; emit stateChanged(deviceState); } } else { bytesAvailable -= err; totalTimeValue += err; resuming = false; if (deviceState != QAudio::ActiveState) { errorState = QAudio::NoError; deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } } return l; } else { memcpy(data,audioBuffer,err); bytesAvailable -= err; totalTimeValue += err; resuming = false; if (deviceState != QAudio::ActiveState) { errorState = QAudio::NoError; deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } return err; } } return 0; }
static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface, LPVOID pvAudio1,DWORD dwLen1, LPVOID pvAudio2,DWORD dwLen2) { IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; snd_pcm_uframes_t writepos; if (!dwLen1) return DS_OK; /* **** */ EnterCriticalSection(&This->pcm_crst); writepos = snd_pcm_bytes_to_frames(This->pcm, (DWORD_PTR)pvAudio1 - (DWORD_PTR)This->mmap_buffer); if (writepos == This->mmap_pos) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t writelen = snd_pcm_bytes_to_frames(This->pcm, dwLen1); TRACE("Committing data\n"); if (This->mmap) This->mmap_pos += snd_pcm_mmap_commit(This->pcm, This->mmap_pos, writelen); else { int ret; ret = snd_pcm_writei(This->pcm, pvAudio1, writelen); if (ret == -EPIPE) { WARN("Underrun occurred\n"); wine_snd_pcm_recover(This->pcm, -EPIPE, 1); ret = snd_pcm_writei(This->pcm, pvAudio1, writelen); /* Advance mmap pointer a little to make dsound notice the underrun and respond to it */ if (ret < writelen) WARN("Short write %ld/%d\n", writelen, ret); This->mmap_pos += This->mmap_commitahead + ret; This->mmap_pos %= This->mmap_buflen_frames; } else if (ret > 0) This->mmap_pos += ret; if (ret < 0) WARN("Committing data: %d / %s (%p %ld)\n", ret, snd_strerror(ret), pvAudio1, writelen); } if (This->mmap_pos == This->mmap_buflen_frames) This->mmap_pos = 0; if (dwLen2) { writelen = snd_pcm_bytes_to_frames(This->pcm, dwLen2); if (This->mmap) { snd_pcm_mmap_begin(This->pcm, &areas, &This->mmap_pos, &writelen); This->mmap_pos += snd_pcm_mmap_commit(This->pcm, This->mmap_pos, writelen); } else { int ret; ret = snd_pcm_writei(This->pcm, pvAudio2, writelen); if (ret < writelen) WARN("Short write %ld/%d\n", writelen, ret); This->mmap_pos = ret > 0 ? ret : 0; } assert(This->mmap_pos < This->mmap_buflen_frames); } } LeaveCriticalSection(&This->pcm_crst); /* **** */ return DS_OK; }
static ssize_t alsa_write(void *data, const void *buf_, size_t size_) { alsa_t *alsa = (alsa_t*)data; const uint8_t *buf = (const uint8_t*)buf_; bool eagain_retry = true; snd_pcm_sframes_t written = 0; snd_pcm_sframes_t size = snd_pcm_bytes_to_frames(alsa->pcm, size_); while (size) { if (!alsa->nonblock) { int rc = snd_pcm_wait(alsa->pcm, -1); if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR) { if (snd_pcm_recover(alsa->pcm, rc, 1) < 0) { RARCH_ERR("[ALSA]: (#1) Failed to recover from error (%s)\n", snd_strerror(rc)); return -1; } continue; } } snd_pcm_sframes_t frames = snd_pcm_writei(alsa->pcm, buf, size); if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) { if (snd_pcm_recover(alsa->pcm, frames, 1) < 0) { RARCH_ERR("[ALSA]: (#2) Failed to recover from error (%s)\n", snd_strerror(frames)); return -1; } break; } else if (frames == -EAGAIN && !alsa->nonblock) // Definitely not supposed to happen. { RARCH_WARN("[ALSA]: poll() was signaled, but EAGAIN returned from write.\n" "Your ALSA driver might be subtly broken.\n"); if (eagain_retry) { eagain_retry = false; continue; } else return written; } else if (frames == -EAGAIN) // Expected if we're running nonblock. { return written; } else if (frames < 0) { RARCH_ERR("[ALSA]: Unknown error occured (%s).\n", snd_strerror(frames)); return -1; } written += frames; buf += (frames << 1) * (alsa->has_float ? sizeof(float) : sizeof(int16_t)); size -= frames; } return written; }
ssize_t AudioStreamInALSA::read(void *buffer, ssize_t bytes) { AutoMutex lock(mLock); if (!mPowerLock) { acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock"); mPowerLock = true; } acoustic_device_t *aDev = acoustics(); // If there is an acoustics module read method, then it overrides this // implementation (unlike AudioStreamOutALSA write). if (aDev && aDev->read) return aDev->read(aDev, buffer, bytes); snd_pcm_sframes_t n, frames = snd_pcm_bytes_to_frames(mHandle->handle, bytes); status_t err; do { n = snd_pcm_readi(mHandle->handle, buffer, frames); #ifdef DROPFRAME #ifdef DROPFRAME2 FrameNumber++; if (!(FrameNumber % 17) && (FrameNumber <= 1700)) { n = snd_pcm_readi(mHandle->handle, buffer, frames); } if (!(FrameNumber % 176)) { n = snd_pcm_readi(mHandle->handle, buffer, frames); } if (FrameNumber == 1764) { FrameNumber = 0; n = snd_pcm_readi(mHandle->handle, buffer, frames); } #else FrameNumber++; if (FrameNumber == 624/*137* rane@2012 07 20*/) { FrameNumber = 0; n = snd_pcm_readi(mHandle->handle, buffer, frames); } #endif #endif if (n < frames) { if (mHandle->handle) { if (n < 0) { n = snd_pcm_recover(mHandle->handle, n, 0); if (aDev && aDev->recover) aDev->recover(aDev, n); } else n = snd_pcm_prepare(mHandle->handle); } return static_cast<ssize_t>(n); } } while (n == -EAGAIN); return static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n)); }
int alsa_play_play(alsa_play_t alsa_play, glc_audio_data_header_t *audio_hdr, char *data) { snd_pcm_uframes_t frames, rem; snd_pcm_sframes_t ret = 0; unsigned int c; if (audio_hdr->id != alsa_play->id) return 0; if (!alsa_play->pcm) { glc_log(alsa_play->glc, GLC_ERROR, "alsa_play", "broken stream %d", alsa_play->id); return EINVAL; } frames = snd_pcm_bytes_to_frames(alsa_play->pcm, audio_hdr->size); glc_utime_t time = glc_state_time(alsa_play->glc); glc_utime_t duration = ((glc_utime_t) 1000000000 * (glc_utime_t) frames) / (glc_utime_t) alsa_play->rate; if (time + alsa_play->silence_threshold + duration < audio_hdr->time) { struct timespec ts = { .tv_sec = (audio_hdr->time - time - duration - alsa_play->silence_threshold)/1000000000, .tv_nsec = (audio_hdr->time - time - duration - alsa_play->silence_threshold)%1000000000 }; nanosleep(&ts,NULL); } /* * This condition determine what will be the initial audio packet. * it is preferable to be ahead by < duration/2 than behind * the video by > duration/2 */ else if (time > audio_hdr->time + duration/2) { glc_log(alsa_play->glc, GLC_DEBUG, "alsa_play", "dropped packet. now %" PRId64 " ts %" PRId64, time, audio_hdr->time); return 0; } rem = frames; while (rem > 0) { /* alsa is horrible... */ /*snd_pcm_wait(alsa_play->pcm, duration);*/ if (alsa_play->flags & GLC_AUDIO_INTERLEAVED) ret = snd_pcm_writei(alsa_play->pcm, &data[snd_pcm_frames_to_bytes(alsa_play->pcm, frames - rem)], rem); else { for (c = 0; c < alsa_play->channels; c++) alsa_play->bufs[c] = &data[snd_pcm_samples_to_bytes(alsa_play->pcm, frames) * c + snd_pcm_samples_to_bytes(alsa_play->pcm, frames - rem)]; ret = snd_pcm_writen(alsa_play->pcm, alsa_play->bufs, rem); } if (ret == 0) break; if ((ret == -EBUSY) || (ret == -EAGAIN)) break; else if (ret < 0) { if ((ret = alsa_play_xrun(alsa_play, ret))) { glc_log(alsa_play->glc, GLC_ERROR, "alsa_play", "xrun recovery failed: %s", snd_strerror(-ret)); return ret; } } else rem -= ret; } return 0; }
ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes) { android::AutoMutex lock(mLock); if (!mPowerLock) { acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock"); mPowerLock = true; } acoustic_device_t *aDev = acoustics(); // For output, we will pass the data on to the acoustics module, but the actual // data is expected to be sent to the audio device directly as well. if (aDev && aDev->write) aDev->write(aDev, buffer, bytes); snd_pcm_sframes_t n; size_t sent = 0; status_t err = 0; do { if (mHandle->mmap) n = snd_pcm_mmap_writei(mHandle->handle, (char *)buffer + sent, snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent)); else n = snd_pcm_writei(mHandle->handle, (char *)buffer + sent, snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent)); if (n == -EBADFD) { LOGW("badstate and do recovery....."); switch(snd_pcm_state(mHandle->handle)){ case SND_PCM_STATE_SETUP: err = snd_pcm_prepare(mHandle->handle); if(err < 0) LOGW("snd_pcm_prepare failed"); break; case SND_PCM_STATE_SUSPENDED: snd_pcm_resume(mHandle->handle); if(err < 0) LOGW("snd_pcm_resume failed"); snd_pcm_prepare(mHandle->handle); if(err < 0) LOGW("snd_pcm_prepare failed"); break; default: // Somehow the stream is in a bad state. The driver probably // has a bug and snd_pcm_recover() doesn't seem to handle this. mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode); break; } if(err < 0 ) mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode); if (aDev && aDev->recover) aDev->recover(aDev, n); } else if (n < 0) { if (mHandle->handle) { LOGW("underrun and do recovery....."); // snd_pcm_recover() will return 0 if successful in recovering from // an error, or -errno if the error was unrecoverable. n = snd_pcm_recover(mHandle->handle, n, 1); if (aDev && aDev->recover) aDev->recover(aDev, n); if (n) return static_cast<ssize_t>(n); } } else { mFrameCount += n; sent += static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n)); } } while (mHandle->handle && sent < bytes); return sent; }
bool PAPlayer::AddPacketsToStream(int stream, CAudioDecoder &dec) { if (!m_pStream[stream] || dec.GetStatus() == STATUS_NO_FILE) return false; bool ret = false; int nAvail = snd_pcm_frames_to_bytes(m_pStream[stream], snd_pcm_avail_update(m_pStream[stream])); if (nAvail < PACKET_SIZE) { return false; } if (m_resampler[stream].GetData(m_packet[stream][0].packet)) { // got some data from our resampler - construct audio packet m_packet[stream][0].length = PACKET_SIZE; m_packet[stream][0].stream = stream; unsigned char *pcmPtr = m_packet[stream][0].packet; // handle volume de-amp m_amp[stream].DeAmplify((short *)pcmPtr, m_packet[stream][0].length / 2); StreamCallback(&m_packet[stream][0]); while ( pcmPtr < m_packet[stream][0].packet + m_packet[stream][0].length) { int nPeriodSize = snd_pcm_frames_to_bytes(m_pStream[stream],m_periods[stream]); if ( pcmPtr + nPeriodSize > m_packet[stream][0].packet + m_packet[stream][0].length) { nPeriodSize = m_packet[stream][0].packet + m_packet[stream][0].length - pcmPtr; } int framesToWrite = snd_pcm_bytes_to_frames(m_pStream[stream],nPeriodSize); int writeResult = snd_pcm_writei(m_pStream[stream], pcmPtr, framesToWrite); if ( writeResult == -EPIPE ) { CLog::Log(LOGDEBUG, "PAPlayer::AddPacketsToStream - buffer underun (tried to write %d frames)", framesToWrite); int err = snd_pcm_prepare(m_pStream[stream]); CHECK_ALSA(LOGERROR,"prepare after EPIPE", err); } else if (writeResult != framesToWrite) { CLog::Log(LOGERROR, "PAPlayer::AddPacketsToStream - failed to write %d frames. " "bad write (err: %d) - %s", framesToWrite, writeResult, snd_strerror(writeResult)); break; } //else // m_bytesSentOut += nPeriodSize; pcmPtr += nPeriodSize; } // something done ret = true; } else { // resampler wants more data - let's feed it int amount = m_resampler[stream].GetInputSamples(); if (amount > 0 && amount <= (int)dec.GetDataSize()) { // needs some data - let's feed it m_resampler[stream].PutFloatData((float *)dec.GetData(amount), amount); ret = true; } } return ret; }
/** * Allocate the memory-mapped buffer for direct sound, and set up the * callback. */ static int CreateMMAP(IDsCaptureDriverBufferImpl* pdbi) { snd_pcm_t *pcm = pdbi->pcm; snd_pcm_format_t format; snd_pcm_uframes_t frames, ofs, avail, psize, boundary; unsigned int channels, bits_per_sample, bits_per_frame; int err, mmap_mode; const snd_pcm_channel_area_t *areas; snd_pcm_hw_params_t *hw_params = pdbi->hw_params; snd_pcm_sw_params_t *sw_params = pdbi->sw_params; mmap_mode = snd_pcm_type(pcm); if (mmap_mode == SND_PCM_TYPE_HW) TRACE("mmap'd buffer is a direct hardware buffer.\n"); else if (mmap_mode == SND_PCM_TYPE_DMIX) TRACE("mmap'd buffer is an ALSA dmix buffer\n"); else TRACE("mmap'd buffer is an ALSA type %d buffer\n", mmap_mode); err = snd_pcm_hw_params_get_period_size(hw_params, &psize, NULL); err = snd_pcm_hw_params_get_format(hw_params, &format); err = snd_pcm_hw_params_get_buffer_size(hw_params, &frames); err = snd_pcm_hw_params_get_channels(hw_params, &channels); bits_per_sample = snd_pcm_format_physical_width(format); bits_per_frame = bits_per_sample * channels; if (TRACE_ON(dsalsa)) ALSA_TraceParameters(hw_params, NULL, FALSE); TRACE("format=%s frames=%ld channels=%d bits_per_sample=%d bits_per_frame=%d\n", snd_pcm_format_name(format), frames, channels, bits_per_sample, bits_per_frame); pdbi->mmap_buflen_frames = frames; snd_pcm_sw_params_current(pcm, sw_params); snd_pcm_sw_params_set_start_threshold(pcm, sw_params, 0); snd_pcm_sw_params_get_boundary(sw_params, &boundary); snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, boundary); snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, INT_MAX); snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0); snd_pcm_sw_params_set_avail_min(pcm, sw_params, 0); err = snd_pcm_sw_params(pcm, sw_params); pdbi->mmap_ofs_bytes = 0; if (!pdbi->mmap) { pdbi->mmap_buffer = NULL; frames = snd_pcm_bytes_to_frames(pdbi->pcm, pdbi->mmap_buflen_bytes); snd_pcm_format_set_silence(format, pdbi->presented_buffer, frames); pdbi->mmap_pos = 0; } else { err = snd_pcm_mmap_begin(pcm, &areas, &ofs, &avail); if ( err < 0 ) { ERR("Can't map sound device for direct access: %s/%d\n", snd_strerror(err), err); return DSERR_GENERIC; } snd_pcm_format_set_silence(format, areas->addr, pdbi->mmap_buflen_frames); pdbi->mmap_pos = ofs + snd_pcm_mmap_commit(pcm, ofs, 0); pdbi->mmap_buffer = areas->addr; } TRACE("created mmap buffer of %ld frames (%d bytes) at %p\n", pdbi->mmap_buflen_frames, pdbi->mmap_buflen_bytes, pdbi->mmap_buffer); return DS_OK; }
/** Fill buffers, for starting and stopping * Alsa won't start playing until everything is filled up * This also updates mmap_pos * * Returns: Amount of periods in use so snd_pcm_avail_update * doesn't have to be called up to 4x in GetPosition() */ static snd_pcm_uframes_t CommitAll(IDsCaptureDriverBufferImpl *This, DWORD forced) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t used; const snd_pcm_uframes_t commitahead = This->mmap_buflen_frames; used = This->mmap_buflen_frames - snd_pcm_avail_update(This->pcm); TRACE("%p needs to commit to %lu, used: %lu\n", This, commitahead, used); if (used < commitahead && (forced || This->play_looping)) { snd_pcm_uframes_t done, putin = commitahead - used; if (This->mmap) { snd_pcm_mmap_begin(This->pcm, &areas, &This->mmap_pos, &putin); CopyData(This, This->mmap_pos, putin); done = snd_pcm_mmap_commit(This->pcm, This->mmap_pos, putin); This->mmap_pos += done; used += done; putin = commitahead - used; if (This->mmap_pos == This->mmap_buflen_frames && (snd_pcm_sframes_t)putin > 0 && This->play_looping) { This->mmap_ofs_bytes += snd_pcm_frames_to_bytes(This->pcm, This->mmap_buflen_frames); This->mmap_ofs_bytes %= This->mmap_buflen_bytes; snd_pcm_mmap_begin(This->pcm, &areas, &This->mmap_pos, &putin); CopyData(This, This->mmap_pos, putin); done = snd_pcm_mmap_commit(This->pcm, This->mmap_pos, putin); This->mmap_pos += done; used += done; } } else { DWORD pos; snd_pcm_sframes_t ret; snd_pcm_uframes_t cap = snd_pcm_bytes_to_frames(This->pcm, This->mmap_buflen_bytes); pos = realpos_to_fakepos(This, This->mmap_pos); if (This->mmap_pos + putin > cap) putin = cap - This->mmap_pos; ret = snd_pcm_readi(This->pcm, This->presented_buffer + pos, putin); if (ret == -EPIPE) { WARN("Underrun occurred\n"); snd_pcm_prepare(This->pcm); ret = snd_pcm_readi(This->pcm, This->presented_buffer + pos, putin); snd_pcm_start(This->pcm); } if (ret < 0) { WARN("Committing data: %ld / %s (%ld)\n", ret, snd_strerror(ret), putin); ret = 0; } This->mmap_pos += ret; used += ret; /* At this point mmap_pos may be >= This->mmap_pos this is harmless * realpos_to_fakepos handles it well, and below it is truncated */ putin = commitahead - used; if (putin > 0) { pos = realpos_to_fakepos(This, This->mmap_pos); ret = snd_pcm_readi(This->pcm, This->presented_buffer + pos, putin); if (ret > 0) { This->mmap_pos += ret; used += ret; } } } } if (This->mmap_pos >= This->mmap_buflen_frames) { This->mmap_ofs_bytes += snd_pcm_frames_to_bytes(This->pcm, This->mmap_buflen_frames); This->mmap_ofs_bytes %= This->mmap_buflen_bytes; This->mmap_pos -= This->mmap_buflen_frames; } return used; }