75185


spresenseメインボードとB-stem PDA01
SPRESENSE用機能拡張ボードを用いて開発を行っています。実現したいことは、超音波信号を再生し、その伝播や反響を取得するために96kHzサンプリングで信号を再生し、同時に96kHz以上（おそらく、192kHz)で録音をしたいと思っています。  
素人質問で申し訳ないですが、このような機能は実現可能か知りたいです。

できればarduinoにて開発したいのですが、マルチコア処理ですとaudio libraryは使えないとなっていました。audio
libraryに信号の再生と録音を同時に行う機能は含まれていますか？  
もし、上記が実現できない場合はspresense SDKによる開発で実現可能でしょうか？  
よろしくお願いします。


CC BY-SA 4.0
2021-04-07T04:10:14.333
75132
2021-04-12T02:20:16.560
2021-04-07T16:50:52.940
3060
44678
question
spresense
spresenseで96kHzサンプリングレートにて音声の再生と録音を同時に行いたい


SPRESENSEには録音・再生のサンプル（voice_effector）はありますが、再生・録音は見当たらないですね（あまりそのようなユースケースがないからかと…）。

ただ、SPRESENSEのオーディオ出力は、人が聴くことができる20-20kHzに最適化されていると思いますので、超音波音源を再生しても十分なゲインを持った超音波出力を得ることは難しいと思います。

超音波信号を扱うには、超音波の帯域まで対応しているトランスミッターとレシーバーを使うのがよいと思います。その場合、超音波の生成には、SPRESENSEのPWMが使えます。

例えば、SPRESENSEで40kHzのPWMのパルスを生成し、それを超音波トランスミッターで出力。そのパルスを超音波レシーバーで受け、オペアンプで増幅し、SPRESENSEのマイク入力(±45mV)でアナログ波形をとれば、超音波パルスの反響・伝搬の様子をとらえることができると思います。

試しに、36cmと72cmに障害物を置いてやってみたら、2msec、4msecあたりに２つの山が観測できたので、SLAMのようなことができるかもしれませんね。

SPRESENSEを使った例ではありませんが、増幅回路の構成は次のサイトが参考になると思います。  
  
超音波センサーで物体の距離を測ってみます  
<http://zattouka.net/GarageHouse/micon/MPLAB/16F1827/USS/USS.htm>  
  
  
また、Arduino IDE による PWM の生成については、私が過去に Stackoverflow
で回答していますので、参考にしてください。（今は、pwm_devinit() は不要なようです）

[Arduino
IDEでPWM出力を15Bitで扱う方法](https://ja.stackoverflow.com/questions/65475/arduino-
ide%e3%81%a7pwm%e5%87%ba%e5%8a%9b%e3%82%9215bit%e3%81%a7%e6%89%b1%e3%81%86%e6%96%b9%e6%b3%95/65509#65509)

以上、ご参考になれば。


CC BY-SA 4.0
2021-04-08T16:56:10.783
75170
2021-04-08T16:56:10.783
27334
75132
answer


興味があったので、実際に記録と再生を同時に動かすことをやってみました。  
基本的にできると思います。自分はできました。

サンプルにある  
player_objIf.ino  
と  
recorder_wav_objif.ino  
をマージすればよいです。

ただし、  
player_objIf.ino  
は、MP3の再生なので、96kHzを再生するのであれば、WAVしかなく、その変更が必要です。

[code]

    thePlayer->init(MediaPlayer::Player0, AS_CODECTYPE_WAV, "/mnt/sd0/BIN", AS_SAMPLINGRATE_96000, AS_CHANNEL_MONO);
    
[/code]

また、記録もノーマルレゾリューションなので、

[code]

    static const uint32_t recoding_sampling_rate = 192000;
    static const uint8_t  recoding_cannel_number = 1;
    static const uint8_t  recoding_bit_length = 16;
    
[/code]

にしました。

ちなみに、記録は、16k,48k,192kしかないので、192kになりますね。

で、ハイレゾの記録再生同時になると、SDカードのアクセス速度がネックになるので、読み書きのバッファが足りなくなるので、変更しています。

[code]

     thePlayer->activate(MediaPlayer::Player0, mediaplayer_done_callback, 1024*2*20);
      theRecorder->activate(AS_SETRECDR_STS_INPUTDEVICE_MIC, mediarecorder_done_callback, (768*2*8)*30);
    
[/code]

サイズは合わせて調整すると良いと思います。

で、このサイズだと、メモリが足りなくなるので、  
タブにある  
tool/  
Memory:768KB(default)  
を  
Memory:896KB  
に替えました。

もう一点、終端処理のケアが必要になります。  
おそらく、記録と再生を別なタスクにすればよいのかもしれないですが、  
双方の停止でアプリのループが止まるため、どちらかが止まると両方止まります。

なので、終端処理を以下にしました。

[code]

    stop_player:    
      theRecorder->stop();
      thePlayer->stop(MediaPlayer::Player0);
      execute_frames();    
      /* Get ramaining data(flushing) */
      sleep(1); /* For data pipline stop */
      theRecorder->writeWavHeader(recFile);
      puts("Update Header!");
      playFile.close();
      recFile.close();
    
      thePlayer->deactivate(MediaPlayer::Player0);
      theRecorder->deactivate();
      thePlayer->end();
      theRecorder->end();
      puts("End Recording");
      exit(1);
    
[/code]

特に記録側のstopは、ループ側にstopがあるので、こちらに移動してきました。

これで、動かしたところ、動作できています。

ご参考になれば。

[code]

    #include &lt;SDHCI.h>
    #include &lt;MediaRecorder.h>
    #include &lt;MediaPlayer.h>
    #include &lt;OutputMixer.h>
    #include &lt;MemoryUtil.h>
    
    #define RECORD_FILE_NAME "Recorde.wav"
    #define ANALOG_MIC_GAIN  0 /* +0dB */
    SDClass theSD;
    
    MediaRecorder *theRecorder;
    MediaPlayer *thePlayer;
    OutputMixer *theMixer;
    
    File playFile;
    File recFile;
    
    bool ErrEnd = false;
    
    
    /* Recording bit rate
     * Set in bps.
     */
    
    //static const uint32_t recoding_bitrate = 48000*8*2;
    static const uint32_t recoding_bitrate = 192000*8*2;
    
    /* Sampling rate
     * Set 16000 or 48000
     */
    
    static const uint32_t recoding_sampling_rate = 192000;
    
    /* Number of input channels
     * Set either 1, 2, or 4.
     */
    
    static const uint8_t  recoding_cannel_number = 1;
    
    /* Audio bit depth
     * Set 16 or 24
     */
    
    static const uint8_t  recoding_bit_length = 16;
    
    /* Recording time[second] */
    
    static const uint32_t recoding_time = 30;
    
    /* Bytes per second */
    
    static const int32_t  recoding_byte_per_second = (recoding_bitrate / 8);
    
    /* Total recording size */
    
    static const int32_t  recoding_size = recoding_byte_per_second * recoding_time;
    
    /* One frame size
     * Calculated with 1152 samples per frame.
     */
    
    static const uint32_t frame_size  = (1152 * (recoding_bitrate / 8)) / recoding_sampling_rate;
    
    /* Buffer size
     * Align in 512byte units based on frame size.
     */
    
    static const uint32_t buffer_size = (frame_size + 511) & ~511;
    static uint8_t        s_buffer[buffer_size];
    
    /**
     * @brief Audio attention callback
     *
     * When audio internal error occurc, this function will be called back.
     */
    
    static void attention_cb(const ErrorAttentionParam *atprm)
    {
      puts("Attention!");
      
      if (atprm->error_code >= AS_ATTENTION_CODE_WARNING)
        {
          ErrEnd = true;
       }
    }
    
    static void mediarecorder_attention_cb(const ErrorAttentionParam *atprm)
    {
      puts("Attention!");
      
      if (atprm->error_code >= AS_ATTENTION_CODE_WARNING)
        {
          ErrEnd = true;
       }
    }
    
    /**
     * @brief Mixer done callback procedure
     *
     * @param [in] requester_dtq    MsgQueId type
     * @param [in] reply_of         MsgType type
     * @param [in,out] done_param   AsOutputMixDoneParam type pointer
     */
    static void outputmixer_done_callback(MsgQueId requester_dtq,
                                          MsgType reply_of,
                                          AsOutputMixDoneParam *done_param)
    {
      return;
    }
    
    /**
     * @brief Mixer data send callback procedure
     *
     * @param [in] identifier   Device identifier
     * @param [in] is_end       For normal request give false, for stop request give true
     */
    static void outmixer_send_callback(int32_t identifier, bool is_end)
    {
      AsRequestNextParam next;
    
      next.type = (!is_end) ? AsNextNormalRequest : AsNextStopResRequest;
    
      AS_RequestNextPlayerProcess(AS_PLAYER_ID_0, &next);
    
      return;
    }
    
    /**
     * @brief Player done callback procedure
     *
     * @param [in] event        AsPlayerEvent type indicator
     * @param [in] result       Result
     * @param [in] sub_result   Sub result
     *
     * @return true on success, false otherwise
     */
    static bool mediaplayer_done_callback(AsPlayerEvent event, uint32_t result, uint32_t sub_result)
    {
      printf("mp cb %x %x %x\n", event, result, sub_result);
    
      return true;
    }
    
    /**
     * @brief Player decode callback procedure
     *
     * @param [in] pcm_param    AsPcmDataParam type
     */
    void mediaplayer_decode_callback(AsPcmDataParam pcm_param)
    {  
      theMixer->sendData(OutputMixer0,
                         outmixer_send_callback,
                         pcm_param);
    }
    
    /**
     * @brief Recorder done callback procedure
     *
     * @param [in] event        AsRecorderEvent type indicator
     * @param [in] result       Result
     * @param [in] sub_result   Sub result
     *
     * @return true on success, false otherwise
     */
    
    static bool mediarecorder_done_callback(AsRecorderEvent event, uint32_t result, uint32_t sub_result)
    {
      return true;
    }
    
    /**
     * @brief Setup Player and Mixer
     *
     * Set output device to Speakers/Headphones <br>
     * Initialize main player to decode stereo mp3 stream with 48 kb/s sample rate <br>
     * System directory "/mnt/sd0/BIN" will be searched for MP3 decoder (MP3DEC file)
     * Open "Sound.mp3" file <br>
     * Set volume to -16.0 dB
     */
    void setup()
    {
      /* Initialize SD */
      while (!theSD.begin())
        {
          /* wait until SD card is mounted. */
          Serial.println("Insert SD card.");
        }
    
      /* Initialize memory pools and message libs */
      
      initMemoryPools();
      createStaticPools(MEM_LAYOUT_RECORDINGPLAYER);
      
      /* start audio system */
      
      thePlayer = MediaPlayer::getInstance();
      theMixer  = OutputMixer::getInstance();
      theRecorder = MediaRecorder::getInstance();
    
      thePlayer->begin();
      theMixer->begin();
      theRecorder->begin(mediarecorder_attention_cb);
      
      puts("initialization Audio Library");
    
      /* Create Objects */
    
      thePlayer->create(MediaPlayer::Player0, attention_cb);
    
      theMixer->create(attention_cb);
    
      /* Set rendering clock */
    
      theMixer->setRenderingClkMode(OUTPUTMIXER_RNDCLK_HIRESO);
    
      /* Activate Player Object */
    
      thePlayer->activate(MediaPlayer::Player0, mediaplayer_done_callback, 1024*2*20);
    
      /* Activate Mixer Object.
       * Set output device to speaker with 2nd argument.
       * If you want to change the output device to I2S,
       * specify "I2SOutputDevice" as an argument.
       */
    
      theMixer->activate(OutputMixer0, HPOutputDevice, outputmixer_done_callback);
      theRecorder->activate(AS_SETRECDR_STS_INPUTDEVICE_MIC, mediarecorder_done_callback, (768*2*8)*30);
    
      usleep(100 * 1000);
    
      /*
       * Initialize main player to decode stereo mp3 stream with 48 kb/s sample rate
       * Search for MP3 codec in "/mnt/sd0/BIN" directory
       */
      thePlayer->init(MediaPlayer::Player0, AS_CODECTYPE_WAV, "/mnt/sd0/BIN", AS_SAMPLINGRATE_96000, AS_CHANNEL_MONO);
    
      /*
       * Initialize main player to decode stereo mp3 stream with 48 kb/s sample rate
       * Search for MP3 codec in "/mnt/sd0/BIN" directory
       */
      theRecorder->init(AS_CODECTYPE_WAV,
                        recoding_cannel_number,
                        recoding_sampling_rate,
                        recoding_bit_length,
                        recoding_bitrate,
                        "/mnt/sd0/BIN");
    
      /* Initialize SD */
      while (!theSD.begin())
        {
          /* wait until SD card is mounted. */
          Serial.println("Insert SD card.");
        }
    
      playFile = theSD.open("AUDIO/Sound.wav");
      recFile  = theSD.open(RECORD_FILE_NAME, FILE_WRITE);
    
      /* Verify file open */
      if (!playFile)
        {
          printf("File open error\n");
          exit(1);
        }
      printf("Open! %d\n", recFile);
    
      /* Verify file open */
      if (!recFile)
        {
          printf("File open error\n");
          exit(1);
        }
      printf("Open! %d\n", recFile);
    
      /* Send first frames to be decoded */
      err_t err = thePlayer->writeFrames(MediaPlayer::Player0, playFile);
    
      if (err != MEDIAPLAYER_ECODE_OK)
        {
          printf("File Read Error! =%d\n",err);
          playFile.close();
          exit(1);
        }
    
      puts("Play!");
    
      /* Main volume set to -16.0 dB, Main player and sub player set to 0 dB */
      theMixer->setVolume(-160, 0, 0);
    
      // Start Player
      thePlayer->start(MediaPlayer::Player0, mediaplayer_decode_callback);
    
       /* Set Gain */
      theRecorder->setMicGain(ANALOG_MIC_GAIN);
    
      /* Start Recorder */
      theRecorder->start();
    }
    
    /**
     * @brief Execute one frame
     */
    err_t execute_aframe(uint32_t* size)
    {
      err_t err = theRecorder->readFrames(s_buffer, buffer_size, size);
    
      if(((err == MEDIARECORDER_ECODE_OK) || (err == MEDIARECORDER_ECODE_INSUFFICIENT_BUFFER_AREA)) && (*size > 0))
        {
        }else{
          return err;
        }
      int ret = recFile.write((uint8_t*)&s_buffer, *size);
      if (ret < 0)
        {
          puts("File write error.");
          err = MEDIARECORDER_ECODE_FILEACCESS_ERROR;
        }
    
      return err;
    }
    
    /**
     * @brief Execute frames for FIFO empty
     */
    void execute_frames()
    {
      uint32_t read_size = 0;
      do
        {
          err_t err = execute_aframe(&read_size);
          if ((err != MEDIARECORDER_ECODE_OK)
           && (err != MEDIARECORDER_ECODE_INSUFFICIENT_BUFFER_AREA))
            {
              break;
            }
        }
      while (read_size > 0);
    }
    
    /**
     * @brief Play audio frames until file ends
     */
    void loop()
    {
        static int32_t total_size = 0;
        uint32_t read_size = 0;
        err_t err;
    
    #if 1
    /* Execute audio data */
      err = execute_aframe(&read_size);
      if (err != MEDIARECORDER_ECODE_OK && err != MEDIARECORDER_ECODE_INSUFFICIENT_BUFFER_AREA)
        {
          puts("Recording Error!");
          goto stop_player;
        }
      else if (read_size>0)
        {
          total_size += read_size;
        }
    
      /* Stop Recording */
      if (total_size > recoding_size)
        {
          goto stop_player;
        }
    #endif
    
    #if 1
      /* Send new frames to decode in a loop until file ends */
      err = thePlayer->writeFrames(MediaPlayer::Player0, playFile);
    
      /*  Tell when player file ends */
      if (err == MEDIAPLAYER_ECODE_FILEEND)
        {
          printf("Main player File End!\n");
        }
    
      /* Show error code from player and stop */
      if (err)
        {
          printf("Main player error code: %d\n", err);
          goto stop_player;
        }
        
      if (ErrEnd)
        {
          printf("Error End\n");
          goto stop_player;
        }
    #endif
    
      /* Don't go further and continue play */
      return;
    
    stop_player:    
      theRecorder->stop();
      thePlayer->stop(MediaPlayer::Player0);
      execute_frames();    
      /* Get ramaining data(flushing) */
      sleep(1); /* For data pipline stop */
      theRecorder->writeWavHeader(recFile);
      puts("Update Header!");
      playFile.close();
      recFile.close();
    
      thePlayer->deactivate(MediaPlayer::Player0);
      theRecorder->deactivate();
      thePlayer->end();
      theRecorder->end();
      puts("End Recording");
      exit(1);
    
    }
    
[/code]


CC BY-SA 4.0
2021-04-09T18:58:45.947
75185
2021-04-12T02:20:16.560
2021-04-12T02:20:16.560
3060
32281
75132
answer