28 KiB
Ghi âm - Wio Terminal
Trong phần này của bài học, bạn sẽ viết mã để ghi âm trên Wio Terminal của mình. Việc ghi âm sẽ được điều khiển bởi một trong các nút trên đỉnh của Wio Terminal.
Lập trình thiết bị để ghi âm
Bạn có thể ghi âm từ micro bằng cách sử dụng mã C++. Wio Terminal chỉ có 192KB RAM, không đủ để ghi âm dài hơn vài giây. Tuy nhiên, nó có 4MB bộ nhớ flash, vì vậy bạn có thể sử dụng bộ nhớ này để lưu trữ âm thanh đã ghi.
Micro tích hợp ghi lại tín hiệu analog, sau đó được chuyển đổi thành tín hiệu số mà Wio Terminal có thể sử dụng. Khi ghi âm, dữ liệu cần được ghi lại đúng thời điểm - ví dụ, để ghi âm ở tần số 16KHz, âm thanh cần được ghi lại chính xác 16.000 lần mỗi giây, với các khoảng thời gian đều nhau giữa mỗi mẫu. Thay vì sử dụng mã của bạn để làm điều này, bạn có thể sử dụng bộ điều khiển truy cập bộ nhớ trực tiếp (DMAC). Đây là một mạch có thể ghi lại tín hiệu từ một nguồn nào đó và ghi vào bộ nhớ mà không làm gián đoạn mã của bạn đang chạy trên bộ xử lý.
✅ Đọc thêm về DMA trên trang truy cập bộ nhớ trực tiếp trên Wikipedia.
DMAC có thể ghi âm từ ADC ở các khoảng thời gian cố định, chẳng hạn như 16.000 lần mỗi giây cho âm thanh 16KHz. Nó có thể ghi dữ liệu đã ghi vào một bộ đệm bộ nhớ được phân bổ trước, và khi bộ đệm này đầy, nó sẽ thông báo cho mã của bạn xử lý. Việc sử dụng bộ nhớ này có thể làm chậm quá trình ghi âm, nhưng bạn có thể thiết lập nhiều bộ đệm. DMAC ghi vào bộ đệm 1, sau đó khi bộ đệm này đầy, nó thông báo cho mã của bạn xử lý bộ đệm 1, trong khi DMAC ghi vào bộ đệm 2. Khi bộ đệm 2 đầy, nó thông báo cho mã của bạn và quay lại ghi vào bộ đệm 1. Bằng cách này, miễn là bạn xử lý mỗi bộ đệm trong thời gian ngắn hơn thời gian cần để làm đầy một bộ đệm, bạn sẽ không mất dữ liệu.
Khi mỗi bộ đệm đã được ghi lại, nó có thể được ghi vào bộ nhớ flash. Bộ nhớ flash cần được ghi vào bằng các địa chỉ xác định, chỉ định nơi ghi và kích thước cần ghi, tương tự như cập nhật một mảng byte trong bộ nhớ. Bộ nhớ flash có tính hạt, nghĩa là các thao tác xóa và ghi phụ thuộc không chỉ vào kích thước cố định mà còn phải căn chỉnh với kích thước đó. Ví dụ, nếu tính hạt là 4096 byte và bạn yêu cầu xóa tại địa chỉ 4200, nó có thể xóa tất cả dữ liệu từ địa chỉ 4096 đến 8192. Điều này có nghĩa là khi bạn ghi dữ liệu âm thanh vào bộ nhớ flash, nó phải được chia thành các khối có kích thước phù hợp.
Nhiệm vụ - cấu hình bộ nhớ flash
-
Tạo một dự án Wio Terminal mới bằng PlatformIO. Đặt tên dự án này là
smart-timer
. Thêm mã vào hàmsetup
để cấu hình cổng nối tiếp. -
Thêm các thư viện phụ thuộc sau vào tệp
platformio.ini
để cung cấp quyền truy cập vào bộ nhớ flash:lib_deps = seeed-studio/Seeed Arduino FS @ 2.1.1 seeed-studio/Seeed Arduino SFUD @ 2.0.2
-
Mở tệp
main.cpp
và thêm chỉ thịinclude
sau cho thư viện bộ nhớ flash vào đầu tệp:#include <sfud.h> #include <SPI.h>
🎓 SFUD là viết tắt của Serial Flash Universal Driver, một thư viện được thiết kế để hoạt động với tất cả các chip bộ nhớ flash.
-
Trong hàm
setup
, thêm mã sau để thiết lập thư viện lưu trữ flash:while (!(sfud_init() == SFUD_SUCCESS)) ; sfud_qspi_fast_read_enable(sfud_get_device(SFUD_W25Q32_DEVICE_INDEX), 2);
Đoạn mã này lặp lại cho đến khi thư viện SFUD được khởi tạo, sau đó bật chế độ đọc nhanh. Bộ nhớ flash tích hợp có thể được truy cập bằng Giao diện Ngoại vi Nối tiếp Hàng đợi (QSPI), một loại bộ điều khiển SPI cho phép truy cập liên tục thông qua hàng đợi với mức sử dụng bộ xử lý tối thiểu. Điều này giúp việc đọc và ghi vào bộ nhớ flash nhanh hơn.
-
Tạo một tệp mới trong thư mục
src
có tên làflash_writer.h
. -
Thêm đoạn mã sau vào đầu tệp này:
#pragma once #include <Arduino.h> #include <sfud.h>
Đoạn mã này bao gồm một số tệp tiêu đề cần thiết, bao gồm tệp tiêu đề cho thư viện SFUD để tương tác với bộ nhớ flash.
-
Định nghĩa một lớp trong tệp tiêu đề mới này có tên là
FlashWriter
:class FlashWriter { public: private: };
-
Trong phần
private
, thêm đoạn mã sau:byte *_sfudBuffer; size_t _sfudBufferSize; size_t _sfudBufferPos; size_t _sfudBufferWritePos; const sfud_flash *_flash;
Đoạn mã này định nghĩa một số trường cho bộ đệm được sử dụng để lưu trữ dữ liệu trước khi ghi vào bộ nhớ flash. Có một mảng byte
_sfudBuffer
để ghi dữ liệu, và khi bộ đệm này đầy, dữ liệu sẽ được ghi vào bộ nhớ flash. Trường_sfudBufferPos
lưu vị trí hiện tại để ghi vào bộ đệm này, và_sfudBufferWritePos
lưu vị trí trong bộ nhớ flash để ghi vào._flash
là một con trỏ đến bộ nhớ flash để ghi - một số vi điều khiển có nhiều chip bộ nhớ flash. -
Thêm phương thức sau vào phần
public
để khởi tạo lớp này:void init() { _flash = sfud_get_device_table() + 0; _sfudBufferSize = _flash->chip.erase_gran; _sfudBuffer = new byte[_sfudBufferSize]; _sfudBufferPos = 0; _sfudBufferWritePos = 0; }
Phương thức này cấu hình bộ nhớ flash trên Wio Terminal để ghi vào, và thiết lập các bộ đệm dựa trên kích thước hạt của bộ nhớ flash. Phương thức này nằm trong một phương thức
init
, thay vì một constructor, vì nó cần được gọi sau khi bộ nhớ flash đã được thiết lập trong hàmsetup
. -
Thêm đoạn mã sau vào phần
public
:void writeSfudBuffer(byte b) { _sfudBuffer[_sfudBufferPos++] = b; if (_sfudBufferPos == _sfudBufferSize) { sfud_erase_write(_flash, _sfudBufferWritePos, _sfudBufferSize, _sfudBuffer); _sfudBufferWritePos += _sfudBufferSize; _sfudBufferPos = 0; } } void writeSfudBuffer(byte *b, size_t len) { for (size_t i = 0; i < len; ++i) { writeSfudBuffer(b[i]); } } void flushSfudBuffer() { if (_sfudBufferPos > 0) { sfud_erase_write(_flash, _sfudBufferWritePos, _sfudBufferSize, _sfudBuffer); _sfudBufferWritePos += _sfudBufferSize; _sfudBufferPos = 0; } }
Đoạn mã này định nghĩa các phương thức để ghi byte vào hệ thống lưu trữ flash. Nó hoạt động bằng cách ghi vào một bộ đệm trong bộ nhớ có kích thước phù hợp với bộ nhớ flash, và khi bộ đệm này đầy, nó sẽ được ghi vào bộ nhớ flash, xóa bất kỳ dữ liệu hiện có tại vị trí đó. Ngoài ra còn có một phương thức
flushSfudBuffer
để ghi một bộ đệm chưa đầy, vì dữ liệu được ghi lại sẽ không phải là bội số chính xác của kích thước hạt, nên phần cuối của dữ liệu cần được ghi.💁 Phần cuối của dữ liệu sẽ ghi thêm dữ liệu không mong muốn, nhưng điều này không sao vì chỉ dữ liệu cần thiết sẽ được đọc.
Nhiệm vụ - thiết lập ghi âm
-
Tạo một tệp mới trong thư mục
src
có tên làconfig.h
. -
Thêm đoạn mã sau vào đầu tệp này:
#pragma once #define RATE 16000 #define SAMPLE_LENGTH_SECONDS 4 #define SAMPLES RATE * SAMPLE_LENGTH_SECONDS #define BUFFER_SIZE (SAMPLES * 2) + 44 #define ADC_BUF_LEN 1600
Đoạn mã này thiết lập một số hằng số cho việc ghi âm.
Hằng số Giá trị Mô tả RATE 16000 Tần số mẫu cho âm thanh. 16.000 là 16KHz SAMPLE_LENGTH_SECONDS 4 Độ dài của âm thanh cần ghi. Được đặt là 4 giây. Để ghi âm lâu hơn, tăng giá trị này. SAMPLES 64000 Tổng số mẫu âm thanh sẽ được ghi lại. Được đặt là tần số mẫu * số giây BUFFER_SIZE 128044 Kích thước của bộ đệm âm thanh để ghi. Âm thanh sẽ được ghi dưới dạng tệp WAV, bao gồm 44 byte tiêu đề, sau đó là 128.000 byte dữ liệu âm thanh (mỗi mẫu là 2 byte) ADC_BUF_LEN 1600 Kích thước của các bộ đệm được sử dụng để ghi âm từ DMAC 💁 Nếu bạn thấy 4 giây là quá ngắn để yêu cầu bộ đếm thời gian, bạn có thể tăng giá trị
SAMPLE_LENGTH_SECONDS
, và tất cả các giá trị khác sẽ được tính toán lại. -
Tạo một tệp mới trong thư mục
src
có tên làmic.h
. -
Thêm đoạn mã sau vào đầu tệp này:
#pragma once #include <Arduino.h> #include "config.h" #include "flash_writer.h"
Đoạn mã này bao gồm một số tệp tiêu đề cần thiết, bao gồm các tệp tiêu đề
config.h
vàFlashWriter
. -
Thêm đoạn mã sau để định nghĩa một lớp
Mic
có thể ghi từ micro:class Mic { public: Mic() { _isRecording = false; _isRecordingReady = false; } void startRecording() { _isRecording = true; _isRecordingReady = false; } bool isRecording() { return _isRecording; } bool isRecordingReady() { return _isRecordingReady; } private: volatile bool _isRecording; volatile bool _isRecordingReady; FlashWriter _writer; }; Mic mic;
Lớp này hiện chỉ có một vài trường để theo dõi xem việc ghi âm đã bắt đầu hay chưa, và liệu một bản ghi đã sẵn sàng để sử dụng hay chưa. Khi DMAC được thiết lập, nó liên tục ghi vào các bộ đệm bộ nhớ, vì vậy cờ
_isRecording
xác định liệu các bộ đệm này có nên được xử lý hay bỏ qua. Cờ_isRecordingReady
sẽ được đặt khi 4 giây âm thanh cần thiết đã được ghi lại. Trường_writer
được sử dụng để lưu dữ liệu âm thanh vào bộ nhớ flash.Một biến toàn cục sau đó được khai báo cho một thể hiện của lớp
Mic
. -
Thêm đoạn mã sau vào phần
private
của lớpMic
:typedef struct { uint16_t btctrl; uint16_t btcnt; uint32_t srcaddr; uint32_t dstaddr; uint32_t descaddr; } dmacdescriptor; // Globals - DMA and ADC volatile dmacdescriptor _wrb[DMAC_CH_NUM] __attribute__((aligned(16))); dmacdescriptor _descriptor_section[DMAC_CH_NUM] __attribute__((aligned(16))); dmacdescriptor _descriptor __attribute__((aligned(16))); void configureDmaAdc() { // Configure DMA to sample from ADC at a regular interval (triggered by timer/counter) DMAC->BASEADDR.reg = (uint32_t)_descriptor_section; // Specify the location of the descriptors DMAC->WRBADDR.reg = (uint32_t)_wrb; // Specify the location of the write back descriptors DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf); // Enable the DMAC peripheral DMAC->Channel[1].CHCTRLA.reg = DMAC_CHCTRLA_TRIGSRC(TC5_DMAC_ID_OVF) | // Set DMAC to trigger on TC5 timer overflow DMAC_CHCTRLA_TRIGACT_BURST; // DMAC burst transfer _descriptor.descaddr = (uint32_t)&_descriptor_section[1]; // Set up a circular descriptor _descriptor.srcaddr = (uint32_t)&ADC1->RESULT.reg; // Take the result from the ADC0 RESULT register _descriptor.dstaddr = (uint32_t)_adc_buf_0 + sizeof(uint16_t) * ADC_BUF_LEN; // Place it in the adc_buf_0 array _descriptor.btcnt = ADC_BUF_LEN; // Beat count _descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | // Beat size is HWORD (16-bits) DMAC_BTCTRL_DSTINC | // Increment the destination address DMAC_BTCTRL_VALID | // Descriptor is valid DMAC_BTCTRL_BLOCKACT_SUSPEND; // Suspend DMAC channel 0 after block transfer memcpy(&_descriptor_section[0], &_descriptor, sizeof(_descriptor)); // Copy the descriptor to the descriptor section _descriptor.descaddr = (uint32_t)&_descriptor_section[0]; // Set up a circular descriptor _descriptor.srcaddr = (uint32_t)&ADC1->RESULT.reg; // Take the result from the ADC0 RESULT register _descriptor.dstaddr = (uint32_t)_adc_buf_1 + sizeof(uint16_t) * ADC_BUF_LEN; // Place it in the adc_buf_1 array _descriptor.btcnt = ADC_BUF_LEN; // Beat count _descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | // Beat size is HWORD (16-bits) DMAC_BTCTRL_DSTINC | // Increment the destination address DMAC_BTCTRL_VALID | // Descriptor is valid DMAC_BTCTRL_BLOCKACT_SUSPEND; // Suspend DMAC channel 0 after block transfer memcpy(&_descriptor_section[1], &_descriptor, sizeof(_descriptor)); // Copy the descriptor to the descriptor section // Configure NVIC NVIC_SetPriority(DMAC_1_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for DMAC1 to 0 (highest) NVIC_EnableIRQ(DMAC_1_IRQn); // Connect DMAC1 to Nested Vector Interrupt Controller (NVIC) // Activate the suspend (SUSP) interrupt on DMAC channel 1 DMAC->Channel[1].CHINTENSET.reg = DMAC_CHINTENSET_SUSP; // Configure ADC ADC1->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_AIN12_Val; // Set the analog input to ADC0/AIN2 (PB08 - A4 on Metro M4) while (ADC1->SYNCBUSY.bit.INPUTCTRL) ; // Wait for synchronization ADC1->SAMPCTRL.bit.SAMPLEN = 0x00; // Set max Sampling Time Length to half divided ADC clock pulse (2.66us) while (ADC1->SYNCBUSY.bit.SAMPCTRL) ; // Wait for synchronization ADC1->CTRLA.reg = ADC_CTRLA_PRESCALER_DIV128; // Divide Clock ADC GCLK by 128 (48MHz/128 = 375kHz) ADC1->CTRLB.reg = ADC_CTRLB_RESSEL_12BIT | // Set ADC resolution to 12 bits ADC_CTRLB_FREERUN; // Set ADC to free run mode while (ADC1->SYNCBUSY.bit.CTRLB) ; // Wait for synchronization ADC1->CTRLA.bit.ENABLE = 1; // Enable the ADC while (ADC1->SYNCBUSY.bit.ENABLE) ; // Wait for synchronization ADC1->SWTRIG.bit.START = 1; // Initiate a software trigger to start an ADC conversion while (ADC1->SYNCBUSY.bit.SWTRIG) ; // Wait for synchronization // Enable DMA channel 1 DMAC->Channel[1].CHCTRLA.bit.ENABLE = 1; // Configure Timer/Counter 5 GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | // Enable peripheral channel for TC5 GCLK_PCHCTRL_GEN_GCLK1; // Connect generic clock 0 at 48MHz TC5->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ; // Set TC5 to Match Frequency (MFRQ) mode TC5->COUNT16.CC[0].reg = 3000 - 1; // Set the trigger to 16 kHz: (4Mhz / 16000) - 1 while (TC5->COUNT16.SYNCBUSY.bit.CC0) ; // Wait for synchronization // Start Timer/Counter 5 TC5->COUNT16.CTRLA.bit.ENABLE = 1; // Enable the TC5 timer while (TC5->COUNT16.SYNCBUSY.bit.ENABLE) ; // Wait for synchronization } uint16_t _adc_buf_0[ADC_BUF_LEN]; uint16_t _adc_buf_1[ADC_BUF_LEN];
Đoạn mã này định nghĩa một phương thức
configureDmaAdc
để cấu hình DMAC, kết nối nó với ADC và thiết lập để điền vào hai bộ đệm xen kẽ,_adc_buf_0
và_adc_buf_1
.💁 Một trong những nhược điểm của phát triển vi điều khiển là sự phức tạp của mã cần thiết để tương tác với phần cứng, vì mã của bạn chạy ở mức rất thấp, tương tác trực tiếp với phần cứng. Mã này phức tạp hơn so với những gì bạn sẽ viết cho một máy tính đơn bo hoặc máy tính để bàn vì không có hệ điều hành hỗ trợ. Có một số thư viện có sẵn có thể đơn giản hóa điều này, nhưng vẫn có rất nhiều sự phức tạp.
-
Bên dưới đoạn mã này, thêm đoạn mã sau:
// WAV files have a header. This struct defines that header struct wavFileHeader { char riff[4]; /* "RIFF" */ long flength; /* file length in bytes */ char wave[4]; /* "WAVE" */ char fmt[4]; /* "fmt " */ long chunk_size; /* size of FMT chunk in bytes (usually 16) */ short format_tag; /* 1=PCM, 257=Mu-Law, 258=A-Law, 259=ADPCM */ short num_chans; /* 1=mono, 2=stereo */ long srate; /* Sampling rate in samples per second */ long bytes_per_sec; /* bytes per second = srate*bytes_per_samp */ short bytes_per_samp; /* 2=16-bit mono, 4=16-bit stereo */ short bits_per_samp; /* Number of bits per sample */ char data[4]; /* "data" */ long dlength; /* data length in bytes (filelength - 44) */ }; void initBufferHeader() { wavFileHeader wavh; strncpy(wavh.riff, "RIFF", 4); strncpy(wavh.wave, "WAVE", 4); strncpy(wavh.fmt, "fmt ", 4); strncpy(wavh.data, "data", 4); wavh.chunk_size = 16; wavh.format_tag = 1; // PCM wavh.num_chans = 1; // mono wavh.srate = RATE; wavh.bytes_per_sec = (RATE * 1 * 16 * 1) / 8; wavh.bytes_per_samp = 2; wavh.bits_per_samp = 16; wavh.dlength = RATE * 2 * 1 * 16 / 2; wavh.flength = wavh.dlength + 44; _writer.writeSfudBuffer((byte *)&wavh, 44); }
Đoạn mã này định nghĩa tiêu đề WAV dưới dạng một cấu trúc chiếm 44 byte bộ nhớ. Nó ghi thông tin chi tiết vào tiêu đề về tần số, kích thước và số kênh của tệp âm thanh. Tiêu đề này sau đó được ghi vào bộ nhớ flash.
-
Bên dưới đoạn mã này, thêm đoạn mã sau để khai báo một phương thức được gọi khi các bộ đệm âm thanh sẵn sàng để xử lý:
void audioCallback(uint16_t *buf, uint32_t buf_len) { static uint32_t idx = 44; if (_isRecording) { for (uint32_t i = 0; i < buf_len; i++) { int16_t audio_value = ((int16_t)buf[i] - 2048) * 16; _writer.writeSfudBuffer(audio_value & 0xFF); _writer.writeSfudBuffer((audio_value >> 8) & 0xFF); } idx += buf_len; if (idx >= BUFFER_SIZE) { _writer.flushSfudBuffer(); idx = 44; _isRecording = false; _isRecordingReady = true; } } }
Các bộ đệm âm thanh là các mảng số nguyên 16-bit chứa âm thanh từ ADC. ADC trả về các giá trị không dấu 12-bit (0-1023), vì vậy các giá trị này cần được chuyển đổi thành các giá trị có dấu 16-bit, sau đó chuyển đổi thành 2 byte để lưu trữ dưới dạng dữ liệu nhị phân thô.
Các byte này được ghi vào các bộ đệm bộ nhớ flash. Việc ghi bắt đầu từ chỉ số 44 - đây là độ lệch từ 44 byte đã được ghi dưới dạng tiêu đề tệp WAV. Khi tất cả các byte cần thiết cho độ dài âm thanh yêu cầu đã được ghi lại, dữ liệu còn lại sẽ được ghi vào bộ nhớ flash.
-
Trong phần
public
của lớpMic
, thêm đoạn mã sau:void dmaHandler() { static uint8_t count = 0; if (DMAC->Channel[1].CHINTFLAG.bit.SUSP) { DMAC->Channel[1].CHCTRLB.reg = DMAC_CHCTRLB_CMD_RESUME; DMAC->Channel[1].CHINTFLAG.bit.SUSP = 1; if (count) { audioCallback(_adc_buf_0, ADC_BUF_LEN); } else { audioCallback(_adc_buf_1, ADC_BUF_LEN); } count = (count + 1) % 2; } }
Đoạn mã này sẽ được DMAC gọi để thông báo cho mã của bạn xử lý các bộ đệm. Nó kiểm tra xem có dữ liệu để xử lý hay không, và gọi phương thức
audioCallback
với bộ đệm tương ứng. -
Bên ngoài lớp, sau khai báo
Mic mic;
, thêm đoạn mã sau:void DMAC_1_Handler() { mic.dmaHandler(); }
Hàm
DMAC_1_Handler
sẽ được DMAC gọi khi các bộ đệm sẵn sàng để xử lý. Hàm này được tìm theo tên, vì vậy chỉ cần tồn tại để được gọi. -
Thêm hai phương thức sau vào phần
public
của lớpMic
:void init() { analogReference(AR_INTERNAL2V23); _writer.init(); initBufferHeader(); configureDmaAdc(); } void reset() { _isRecordingReady = false; _isRecording = false; _writer.reset(); initBufferHeader(); }
Phương thức
init
chứa mã để khởi tạo lớpMic
. Phương thức này thiết lập điện áp chính xác cho chân Mic, thiết lập trình ghi bộ nhớ flash, ghi tiêu đề tệp WAV, và cấu hình DMAC. Phương thứcreset
đặt lại bộ nhớ flash và ghi lại tiêu đề sau khi âm thanh đã được ghi và sử dụng.
Nhiệm vụ - ghi âm
-
Trong tệp
main.cpp
, thêm chỉ thịinclude
cho tệp tiêu đềmic.h
:#include "mic.h"
-
Trong hàm
setup
, khởi tạo nút C. Việc ghi âm sẽ bắt đầu khi nút này được nhấn và tiếp tục trong 4 giây:pinMode(WIO_KEY_C, INPUT_PULLUP);
-
Bên dưới đoạn mã này, khởi tạo micro, sau đó in ra console rằng âm thanh đã sẵn sàng để ghi:
mic.init(); Serial.println("Ready.");
-
Phía trên hàm
loop
, định nghĩa một hàm để xử lý âm thanh đã ghi. Hiện tại hàm này không làm gì, nhưng sau này trong bài học, nó sẽ gửi âm thanh để chuyển đổi thành văn bản:void processAudio() { }
-
Thêm đoạn mã sau vào hàm
loop
:void loop() { if (digitalRead(WIO_KEY_C) == LOW && !mic.isRecording()) { Serial.println("Starting recording..."); mic.startRecording(); } if (!mic.isRecording() && mic.isRecordingReady()) { Serial.println("Finished recording"); processAudio(); mic.reset(); } }
Đoạn mã này kiểm tra nút C, và nếu nút này được nhấn và việc ghi âm chưa bắt đầu, thì trường
_isRecording
của lớpMic
được đặt thành true. Điều này sẽ khiến phương thứcaudioCallback
của lớpMic
lưu trữ âm thanh cho đến khi 4 giây đã được ghi lại. Khi 4 giây âm thanh đã được ghi lại, trường_isRecording
được đặt thành false, và trường_isRecordingReady
được đặt thành true. Trường này sau đó được kiểm tra trong hàmloop
, và khi đúng, hàmprocessAudio
được gọi, sau đó lớp mic được đặt lại. -
Biên dịch mã này, tải nó lên Wio Terminal của bạn và kiểm tra qua serial monitor. Nhấn nút C (nút ở phía bên trái, gần công tắc nguồn nhất), và nói. 4 giây âm thanh sẽ được ghi lại.
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time --- More details at http://bit.ly/pio-monitor-filters --- Miniterm on /dev/cu.usbmodem1101 9600,8,N,1 --- --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- Ready. Starting recording... Finished recording
💁 Bạn có thể tìm thấy mã này trong thư mục code-record/wio-terminal. Chương trình ghi âm của bạn đã thành công!
Tuyên bố miễn trừ trách nhiệm:
Tài liệu này đã được dịch bằng dịch vụ dịch thuật AI Co-op Translator. Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng các bản dịch tự động có thể chứa lỗi hoặc không chính xác. Tài liệu gốc bằng ngôn ngữ bản địa nên được coi là nguồn thông tin chính thức. Đối với các thông tin quan trọng, khuyến nghị sử dụng dịch vụ dịch thuật chuyên nghiệp bởi con người. Chúng tôi không chịu trách nhiệm cho bất kỳ sự hiểu lầm hoặc diễn giải sai nào phát sinh từ việc sử dụng bản dịch này.