pcm.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // Module for interfacing with PCM functionality
  2. #include "module.h"
  3. #include "lauxlib.h"
  4. #include "task/task.h"
  5. #include <string.h>
  6. #include <stdlib.h>
  7. #include "pcm/pcm.h"
  8. #include "pcm/pcm_drv.h"
  9. #define GET_PUD() pud_t *pud = (pud_t *)luaL_checkudata(L, 1, "pcm.driver"); \
  10. cfg_t *cfg = &(pud->cfg);
  11. #define UNREF_CB(_cb) luaL_unref( L, LUA_REGISTRYINDEX, _cb ); \
  12. _cb = LUA_NOREF;
  13. #define COND_REF(_cond) _cond ? luaL_ref( L, LUA_REGISTRYINDEX ) : LUA_NOREF
  14. // task handles
  15. task_handle_t pcm_data_vu_task, pcm_data_play_task, pcm_start_play_task;
  16. static void dispatch_callback( lua_State *L, int self_ref, int cb_ref, int returns )
  17. {
  18. if (cb_ref != LUA_NOREF) {
  19. lua_rawgeti( L, LUA_REGISTRYINDEX, cb_ref );
  20. lua_rawgeti( L, LUA_REGISTRYINDEX, self_ref );
  21. luaL_pcallx( L, 1, returns );
  22. }
  23. }
  24. static int pcm_drv_free( lua_State *L )
  25. {
  26. GET_PUD();
  27. UNREF_CB( cfg->cb_data_ref );
  28. UNREF_CB( cfg->cb_drained_ref );
  29. UNREF_CB( cfg->cb_paused_ref );
  30. UNREF_CB( cfg->cb_stopped_ref );
  31. UNREF_CB( cfg->cb_vu_ref );
  32. UNREF_CB( cfg->self_ref );
  33. if (cfg->bufs[0].data) {
  34. free( cfg->bufs[0].data );
  35. cfg->bufs[0].data = NULL;
  36. }
  37. if (cfg->bufs[1].data) {
  38. free( cfg->bufs[1].data );
  39. cfg->bufs[1].data = NULL;
  40. }
  41. return 0;
  42. }
  43. // Lua: drv:close()
  44. static int pcm_drv_close( lua_State *L )
  45. {
  46. GET_PUD();
  47. pud->drv->close( cfg );
  48. return pcm_drv_free( L );
  49. }
  50. // Lua: drv:stop(self)
  51. static int pcm_drv_stop( lua_State *L )
  52. {
  53. GET_PUD();
  54. // throttle ISR and reader
  55. cfg->isr_throttled = -1;
  56. pud->drv->stop( cfg );
  57. // invalidate the buffers
  58. cfg->bufs[0].empty = cfg->bufs[1].empty = TRUE;
  59. dispatch_callback( L, cfg->self_ref, cfg->cb_stopped_ref, 0 );
  60. return 0;
  61. }
  62. // Lua: drv:pause(self)
  63. static int pcm_drv_pause( lua_State *L )
  64. {
  65. GET_PUD();
  66. // throttle ISR and reader
  67. cfg->isr_throttled = -1;
  68. pud->drv->stop( cfg );
  69. dispatch_callback( L, cfg->self_ref, cfg->cb_paused_ref, 0 );
  70. return 0;
  71. }
  72. static void pcm_start_play( task_param_t param, uint8 prio )
  73. {
  74. lua_State *L = lua_getstate();
  75. pud_t *pud = (pud_t *)param;
  76. cfg_t *cfg = &(pud->cfg);
  77. // stop driver before starting it in case it hasn't been stopped from Lua
  78. pud->drv->stop( cfg );
  79. if (!pud->drv->play( cfg )) {
  80. luaL_error( L, "pcm driver start" );
  81. }
  82. // unthrottle ISR and reader
  83. pud->cfg.isr_throttled = 0;
  84. }
  85. // Lua: drv:play(self, rate)
  86. static int pcm_drv_play( lua_State *L )
  87. {
  88. GET_PUD();
  89. cfg->rate = luaL_optinteger( L, 2, PCM_RATE_8K );
  90. luaL_argcheck( L, (cfg->rate >= PCM_RATE_1K) && (cfg->rate <= PCM_RATE_16K), 2, "invalid bit rate" );
  91. if (cfg->self_ref == LUA_NOREF) {
  92. lua_pushvalue( L, 1 ); // copy self userdata to the top of stack
  93. cfg->self_ref = luaL_ref( L, LUA_REGISTRYINDEX );
  94. }
  95. // schedule actions for play in separate task since drv:play() might have been called
  96. // in the callback fn of pcm_data_play() which in turn gets called when starting play...
  97. task_post_low( pcm_start_play_task, (os_param_t)pud );
  98. return 0;
  99. }
  100. // Lua: drv.on(self, event, cb_fn)
  101. static int pcm_drv_on( lua_State *L )
  102. {
  103. size_t len;
  104. const char *event;
  105. uint8_t is_func = FALSE;
  106. GET_PUD();
  107. event = luaL_checklstring( L, 2, &len );
  108. if (lua_isfunction(L, 3)) {
  109. lua_pushvalue( L, 3 ); // copy argument (func) to the top of stack
  110. is_func = TRUE;
  111. }
  112. if ((len == 4) && (strcmp( event, "data" ) == 0)) {
  113. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_data_ref);
  114. cfg->cb_data_ref = COND_REF( is_func );
  115. } else if ((len == 7) && (strcmp( event, "drained" ) == 0)) {
  116. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_drained_ref);
  117. cfg->cb_drained_ref = COND_REF( is_func );
  118. } else if ((len == 6) && (strcmp( event, "paused" ) == 0)) {
  119. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_paused_ref);
  120. cfg->cb_paused_ref = COND_REF( is_func );
  121. } else if ((len == 7) && (strcmp( event, "stopped" ) == 0)) {
  122. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_stopped_ref);
  123. cfg->cb_stopped_ref = COND_REF( is_func );
  124. } else if ((len == 2) && (strcmp( event, "vu" ) == 0)) {
  125. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_vu_ref);
  126. cfg->cb_vu_ref = COND_REF( is_func );
  127. int freq = luaL_optinteger( L, 4, 10 );
  128. luaL_argcheck( L, (freq > 0) && (freq <= 200), 4, "invalid range" );
  129. cfg->vu_freq = (uint8_t)freq;
  130. } else {
  131. if (is_func) {
  132. // need to pop pushed function arg
  133. lua_pop( L, 1 );
  134. }
  135. return luaL_error( L, "method not supported" );
  136. }
  137. return 0;
  138. }
  139. // Lua: pcm.new( type, pin )
  140. static int pcm_new( lua_State *L )
  141. {
  142. pud_t *pud = (pud_t *) lua_newuserdata( L, sizeof( pud_t ) );
  143. cfg_t *cfg = &(pud->cfg);
  144. int driver;
  145. cfg->rbuf_idx = cfg->fbuf_idx = 0;
  146. cfg->isr_throttled = -1; // start ISR and reader in throttled mode
  147. driver = luaL_checkinteger( L, 1 );
  148. luaL_argcheck( L, (driver >= 0) && (driver < PCM_DRIVER_END), 1, "invalid driver" );
  149. cfg->self_ref = LUA_NOREF;
  150. cfg->cb_data_ref = cfg->cb_drained_ref = LUA_NOREF;
  151. cfg->cb_paused_ref = cfg->cb_stopped_ref = LUA_NOREF;
  152. cfg->cb_vu_ref = LUA_NOREF;
  153. cfg->bufs[0].buf_size = cfg->bufs[1].buf_size = 0;
  154. cfg->bufs[0].data = cfg->bufs[1].data = NULL;
  155. cfg->bufs[0].len = cfg->bufs[1].len = 0;
  156. cfg->bufs[0].rpos = cfg->bufs[1].rpos = 0;
  157. cfg->bufs[0].empty = cfg->bufs[1].empty = TRUE;
  158. cfg->vu_freq = 10;
  159. if (driver == PCM_DRIVER_SD) {
  160. cfg->pin = luaL_checkinteger( L, 2 );
  161. MOD_CHECK_ID(sigma_delta, cfg->pin);
  162. pud->drv = &pcm_drv_sd;
  163. pud->drv->init( cfg );
  164. /* set its metatable */
  165. lua_pushvalue( L, -1 ); // copy self userdata to the top of stack
  166. luaL_getmetatable( L, "pcm.driver" );
  167. lua_setmetatable( L, -2 );
  168. return 1;
  169. } else {
  170. pud->drv = NULL;
  171. return 0;
  172. }
  173. }
  174. LROT_BEGIN(pcm_driver, NULL, LROT_MASK_GC_INDEX)
  175. LROT_FUNCENTRY( __gc, pcm_drv_free )
  176. LROT_TABENTRY( __index, pcm_driver )
  177. LROT_FUNCENTRY( play, pcm_drv_play )
  178. LROT_FUNCENTRY( pause, pcm_drv_pause )
  179. LROT_FUNCENTRY( stop, pcm_drv_stop )
  180. LROT_FUNCENTRY( close, pcm_drv_close )
  181. LROT_FUNCENTRY( on, pcm_drv_on )
  182. LROT_END(pcm_driver, NULL, LROT_MASK_GC_INDEX)
  183. // Module function map
  184. LROT_BEGIN(pcm, NULL, 0)
  185. LROT_FUNCENTRY( new, pcm_new )
  186. LROT_NUMENTRY( SD, PCM_DRIVER_SD )
  187. LROT_NUMENTRY( RATE_1K, PCM_RATE_1K )
  188. LROT_NUMENTRY( RATE_2K, PCM_RATE_2K )
  189. LROT_NUMENTRY( RATE_4K, PCM_RATE_4K )
  190. LROT_NUMENTRY( RATE_5K, PCM_RATE_5K )
  191. LROT_NUMENTRY( RATE_8K, PCM_RATE_8K )
  192. LROT_NUMENTRY( RATE_10K, PCM_RATE_10K )
  193. LROT_NUMENTRY( RATE_12K, PCM_RATE_12K )
  194. LROT_NUMENTRY( RATE_16K, PCM_RATE_16K )
  195. LROT_END(pcm, NULL, 0)
  196. int luaopen_pcm( lua_State *L ) {
  197. luaL_rometatable( L, "pcm.driver", LROT_TABLEREF(pcm_driver));
  198. pcm_data_vu_task = task_get_id( pcm_data_vu );
  199. pcm_data_play_task = task_get_id( pcm_data_play );
  200. pcm_start_play_task = task_get_id( pcm_start_play );
  201. return 0;
  202. }
  203. NODEMCU_MODULE(PCM, "pcm", pcm, luaopen_pcm);