jasonzhang 3 سال پیش
والد
کامیت
e5837c7ada
8فایلهای تغییر یافته به همراه1734 افزوده شده و 1735 حذف شده
  1. 201 201
      LICENSE
  2. 139 139
      README.md
  3. 140 140
      README_ZH.md
  4. 26 26
      SConscript
  5. 204 204
      inc/mp3_player.h
  6. 99 99
      inc/mp3_tag.h
  7. 716 718
      src/mp3_player.c
  8. 209 208
      src/mp3_player_cmd.c

+ 201 - 201
LICENSE

@@ -1,201 +1,201 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 139 - 139
README.md

@@ -1,139 +1,139 @@
-# mp3player
-
-[中文页](README_ZH.md) | English
-
-## 1. Introduction
-
-**mp3player** is a simple mp3 format music player that provides functions for playing mp3 files,decode id3v1,id3v2 tag, supporting functions such as play, stop, pause, resume, seek,and volume adjustment.
-
-### 1.1. File structure
-
-| Folder | Description |
-| ---- | ---- |
-| src | Core source code, which mainly implements mp3 playback and tag decode, and export Finsh command line |
-| inc | Header file directory |
-
-### 1.2 License
-
-The mp3player package complies with the Apache 2.0 license, see the `LICENSE` file for details.
-
-### 1.3 Dependency
-
-- RT-Thread 4.0+
-- RT-Thread Audio driver framework
-- optparse command line parameter parsing package
-- helix mp3 decoder
-
-### 1.4 Configuration Macro Description
-
-
-```shell
- --- mp3 player: Minimal music player for mp3 file play.   
- [*]   Enable mp3 player                                   
- (sound0) The play device name                                       
- (2048) mp3 input buffer size                                   
- (4608) mp3 output buffer size   
- (50)  mp3 player default volume                                 
-       Version (v1.0.0)  --->  
-```
-
-**The play device name**: Specify the sound card device used for playback, default `sound0`
-
-## 2. Use
-
-Common functions of mp3player have been exported to Finsh command line for developers to test and use.
-
-**The functions provided by the play command are as follows**
-
-```shell
-msh />mp3play -help
-usage: mp3play [option] [target] ...
-
-usage options:
-  -h,     --help                     Print defined help message.
-  -s URI, --start=URI                Play mp3 music with URI(local files).
-  -t,     --stop                     Stop playing music.
-  -p,     --pause                    Pause the music.
-  -r,     --resume                   Resume the music.
-  -v lvl, --volume=lvl               Change the volume(0~99).
-  -d,     --dump                     Dump play relevant information.
-  -j      --jump                     Jump to seconds that given.
-```
-
-### 2.1 Play function
-
-- Start playing
-
-```shell
-msh />mp3play -s bryan_adams_-_here_i_am.mp3
-[I/mp3 player]: play start, uri=bryan_adams_-_here_i_am.mp3
-msh />------------MP3 INFO------------
-Title:Here I Am
-Artist:Bryan Adams
-Year:2002
-Comment:Spirit: Stallion Of The Cimarr
-Genre:Blues
-Length:04:45
-Bitrate:320 kbit/s
-Frequency:44100 Hz
---------------------------------
-```
-- seek play
-```shell
-jump to 100 seconds
-msh />mp3play -j 100
-```
-
-- Stop play
-
-```shell
-msh />mp3play -t
-[I/mp3 player] play end
-```
-
-- Pause playback
-
-```shell
-msh />mp3play -p
-```
-
-- Resume playback
-
-```shell
-msh />mp3play -r
-```
-
-- Set volume
-
-```shell
-msh />mp3play -v 50
-```
-
-```shell
-msh />mp3play -d
-
-- dump info
-mp3_player status:
-uri     - bryan_adams_-_here_i_am.mp3
-status  - PLAYING
-volume  - 10
-00:03 / 04:45
-------------MP3 INFO------------
-Title:Here I Am
-Artist:Bryan Adams
-Year:2002
-Comment:Spirit: Stallion Of The Cimarr
-Genre:Blues
-Length:04:45
-Bitrate:320 kbit/s
-Frequency:44100 Hz
---------------------------------
-```
-## 3. Matters needing attention
-
-- 
-
-## 4. Contact
-
-- Maintenance: MrzhangF1ghter
-- Homepage: https://github.com/MrzhangF1ghter/mp3player
+# mp3player
+
+[中文页](README_ZH.md) | English
+
+## 1. Introduction
+
+**mp3player** is a simple mp3 format music player that provides functions for playing mp3 files,decode id3v1,id3v2 tag, supporting functions such as play, stop, pause, resume, seek,and volume adjustment.
+
+### 1.1. File structure
+
+| Folder | Description |
+| ---- | ---- |
+| src | Core source code, which mainly implements mp3 playback and tag decode, and export Finsh command line |
+| inc | Header file directory |
+
+### 1.2 License
+
+The mp3player package complies with the Apache 2.0 license, see the `LICENSE` file for details.
+
+### 1.3 Dependency
+
+- RT-Thread 4.0+
+- RT-Thread Audio driver framework
+- optparse command line parameter parsing package
+- helix mp3 decoder
+
+### 1.4 Configuration Macro Description
+
+
+```shell
+ --- mp3 player: Minimal music player for mp3 file play.   
+ [*]   Enable mp3 player                                   
+ (sound0) The play device name                                       
+ (2048) mp3 input buffer size                                   
+ (4608) mp3 output buffer size   
+ (50)  mp3 player default volume                                 
+       Version (v1.0.0)  --->  
+```
+
+**The play device name**: Specify the sound card device used for playback, default `sound0`
+
+## 2. Use
+
+Common functions of mp3player have been exported to Finsh command line for developers to test and use.
+
+**The functions provided by the play command are as follows**
+
+```shell
+msh />mp3play -help
+usage: mp3play [option] [target] ...
+
+usage options:
+  -h,     --help                     Print defined help message.
+  -s URI, --start=URI                Play mp3 music with URI(local files).
+  -t,     --stop                     Stop playing music.
+  -p,     --pause                    Pause the music.
+  -r,     --resume                   Resume the music.
+  -v lvl, --volume=lvl               Change the volume(0~99).
+  -d,     --dump                     Dump play relevant information.
+  -j      --jump                     Jump to seconds that given.
+```
+
+### 2.1 Play function
+
+- Start playing
+
+```shell
+msh />mp3play -s bryan_adams_-_here_i_am.mp3
+[I/mp3 player]: play start, uri=bryan_adams_-_here_i_am.mp3
+msh />------------MP3 INFO------------
+Title:Here I Am
+Artist:Bryan Adams
+Year:2002
+Comment:Spirit: Stallion Of The Cimarr
+Genre:Blues
+Length:04:45
+Bitrate:320 kbit/s
+Frequency:44100 Hz
+--------------------------------
+```
+- seek play
+```shell
+jump to 100 seconds
+msh />mp3play -j 100
+```
+
+- Stop play
+
+```shell
+msh />mp3play -t
+[I/mp3 player] play end
+```
+
+- Pause playback
+
+```shell
+msh />mp3play -p
+```
+
+- Resume playback
+
+```shell
+msh />mp3play -r
+```
+
+- Set volume
+
+```shell
+msh />mp3play -v 50
+```
+
+```shell
+msh />mp3play -d
+
+- dump info
+mp3_player status:
+uri     - bryan_adams_-_here_i_am.mp3
+status  - PLAYING
+volume  - 10
+00:03 / 04:45
+------------MP3 INFO------------
+Title:Here I Am
+Artist:Bryan Adams
+Year:2002
+Comment:Spirit: Stallion Of The Cimarr
+Genre:Blues
+Length:04:45
+Bitrate:320 kbit/s
+Frequency:44100 Hz
+--------------------------------
+```
+## 3. Matters needing attention
+
+- 
+
+## 4. Contact
+
+- Maintenance: MrzhangF1ghter
+- Homepage: https://github.com/MrzhangF1ghter/mp3player

+ 140 - 140
README_ZH.md

@@ -1,140 +1,140 @@
-# mp3player
-
-中文页 | [English](README.md)
-
-## 1. 简介
-
-**mp3player** 是一个简易的 mp3 格式的音乐播放器,提供播放mp3 文件的功能,支持获取MP3 标签信息、播放、跳转、停止、暂停、恢复,以及音量调节等功能。
-
-### 1.1. 文件结构
-
-| 文件夹 | 说明 |
-| ---- | ---- |
-| src  | 核心源码,主要实现 MP3 播放和MP3 标签解析,以及导出 Finsh 命令行 |
-| inc  | 头文件目录 |
-
-### 1.2 许可证
-
-mp3player package 遵循 Apache 2.0 许可,详见 `LICENSE` 文件。
-
-### 1.3 依赖
-
-- RT-Thread 4.0+
-- RT-Thread Audio 驱动框架
-- optparse 命令行参数解析软件包
-- helix MP3解码软件包
-
-### 1.4 配置宏说明
-
-```shell
- --- mp3 player: Minimal music player for mp3 file play.   
- [*]   Enable mp3 player                                   
- (sound0) The play device name                                       
- (2048) mp3 input buffer size                                   
- (4608) mp3 output buffer size   
- (50)  mp3 player default volume                                 
-       Version (v1.0.0)  --->  
-```
-
-**The play device name**:指定播放使用的声卡设备,默认`sound0`  
-
-## 2. 使用
-
-mp3player 的常用功能已经导出到 Finsh 命令行,以便开发者测试和使用。
-
-**播放命令提供的功能如下 **
-
-```shell
-msh />mp3play -help
-usage: mp3play [option] [target] ...
-
-usage options:
-  -h,     --help                     Print defined help message.
-  -s URI, --start=URI                Play mp3 music with URI(local files).
-  -t,     --stop                     Stop playing music.
-  -p,     --pause                    Pause the music.
-  -r,     --resume                   Resume the music.
-  -v lvl, --volume=lvl               Change the volume(0~99).
-  -d,     --dump                     Dump play relevant information.
-  -j      --jump                     Jump to seconds that given.
-```
-
-### 2.1 播放功能
-
-- 开始播放
-
-```shell
-msh />mp3play -s bryan_adams_-_here_i_am.mp3
-[I/mp3 player]: play start, uri=bryan_adams_-_here_i_am.mp3
-msh />------------MP3 INFO------------
-Title:Here I Am
-Artist:Bryan Adams
-Year:2002
-Comment:Spirit: Stallion Of The Cimarr
-Genre:Blues
-Length:04:45
-Bitrate:320 kbit/s
-Frequency:44100 Hz
---------------------------------
-```
-
-- 跳转播放
-```shell
-跳转至100秒
-msh />mp3play -j 100
-```
-
-- 停止播放
-
-```shell
-msh />mp3play -t
-[I/mp3 player] play end
-```
-
-- 暂停播放
-
-```shell
-msh />mp3play -p
-```
-
-- 恢复播放
-
-```shell
-msh />mp3play -r
-```
-
-- 设置音量
-
-```shell
-msh />mp3play -v 50
-```
-
-- 打印播放信息
-```shell
-msh />mp3play -d
-
-mp3_player status:
-uri     - bryan_adams_-_here_i_am.mp3
-status  - PLAYING
-volume  - 10
-00:03 / 04:45
-------------MP3 INFO------------
-Title:Here I Am
-Artist:Bryan Adams
-Year:2002
-Comment:Spirit: Stallion Of The Cimarr
-Genre:Blues
-Length:04:45
-Bitrate:320 kbit/s
-Frequency:44100 Hz
---------------------------------
-```
-
-## 3. 注意事项
-
-- 待补充
-
-## 4. 联系方式
-
-- 维护:MrzhangF1ghter
-- 主页:https://github.com/MrzhangF1ghter/mp3player
+# mp3player
+
+中文页 | [English](README.md)
+
+## 1. 简介
+
+**mp3player** 是一个简易的 mp3 格式的音乐播放器,提供播放mp3 文件的功能,支持获取MP3 标签信息、播放、跳转、停止、暂停、恢复,以及音量调节等功能。
+
+### 1.1. 文件结构
+
+| 文件夹 | 说明 |
+| ---- | ---- |
+| src  | 核心源码,主要实现 MP3 播放和MP3 标签解析,以及导出 Finsh 命令行 |
+| inc  | 头文件目录 |
+
+### 1.2 许可证
+
+mp3player package 遵循 Apache 2.0 许可,详见 `LICENSE` 文件。
+
+### 1.3 依赖
+
+- RT-Thread 4.0+
+- RT-Thread Audio 驱动框架
+- optparse 命令行参数解析软件包
+- helix MP3解码软件包
+
+### 1.4 配置宏说明
+
+```shell
+ --- mp3 player: Minimal music player for mp3 file play.   
+ [*]   Enable mp3 player                                   
+ (sound0) The play device name                                       
+ (2048) mp3 input buffer size                                   
+ (4608) mp3 output buffer size   
+ (50)  mp3 player default volume                                 
+       Version (v1.0.0)  --->  
+```
+
+**The play device name**:指定播放使用的声卡设备,默认`sound0`  
+
+## 2. 使用
+
+mp3player 的常用功能已经导出到 Finsh 命令行,以便开发者测试和使用。
+
+**播放命令提供的功能如下 **
+
+```shell
+msh />mp3play -help
+usage: mp3play [option] [target] ...
+
+usage options:
+  -h,     --help                     Print defined help message.
+  -s URI, --start=URI                Play mp3 music with URI(local files).
+  -t,     --stop                     Stop playing music.
+  -p,     --pause                    Pause the music.
+  -r,     --resume                   Resume the music.
+  -v lvl, --volume=lvl               Change the volume(0~99).
+  -d,     --dump                     Dump play relevant information.
+  -j      --jump                     Jump to seconds that given.
+```
+
+### 2.1 播放功能
+
+- 开始播放
+
+```shell
+msh />mp3play -s bryan_adams_-_here_i_am.mp3
+[I/mp3 player]: play start, uri=bryan_adams_-_here_i_am.mp3
+msh />------------MP3 INFO------------
+Title:Here I Am
+Artist:Bryan Adams
+Year:2002
+Comment:Spirit: Stallion Of The Cimarr
+Genre:Blues
+Length:04:45
+Bitrate:320 kbit/s
+Frequency:44100 Hz
+--------------------------------
+```
+
+- 跳转播放
+```shell
+跳转至100秒
+msh />mp3play -j 100
+```
+
+- 停止播放
+
+```shell
+msh />mp3play -t
+[I/mp3 player] play end
+```
+
+- 暂停播放
+
+```shell
+msh />mp3play -p
+```
+
+- 恢复播放
+
+```shell
+msh />mp3play -r
+```
+
+- 设置音量
+
+```shell
+msh />mp3play -v 50
+```
+
+- 打印播放信息
+```shell
+msh />mp3play -d
+
+mp3_player status:
+uri     - bryan_adams_-_here_i_am.mp3
+status  - PLAYING
+volume  - 10
+00:03 / 04:45
+------------MP3 INFO------------
+Title:Here I Am
+Artist:Bryan Adams
+Year:2002
+Comment:Spirit: Stallion Of The Cimarr
+Genre:Blues
+Length:04:45
+Bitrate:320 kbit/s
+Frequency:44100 Hz
+--------------------------------
+```
+
+## 3. 注意事项
+
+- 待补充
+
+## 4. 联系方式
+
+- 维护:MrzhangF1ghter
+- 主页:https://github.com/MrzhangF1ghter/mp3player

+ 26 - 26
SConscript

@@ -1,26 +1,26 @@
-'''
-Author: your name
-Date: 2021-05-25 14:57:35
-LastEditTime: 2021-06-04 14:31:00
-LastEditors: Please set LastEditors
-Description: In User Settings Edit
-FilePath: \gd32f407-sbrgate\third-packages\mp3player\SConscript
-'''
-Import('rtconfig')
-from building import *
-
-cwd = GetCurrentDir()
-
-path = [cwd,
-    cwd + '/inc']
-
-src = []
-src +=  Split('''
-        src/mp3_player.c
-        src/mp3_player_cmd.c
-        src/mp3_tag.c
-        ''')
-
-group = DefineGroup('mp3player', src, depend = ['PKG_USING_MP3PLAYER'], CPPPATH = path)
-
-Return('group')
+'''
+Author: your name
+Date: 2021-05-25 14:57:35
+LastEditTime: 2021-06-04 14:31:00
+LastEditors: Please set LastEditors
+Description: In User Settings Edit
+FilePath: \gd32f407-sbrgate\third-packages\mp3player\SConscript
+'''
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+
+path = [cwd,
+    cwd + '/inc']
+
+src = []
+src +=  Split('''
+        src/mp3_player.c
+        src/mp3_player_cmd.c
+        src/mp3_tag.c
+        ''')
+
+group = DefineGroup('mp3player', src, depend = ['PKG_USING_MP3PLAYER'], CPPPATH = path)
+
+Return('group')

+ 204 - 204
inc/mp3_player.h

@@ -1,204 +1,204 @@
-/*
- * SPDX-License-Identifier: Apache-2.0
- *
- * Date           Author       Notes
- * 2021-06-02     MrzhangF1ghter    first implementation
- */
-
-#ifndef __MP3_PLAYER_H__
-#define __MP3_PLAYER_H__
-
-#include <stdio.h>
-#include <rtdevice.h>
-
-#include "mp3dec.h" /* helix include files */
-
-enum MSG_TYPE
-{
-    MSG_NONE = 0,
-    MSG_START = 1,
-    MSG_STOP = 2,
-    MSG_PAUSE = 3,
-    MSG_RESUME = 4,
-};
-
-enum PLAYER_EVENT
-{
-    PLAYER_EVENT_NONE = 0,
-    PLAYER_EVENT_PLAY = 1,
-    PLAYER_EVENT_STOP = 2,
-    PLAYER_EVENT_PAUSE = 3,
-    PLAYER_EVENT_RESUME = 4,
-};
-
-struct play_msg
-{
-    int type;
-    void *data;
-};
-
-#pragma pack(1)
-typedef struct
-{
-    uint8_t *read_ptr;
-    int read_offset;
-    int bytes_left;
-} decode_oper_t;
-
-/* 
- * music basic info structure definition
- */
-typedef struct
-{
-    uint8_t title[30];
-    uint8_t artist[30];
-    uint8_t year[4];
-    uint8_t comment[30];
-    uint8_t genre;
-} mp3_basic_info_t;
-
-/* 
- * mp3_info structure definition
- */
-typedef struct
-{
-    mp3_basic_info_t mp3_basic_info;
-    uint32_t total_seconds;
-    uint32_t curent_seconds;
-
-    uint32_t bitrate;
-    uint32_t samplerate;
-    uint16_t outsamples;
-    uint8_t vbr;
-    uint32_t data_start;
-    long file_size;
-} mp3_info_t;
-
-/* 
- * mp3 player main structure definition
- */
-struct mp3_player
-{
-    int state;
-    char *uri;
-    uint8_t *in_buffer;
-    uint16_t *out_buffer;
-    rt_device_t audio_device;
-    rt_mq_t mq;
-    rt_mutex_t lock;
-    struct rt_completion ack;
-    FILE *fp;
-
-    int volume;
-
-    /* helix decoder */
-    HMP3Decoder mp3_decoder;
-    MP3FrameInfo mp3_frameinfo;
-
-    mp3_info_t mp3_info;
-
-    decode_oper_t decode_oper;
-};
-#pragma pack()
-
-/**
- * mp3 player status
- */
-enum PLAYER_STATE
-{
-    PLAYER_STATE_STOPED = 0,
-    PLAYER_STATE_PLAYING = 1,
-    PLAYER_STATE_PAUSED = 2,
-};
-
-/**
- * @brief             Play wav music
- *
- * @param uri         the pointer for file path
- *
- * @return
- *      - 0      Success
- *      - others Failed
- */
-int mp3_player_play(char *uri);
-
-/**
- * @brief             Stop music
- *
- * @return
- *      - 0      Success
- *      - others Failed
- */
-int mp3_player_stop(void);
-
-/**
- * @brief             Pause music
- *
- * @return
- *      - 0      Success
- *      - others Failed
- */
-int mp3_player_pause(void);
-
-/**
- * @brief             Resume music
- *
- * @return
- *      - 0      Success
- *      - others Failed
- */
-int mp3_player_resume(void);
-
-/**
- * @brief             Sev volume
- *
- * @param volume      volume value(0 ~ 99)
- *
- * @return
- *      - 0      Success
- *      - others Failed
- */
-int mp3_player_volume_set(int volume);
-
-/**
- * @brief             Get volume
- *
- * @return            volume value(0~00)
- */
-int mp3_player_volume_get(void);
-
-/**
- * @brief             Get wav player state
- *
- * @return
- *      - PLAYER_STATE_STOPED   stoped status
- *      - PLAYER_STATE_PLAYING  playing status
- *      - PLAYER_STATE_PAUSED   paused
- */
-int mp3_player_state_get(void);
-
-/**
- * @brief             Get the uri that is currently playing
- *
- * @return            uri that is currently playing
- */
-char *mp3_player_uri_get(void);
-
-/**
- * @brief             show mp3 info
- */
-void mp3_info_show(void);
-
-/**
- * @brief             show mp3 info
- */
-void mp3_disp_time(void);
-
-/**
- * @brief             seek to destination seconds
- *
- * @return            the error code,0 on success
- */
-rt_err_t mp3_seek(uint32_t seconds);
-
-#endif
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2021-06-02     MrzhangF1ghter    first implementation
+ */
+
+#ifndef __MP3_PLAYER_H__
+#define __MP3_PLAYER_H__
+
+#include <stdio.h>
+#include <rtdevice.h>
+
+#include "mp3dec.h" /* helix include files */
+
+enum MSG_TYPE
+{
+    MSG_NONE = 0,
+    MSG_START = 1,
+    MSG_STOP = 2,
+    MSG_PAUSE = 3,
+    MSG_RESUME = 4,
+};
+
+enum PLAYER_EVENT
+{
+    PLAYER_EVENT_NONE = 0,
+    PLAYER_EVENT_PLAY = 1,
+    PLAYER_EVENT_STOP = 2,
+    PLAYER_EVENT_PAUSE = 3,
+    PLAYER_EVENT_RESUME = 4,
+};
+
+struct play_msg
+{
+    int type;
+    void *data;
+};
+
+#pragma pack(1)
+typedef struct
+{
+    uint8_t *read_ptr;
+    int read_offset;
+    int bytes_left;
+} decode_oper_t;
+
+/* 
+ * music basic info structure definition
+ */
+typedef struct
+{
+    uint8_t title[30];
+    uint8_t artist[30];
+    uint8_t year[4];
+    uint8_t comment[30];
+    uint8_t genre;
+} mp3_basic_info_t;
+
+/* 
+ * mp3_info structure definition
+ */
+typedef struct
+{
+    mp3_basic_info_t mp3_basic_info;
+    uint32_t total_seconds;
+    uint32_t curent_seconds;
+
+    uint32_t bitrate;
+    uint32_t samplerate;
+    uint16_t outsamples;
+    uint8_t vbr;
+    uint32_t data_start;
+    long file_size;
+} mp3_info_t;
+
+/* 
+ * mp3 player main structure definition
+ */
+struct mp3_player
+{
+    int state;
+    char *uri;
+    uint8_t *in_buffer;
+    uint16_t *out_buffer;
+    rt_device_t audio_device;
+    rt_mq_t mq;
+    rt_mutex_t lock;
+    struct rt_completion ack;
+    FILE *fp;
+
+    int volume;
+
+    /* helix decoder */
+    HMP3Decoder mp3_decoder;
+    MP3FrameInfo mp3_frameinfo;
+
+    mp3_info_t mp3_info;
+
+    decode_oper_t decode_oper;
+};
+#pragma pack()
+
+/**
+ * mp3 player status
+ */
+enum PLAYER_STATE
+{
+    PLAYER_STATE_STOPED = 0,
+    PLAYER_STATE_PLAYING = 1,
+    PLAYER_STATE_PAUSED = 2,
+};
+
+/**
+ * @brief             Play wav music
+ *
+ * @param uri         the pointer for file path
+ *
+ * @return
+ *      - 0      Success
+ *      - others Failed
+ */
+int mp3_player_play(char *uri);
+
+/**
+ * @brief             Stop music
+ *
+ * @return
+ *      - 0      Success
+ *      - others Failed
+ */
+int mp3_player_stop(void);
+
+/**
+ * @brief             Pause music
+ *
+ * @return
+ *      - 0      Success
+ *      - others Failed
+ */
+int mp3_player_pause(void);
+
+/**
+ * @brief             Resume music
+ *
+ * @return
+ *      - 0      Success
+ *      - others Failed
+ */
+int mp3_player_resume(void);
+
+/**
+ * @brief             Sev volume
+ *
+ * @param volume      volume value(0 ~ 99)
+ *
+ * @return
+ *      - 0      Success
+ *      - others Failed
+ */
+int mp3_player_volume_set(int volume);
+
+/**
+ * @brief             Get volume
+ *
+ * @return            volume value(0~00)
+ */
+int mp3_player_volume_get(void);
+
+/**
+ * @brief             Get wav player state
+ *
+ * @return
+ *      - PLAYER_STATE_STOPED   stoped status
+ *      - PLAYER_STATE_PLAYING  playing status
+ *      - PLAYER_STATE_PAUSED   paused
+ */
+int mp3_player_state_get(void);
+
+/**
+ * @brief             Get the uri that is currently playing
+ *
+ * @return            uri that is currently playing
+ */
+char *mp3_player_uri_get(void);
+
+/**
+ * @brief             show mp3 info
+ */
+void mp3_info_show(void);
+
+/**
+ * @brief             show mp3 info
+ */
+void mp3_disp_time(void);
+
+/**
+ * @brief             seek to destination seconds
+ *
+ * @return            the error code,0 on success
+ */
+rt_err_t mp3_seek(uint32_t seconds);
+
+#endif

+ 99 - 99
inc/mp3_tag.h

@@ -1,99 +1,99 @@
-/*
- * SPDX-License-Identifier: Apache-2.0
- *
- * Date           Author       Notes
- * 2021-06-02     MrzhangF1ghter    first implementation
- */
-
-#ifndef __MP3_TAG_H__
-#define __MP3_TAG_H__
-
-#include <stdint.h>
-#include <stdio.h>
-#include "mp3_player.h"
-
-#define TITLE_LEN_MAX 30
-#define ARTIST_LEN_MAX 30
-#define ALBUM_LEN_MAX 30
-
-/* 
- *  ID3V1 TAG
- */
-typedef struct
-{
-    uint8_t id[3];
-    uint8_t title[30];
-    uint8_t artist[30];
-    uint8_t year[4];
-    uint8_t comment[30];
-    uint8_t genre;
-} ID3V1_Tag_t;
-
-/* 
- *  ID3V2 TAG header
- */
-typedef struct
-{
-    uint8_t id[3];
-    uint8_t mversion;
-    uint8_t sversion;
-    uint8_t flags;
-    uint8_t size[4];
-} ID3V2_TagHead_t;
-
-/* 
- *  ID3V2.3 TAG header
- */
-typedef struct
-{
-    uint8_t id[4];
-    uint8_t size[4];
-    uint16_t flags;
-} ID3V23_FrameHead_t;
-
-/* 
- *  MP3 Xing Frame
- */
-typedef struct
-{
-    uint8_t id[4];
-    uint8_t flags[4];
-    uint8_t frames[4];
-    uint8_t fsize[4];
-} MP3_FrameXing_t;
-
-/* 
- *  MP3 VBRI Frame
- */
-typedef struct
-{
-    uint8_t id[4];      /* frame id:Xing/Info */
-    uint8_t version[2]; /* VBRI Version */
-    uint8_t delay[2];   /* delay */
-    uint8_t quality[2]; /* audio quality,0~100 */
-    uint8_t fsize[4];   /* file size */
-    uint8_t frames[4];  /* total frame */
-} MP3_FrameVBRI_t;
-
-/**
- * @description: Get genre string by genre id
- * @param {uint16_t} genre_id [0,147]
- * @return {char *} genre string
- */
-char *mp3_get_genre_string_by_id(uint16_t genre_id);
-
-/**
- * @description: get mp3 tag info
- * @param {struct mp3_player} *player
- * @return the error code,0 on success
- */
-rt_err_t mp3_get_info(struct mp3_player *player);
-
-/**
- * @description: print mp3 info
- * @param {mp3_info_t} mp3_info
- * @return the error code,0 on success
- */
-rt_err_t mp3_info_print(mp3_info_t mp3_info);
-
-#endif
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2021-06-02     MrzhangF1ghter    first implementation
+ */
+
+#ifndef __MP3_TAG_H__
+#define __MP3_TAG_H__
+
+#include <stdint.h>
+#include <stdio.h>
+#include "mp3_player.h"
+
+#define TITLE_LEN_MAX 30
+#define ARTIST_LEN_MAX 30
+#define ALBUM_LEN_MAX 30
+
+/* 
+ *  ID3V1 TAG
+ */
+typedef struct
+{
+    uint8_t id[3];
+    uint8_t title[30];
+    uint8_t artist[30];
+    uint8_t year[4];
+    uint8_t comment[30];
+    uint8_t genre;
+} ID3V1_Tag_t;
+
+/* 
+ *  ID3V2 TAG header
+ */
+typedef struct
+{
+    uint8_t id[3];
+    uint8_t mversion;
+    uint8_t sversion;
+    uint8_t flags;
+    uint8_t size[4];
+} ID3V2_TagHead_t;
+
+/* 
+ *  ID3V2.3 TAG header
+ */
+typedef struct
+{
+    uint8_t id[4];
+    uint8_t size[4];
+    uint16_t flags;
+} ID3V23_FrameHead_t;
+
+/* 
+ *  MP3 Xing Frame
+ */
+typedef struct
+{
+    uint8_t id[4];
+    uint8_t flags[4];
+    uint8_t frames[4];
+    uint8_t fsize[4];
+} MP3_FrameXing_t;
+
+/* 
+ *  MP3 VBRI Frame
+ */
+typedef struct
+{
+    uint8_t id[4];      /* frame id:Xing/Info */
+    uint8_t version[2]; /* VBRI Version */
+    uint8_t delay[2];   /* delay */
+    uint8_t quality[2]; /* audio quality,0~100 */
+    uint8_t fsize[4];   /* file size */
+    uint8_t frames[4];  /* total frame */
+} MP3_FrameVBRI_t;
+
+/**
+ * @description: Get genre string by genre id
+ * @param {uint16_t} genre_id [0,147]
+ * @return {char *} genre string
+ */
+char *mp3_get_genre_string_by_id(uint16_t genre_id);
+
+/**
+ * @description: get mp3 tag info
+ * @param {struct mp3_player} *player
+ * @return the error code,0 on success
+ */
+rt_err_t mp3_get_info(struct mp3_player *player);
+
+/**
+ * @description: print mp3 info
+ * @param {mp3_info_t} mp3_info
+ * @return the error code,0 on success
+ */
+rt_err_t mp3_info_print(mp3_info_t mp3_info);
+
+#endif

+ 716 - 718
src/mp3_player.c

@@ -1,718 +1,716 @@
-/*
- * SPDX-License-Identifier: Apache-2.0
- *
- * Date           Author       Notes
- * 2021-06-02     MrzhangF1ghter    first implementation
- */
-
-#include "mp3_player.h"
-#include <string.h>
-
-#define LOG_TAG "mp3 player"
-#define LOG_LVL DBG_INFO
-#include <ulog.h>
-
-#include "mp3_tag.h"
-
-#define VOLUME_MIN (0)
-#define VOLUME_MAX (100)
-
-#define MP3_PLAYER_MSG_SIZE (10)
-#define MP3_THREAD_STATCK_SIZE (1024 * 2)
-#define MP3_THREAD_PRIORITY (15)
-
-static struct mp3_player player = {0};
-
-#if (LOG_LVL >= DBG_LOG)
-
-static const char *state_str[] =
-    {
-        "STOPPED",
-        "PLAYING",
-        "PAUSED",
-};
-
-static const char *event_str[] =
-    {
-        "NONE",
-        "PLAY"
-        "STOP"
-        "PAUSE"
-        "RESUME"};
-
-#endif
-
-static char *MP3Decode_ERR_CODE_get(int err_code);
-
-/**
- * @description: lock player
- * @param None
- * @return None
- */
-static void play_lock(void)
-{
-    rt_mutex_take(player.lock, RT_WAITING_FOREVER);
-}
-
-/**
- * @description: unlock player
- * @param None
- * @return None
- */
-static void play_unlock(void)
-{
-    rt_mutex_release(player.lock);
-}
-
-/**
- * @description: send msg to player
- * @param {struct mp3_player} *player
- * @param {int} type
- * @param {void} *data
- * @return the error code,0 on success
- */
-static rt_err_t play_msg_send(struct mp3_player *player, int type, void *data)
-{
-    struct play_msg msg;
-
-    msg.type = type;
-    msg.data = data;
-
-    return rt_mq_send(player->mq, &msg, sizeof(struct play_msg));
-}
-
-/**
- * @description: start playing
- * @param {char} *uri
- * @return the error code,0 on success
- */
-int mp3_player_play(char *uri)
-{
-    rt_err_t result = RT_EOK;
-
-    rt_completion_init(&player.ack);
-    play_lock();
-    if (player.state != PLAYER_STATE_STOPED)
-    {
-        mp3_player_stop();
-    }
-    if (player.uri)
-    {
-        rt_free(player.uri);
-    }
-    player.uri = rt_strdup(uri);
-    result = play_msg_send(&player, MSG_START, RT_NULL);
-    rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
-    play_unlock();
-
-    return result;
-}
-
-/**
- * @description: stop playing
- * @param None
- * @return the error code,0 on success
- */
-int mp3_player_stop(void)
-{
-    rt_err_t result = RT_EOK;
-
-    rt_completion_init(&player.ack);
-
-    play_lock();
-    if (player.state != PLAYER_STATE_STOPED)
-    {
-        result = play_msg_send(&player, MSG_STOP, RT_NULL);
-        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
-    }
-    play_unlock();
-
-    return result;
-}
-
-/**
- * @description: pause playing
- * @param None
- * @return the error code,0 on success
- */
-int mp3_player_pause(void)
-{
-    rt_err_t result = RT_EOK;
-
-    rt_completion_init(&player.ack);
-    play_lock();
-    if (player.state == PLAYER_STATE_PLAYING)
-    {
-        result = play_msg_send(&player, MSG_PAUSE, RT_NULL);
-        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
-    }
-    play_unlock();
-
-    return result;
-}
-
-/**
- * @description: resume playing
- * @param None
- * @return the error code,0 on success
- */
-int mp3_player_resume(void)
-{
-    rt_err_t result = RT_EOK;
-    rt_completion_init(&player.ack);
-    play_lock();
-    if (player.state == PLAYER_STATE_PAUSED)
-    {
-        result = play_msg_send(&player, MSG_RESUME, RT_NULL);
-        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
-    }
-    play_unlock();
-    return result;
-}
-
-/**
- * @description: set volume
- * @param {int} volume
- * @return the error code,0 on success
- */
-int mp3_player_volume_set(int volume)
-{
-    struct rt_audio_caps caps;
-    if (volume < VOLUME_MIN)
-        volume = VOLUME_MIN;
-    else if (volume > VOLUME_MAX)
-        volume = VOLUME_MAX;
-    player.audio_device = rt_device_find(MP3_SOUND_DEVICE_NAME);
-    if (player.audio_device == RT_NULL)
-        return RT_ERROR;
-
-    player.volume = volume;
-    caps.main_type = AUDIO_TYPE_MIXER;
-    caps.sub_type = AUDIO_MIXER_VOLUME;
-    caps.udata.value = volume;
-
-    LOG_D("set volume = %d", volume);
-    return rt_device_control(player.audio_device, AUDIO_CTL_CONFIGURE, &caps);
-}
-
-/**
- * @description: get current player volume
- * @param None
- * @return volume
- */
-int mp3_player_volume_get(void)
-{
-    return player.volume;
-}
-
-/**
- * @description: get current player state
- * @param None
- * @return enum PLAYER_STATE
- */
-int mp3_player_state_get(void)
-{
-    return player.state;
-}
-
-/**
- * @description: get current player uri
- * @param None
- * @return pointer to uri
- */
-char *mp3_player_uri_get(void)
-{
-    return player.uri;
-}
-
-/**
- * @description: get mp3 current time in seconds
- * @param {FILE} *fp
- * @param {mp3_info_t} *mp3_info
- * @return current seconds,-1 on error
- */
-uint32_t mp3_get_cur_seconds(void)
-{
-    uint32_t fpos = 0;
-    uint32_t fptr;
-    uint32_t curent_seconds;
-
-    if (player.fp == RT_NULL)
-    {
-        return -1;
-    }
-    fptr = ftell(player.fp);
-    if (fptr > player.mp3_info.data_start)
-        fpos = fptr - player.mp3_info.data_start;
-
-    curent_seconds = fpos * player.mp3_info.total_seconds / (player.mp3_info.file_size - player.mp3_info.data_start);
-    player.mp3_info.curent_seconds = curent_seconds;
-    return curent_seconds;
-}
-
-/**
- * @description: seek to destination seconds
- * @param {uint32_t} seconds
- * @return the error code,0 on success
- */
-rt_err_t mp3_seek(uint32_t seconds)
-{
-    long fpos;
-    if (seconds > player.mp3_info.total_seconds)
-        return RT_ERROR;
-    /* calculate position by seconds*/
-    fpos = seconds * (player.mp3_info.bitrate / 8) + player.mp3_info.data_start;
-    if (fpos < player.mp3_info.data_start)
-        return RT_ERROR;
-    return fseek(player.fp, fpos + player.mp3_info.data_start, SEEK_SET);
-}
-
-/**
- * @description: show mp3 info
- * @param None
- * @return None
- */
-void mp3_info_show(void)
-{
-    mp3_info_print(player.mp3_info);
-}
-
-/**
- * @description: show mp3 current seconds
- * @param None
- * @return None
- */
-void mp3_disp_time(void)
-{
-    uint32_t cur_seconds;
-    cur_seconds = mp3_get_cur_seconds();
-    rt_kprintf("%02d:%02d / %02d:%02d\r\n", cur_seconds / 60, cur_seconds % 60, player.mp3_info.total_seconds / 60, player.mp3_info.total_seconds % 60);
-}
-
-/**
- * @description: open mp3 player
- * @param {struct mp3_player} *player
- * @return the error code,0 on success
- */
-static rt_err_t mp3_player_open(struct mp3_player *player)
-{
-    rt_err_t result = RT_EOK;
-    struct rt_audio_caps caps;
-
-    /* find device */
-    player->audio_device = rt_device_find(MP3_SOUND_DEVICE_NAME);
-    if (player->audio_device == RT_NULL)
-    {
-        LOG_E("audio_device %s not found", MP3_SOUND_DEVICE_NAME);
-        result = -RT_ERROR;
-        goto __exit;
-    }
-
-    /* open file */
-    player->fp = fopen(player->uri, "rb"); /* readonly */
-    if (player->fp == RT_NULL)
-    {
-        LOG_E("open file %s failed", player->uri);
-        result = -RT_ERROR;
-        goto __exit;
-    }
-
-    /* open sound device */
-    result = rt_device_open(player->audio_device, RT_DEVICE_OFLAG_WRONLY);
-    if (result != RT_EOK)
-    {
-        LOG_E("open %s audio_device failed", MP3_SOUND_DEVICE_NAME);
-        goto __exit;
-    }
-
-    /* init decoder */
-    player->mp3_decoder = MP3InitDecoder();
-    if (player->mp3_decoder == 0)
-    {
-        LOG_E("initialize helix mp3 decoder fail!");
-        result = RT_ERROR;
-        goto __exit;
-    }
-
-    /* set sampletate,channels, samplebits */
-    caps.main_type = AUDIO_TYPE_OUTPUT;
-    caps.sub_type = AUDIO_DSP_PARAM;
-    caps.udata.config.samplerate = 44100;
-    caps.udata.config.channels = 2;
-    caps.udata.config.samplebits = 16;
-    rt_device_control(player->audio_device, AUDIO_CTL_CONFIGURE, &caps);
-
-    return RT_EOK;
-
-__exit:
-    if (player->fp)
-    {
-        fclose(player->fp);
-        player->fp = RT_NULL;
-    }
-
-    if (player->audio_device)
-    {
-        rt_device_close(player->audio_device);
-        player->audio_device = RT_NULL;
-    }
-
-    if (player->mp3_decoder)
-    {
-        MP3FreeDecoder(player->mp3_decoder);
-    }
-
-    return result;
-}
-
-/**
- * @description:close mp3 player 
- * @param {struct mp3_player} *player
- * @return None
- */
-static void mp3_player_close(struct mp3_player *player)
-{
-    if (player->fp)
-    {
-        fclose(player->fp);
-        player->fp = RT_NULL;
-    }
-    if (player->audio_device)
-    {
-        rt_device_close(player->audio_device);
-        player->audio_device = RT_NULL;
-    }
-    if (player->mp3_decoder)
-    {
-        MP3FreeDecoder(player->mp3_decoder);
-    }
-    LOG_D("close mp3 player");
-}
-
-/**
- * @description: player event handler
- * @param {struct mp3_player} *player
- * @param {int} timeout
- * @return {int} mp3 event
- */
-static int mp3_player_event_handler(struct mp3_player *player, int timeout)
-{
-    int event;
-    rt_err_t result;
-    struct play_msg msg;
-#if (LOG_LVL >= DBG_LOG)
-    rt_uint8_t last_state;
-#endif
-
-    result = rt_mq_recv(player->mq, &msg, sizeof(struct play_msg), timeout);
-    if (result != RT_EOK)
-    {
-        event = PLAYER_EVENT_NONE;
-        return event;
-    }
-#if (LOG_LVL >= DBG_LOG)
-    last_state = player->state;
-#endif
-    switch (msg.type)
-    {
-    case MSG_START:
-        event = PLAYER_EVENT_PLAY;
-        player->state = PLAYER_STATE_PLAYING;
-        break;
-    case MSG_STOP:
-        event = PLAYER_EVENT_STOP;
-        player->state = PLAYER_STATE_STOPED;
-        break;
-
-    case MSG_PAUSE:
-        event = PLAYER_EVENT_PAUSE;
-        player->state = PLAYER_STATE_PAUSED;
-        break;
-
-    case MSG_RESUME:
-        event = PLAYER_EVENT_RESUME;
-        player->state = PLAYER_STATE_PLAYING;
-        break;
-
-    default:
-        event = PLAYER_EVENT_NONE;
-        break;
-    }
-    rt_completion_done(&player->ack);
-#if (LOG_LVL >= DBG_LOG)
-    LOG_D("EVENT:%s, STATE:%s -> %s", event_str[event], state_str[last_state], state_str[player->state]);
-#endif
-
-    return event;
-}
-
-/**
- * @description: mp3 player thread
- * @param {void *}parameter
- * @return None
- */
-static void mp3_player_entry(void *parameter)
-{
-    rt_err_t result = RT_EOK;
-    rt_int32_t size;
-    int event;
-
-    uint32_t count = 0;
-
-    /* decoder relate */
-    int i = 0;
-    int err;
-
-    uint32_t cur_ms = 0;
-
-    player.in_buffer = rt_malloc(MP3_INPUT_BUFFER_SIZE);
-    if (player.in_buffer == RT_NULL)
-    {
-        LOG_E("can not malloc input buffer for mp3 player.");
-        return;
-    }
-    player.out_buffer = rt_malloc(MP3_OUTPUT_BUFFER_SIZE);
-    if (player.out_buffer == RT_NULL)
-    {
-        LOG_E("can not malloc output buffer for mp3 player.");
-        return;
-    }
-    memset(player.in_buffer, 0, MP3_INPUT_BUFFER_SIZE);
-    memset(player.out_buffer, 0, MP3_OUTPUT_BUFFER_SIZE);
-
-    player.mq = rt_mq_create("mp3_mq", 10, sizeof(struct play_msg), RT_IPC_FLAG_FIFO);
-    if (player.mq == RT_NULL)
-        goto __exit;
-
-    player.lock = rt_mutex_create("mp3_lock", RT_IPC_FLAG_FIFO);
-    if (player.lock == RT_NULL)
-        goto __exit;
-
-    player.volume = MP3_PLAYER_VOLUME_DEFAULT;
-    /* set volume */
-    mp3_player_volume_set(player.volume);
-
-    while (1)
-    {
-        /* wait play event forever */
-        event = mp3_player_event_handler(&player, RT_WAITING_FOREVER);
-        if (event != PLAYER_EVENT_PLAY)
-            continue;
-
-        /* open mp3 player */
-        result = mp3_player_open(&player);
-        if (result != RT_EOK)
-        {
-            player.state = PLAYER_STATE_STOPED;
-            LOG_I("open mp3 player failed");
-            continue;
-        }
-        LOG_I("play start, uri=%s", player.uri);
-        /* get current mp3 basic info  */
-        if (mp3_get_info(&player) == RT_EOK)
-        {
-            mp3_info_print(player.mp3_info);
-        }
-        
-        fseek(player.fp, player.mp3_info.data_start, SEEK_SET);
-        size = fread(player.in_buffer, 1, MP3_INPUT_BUFFER_SIZE, player.fp);
-        if (size <= 0)
-            goto __exit;
-
-        /* set read ptr to inputbuffer */
-        player.decode_oper.read_ptr = player.in_buffer;
-        player.decode_oper.bytes_left = size;
-
-        while (1)
-        {
-            event = mp3_player_event_handler(&player, RT_WAITING_NO);
-            switch (event)
-            {
-            case PLAYER_EVENT_NONE:
-            {
-                /* find syncword */
-                player.decode_oper.read_offset = MP3FindSyncWord(player.decode_oper.read_ptr, player.decode_oper.bytes_left);
-                if (player.decode_oper.read_offset < 0) /* can not find syncword */
-                {
-                    size = fread(player.in_buffer, 1, MP3_INPUT_BUFFER_SIZE, player.fp);
-                    if (size <= 0)
-                        goto __exit;
-                    player.decode_oper.read_ptr = player.in_buffer;
-                    player.decode_oper.bytes_left = size;
-                    continue;
-                }
-
-                player.decode_oper.read_ptr += player.decode_oper.read_offset;   /* move read pointer to syncword */
-                player.decode_oper.bytes_left -= player.decode_oper.read_offset; /* data size after syncword */
-                if (player.decode_oper.bytes_left < MAINBUF_SIZE * 2)            /* append data */
-                {
-                    i = (uint32_t)(player.decode_oper.bytes_left) & 3;
-                    if (i)
-                        i = 4 - i; /* bytes need to append */
-                    memcpy(player.in_buffer + i, player.decode_oper.read_ptr, player.decode_oper.bytes_left);
-                    player.decode_oper.read_ptr = player.in_buffer + i;
-                    size = fread(player.in_buffer + player.decode_oper.bytes_left + i, 1, MP3_INPUT_BUFFER_SIZE - player.decode_oper.bytes_left - i, player.fp); /* copy at aligned position */
-                    player.decode_oper.bytes_left += size;
-                }
-                /* start decode */
-                err = MP3Decode(player.mp3_decoder, &player.decode_oper.read_ptr, &player.decode_oper.bytes_left, (short *)player.out_buffer, 0);
-                if (err != ERR_MP3_NONE)
-                {
-                    switch (err)
-                    {
-                    case ERR_MP3_INDATA_UNDERFLOW:
-                        LOG_D("ERR_MP3_INDATA_UNDERFLOW");
-                        size = fread(player.in_buffer, 1, MP3_INPUT_BUFFER_SIZE, player.fp); /* append data */
-                        player.decode_oper.read_ptr = player.in_buffer;
-                        player.decode_oper.bytes_left = size;
-                        break;
-                    case ERR_MP3_MAINDATA_UNDERFLOW:
-                        /* do nothing - next call to decode will provide more mainData */
-                        LOG_D("ERR_MP3_MAINDATA_UNDERFLOW");
-                        break;
-                    default:
-                        LOG_D("%s", MP3Decode_ERR_CODE_get(err));
-                        if (player.decode_oper.bytes_left > 0)
-                        {
-                            player.decode_oper.bytes_left--;
-                            player.decode_oper.read_ptr++;
-                        }
-                        break;
-                    }
-                }
-                else /* decode success */
-                {
-                    MP3GetLastFrameInfo(player.mp3_decoder, &player.mp3_frameinfo); /* get decode info */
-                    player.mp3_info.outsamples = player.mp3_frameinfo.outputSamps;
-                    if (player.mp3_info.outsamples > 0)
-                    {
-                        if (player.mp3_frameinfo.nChans == 1) /* Mono */
-                        {
-                            /* Mono need to copy one channel to another */
-                            for (i = player.mp3_info.outsamples - 1; i >= 0; i--)
-                            {
-                                player.out_buffer[i * 2] = player.out_buffer[i];
-                                player.out_buffer[i * 2 + 1] = player.out_buffer[i];
-                            }
-                            player.mp3_info.outsamples *= 2;
-                        }
-                    }
-                    if (player.mp3_frameinfo.samprate != player.mp3_info.samplerate && player.mp3_info.vbr)
-                    {
-                        /* set samplerate by frameinfo*/
-                        player.mp3_info.samplerate = player.mp3_frameinfo.samprate;
-                        struct rt_audio_caps caps;
-                        /* set sampletate,channels, samplebits */
-                        caps.main_type = AUDIO_TYPE_OUTPUT;
-                        caps.sub_type = AUDIO_DSP_PARAM;
-                        caps.udata.config.samplerate = player.mp3_info.samplerate;
-                        caps.udata.config.channels = 2;
-                        caps.udata.config.samplebits = 16;
-                        rt_device_control(player.audio_device, AUDIO_CTL_CONFIGURE, &caps);
-                    }
-                    /* write pcm data to soundcard */
-                    rt_device_write(player.audio_device, 0, (uint8_t *)player.out_buffer, MP3_OUTPUT_BUFFER_SIZE);
-                }
-                if (ftell(player.fp) >= player.mp3_info.file_size)
-                {
-                    /* FILE END*/
-                    player.state = PLAYER_STATE_STOPED;
-                }
-                break;
-            }
-            case PLAYER_EVENT_PAUSE:
-            {
-                /* wait resume or stop event forever */
-                event = mp3_player_event_handler(&player, RT_WAITING_FOREVER);
-            }
-
-            default:
-                break;
-            }
-            if (player.state == PLAYER_STATE_STOPED)
-            {
-                break;
-            }
-        }
-        /* close mp3 player */
-        mp3_player_close(&player);
-        LOG_I("play end");
-    }
-
-__exit:
-    if (player.in_buffer)
-    {
-        rt_free(player.in_buffer);
-        player.in_buffer = RT_NULL;
-    }
-
-    if (player.out_buffer)
-    {
-        rt_free(player.out_buffer);
-        player.out_buffer = RT_NULL;
-    }
-
-    if (player.mq)
-    {
-        rt_mq_delete(player.mq);
-        player.mq = RT_NULL;
-    }
-
-    if (player.lock)
-    {
-        rt_mutex_delete(player.lock);
-        player.lock = RT_NULL;
-    }
-}
-
-int mp3_player_init(void)
-{
-    rt_thread_t tid;
-
-    tid = rt_thread_create("mp3_player",
-                           mp3_player_entry,
-                           RT_NULL,
-                           MP3_THREAD_STATCK_SIZE,
-                           MP3_THREAD_PRIORITY, 10);
-    if (tid)
-        rt_thread_startup(tid);
-
-    return RT_EOK;
-}
-
-INIT_APP_EXPORT(mp3_player_init);
-
-static char *MP3Decode_ERR_CODE_get(int err_code)
-{
-    switch (err_code)
-    {
-    case ERR_MP3_NONE:
-        return "ERR_MP3_NONE";
-    case ERR_MP3_INDATA_UNDERFLOW:
-        return "ERR_MP3_INDATA_UNDERFLOW";
-    case ERR_MP3_MAINDATA_UNDERFLOW:
-        return "ERR_MP3_MAINDATA_UNDERFLOW";
-    case ERR_MP3_FREE_BITRATE_SYNC:
-        return "ERR_MP3_FREE_BITRATE_SYNC";
-    case ERR_MP3_OUT_OF_MEMORY:
-        return "ERR_MP3_OUT_OF_MEMORY";
-    case ERR_MP3_NULL_POINTER:
-        return "ERR_MP3_NULL_POINTER";
-    case ERR_MP3_INVALID_FRAMEHEADER:
-        return "ERR_MP3_INVALID_FRAMEHEADER";
-    case ERR_MP3_INVALID_SIDEINFO:
-        return "ERR_MP3_INVALID_SIDEINFO";
-    case ERR_MP3_INVALID_SCALEFACT:
-        return "ERR_MP3_INVALID_SCALEFACT";
-    case ERR_MP3_INVALID_HUFFCODES:
-        return "ERR_MP3_INVALID_HUFFCODES";
-    case ERR_MP3_INVALID_DEQUANTIZE:
-        return "ERR_MP3_INVALID_DEQUANTIZE";
-    case ERR_MP3_INVALID_IMDCT:
-        return "ERR_MP3_INVALID_IMDCT";
-    case ERR_MP3_INVALID_SUBBAND:
-        return "ERR_MP3_INVALID_SUBBAND";
-    case ERR_UNKNOWN:
-        return "ERR_UNKNOWN";
-    }
-}
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2021-06-02     MrzhangF1ghter    first implementation
+ * 2022-10-27     MrzhangF1ghter    fix warning
+ */
+
+#include "mp3_player.h"
+#include <string.h>
+
+#define LOG_TAG "mp3 player"
+#define LOG_LVL DBG_INFO
+#include <ulog.h>
+
+#include "mp3_tag.h"
+
+#define VOLUME_MIN (0)
+#define VOLUME_MAX (100)
+
+#define MP3_PLAYER_MSG_SIZE (10)
+#define MP3_THREAD_STATCK_SIZE (1024 * 2)
+#define MP3_THREAD_PRIORITY (15)
+
+static struct mp3_player player = {0};
+
+#if (LOG_LVL >= DBG_LOG)
+
+static const char *state_str[] =
+    {
+        "STOPPED",
+        "PLAYING",
+        "PAUSED",
+};
+
+static const char *event_str[] =
+    {
+        "NONE",
+        "PLAY"
+        "STOP"
+        "PAUSE"
+        "RESUME"};
+
+#endif
+
+static char *MP3Decode_ERR_CODE_get(int err_code);
+
+/**
+ * @description: lock player
+ * @param None
+ * @return None
+ */
+static void play_lock(void)
+{
+    rt_mutex_take(player.lock, RT_WAITING_FOREVER);
+}
+
+/**
+ * @description: unlock player
+ * @param None
+ * @return None
+ */
+static void play_unlock(void)
+{
+    rt_mutex_release(player.lock);
+}
+
+/**
+ * @description: send msg to player
+ * @param {struct mp3_player} *player
+ * @param {int} type
+ * @param {void} *data
+ * @return the error code,0 on success
+ */
+static rt_err_t play_msg_send(struct mp3_player *player, int type, void *data)
+{
+    struct play_msg msg;
+
+    msg.type = type;
+    msg.data = data;
+
+    return rt_mq_send(player->mq, &msg, sizeof(struct play_msg));
+}
+
+/**
+ * @description: start playing
+ * @param {char} *uri
+ * @return the error code,0 on success
+ */
+int mp3_player_play(char *uri)
+{
+    rt_err_t result = RT_EOK;
+
+    rt_completion_init(&player.ack);
+    play_lock();
+    if (player.state != PLAYER_STATE_STOPED)
+    {
+        mp3_player_stop();
+    }
+    if (player.uri)
+    {
+        rt_free(player.uri);
+    }
+    player.uri = rt_strdup(uri);
+    result = play_msg_send(&player, MSG_START, RT_NULL);
+    rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
+    play_unlock();
+
+    return result;
+}
+
+/**
+ * @description: stop playing
+ * @param None
+ * @return the error code,0 on success
+ */
+int mp3_player_stop(void)
+{
+    rt_err_t result = RT_EOK;
+
+    rt_completion_init(&player.ack);
+
+    play_lock();
+    if (player.state != PLAYER_STATE_STOPED)
+    {
+        result = play_msg_send(&player, MSG_STOP, RT_NULL);
+        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
+    }
+    play_unlock();
+
+    return result;
+}
+
+/**
+ * @description: pause playing
+ * @param None
+ * @return the error code,0 on success
+ */
+int mp3_player_pause(void)
+{
+    rt_err_t result = RT_EOK;
+
+    rt_completion_init(&player.ack);
+    play_lock();
+    if (player.state == PLAYER_STATE_PLAYING)
+    {
+        result = play_msg_send(&player, MSG_PAUSE, RT_NULL);
+        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
+    }
+    play_unlock();
+
+    return result;
+}
+
+/**
+ * @description: resume playing
+ * @param None
+ * @return the error code,0 on success
+ */
+int mp3_player_resume(void)
+{
+    rt_err_t result = RT_EOK;
+    rt_completion_init(&player.ack);
+    play_lock();
+    if (player.state == PLAYER_STATE_PAUSED)
+    {
+        result = play_msg_send(&player, MSG_RESUME, RT_NULL);
+        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
+    }
+    play_unlock();
+    return result;
+}
+
+/**
+ * @description: set volume
+ * @param {int} volume
+ * @return the error code,0 on success
+ */
+int mp3_player_volume_set(int volume)
+{
+    struct rt_audio_caps caps;
+    if (volume < VOLUME_MIN)
+        volume = VOLUME_MIN;
+    else if (volume > VOLUME_MAX)
+        volume = VOLUME_MAX;
+    player.audio_device = rt_device_find(MP3_SOUND_DEVICE_NAME);
+    if (player.audio_device == RT_NULL)
+        return RT_ERROR;
+
+    player.volume = volume;
+    caps.main_type = AUDIO_TYPE_MIXER;
+    caps.sub_type = AUDIO_MIXER_VOLUME;
+    caps.udata.value = volume;
+
+    LOG_D("set volume = %d", volume);
+    return rt_device_control(player.audio_device, AUDIO_CTL_CONFIGURE, &caps);
+}
+
+/**
+ * @description: get current player volume
+ * @param None
+ * @return volume
+ */
+int mp3_player_volume_get(void)
+{
+    return player.volume;
+}
+
+/**
+ * @description: get current player state
+ * @param None
+ * @return enum PLAYER_STATE
+ */
+int mp3_player_state_get(void)
+{
+    return player.state;
+}
+
+/**
+ * @description: get current player uri
+ * @param None
+ * @return pointer to uri
+ */
+char *mp3_player_uri_get(void)
+{
+    return player.uri;
+}
+
+/**
+ * @description: get mp3 current time in seconds
+ * @param {FILE} *fp
+ * @param {mp3_info_t} *mp3_info
+ * @return current seconds,-1 on error
+ */
+uint32_t mp3_get_cur_seconds(void)
+{
+    uint32_t fpos = 0;
+    uint32_t fptr;
+    uint32_t curent_seconds;
+
+    if (player.fp == RT_NULL)
+    {
+        return 0;
+    }
+    fptr = ftell(player.fp);
+    if (fptr > player.mp3_info.data_start)
+        fpos = fptr - player.mp3_info.data_start;
+
+    curent_seconds = fpos * player.mp3_info.total_seconds / (player.mp3_info.file_size - player.mp3_info.data_start);
+    player.mp3_info.curent_seconds = curent_seconds;
+    return curent_seconds;
+}
+
+/**
+ * @description: seek to destination seconds
+ * @param {uint32_t} seconds
+ * @return the error code,0 on success
+ */
+rt_err_t mp3_seek(uint32_t seconds)
+{
+    long fpos;
+    if (seconds > player.mp3_info.total_seconds)
+        return RT_ERROR;
+    /* calculate position by seconds*/
+    fpos = seconds * (player.mp3_info.bitrate / 8) + player.mp3_info.data_start;
+    if (fpos < player.mp3_info.data_start)
+        return RT_ERROR;
+    return fseek(player.fp, fpos + player.mp3_info.data_start, SEEK_SET);
+}
+
+/**
+ * @description: show mp3 info
+ * @param None
+ * @return None
+ */
+void mp3_info_show(void)
+{
+    mp3_info_print(player.mp3_info);
+}
+
+/**
+ * @description: show mp3 current seconds
+ * @param None
+ * @return None
+ */
+void mp3_disp_time(void)
+{
+    uint32_t cur_seconds;
+    cur_seconds = mp3_get_cur_seconds();
+    rt_kprintf("%02d:%02d / %02d:%02d\r\n", cur_seconds / 60, cur_seconds % 60, player.mp3_info.total_seconds / 60, player.mp3_info.total_seconds % 60);
+}
+
+/**
+ * @description: open mp3 player
+ * @param {struct mp3_player} *player
+ * @return the error code,0 on success
+ */
+static rt_err_t mp3_player_open(struct mp3_player *player)
+{
+    rt_err_t result = RT_EOK;
+    struct rt_audio_caps caps;
+
+    /* find device */
+    player->audio_device = rt_device_find(MP3_SOUND_DEVICE_NAME);
+    if (player->audio_device == RT_NULL)
+    {
+        LOG_E("audio_device %s not found", MP3_SOUND_DEVICE_NAME);
+        result = -RT_ERROR;
+        goto __exit;
+    }
+
+    /* open file */
+    player->fp = fopen(player->uri, "rb"); /* readonly */
+    if (player->fp == RT_NULL)
+    {
+        LOG_E("open file %s failed", player->uri);
+        result = -RT_ERROR;
+        goto __exit;
+    }
+
+    /* open sound device */
+    result = rt_device_open(player->audio_device, RT_DEVICE_OFLAG_WRONLY);
+    if (result != RT_EOK)
+    {
+        LOG_E("open %s audio_device failed", MP3_SOUND_DEVICE_NAME);
+        goto __exit;
+    }
+
+    /* init decoder */
+    player->mp3_decoder = MP3InitDecoder();
+    if (player->mp3_decoder == 0)
+    {
+        LOG_E("initialize helix mp3 decoder fail!");
+        result = RT_ERROR;
+        goto __exit;
+    }
+
+    /* set sampletate,channels, samplebits */
+    caps.main_type = AUDIO_TYPE_OUTPUT;
+    caps.sub_type = AUDIO_DSP_PARAM;
+    caps.udata.config.samplerate = 44100;
+    caps.udata.config.channels = 2;
+    caps.udata.config.samplebits = 16;
+    rt_device_control(player->audio_device, AUDIO_CTL_CONFIGURE, &caps);
+
+    return RT_EOK;
+
+__exit:
+    if (player->fp)
+    {
+        fclose(player->fp);
+        player->fp = RT_NULL;
+    }
+
+    if (player->audio_device)
+    {
+        rt_device_close(player->audio_device);
+        player->audio_device = RT_NULL;
+    }
+
+    if (player->mp3_decoder)
+    {
+        MP3FreeDecoder(player->mp3_decoder);
+    }
+
+    return result;
+}
+
+/**
+ * @description:close mp3 player 
+ * @param {struct mp3_player} *player
+ * @return None
+ */
+static void mp3_player_close(struct mp3_player *player)
+{
+    if (player->fp)
+    {
+        fclose(player->fp);
+        player->fp = RT_NULL;
+    }
+    if (player->audio_device)
+    {
+        rt_device_close(player->audio_device);
+        player->audio_device = RT_NULL;
+    }
+    if (player->mp3_decoder)
+    {
+        MP3FreeDecoder(player->mp3_decoder);
+    }
+    LOG_D("close mp3 player");
+}
+
+/**
+ * @description: player event handler
+ * @param {struct mp3_player} *player
+ * @param {int} timeout
+ * @return {int} mp3 event
+ */
+static int mp3_player_event_handler(struct mp3_player *player, int timeout)
+{
+    int event;
+    rt_err_t result;
+    struct play_msg msg;
+#if (LOG_LVL >= DBG_LOG)
+    rt_uint8_t last_state;
+#endif
+
+    result = rt_mq_recv(player->mq, &msg, sizeof(struct play_msg), timeout);
+    if (result != RT_EOK)
+    {
+        event = PLAYER_EVENT_NONE;
+        return event;
+    }
+#if (LOG_LVL >= DBG_LOG)
+    last_state = player->state;
+#endif
+    switch (msg.type)
+    {
+    case MSG_START:
+        event = PLAYER_EVENT_PLAY;
+        player->state = PLAYER_STATE_PLAYING;
+        break;
+    case MSG_STOP:
+        event = PLAYER_EVENT_STOP;
+        player->state = PLAYER_STATE_STOPED;
+        break;
+
+    case MSG_PAUSE:
+        event = PLAYER_EVENT_PAUSE;
+        player->state = PLAYER_STATE_PAUSED;
+        break;
+
+    case MSG_RESUME:
+        event = PLAYER_EVENT_RESUME;
+        player->state = PLAYER_STATE_PLAYING;
+        break;
+
+    default:
+        event = PLAYER_EVENT_NONE;
+        break;
+    }
+    rt_completion_done(&player->ack);
+#if (LOG_LVL >= DBG_LOG)
+    LOG_D("EVENT:%s, STATE:%s -> %s", event_str[event], state_str[last_state], state_str[player->state]);
+#endif
+
+    return event;
+}
+
+/**
+ * @description: mp3 player thread
+ * @param {void *}parameter
+ * @return None
+ */
+static void mp3_player_entry(void *parameter)
+{
+    rt_err_t result = RT_EOK;
+    rt_int32_t size;
+    int event;
+
+    /* decoder relate */
+    int i = 0;
+    int err;
+
+    player.in_buffer = rt_malloc(MP3_INPUT_BUFFER_SIZE);
+    if (player.in_buffer == RT_NULL)
+    {
+        LOG_E("can not malloc input buffer for mp3 player.");
+        return;
+    }
+    player.out_buffer = rt_malloc(MP3_OUTPUT_BUFFER_SIZE);
+    if (player.out_buffer == RT_NULL)
+    {
+        LOG_E("can not malloc output buffer for mp3 player.");
+        return;
+    }
+    memset(player.in_buffer, 0, MP3_INPUT_BUFFER_SIZE);
+    memset(player.out_buffer, 0, MP3_OUTPUT_BUFFER_SIZE);
+
+    player.mq = rt_mq_create("mp3_mq", 10, sizeof(struct play_msg), RT_IPC_FLAG_FIFO);
+    if (player.mq == RT_NULL)
+        goto __exit;
+
+    player.lock = rt_mutex_create("mp3_lock", RT_IPC_FLAG_FIFO);
+    if (player.lock == RT_NULL)
+        goto __exit;
+
+    player.volume = MP3_PLAYER_VOLUME_DEFAULT;
+    /* set volume */
+    mp3_player_volume_set(player.volume);
+
+    while (1)
+    {
+        /* wait play event forever */
+        event = mp3_player_event_handler(&player, RT_WAITING_FOREVER);
+        if (event != PLAYER_EVENT_PLAY)
+            continue;
+
+        /* open mp3 player */
+        result = mp3_player_open(&player);
+        if (result != RT_EOK)
+        {
+            player.state = PLAYER_STATE_STOPED;
+            LOG_I("open mp3 player failed");
+            continue;
+        }
+        LOG_I("play start, uri=%s", player.uri);
+        /* get current mp3 basic info  */
+        if (mp3_get_info(&player) == RT_EOK)
+        {
+            mp3_info_print(player.mp3_info);
+        }
+        
+        fseek(player.fp, player.mp3_info.data_start, SEEK_SET);
+        size = fread(player.in_buffer, 1, MP3_INPUT_BUFFER_SIZE, player.fp);
+        if (size <= 0)
+            goto __exit;
+
+        /* set read ptr to inputbuffer */
+        player.decode_oper.read_ptr = player.in_buffer;
+        player.decode_oper.bytes_left = size;
+
+        while (1)
+        {
+            event = mp3_player_event_handler(&player, RT_WAITING_NO);
+            switch (event)
+            {
+            case PLAYER_EVENT_NONE:
+            {
+                /* find syncword */
+                player.decode_oper.read_offset = MP3FindSyncWord(player.decode_oper.read_ptr, player.decode_oper.bytes_left);
+                if (player.decode_oper.read_offset < 0) /* can not find syncword */
+                {
+                    size = fread(player.in_buffer, 1, MP3_INPUT_BUFFER_SIZE, player.fp);
+                    if (size <= 0)
+                        goto __exit;
+                    player.decode_oper.read_ptr = player.in_buffer;
+                    player.decode_oper.bytes_left = size;
+                    continue;
+                }
+
+                player.decode_oper.read_ptr += player.decode_oper.read_offset;   /* move read pointer to syncword */
+                player.decode_oper.bytes_left -= player.decode_oper.read_offset; /* data size after syncword */
+                if (player.decode_oper.bytes_left < MAINBUF_SIZE * 2)            /* append data */
+                {
+                    i = (uint32_t)(player.decode_oper.bytes_left) & 3;
+                    if (i)
+                        i = 4 - i; /* bytes need to append */
+                    memcpy(player.in_buffer + i, player.decode_oper.read_ptr, player.decode_oper.bytes_left);
+                    player.decode_oper.read_ptr = player.in_buffer + i;
+                    size = fread(player.in_buffer + player.decode_oper.bytes_left + i, 1, MP3_INPUT_BUFFER_SIZE - player.decode_oper.bytes_left - i, player.fp); /* copy at aligned position */
+                    player.decode_oper.bytes_left += size;
+                }
+                /* start decode */
+                err = MP3Decode(player.mp3_decoder, &player.decode_oper.read_ptr, &player.decode_oper.bytes_left, (short *)player.out_buffer, 0);
+                if (err != ERR_MP3_NONE)
+                {
+                    switch (err)
+                    {
+                    case ERR_MP3_INDATA_UNDERFLOW:
+                        LOG_D("ERR_MP3_INDATA_UNDERFLOW");
+                        size = fread(player.in_buffer, 1, MP3_INPUT_BUFFER_SIZE, player.fp); /* append data */
+                        player.decode_oper.read_ptr = player.in_buffer;
+                        player.decode_oper.bytes_left = size;
+                        break;
+                    case ERR_MP3_MAINDATA_UNDERFLOW:
+                        /* do nothing - next call to decode will provide more mainData */
+                        LOG_D("ERR_MP3_MAINDATA_UNDERFLOW");
+                        break;
+                    default:
+                        LOG_D("%s", MP3Decode_ERR_CODE_get(err));
+                        if (player.decode_oper.bytes_left > 0)
+                        {
+                            player.decode_oper.bytes_left--;
+                            player.decode_oper.read_ptr++;
+                        }
+                        break;
+                    }
+                }
+                else /* decode success */
+                {
+                    MP3GetLastFrameInfo(player.mp3_decoder, &player.mp3_frameinfo); /* get decode info */
+                    player.mp3_info.outsamples = player.mp3_frameinfo.outputSamps;
+                    if (player.mp3_info.outsamples > 0)
+                    {
+                        if (player.mp3_frameinfo.nChans == 1) /* Mono */
+                        {
+                            /* Mono need to copy one channel to another */
+                            for (i = player.mp3_info.outsamples - 1; i >= 0; i--)
+                            {
+                                player.out_buffer[i * 2] = player.out_buffer[i];
+                                player.out_buffer[i * 2 + 1] = player.out_buffer[i];
+                            }
+                            player.mp3_info.outsamples *= 2;
+                        }
+                    }
+                    if (player.mp3_frameinfo.samprate != player.mp3_info.samplerate && player.mp3_info.vbr)
+                    {
+                        /* set samplerate by frameinfo*/
+                        player.mp3_info.samplerate = player.mp3_frameinfo.samprate;
+                        struct rt_audio_caps caps;
+                        /* set sampletate,channels, samplebits */
+                        caps.main_type = AUDIO_TYPE_OUTPUT;
+                        caps.sub_type = AUDIO_DSP_PARAM;
+                        caps.udata.config.samplerate = player.mp3_info.samplerate;
+                        caps.udata.config.channels = 2;
+                        caps.udata.config.samplebits = 16;
+                        rt_device_control(player.audio_device, AUDIO_CTL_CONFIGURE, &caps);
+                    }
+                    /* write pcm data to soundcard */
+                    rt_device_write(player.audio_device, 0, (uint8_t *)player.out_buffer, MP3_OUTPUT_BUFFER_SIZE);
+                }
+                if (ftell(player.fp) >= player.mp3_info.file_size)
+                {
+                    /* FILE END*/
+                    player.state = PLAYER_STATE_STOPED;
+                }
+                break;
+            }
+            case PLAYER_EVENT_PAUSE:
+            {
+                /* wait resume or stop event forever */
+                event = mp3_player_event_handler(&player, RT_WAITING_FOREVER);
+            }
+
+            default:
+                break;
+            }
+            if (player.state == PLAYER_STATE_STOPED)
+            {
+                break;
+            }
+        }
+        /* close mp3 player */
+        mp3_player_close(&player);
+        LOG_I("play end");
+    }
+
+__exit:
+    if (player.in_buffer)
+    {
+        rt_free(player.in_buffer);
+        player.in_buffer = RT_NULL;
+    }
+
+    if (player.out_buffer)
+    {
+        rt_free(player.out_buffer);
+        player.out_buffer = RT_NULL;
+    }
+
+    if (player.mq)
+    {
+        rt_mq_delete(player.mq);
+        player.mq = RT_NULL;
+    }
+
+    if (player.lock)
+    {
+        rt_mutex_delete(player.lock);
+        player.lock = RT_NULL;
+    }
+}
+
+int mp3_player_init(void)
+{
+    rt_thread_t tid;
+
+    tid = rt_thread_create("mp3_player",
+                           mp3_player_entry,
+                           RT_NULL,
+                           MP3_THREAD_STATCK_SIZE,
+                           MP3_THREAD_PRIORITY, 10);
+    if (tid)
+        rt_thread_startup(tid);
+
+    return RT_EOK;
+}
+
+INIT_APP_EXPORT(mp3_player_init);
+
+static char *MP3Decode_ERR_CODE_get(int err_code)
+{
+    switch (err_code)
+    {
+    case ERR_MP3_NONE:
+        return "ERR_MP3_NONE";
+    case ERR_MP3_INDATA_UNDERFLOW:
+        return "ERR_MP3_INDATA_UNDERFLOW";
+    case ERR_MP3_MAINDATA_UNDERFLOW:
+        return "ERR_MP3_MAINDATA_UNDERFLOW";
+    case ERR_MP3_FREE_BITRATE_SYNC:
+        return "ERR_MP3_FREE_BITRATE_SYNC";
+    case ERR_MP3_OUT_OF_MEMORY:
+        return "ERR_MP3_OUT_OF_MEMORY";
+    case ERR_MP3_NULL_POINTER:
+        return "ERR_MP3_NULL_POINTER";
+    case ERR_MP3_INVALID_FRAMEHEADER:
+        return "ERR_MP3_INVALID_FRAMEHEADER";
+    case ERR_MP3_INVALID_SIDEINFO:
+        return "ERR_MP3_INVALID_SIDEINFO";
+    case ERR_MP3_INVALID_SCALEFACT:
+        return "ERR_MP3_INVALID_SCALEFACT";
+    case ERR_MP3_INVALID_HUFFCODES:
+        return "ERR_MP3_INVALID_HUFFCODES";
+    case ERR_MP3_INVALID_DEQUANTIZE:
+        return "ERR_MP3_INVALID_DEQUANTIZE";
+    case ERR_MP3_INVALID_IMDCT:
+        return "ERR_MP3_INVALID_IMDCT";
+    case ERR_MP3_INVALID_SUBBAND:
+        return "ERR_MP3_INVALID_SUBBAND";
+    case ERR_UNKNOWN:
+        return "ERR_UNKNOWN";
+    }
+	return "ERR_UNKNOWN";
+}

+ 209 - 208
src/mp3_player_cmd.c

@@ -1,208 +1,209 @@
-/*
- * SPDX-License-Identifier: Apache-2.0
- *
- * Date           Author       Notes
- * 2021-06-02     MrzhangF1ghter    first implementation
- */
-
-#include "mp3_player.h"
-
-#include <rtthread.h>
-#include <rtdevice.h>
-#include <optparse.h>
-#include <mp3_player.h>
-
-#include <stdlib.h>
-
-enum MP3_PLAYER_ACTTION
-{
-    MP3_PLAYER_ACTION_HELP = 0,
-    MP3_PLAYER_ACTION_START = 1,
-    MP3_PLAYER_ACTION_STOP = 2,
-    MP3_PLAYER_ACTION_PAUSE = 3,
-    MP3_PLAYER_ACTION_RESUME = 4,
-    MP3_PLAYER_ACTION_VOLUME = 5,
-    MP3_PLAYER_ACTION_DUMP = 6,
-    MP3_PLAYER_ACTION_JUMP = 7
-};
-
-struct mp3_play_args
-{
-    int action;
-    char *uri;
-    int volume;
-    int seconds;
-};
-
-static const char *state_str[] =
-    {
-        "STOPPED",
-        "PLAYING",
-        "PAUSED",
-};
-
-static struct optparse_long opts[] =
-    {
-        {"help", 'h', OPTPARSE_NONE},
-        {"start", 's', OPTPARSE_REQUIRED},
-        {"stop", 't', OPTPARSE_NONE},
-        {"pause", 'p', OPTPARSE_NONE},
-        {"resume", 'r', OPTPARSE_NONE},
-        {"volume", 'v', OPTPARSE_REQUIRED},
-        {"dump", 'd', OPTPARSE_NONE},
-        {"jump", 'j', OPTPARSE_REQUIRED},
-        {NULL, 0, OPTPARSE_NONE}};
-
-static void usage(void)
-{
-    rt_kprintf("usage: mp3_play [option] [target] ...\n\n");
-    rt_kprintf("usage options:\n");
-    rt_kprintf("  -h,     --help                     Print defined help message.\n");
-    rt_kprintf("  -s URI, --start=URI                Play mp3 music with URI(local files).\n");
-    rt_kprintf("  -t,     --stop                     Stop playing music.\n");
-    rt_kprintf("  -p,     --pause                    Pause the music.\n");
-    rt_kprintf("  -r,     --resume                   Resume the music.\n");
-    rt_kprintf("  -v lvl, --volume=lvl               Change the volume(0~99).\n");
-    rt_kprintf("  -d,     --dump                     Dump play relevant information.\n");
-    rt_kprintf("  -j,     --jump                     Jump to seconds that given.\n");
-}
-
-static void dump_status(void)
-{
-    rt_kprintf("\nmp3_player status:\n");
-    rt_kprintf("uri     - %s\n", mp3_player_uri_get());
-    rt_kprintf("status  - %s\n", state_str[mp3_player_state_get()]);
-    rt_kprintf("volume  - %d\n", mp3_player_volume_get());
-    mp3_disp_time();
-    mp3_info_show();
-}
-
-int mp3_play_args_prase(int argc, char *argv[], struct mp3_play_args *play_args)
-{
-    int ch;
-    int option_index;
-    struct optparse options;
-    rt_uint8_t action_cnt = 0;
-    rt_err_t result = RT_EOK;
-
-    if (argc == 1)
-    {
-        play_args->action = MP3_PLAYER_ACTION_HELP;
-        return RT_EOK;
-    }
-
-    /* Parse cmd */
-    optparse_init(&options, argv);
-    while ((ch = optparse_long(&options, opts, &option_index)) != -1)
-    {
-        switch (ch)
-        {
-        case 'h':
-            play_args->action = MP3_PLAYER_ACTION_HELP;
-            break;
-
-        case 's':
-            play_args->action = MP3_PLAYER_ACTION_START;
-            play_args->uri = options.optarg;
-            action_cnt++;
-            break;
-
-        case 't':
-            play_args->action = MP3_PLAYER_ACTION_STOP;
-            action_cnt++;
-            break;
-
-        case 'p':
-            play_args->action = MP3_PLAYER_ACTION_PAUSE;
-            action_cnt++;
-            break;
-
-        case 'r':
-            play_args->action = MP3_PLAYER_ACTION_RESUME;
-            action_cnt++;
-            break;
-
-        case 'v':
-            play_args->action = MP3_PLAYER_ACTION_VOLUME;
-            play_args->volume = (options.optarg == RT_NULL) ? (-1) : atoi(options.optarg);
-            action_cnt++;
-            break;
-
-        case 'd':
-            play_args->action = MP3_PLAYER_ACTION_DUMP;
-            break;
-
-        case 'j':
-            play_args->action = MP3_PLAYER_ACTION_JUMP;
-            play_args->seconds = (options.optarg == RT_NULL) ? (-1) : atoi(options.optarg);
-            break;
-
-        default:
-            result = -RT_EINVAL;
-            break;
-        }
-    }
-
-    if (action_cnt > 1)
-    {
-        rt_kprintf("START STOP PAUSE RESUME parameter can't be used at the same time.\n");
-        result = -RT_EINVAL;
-    }
-
-    return result;
-}
-
-int mp3_player(int argc, char *argv[])
-{
-    int result = RT_EOK;
-    struct mp3_play_args play_args = {0};
-
-    result = mp3_play_args_prase(argc, argv, &play_args);
-    if (result != RT_EOK)
-    {
-        usage();
-        return result;
-    }
-
-    switch (play_args.action)
-    {
-    case MP3_PLAYER_ACTION_HELP:
-        usage();
-        break;
-
-    case MP3_PLAYER_ACTION_START:
-        mp3_player_play(play_args.uri);
-        break;
-
-    case MP3_PLAYER_ACTION_STOP:
-        mp3_player_stop();
-        break;
-
-    case MP3_PLAYER_ACTION_PAUSE:
-        mp3_player_pause();
-        break;
-
-    case MP3_PLAYER_ACTION_RESUME:
-        mp3_player_resume();
-        break;
-
-    case MP3_PLAYER_ACTION_VOLUME:
-        mp3_player_volume_set(play_args.volume);
-        break;
-
-    case MP3_PLAYER_ACTION_DUMP:
-        dump_status();
-        break;
-
-    case MP3_PLAYER_ACTION_JUMP:
-        mp3_seek(play_args.seconds);
-        break;
-    default:
-        result = -RT_ERROR;
-        break;
-    }
-
-    return result;
-}
-
-MSH_CMD_EXPORT_ALIAS(mp3_player, mp3play, play mp3 music);
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2021-06-02     MrzhangF1ghter    first implementation
+ * 2022-10-27     MrzhangF1ghter    fix warning
+ */
+
+#include "mp3_player.h"
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <optparse.h>
+#include <mp3_player.h>
+
+#include <stdlib.h>
+
+enum MP3_PLAYER_ACTTION
+{
+    MP3_PLAYER_ACTION_HELP = 0,
+    MP3_PLAYER_ACTION_START = 1,
+    MP3_PLAYER_ACTION_STOP = 2,
+    MP3_PLAYER_ACTION_PAUSE = 3,
+    MP3_PLAYER_ACTION_RESUME = 4,
+    MP3_PLAYER_ACTION_VOLUME = 5,
+    MP3_PLAYER_ACTION_DUMP = 6,
+    MP3_PLAYER_ACTION_JUMP = 7
+};
+
+struct mp3_play_args
+{
+    int action;
+    char *uri;
+    int volume;
+    int seconds;
+};
+
+static const char *state_str[] =
+    {
+        "STOPPED",
+        "PLAYING",
+        "PAUSED",
+};
+
+static struct optparse_long opts[] =
+    {
+        {"help", 'h', OPTPARSE_NONE},
+        {"start", 's', OPTPARSE_REQUIRED},
+        {"stop", 't', OPTPARSE_NONE},
+        {"pause", 'p', OPTPARSE_NONE},
+        {"resume", 'r', OPTPARSE_NONE},
+        {"volume", 'v', OPTPARSE_REQUIRED},
+        {"dump", 'd', OPTPARSE_NONE},
+        {"jump", 'j', OPTPARSE_REQUIRED},
+        {NULL, 0, OPTPARSE_NONE}};
+
+static void usage(void)
+{
+    rt_kprintf("usage: mp3_play [option] [target] ...\n\n");
+    rt_kprintf("usage options:\n");
+    rt_kprintf("  -h,     --help                     Print defined help message.\n");
+    rt_kprintf("  -s URI, --start=URI                Play mp3 music with URI(local files).\n");
+    rt_kprintf("  -t,     --stop                     Stop playing music.\n");
+    rt_kprintf("  -p,     --pause                    Pause the music.\n");
+    rt_kprintf("  -r,     --resume                   Resume the music.\n");
+    rt_kprintf("  -v lvl, --volume=lvl               Change the volume(0~99).\n");
+    rt_kprintf("  -d,     --dump                     Dump play relevant information.\n");
+    rt_kprintf("  -j,     --jump                     Jump to seconds that given.\n");
+}
+
+static void dump_status(void)
+{
+    rt_kprintf("\nmp3_player status:\n");
+    rt_kprintf("uri     - %s\n", mp3_player_uri_get());
+    rt_kprintf("status  - %s\n", state_str[mp3_player_state_get()]);
+    rt_kprintf("volume  - %d\n", mp3_player_volume_get());
+    mp3_disp_time();
+    mp3_info_show();
+}
+
+int mp3_play_args_prase(int argc, char *argv[], struct mp3_play_args *play_args)
+{
+    int ch;
+    int option_index;
+    struct optparse options;
+    rt_uint8_t action_cnt = 0;
+    rt_err_t result = RT_EOK;
+
+    if (argc == 1)
+    {
+        play_args->action = MP3_PLAYER_ACTION_HELP;
+        return RT_EOK;
+    }
+
+    /* Parse cmd */
+    optparse_init(&options, argv);
+    while ((ch = optparse_long(&options, opts, &option_index)) != -1)
+    {
+        switch (ch)
+        {
+        case 'h':
+            play_args->action = MP3_PLAYER_ACTION_HELP;
+            break;
+
+        case 's':
+            play_args->action = MP3_PLAYER_ACTION_START;
+            play_args->uri = options.optarg;
+            action_cnt++;
+            break;
+
+        case 't':
+            play_args->action = MP3_PLAYER_ACTION_STOP;
+            action_cnt++;
+            break;
+
+        case 'p':
+            play_args->action = MP3_PLAYER_ACTION_PAUSE;
+            action_cnt++;
+            break;
+
+        case 'r':
+            play_args->action = MP3_PLAYER_ACTION_RESUME;
+            action_cnt++;
+            break;
+
+        case 'v':
+            play_args->action = MP3_PLAYER_ACTION_VOLUME;
+            play_args->volume = (options.optarg == RT_NULL) ? (-1) : atoi(options.optarg);
+            action_cnt++;
+            break;
+
+        case 'd':
+            play_args->action = MP3_PLAYER_ACTION_DUMP;
+            break;
+
+        case 'j':
+            play_args->action = MP3_PLAYER_ACTION_JUMP;
+            play_args->seconds = (options.optarg == RT_NULL) ? (-1) : atoi(options.optarg);
+            break;
+
+        default:
+            result = -RT_EINVAL;
+            break;
+        }
+    }
+
+    if (action_cnt > 1)
+    {
+        rt_kprintf("START STOP PAUSE RESUME parameter can't be used at the same time.\n");
+        result = -RT_EINVAL;
+    }
+
+    return result;
+}
+
+int mp3_player(int argc, char *argv[])
+{
+    int result = RT_EOK;
+    struct mp3_play_args play_args = {0};
+
+    result = mp3_play_args_prase(argc, argv, &play_args);
+    if (result != RT_EOK)
+    {
+        usage();
+        return result;
+    }
+
+    switch (play_args.action)
+    {
+    case MP3_PLAYER_ACTION_HELP:
+        usage();
+        break;
+
+    case MP3_PLAYER_ACTION_START:
+        mp3_player_play(play_args.uri);
+        break;
+
+    case MP3_PLAYER_ACTION_STOP:
+        mp3_player_stop();
+        break;
+
+    case MP3_PLAYER_ACTION_PAUSE:
+        mp3_player_pause();
+        break;
+
+    case MP3_PLAYER_ACTION_RESUME:
+        mp3_player_resume();
+        break;
+
+    case MP3_PLAYER_ACTION_VOLUME:
+        mp3_player_volume_set(play_args.volume);
+        break;
+
+    case MP3_PLAYER_ACTION_DUMP:
+        dump_status();
+        break;
+
+    case MP3_PLAYER_ACTION_JUMP:
+        mp3_seek(play_args.seconds);
+        break;
+    default:
+        result = -RT_ERROR;
+        break;
+    }
+
+    return result;
+}
+
+MSH_CMD_EXPORT_ALIAS(mp3_player, mp3play, play mp3 music);