Problem
Read this section if you wish to capture acoustic signals using devices other than the sound cards supported by ALSA (Advanced Linux Sound Architecture), the RASP series (System In Frontier, Inc.), and TD-BD-16ADUSB (Tokyo Electron Device), all of which are supported by HARK as standards.
Solution
An A/D converter can be used by a corresponding node created by the user. We describe here a procedure for creating an AudioStreamFromMic node that supports, for example, NewDevice. The overall procedure consists of
Creation of a class NewDeviceRecorder corresponding to the device and placing its source and header files into the librecorder directory in the HARK directory.
Rewriting AudioStreamFromMic so that the created class can be used.
Rewriting Makefile.am, configure.in to compile.
In (a), a class is created that takes data from the device and sends them to a buffer. This class is regarded as the successor to the Recorder class. Initialization prepares the device for use, followed by the operator() method, which removes data from the device. In (b), processing is performed when “NewDevice” is designated as the option is described. The NewDevice is designated and initialized in the constructor, and the signals are set. In (c), Makefile.am and configure.in are changed to correspond to the newly added file. Described below is a sample of AudioStreamFromMic 2, which supports a new device (NewDevice)
#include "BufferedNode.h" #include "Buffer.h" #include "Vector.h" #include <climits> #include <csignal> # include <NewDeviceRecorder.hpp> // Point1 Read a required header file using namespace std; using namespace FD; class AudioStreamFromMic2; DECLARE_NODE( AudioStreamFromMic2); /*Node * * @name AudioStreamFromMic2 * @category MyHARK * @description This node captures an audio stream using microphones and outputs frames. * * @output_name AUDIO * @output_type Matrix<float> * @output_description Windowed wave form. A row index is a channel, and a column index is time. * * @output_name NOT_EOF * @output_type bool * @output_description True if we haven't reach the end of file yet. * * @parameter_name LENGTH * @parameter_type int * @parameter_value 512 * @parameter_description The length of a frame in one channel (in samples). * * @parameter_name ADVANCE * @parameter_type int * @parameter_value 160 * @parameter_description The shift length between adjacent frames (in samples). * * @parameter_name CHANNEL_COUNT * @parameter_type int * @parameter_value 16 * @parameter_description The number of channels. * * @parameter_name SAMPLING_RATE * @parameter_type int * @parameter_value 16000 * @parameter_description Sampling rate (Hz). * * @parameter_name DEVICETYPE // Point2-1 Add the type of a device to be used * @parameter_type string * @parameter_value NewDevice * @parameter_description Device type. * * @parameter_name DEVICE // Point2-2 Add the name of a device to be used * @parameter_type string * @parameter_value /dev/newdevice * @parameter_description The name of device. END*/ // Point3 Describe processing to stop sound recording in the middle void sigint_handler_newdevice(int s) {Recorder* recorder = NewDeviceRecorder GetInstance(); recorder->Stop(); exit(0); } class AudioStreamFromMic2 public BufferedNode { int audioID; int eofID; int length; int advance; int channel_count; int sampling_rate; string device_type; string device; Recorder* recorder; vector<short> buffer; public AudioStreamFromMic2(string nodeName, ParameterSet params) BufferedNode(nodeName, params), recorder(0) { audioID = addOutput("AUDIO"); eofID = addOutput("NOT_EOF"); length = dereference_cast<int> (parameters.get("LENGTH")); advance = dereference_cast<int> (parameters.get("ADVANCE")); channel_count = dereference_cast<int> (parameters.get("CHANNEL_COUNT")); sampling_rate = dereference_cast<int> (parameters.get("SAMPLING_RATE")); device_type = object_cast<String> (parameters.get("DEVICETYPE")); device = object_cast<String> (parameters.get("DEVICE")); // Point4 Create a recorder class corresponding to the device type if (device_type == "NewDevice") {recorder = NewDeviceRecorder GetInstance(); recorder->Initialize(device.c_str(), channel_count, sampling_rate, length * 1024);} else { throw new NodeException(NULL, string("Device type " + device_type + " is not supported."), __FILE__, __LINE__);} inOrder = true;} virtual void initialize() {outputs[audioID]. lookAhead = outputs[eofID]. lookAhead = 1 + max(outputs[audioID]. lookAhead, outputs[eofID]. lookAhead); this->BufferedNode initialize();} virtual void stop() {recorder->Stop();} void calculate(int output_id, int count, Buffer &out) {Buffer &audioBuffer = *(outputs[audioID]. buffer); Buffer &eofBuffer = *(outputs[eofID]. buffer); eofBuffer[count] = TrueObject; RCPtr<Matrix<float> > outputp(new Matrix<float> (channel_count, length)); audioBuffer[count] = outputp; Matrix<float>& output = *outputp; if (count == 0) { //Done only the first time recorder->Start(); buffer.resize(length * channel_count); Recorder BUFFER_STATE state; do {usleep(5000); state = recorder->ReadBuffer(0, length, buffer.begin());} while (state != Recorder OK); // original convertVectorToMatrix(buffer, output, 0);} else { // Normal case (not at start of file) if (advance < length) {Matrix<float>& previous = object_cast<Matrix<float> > ( for (int c = 0; c < length - advance; c++) {for (int r = 0;r < output.nrows();r++) {output(r, c)= previous(r, c + advance);}}} else {for (int c = 0;c < length - advance;c++) {for (int r = 0;r < output.nrows(); r++) {output(r, c)= 0;}}} buffer.resize(advance * channel_count); Recorder BUFFER_STATE state; for (;;) { state = recorder->ReadBuffer((count - 1) * advance + length, advance, buffer.begin()); if (state == Recorder OK) {break;} else {usleep(5000);} } int first_output = length - advance; convertVectorToMatrix(buffer, output, first_output); } bool is_clipping = false; for (int i = 0; i < buffer.size(); i++) { if (!is_clipping && checkClipping(buffer[i])) { is_clipping = true; } } if (is_clipping) { cerr << "[" << count << "][" << getName() << "] clipping" << endl; } } protected void convertVectorToMatrix(const vector<short>& in, Matrix<float>& out, int first_col) { for (int i = 0; i < out.nrows(); i++) { for (int j = first_col; j < out.ncols(); j++) { out(i, j) = (float) in[i + (j - first_col) * out.nrows()]; } } } bool checkClipping(short x) { if (x >= SHRT_MAX || x <= SHRT_MIN) { return true; } else { return false; } } };
The following is the outline of a source code of the class that was spun off from the Recorder class, enabling the NewDevice to be used. We describe the processing required to connect with the device with an initialize function and to read data from the device to the () operator. This source code (NewDeviceRecorder.cpp) is created in the librecorder Folder.
#include "NewDeviceRecorder.hpp" using namespace boost; NewDeviceRecorder* NewDeviceRecorder instance = 0; // This function is executed in another thread, and // records acoustic signals into circular buffer. void NewDeviceRecorder operator()() { for(;;) { // wait during less than (read_buf_size/sampling_rate) [ms] usleep(sleep_time); mutex scoped_lock lk(mutex_buffer); if (state == PAUSE) { continue; } else if (state == STOP) { break; } else if (state == RECORDING) { lk.unlock(); // Point5 Processing to read data from the device is described here. read_buf = receive_data; // Point6 Forward cur_time for the time orresponding to the data read so far. cur_time += timelength_of_read_data; mutex scoped_lock lk(mutex_buffer); buffer.insert(buffer.end(), read_buf.begin(), read_buf.begin() + buff_len); lk.unlock(); } } } int NewDeviceRecorder Initialize(const string& device_name, int chan_count, int samp_rate, size_t buf_size) { // Point7 Processing to initialize variables and devices to be used is described. new_device_open_function }
Discussion
The AudioStreamFromMic module performs only the processing required to read the contents of the buffer belonging to the Recorder class. Data exchange with devices are performed by each class (ALSARecorder, ASIORecorder, and WSRecorder) derived from the Recorder class. Therefore, a new device can be supported by implementing a class corresponding to these derived classes (NewDeviceRecorder).
See Also
The details of constructing a node are described in Section 12.1, “How to create nodes?”.