Skip to content

Commit 3662def

Browse files
authored
Merge pull request #370 from ainyan03/mic_record_bounds
Mic: never write past the record() buffer; reject empty requests
2 parents 639000a + 63fabaa commit 3662def

2 files changed

Lines changed: 88 additions & 65 deletions

File tree

src/utility/Mic_Class.hpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -155,36 +155,38 @@ namespace m5
155155
void setSampleRate(uint32_t sample_rate) { _cfg.sample_rate = sample_rate; }
156156

157157
/// record raw sound wave data.
158-
/// @param rec_data Recording destination array.
159-
/// @param array_len Number of data array elements.
158+
/// A completed request has exactly array_len elements written, never
159+
/// more. A stereo buffer holds L/R pairs; with an odd array_len the last
160+
/// element receives the left sample only. Requests that are already
161+
/// queued when the previous one completes are continuous whatever their
162+
/// length (a capture step that straddles two buffers is carried over).
163+
/// @param rec_data Recording destination array. nullptr returns false.
164+
/// @param array_len Number of data array elements. 0 returns false
165+
/// (nothing is queued and the release callback is not called).
160166
/// @param sample_rate the sampling rate (Hz). 0 is invalid and returns false.
161167
/// @param stereo true=data is stereo / false=data is monaural.
168+
/// @return false when the arguments are invalid, the mic cannot start, or
169+
/// (from the release callback only) no request slot is free.
162170
bool record(uint8_t* rec_data, size_t array_len, uint32_t sample_rate, bool stereo = false)
163171
{
164172
return sample_rate != 0 && _rec_raw(rec_data, array_len, false, sample_rate, stereo);
165173
}
166174

167-
/// record raw sound wave data.
168-
/// @param rec_data Recording destination array.
169-
/// @param array_len Number of data array elements.
170-
/// @param sample_rate the sampling rate (Hz). 0 is invalid and returns false.
171-
/// @param stereo true=data is stereo / false=data is monaural.
175+
/// record raw sound wave data. See the uint8_t overload for the contract.
172176
bool record(int16_t* rec_data, size_t array_len, uint32_t sample_rate, bool stereo = false)
173177
{
174178
return sample_rate != 0 && _rec_raw(rec_data, array_len, true, sample_rate, stereo);
175179
}
176180

177-
/// record raw sound wave data.
178-
/// @param rec_data Recording destination array.
179-
/// @param array_len Number of data array elements.
181+
/// record raw sound wave data at the current sample rate (monaural).
182+
/// See the 4-argument overload for the contract.
180183
bool record(uint8_t* rec_data, size_t array_len)
181184
{ // sample_rate 0 == keep the current rate; resolved under the lock.
182185
return _rec_raw(rec_data, array_len, false, 0, false);
183186
}
184187

185-
/// record raw sound wave data.
186-
/// @param rec_data Recording destination array.
187-
/// @param array_len Number of data array elements.
188+
/// record raw sound wave data at the current sample rate (monaural).
189+
/// See the 4-argument overload for the contract.
188190
bool record(int16_t* rec_data, size_t array_len)
189191
{
190192
return _rec_raw(rec_data, array_len, true, 0, false);

src/utility/Mic_Class.inl

Lines changed: 73 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -616,9 +616,14 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) {
616616
const float f_gain = (float)gain / (oversampling << 1);
617617
size_t src_idx = ~0u;
618618
size_t src_len = 0;
619-
int32_t sum_value[4] = { 0,0 };
619+
int32_t sum_value[2] = { 0, 0 };
620620
int32_t prev_value[2] = { 0, 0 };
621621
const bool in_stereo = self->_cfg.stereo;
622+
// A mono capture step yields two time steps. When a buffer fills after
623+
// the first one, the second is kept here for the next pending request so
624+
// back-to-back recordings stay continuous whatever their length.
625+
int32_t carry_value = 0;
626+
bool carry_valid = false;
622627
int32_t os_remain = oversampling;
623628
const size_t dma_buf_len = self->_cfg.dma_buf_len;
624629
/// dma_buf_len は DMA descriptor のフレーム数として使われる (_setup_i2s の
@@ -666,9 +671,62 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) {
666671
src_len = 0;
667672
sum_value[0] = 0;
668673
sum_value[1] = 0;
674+
carry_valid = false; // buffered input is dropped here as well; the carry is only for back-to-back requests
669675
os_remain = oversampling;
670676
continue;
671677
}
678+
679+
// Write one element in the request's sample format.
680+
auto write_one = [&](int32_t value)
681+
{
682+
if (current_rec->is_16bit)
683+
{
684+
if ( value < INT16_MIN+16) { value = INT16_MIN+16; }
685+
else if (value > INT16_MAX-16) { value = INT16_MAX-16; }
686+
auto dst = (int16_t*)(current_rec->data);
687+
*dst++ = value;
688+
current_rec->data = dst;
689+
}
690+
else
691+
{
692+
value = ((value + 128) >> 8) + 128;
693+
if ( value < 0) { value = 0; }
694+
else if (value > 255) { value = 255; }
695+
auto dst = (uint8_t*)(current_rec->data);
696+
*dst++ = value;
697+
current_rec->data = dst;
698+
}
699+
--dst_remain;
700+
};
701+
// Write one time step (left, right). A mono request takes left only
702+
// (the caller has already averaged a stereo capture); a stereo request
703+
// whose last element is reached after left drops right, so an odd
704+
// stereo length ends with a lone left sample.
705+
auto write_step = [&](int32_t left, int32_t right)
706+
{
707+
write_one(left);
708+
if (current_rec->is_stereo && dst_remain) { write_one(right); }
709+
};
710+
auto release_current = [&](void)
711+
{
712+
// data has been walked forward while filling: step back over the
713+
// whole buffer to hand the caller the pointer they gave record().
714+
const size_t total = current_rec->length.load(std::memory_order_relaxed);
715+
void* released = (uint8_t*)current_rec->data - total * (current_rec->is_16bit ? 2 : 1);
716+
current_rec->length.store(0, std::memory_order_release);
717+
xSemaphoreGive(self->_task_semaphore);
718+
if (self->_cb_buffer_release)
719+
{
720+
self->_cb_buffer_release(self->_cb_buffer_release_args, released, total);
721+
}
722+
};
723+
724+
if (carry_valid)
725+
{
726+
carry_valid = false;
727+
write_step(carry_value, carry_value);
728+
if (dst_remain == 0) { release_current(); continue; }
729+
}
672730
for (;;)
673731
{
674732
if (src_idx >= src_len)
@@ -747,61 +805,22 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) {
747805
}
748806
}
749807

750-
int output_num = 2;
751-
752-
if (in_stereo != current_rec->is_stereo)
753-
{
754-
if (in_stereo)
755-
{ // stereo -> mono convert.
756-
sum_value[0] = (sum_value[0] + sum_value[1] + 1) >> 1;
757-
output_num = 1;
758-
}
759-
else
760-
{ // mono -> stereo convert.
761-
auto tmp = sum_value[1];
762-
sum_value[3] = tmp;
763-
sum_value[2] = tmp;
764-
sum_value[1] = sum_value[0];
765-
output_num = 4;
766-
}
808+
if (in_stereo)
809+
{ // one time step (L, R); a mono request takes the average
810+
if (current_rec->is_stereo) { write_step(sum_value[0], sum_value[1]); }
811+
else { write_one((sum_value[0] + sum_value[1] + 1) >> 1); }
767812
}
768-
for (int i = 0; i < output_num; ++i)
769-
{
770-
auto value = sum_value[i];
771-
if (current_rec->is_16bit)
772-
{
773-
if ( value < INT16_MIN+16) { value = INT16_MIN+16; }
774-
else if (value > INT16_MAX-16) { value = INT16_MAX-16; }
775-
auto dst = (int16_t*)(current_rec->data);
776-
*dst++ = value;
777-
current_rec->data = dst;
778-
}
779-
else
780-
{
781-
value = ((value + 128) >> 8) + 128;
782-
if ( value < 0) { value = 0; }
783-
else if (value > 255) { value = 255; }
784-
auto dst = (uint8_t*)(current_rec->data);
785-
*dst++ = value;
786-
current_rec->data = dst;
787-
}
813+
else
814+
{ // two time steps; a stereo request duplicates each into L and R
815+
write_step(sum_value[0], sum_value[0]);
816+
if (dst_remain) { write_step(sum_value[1], sum_value[1]); }
817+
else { carry_value = sum_value[1]; carry_valid = true; }
788818
}
789819
sum_value[0] = 0;
790820
sum_value[1] = 0;
791-
dst_remain -= output_num;
792-
if ((int32_t)dst_remain <= 0)
821+
if (dst_remain == 0)
793822
{
794-
// data has been walked forward while filling: step back over what
795-
// was written to hand the caller the pointer they gave record().
796-
const size_t total = current_rec->length.load(std::memory_order_relaxed);
797-
const size_t written = total - dst_remain; // dst_remain may have wrapped below 0
798-
void* released = (uint8_t*)current_rec->data - written * (current_rec->is_16bit ? 2 : 1);
799-
current_rec->length.store(0, std::memory_order_release);
800-
xSemaphoreGive(self->_task_semaphore);
801-
if (self->_cb_buffer_release)
802-
{
803-
self->_cb_buffer_release(self->_cb_buffer_release_args, released, total);
804-
}
823+
release_current();
805824
break;
806825
}
807826
}
@@ -1065,6 +1084,9 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) {
10651084
// From the release callback (the capture task itself) nothing may be
10661085
// waited for: an end() holding _rec_lock waits for this task to exit, and
10671086
// a free slot only comes from this task. Try once and report.
1087+
// Nothing to record into is a caller error, not a request: it used to
1088+
// return true without ever invoking the release callback.
1089+
if (recdata == nullptr || array_len == 0) { return false; }
10681090
const bool in_task = (xTaskGetCurrentTaskHandle() == _task_handle.load(std::memory_order_acquire));
10691091
for (;;)
10701092
{
@@ -1090,7 +1112,6 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) {
10901112
{
10911113
int res = _begin_raw(sample_rate);
10921114
if (res <= 0) { return res; }
1093-
if (array_len == 0) { return 1; }
10941115
// Any free slot will do: the consume order comes from the sequence
10951116
// number stamped below, not from which slot a request lands in. A full
10961117
// queue just means "come back later".

0 commit comments

Comments
 (0)