online-audio-source.cc
Go to the documentation of this file.
1 // online/online-audio-source.cc
2 
3 // Copyright 2012 Cisco Systems (author: Matthias Paulik)
4 
5 // Modifications to the original contribution by Cisco Systems made by:
6 // Vassil Panayotov
7 
8 // See ../../COPYING for clarification regarding multiple authors
9 //
10 // Licensed under the Apache License, Version 2.0 (the "License");
11 // you may not use this file except in compliance with the License.
12 // You may obtain a copy of the License at
13 //
14 // http://www.apache.org/licenses/LICENSE-2.0
15 //
16 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
18 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
19 // MERCHANTABLITY OR NON-INFRINGEMENT.
20 // See the Apache 2 License for the specific language governing permissions and
21 // limitations under the License.
22 
23 #include <algorithm>
24 #include <cmath>
25 #include <vector>
26 
27 
28 #ifndef KALDI_NO_PORTAUDIO
29 #include "base/timer.h"
30 #endif // KALDI_NO_PORTAUDIO
31 
32 #include "online-audio-source.h"
33 
34 namespace kaldi {
35 
36 #ifndef KALDI_NO_PORTAUDIO
37 
38 // The actual PortAudio callback - delegates to OnlinePaSource->Callback()
39 int PaCallback(const void *input, void *output,
40  long unsigned frame_count,
41  const PaStreamCallbackTimeInfo *time_info,
42  PaStreamCallbackFlags status_flags,
43  void *user_data) {
44  OnlinePaSource *pa_src = reinterpret_cast<OnlinePaSource*>(user_data);
45  return pa_src->Callback(input, output, frame_count, time_info, status_flags);
46 }
47 
48 
49 OnlinePaSource::OnlinePaSource(const uint32 timeout,
50  const uint32 sample_rate,
51  const uint32 rb_size,
52  const uint32 report_interval)
53  : timeout_(timeout), timed_out_(false),
54  sample_rate_(sample_rate), pa_started_(false),
55  report_interval_(report_interval), nread_calls_(0),
56  noverflows_(0), samples_lost_(0) {
57  using namespace std;
58 
59  // Note this will work for 32bit integers but not for 64bit.
60  // For 64bit integers even double wouldn't work
61  // You would have to use something like
62  // int64 rb_bits = 0; while (rb_size != 0) {++rb_bits; rb_size >>= 1;}
63  // it would be much faster than two logs of FP numbers (even floats), too,
64  // but I don't have the time to test it.
65  float f = Log(static_cast<float>(rb_size)) / Log(static_cast<float>(2));
66  int32 rb_bits = static_cast<int32>(ceil(f));
67  if (rb_bits > 30) // ok, this limit is somewhat arbitrary
68  throw invalid_argument("PortAudio ring buffer too large!");
69  rb_size_ = 1 << rb_bits;
70  ring_buffer_ = new char[rb_size_];
71  ring_buffer_size_t rbs = PaUtil_InitializeRingBuffer(
72  &pa_ringbuf_, sizeof(SampleType),
73  rb_size_ / sizeof(SampleType), ring_buffer_);
74  if (rbs != 0)
75  KALDI_ERR << "PortAudio ring buffer init error";
76 
77  PaError paerr = Pa_Initialize();
78  if (paerr != paNoError)
79  KALDI_ERR << "PortAudio initialization error";
80  // Monophone, 16-bit input hardcoded
81  KALDI_ASSERT(sizeof(SampleType) == 2 &&
82  "The current OnlinePaSource code assumes 16-bit input");
83  paerr = Pa_OpenDefaultStream(&pa_stream_, 1, 0, paInt16, sample_rate_, 0,
84  PaCallback, this);
85  if (paerr != paNoError)
86  KALDI_ERR << "PortAudio failed to open the default stream";
87 }
88 
89 
91  if (pa_started_)
92  Pa_StopStream(pa_stream_);
93  if (pa_stream_ != 0) {
94  Pa_CloseStream(pa_stream_);
95  Pa_Terminate();
96  }
97  if (ring_buffer_ != 0)
98  delete [] ring_buffer_;
99 }
100 
101 
103  if (!pa_started_) { // start stream the first time Read() is called
104  PaError paerr = Pa_StartStream(pa_stream_);
105  if (paerr != paNoError)
106  KALDI_ERR << "Error while trying to open PortAudio stream";
107  pa_started_ = true;
108  }
109  Timer timer;
110  if (report_interval_ != 0
111  && (++nread_calls_ % report_interval_) == 0
112  && noverflows_ > 0) {
113  KALDI_VLOG(1) << noverflows_
114  << " PortAudio ring buffer overflows detected "
115  << "and " << samples_lost_ << " sample(s) were lost";
116  samples_lost_ = noverflows_ = 0;
117  }
118  uint32 nsamples_req = data->Dim(); // samples to request
119  timed_out_ = false;
120  while (true) {
121  ring_buffer_size_t nsamples;
122  nsamples = PaUtil_GetRingBufferReadAvailable(&pa_ringbuf_);
123  if (nsamples >= nsamples_req)
124  break;
125  if (timeout_ > 0) {
126  int32 elapsed = static_cast<int32>(timer.Elapsed() * 1000);
127  if (elapsed > timeout_) {
128  nsamples_req = nsamples;
129  timed_out_ = true;
130  KALDI_VLOG(2) << "OnlinePaSource::Read() timeout";
131  break;
132  }
133  }
134  Pa_Sleep(2);
135  }
136  std::vector<int16> buf(nsamples_req);
137  rbs_t nsamples_rcv;
138  nsamples_rcv = PaUtil_ReadRingBuffer(&pa_ringbuf_, buf.data(), nsamples_req);
139  if (nsamples_rcv != nsamples_req) {
140  KALDI_WARN << "Requested: " << nsamples_req
141  << "; Received: " << nsamples_rcv << " samples";
142  // This would be a PortAudio error.
143  }
144  data->Resize(nsamples_rcv);
145  for (int i = 0; i < nsamples_rcv; ++i)
146  (*data)(i) = static_cast<BaseFloat>(buf[i]);
147 
148  return (nsamples_rcv != 0);
149  // NOTE (Dan): I'm pretty sure this return value is not right, it could be
150  // this way because we're waiting. Vassil or someone will have to figure this
151  // out.
152 }
153 
154 
155 // Accepts the data and writes it to the ring buffer
156 int OnlinePaSource::Callback(const void *input, void *output,
157  ring_buffer_size_t frame_count,
158  const PaStreamCallbackTimeInfo *time_info,
159  PaStreamCallbackFlags status_flags) {
160  if (report_interval_ != 0) {
161  if (frame_count > PaUtil_GetRingBufferWriteAvailable(&pa_ringbuf_))
162  ++noverflows_;
163  }
164  rbs_t written = PaUtil_WriteRingBuffer(&pa_ringbuf_, input, frame_count);
165  samples_lost_ += frame_count - written;
166  return paContinue;
167 }
168 
169 #endif // KALDI_NO_PORTAUDIO
170 
172  KALDI_ASSERT(data->Dim() > 0);
173  int32 n_elem = std::min(src_.Dim() - pos_,
174  static_cast<uint32>(data->Dim()));
175  if (n_elem > 0) {
176  SubVector<BaseFloat> subsrc(src_, pos_, n_elem);
177  if (data->Dim() == subsrc.Dim()) {
178  data->CopyFromVec(subsrc);
179  } else {
180  for (int32 i = 0; i < subsrc.Dim(); ++i)
181  (*data)(i) = subsrc(i);
182  }
183  pos_ += n_elem;
184  }
185  return (pos_ < src_.Dim());
186 }
187 
188 } // namespace kaldi
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
friend int PaCallback(const void *input, void *output, long unsigned frame_count, const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags status_flags, void *user_data)
PaUtilRingBuffer pa_ringbuf_
kaldi::int32 int32
void Resize(MatrixIndexT length, MatrixResizeType resize_type=kSetZero)
Set vector to a specified size (can be zero).
bool Read(Vector< BaseFloat > *data)
void CopyFromVec(const VectorBase< Real > &v)
Copy data from another vector (must match own size).
double Log(double x)
Definition: kaldi-math.h:100
#define KALDI_ERR
Definition: kaldi-error.h:147
#define KALDI_WARN
Definition: kaldi-error.h:150
MatrixIndexT Dim() const
Returns the dimension of the vector.
Definition: kaldi-vector.h:64
int PaCallback(const void *input, void *output, long unsigned frame_count, const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags status_flags, void *user_data)
A class representing a vector.
Definition: kaldi-vector.h:406
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156
OnlinePaSource(const uint32 timeout, const uint32 sample_rate, const uint32 rb_size, const uint32 report_interval)
bool Read(Vector< BaseFloat > *data)
ring_buffer_size_t rbs_t
double Elapsed() const
Returns time in seconds.
Definition: timer.h:74
Represents a non-allocating general vector which can be defined as a sub-vector of higher-level vecto...
Definition: kaldi-vector.h:501
int Callback(const void *input, void *output, ring_buffer_size_t frame_count, const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags status_flags)