RDK Documentation (Open Sourced RDK Components)
music_id.cpp
1 /*
2  * If not stated otherwise in this file or this component's Licenses.txt file the
3  * following copyright and licenses apply:
4  *
5  * Copyright 2016 RDK Management
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18 */
19 #include "music_id.h"
20 #include "audio_converter.h"
21 #include <unistd.h>
22 #include <stdint.h>
23 
24 #define SOCKET_PATH "/tmp/acm-songid"
25 
26 using namespace audiocapturemgr;
27 const unsigned int DEFAULT_PRECAPTURE_DURATION_SEC = 6;
28 static unsigned int ticker = 0;
29 static void connected_callback(void * data)
30 {
31  music_id_client * ptr = static_cast <music_id_client *> (data);
32  ptr->send_clip_via_socket();
33 }
34 
35 music_id_client::music_id_client(q_mgr * manager, preferred_delivery_method_t mode) : audio_capture_client(manager), m_worker_thread_alive(true), m_total_size(0),
36  m_queue_upper_limit_bytes(0), m_request_counter(0), m_enable_wav_header_output(false), m_convert_output(false), m_delivery_method(mode), m_sock_path(SOCKET_PATH + get_suffix(ticker++))
37 {
38  DEBUG("Creating instance.\n");
39  set_precapture_duration(DEFAULT_PRECAPTURE_DURATION_SEC);
40  m_output_properties = {racFormat_e16BitMono, racFreq_e48000, 0, 0, 0}; /*Only format and sampling rate matter for conversion*/
41  m_worker_thread = std::thread(&music_id_client::worker_thread, this);
42 
43  if(SOCKET_OUTPUT == m_delivery_method)
44  {
45  INFO("Socket delivery selected for audio clip.\n");
46  m_sock_adaptor = nullptr; //CID:87243 - Intialize a nullptr
47  m_sock_adaptor = new socket_adaptor();
48  m_sock_adaptor->start_listening(m_sock_path);
49  m_sock_adaptor->register_data_ready_callback(connected_callback, this);
50  }
51 }
52 
53 music_id_client::~music_id_client()
54 {
55  DEBUG("Deleting instance.\n");
56  m_worker_thread_alive = false;
57  if(m_worker_thread.joinable())
58  {
59  m_worker_thread.join();
60  }
61 
62  /*Flush all queues.*/
63  INFO("Flushing request queue. Size is %d\n", m_requests.size());
64  std::list<request_t *>::iterator req_iter;
65  for(req_iter = m_requests.begin(); req_iter != m_requests.end(); req_iter++)
66  {
67  delete (*req_iter);
68  }
69  m_requests.clear();
70 
71  INFO("Flushing buffers.\n");
72  std::list<audio_buffer *>::iterator buf_iter;
73  for(buf_iter = m_queue.begin(); buf_iter != m_queue.end(); buf_iter++)
74  {
75  release_buffer(*buf_iter);
76  }
77  m_queue.clear();
78 
79  if(SOCKET_OUTPUT == m_delivery_method)
80  {
81  delete m_sock_adaptor;
82  INFO("Outbox has %d entries. Flushing.\n", m_outbox.size());
83  for(auto & outbox_entry : m_outbox)
84  {
85  delete outbox_entry;
86  }
87  m_outbox.clear();
88  }
89 }
90 
91 int music_id_client::data_callback(audio_buffer *buf)
92 {
93  lock();
94  m_queue.push_back(buf);
95  m_total_size += buf->m_size;
96  unlock();
97  return 0;
98 }
99 
101 {
102  lock();
103  m_precapture_duration_seconds = seconds;
104  m_precapture_size_bytes = seconds * m_manager->get_data_rate();
105  if(m_queue_upper_limit_bytes < m_precapture_size_bytes)
106  {
107  m_queue_upper_limit_bytes = m_precapture_size_bytes;
108  }
109  compute_queue_size();
110  trim_queue();
111  unlock();
112  return 0;
113 }
114 
116 {
117  int ret;
118  ret = audio_capture_client::set_audio_properties(properties);
119  if(0 == ret)
120  {
121  /* Populate bit rate fields.*/
122  m_precapture_size_bytes = m_precapture_duration_seconds * m_manager->get_data_rate();
123  }
124  return ret;
125 }
126 
128 {
129  audio_capture_client::get_audio_properties(properties);
130  if(m_convert_output)
131  {
132  properties.format = m_output_properties.format;
133  properties.sampling_frequency = m_output_properties.sampling_frequency;
134  }
135 }
136 
137 void music_id_client::trim_queue() //Recommend using lock.
138 {
139  int excess_bytes = m_total_size - m_queue_upper_limit_bytes;
140  DEBUG("excess_bytes = %d\n", excess_bytes);
141  while(0 < excess_bytes)
142  {
143  /* Lose buffers until losing any more would take us below the precapture threshold.*/
144  unsigned int current_buffer_size = m_queue.front()->m_size;
145  if((unsigned int)excess_bytes >= current_buffer_size)
146  {
147  excess_bytes -= current_buffer_size;
148  m_total_size -= current_buffer_size;
149  release_buffer((m_queue.front()));
150  m_queue.pop_front();
151  }
152  else
153  {
154  break;
155  }
156  }
157 }
158 
159 
161 {
162  audio_converter_memory_sink * sink_ptr = nullptr;
163  lock();
164  if(0 != m_outbox.size())
165  {
166  sink_ptr = m_outbox.front();
167  m_outbox.pop_front();
168  }
169  unlock();
170 
171  if(sink_ptr)
172  {
173  INFO("Sending clip.\n");
174  m_sock_adaptor->write_data(sink_ptr->get_buffer(), sink_ptr->get_size());
175  delete sink_ptr;
176  INFO("Done sending.\n");
177  }
178  else
179  {
180  WARN("No data in outbox.\n");
181  }
182  m_sock_adaptor->terminate_current_connection();
183 }
184 
185 int music_id_client::grab_precaptured_sample(const std::string &filename)
186 {
187  int ret;
188  lock();
189  if(SOCKET_OUTPUT == m_delivery_method)
190  {
191  ret = grab_last_n_seconds(m_precapture_duration_seconds);
192  }
193  else
194  {
195  ret = grab_last_n_seconds(filename, m_precapture_duration_seconds);
196  }
197  unlock();
198  return ret;
199 }
200 
201 int music_id_client::grab_last_n_seconds(unsigned int seconds) //for socket mode output
202 {
203 
204  int ret = 0;
205  int data_dump_size = seconds * m_manager->get_data_rate();
206 
207  if(0 != m_queue.size())
208  {
209  audio_properties_t in_properties;
210  audio_capture_client::get_audio_properties(in_properties);
212  if(m_convert_output)
213  {
214  sink = new audio_converter_memory_sink(audiocapturemgr::calculate_data_rate(m_output_properties) * (seconds + 1)); //an extra second to account for the imprecise way in which ACM cuts clips
215  audio_converter converter(in_properties, m_output_properties, *sink);
216  converter.convert(m_queue, data_dump_size);
217  }
218  else
219  {
220  sink = new audio_converter_memory_sink(audiocapturemgr::calculate_data_rate(in_properties) * (seconds + 1));
221  audio_converter converter(in_properties, in_properties, *sink);
222  converter.convert(m_queue, data_dump_size);
223  }
224  m_outbox.push_back(sink);
225  INFO("Precaptured sample placed in outbox.\n");
226  }
227  else
228  {
229  ERROR("Error! Precaptured queue is empty.\n");
230  ret = -1;
231  }
232  return ret;
233 }
234 
235 int music_id_client::grab_last_n_seconds(const std::string &filename, unsigned int seconds) //for file mode output
236 {
237  int ret = 0;
238  int data_dump_size = seconds * m_manager->get_data_rate();
239 
240  if(0 != m_queue.size())
241  {
242  std::ofstream file(filename.c_str(), std::ios::binary);
243  if(file.is_open())
244  {
245  if(m_enable_wav_header_output)
246  {
247  write_default_file_header(file);
248  }
249 
250  audio_properties_t in_properties;
251  audio_capture_client::get_audio_properties(in_properties);
252  audio_converter_file_sink sink(file);
253  if(m_convert_output)
254  {
255  audio_converter converter(in_properties, m_output_properties, sink);
256  converter.convert(m_queue, data_dump_size);
257  }
258  else
259  {
260  audio_converter converter(in_properties, in_properties, sink);
261  converter.convert(m_queue, data_dump_size);
262  }
263 
264  if(m_enable_wav_header_output)
265  {
266  unsigned int payload_size = static_cast<unsigned int>(file.tellp()) - 44;
267  update_file_header_size(file, payload_size);
268  }
269  INFO("Precaptured sample written to %s. File size: %lld bytes\n", filename.c_str(), (long long)file.tellp());//CID:127488 - Type cast
270  }
271  else
272  {
273  ERROR("Could not open file %s.\n", filename.c_str());
274  ret = -1;
275  }
276  }
277  else
278  {
279  ERROR("Error! Precaptured queue is empty.\n");
280  ret = -1;
281  }
282  return ret;
283 }
284 
285 music_id_client::request_id_t music_id_client::grab_fresh_sample(unsigned int seconds, const std::string &filename, request_complete_callback_t cb , void * cb_data)
286 {
287  request_id_t id = -1;
288  lock();
289  request_t *req = new request_t;
290  req->id = m_request_counter++;
291  req->filename = filename;
292  req->length = seconds;
293  req->time_remaining = seconds;
294  req->callback = cb;
295  req->callback_data = cb_data;
296  m_requests.push_back(req);
297  compute_queue_size();
298  unlock();
299  return id;
300 }
301 
302 void music_id_client::compute_queue_size() //needs lock
303 {
304  std::list<request_t *>::iterator iter;
305  unsigned int max_length = m_precapture_duration_seconds;
306  for(iter = m_requests.begin(); iter != m_requests.end(); iter++)
307  {
308  if(max_length < (*iter)->length)
309  {
310  max_length = (*iter)->length;
311  }
312  }
313  INFO("New max length for queue: %d\n", max_length);
314  m_queue_upper_limit_bytes = max_length * m_manager->get_data_rate();
315 }
316 
318 {
319  INFO("Enter.\n");
320  while(m_worker_thread_alive)
321  {
322  DEBUG("Tick.\n");
323  lock();
324  std::list<request_t *>::iterator iter = m_requests.begin();
325  while(iter != m_requests.end())
326  {
327  request_t *request = *iter;
328  if(0 == request->time_remaining--)
329  {
330  INFO("Request %d is up.\n", request->id);
331 
332  int ret;
333  if(SOCKET_OUTPUT == m_delivery_method)
334  {
335  ret = grab_last_n_seconds(request->length);
336  }
337  else
338  {
339  ret = grab_last_n_seconds(request->filename, request->length);
340  }
341  if(0 != ret)
342  {
343  ERROR("Failed to fulfil request %d.\n", (*iter)->id);
344  }
345 
346  if(request->callback)
347  {
348  (request->callback)(request->callback_data, request->filename, ret);
349  }
350  delete request;
351  iter = m_requests.erase(iter);
352  compute_queue_size();
353  }
354  else
355  {
356  iter++;
357  }
358  }
359  trim_queue();
360  unlock();
361  sleep(1);
362  }
363  INFO("Exit.\n");
364 }
365 
366 static void write_32byte_little_endian(uint32_t data, std::ofstream &file)
367 {
368  uint8_t buf[4];
369  buf[0] = (uint8_t)(0xFF & data);
370  buf[1] = (uint8_t)(0xFF & (data >> 8));
371  buf[2] = (uint8_t)(0xFF & (data >> 16));
372  buf[3] = (uint8_t)(0xFF & (data >> 24));
373  file.write((const char *)&buf, 4);
374 }
375 
376 #if 0
377 static void write_32data_big_endian(uint32_t data, std::ofstream &file)
378 {
379  uint8_t buf[4];
380  buf[3] = (uint8_t)(0xFF & data);
381  buf[2] = (uint8_t)(0xFF & (data >> 8));
382  buf[1] = (uint8_t)(0xFF & (data >> 16));
383  buf[0] = (uint8_t)(0xFF & (data >> 24));
384  file.write((const char *)&buf, 4);
385 }
386 #endif
387 static void write_16byte_little_endian(uint16_t data, std::ofstream &file)
388 {
389  uint8_t buf[2];
390  buf[0] = (uint8_t)(0xFF & data);
391  buf[1] = (uint8_t)(0xFF & (data >> 8));
392  file.write((const char *)&buf, 2);
393 }
394 
395 #if 0
396 int music_id_client::write_queue_to_file(const std::string &filename) // needs lock
397 {
398  int ret = 0;
399 
400  if(0 != m_queue.size())
401  {
402  std::ofstream file(filename.c_str(), std::ios::binary);
403  if(file.is_open())
404  {
405  write_file_header(file, m_total_size);
406  std::list <audio_buffer *>::iterator iter;
407  for(iter = m_queue.begin(); iter != m_queue.end(); iter++)
408  {
409  file.write((char *)(*iter)->m_start_ptr, (*iter)->m_size);
410  }
411  INFO("Sample written to %s.\n", filename.c_str());
412  }
413  else
414  {
415  ERROR("Could not open file %s.\n", filename.c_str());
416  ret = -1;
417  }
418  }
419  else
420  {
421  ERROR("Error! queue is empty.\n");
422  ret = -1;
423  }
424  return ret;
425 }
426 #endif
427 
428 int music_id_client::write_default_file_header(std::ofstream &file)
429 {
430  /* Write file header chunk.*/
431  file.write("RIFF", 4);
432  uint32_t chunkSize = 0; //Will be populated later.
433  write_32byte_little_endian(chunkSize, file);
434 
435  file.write("WAVE", 4);
436 
437  /* Write fmt sub-chunk */
438  file.write("fmt ", 4);
439  write_32byte_little_endian(16, file);//SubChunk1Size
440  write_16byte_little_endian(1, file);//Audo format PCM
441 
442 
443  unsigned int bits_per_sample = 0;
444  unsigned int sampling_rate= 0;
445  unsigned int num_channels = 0;
446  unsigned int data_rate = 0;
447  if(m_convert_output)
448  {
449  get_individual_audio_parameters(m_output_properties, sampling_rate, bits_per_sample, num_channels);
450  data_rate = sampling_rate * num_channels * bits_per_sample / 8;
451  }
452  else
453  {
454  audio_properties_t properties;
455  get_audio_properties(properties);
456  get_individual_audio_parameters(properties, sampling_rate, bits_per_sample, num_channels);
457  data_rate = m_manager->get_data_rate();
458  }
459  INFO("Header information: %d channel, %dHz, %d bits per sample audio.\n",
460  num_channels, sampling_rate, bits_per_sample);
461  write_16byte_little_endian((uint16_t)num_channels, file);
462  write_32byte_little_endian(sampling_rate, file);
463  write_32byte_little_endian(data_rate, file);
464  write_16byte_little_endian((uint16_t)(num_channels * bits_per_sample / 8), file); //Block align
465  write_16byte_little_endian((uint16_t)bits_per_sample, file);
466 
467  /* Write data sub-chunk header*/
468  file.write("data", 4);
469  write_32byte_little_endian(chunkSize, file); //Will be populated later.
470  return 0;
471 }
472 int music_id_client::update_file_header_size(std::ofstream &file, unsigned int data_size)
473 {
474  INFO("Finalizing file header. Payload size: %dkB.\n", (data_size/1024));
475  std::streampos original_position = file.tellp();
476 
477  /* Update file header chunk size.*/
478  uint32_t chunkSize = 36 + data_size;
479  file.seekp(4);
480  write_32byte_little_endian(chunkSize, file);
481 
482  /* Update data subchunk size.*/
483  file.seekp(40);
484  write_32byte_little_endian(data_size, file);
485  file.seekp(original_position); //return write pointer to original location
486  return 0;
487 }
488 
489 static const unsigned int MAX_PRECAPTURE_LENGTH_SEC = 120;
491 {
492  //TODO: If necessary, make this a run-time decision based on the current data rate of audio.
493  return MAX_PRECAPTURE_LENGTH_SEC;
494 }
495 
496 unsigned int music_id_client::enable_wav_header(bool isEnabled)
497 {
498  m_enable_wav_header_output = isEnabled;
499  return 0;
500 }
audio_capture_client
Definition: audio_capture_manager.h:219
music_id_client::grab_fresh_sample
request_id_t grab_fresh_sample(unsigned int seconds, const std::string &filename=nullptr, request_complete_callback_t cb=nullptr, void *cb_data=nullptr)
This API requests for new sample.
Definition: music_id.cpp:285
music_id_client
Definition: music_id.h:37
racFreq_e48000
@ racFreq_e48000
Definition: rmfAudioCapture.h:88
q_mgr::get_data_rate
unsigned int get_data_rate()
Returns data rate in bytes per second.
Definition: audio_capture_manager.cpp:285
music_id_client::set_precapture_duration
int set_precapture_duration(unsigned int seconds)
This API is used to set the precapture duration.
Definition: music_id.cpp:100
audio_converter_memory_sink
Definition: audio_converter.h:81
socket_adaptor::terminate_current_connection
void terminate_current_connection()
This api invokes close() to terminate the current connection.
Definition: socket_adaptor.cpp:277
audio_buffer
Definition: audio_buffer.h:25
audiocapturemgr::audio_properties_t
Definition: audio_capture_manager.h:56
music_id_client::grab_precaptured_sample
int grab_precaptured_sample(const std::string &filename=nullptr)
This API writes the precaptured sample to a file for file mode and in socket mode,...
Definition: music_id.cpp:185
audio_converter
Definition: audio_converter.h:33
music_id_client::get_audio_properties
virtual void get_audio_properties(audiocapturemgr::audio_properties_t &properties)
Invokes an API for getting the audio specific properties of the audio capture client.
Definition: music_id.cpp:127
socket_adaptor
Definition: socket_adaptor.h:33
music_id_client::enable_wav_header
unsigned int enable_wav_header(bool isEnabled)
This API is to enable/disable WAV header.
Definition: music_id.cpp:496
racFormat_e16BitMono
@ racFormat_e16BitMono
Definition: rmfAudioCapture.h:74
music_id_client::send_clip_via_socket
void send_clip_via_socket()
This API writes the captured clip data to the socket.
Definition: music_id.cpp:160
q_mgr
Definition: audio_capture_manager.h:70
audio_converter_file_sink
Definition: audio_converter.h:68
music_id_client::get_max_supported_duration
unsigned int get_max_supported_duration()
This API returns maximum precaptured length.
Definition: music_id.cpp:490
music_id_client::worker_thread
void worker_thread()
This function manages a queue of requests for music id samples.
Definition: music_id.cpp:317
music_id_client::set_audio_properties
virtual int set_audio_properties(audiocapturemgr::audio_properties_t &properties)
Invokes an API for setting the audio specific properties of the audio capture client.
Definition: music_id.cpp:115
socket_adaptor::write_data
int write_data(const char *buffer, const unsigned int size)
This api invokes unix write() to write data to the socket.
Definition: socket_adaptor.cpp:64
music_id_client::request_t
Definition: music_id.h:48