main
This commit is contained in:
17
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/.github/stale.yml
vendored
Normal file
17
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/.github/stale.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: wontfix
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
1
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/.piopm
Normal file
1
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/.piopm
Normal file
@ -0,0 +1 @@
|
||||
{"type": "library", "name": "ESP32-audioI2S", "version": "2.1.0", "spec": {"owner": "esphome", "id": 14055, "name": "ESP32-audioI2S", "requirements": null, "uri": null}}
|
674
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/LICENSE
Normal file
674
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
94
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/README.md
Normal file
94
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/README.md
Normal file
@ -0,0 +1,94 @@
|
||||
# ESP32-audioI2S
|
||||
Plays mp3, m4a and wav files from SD card via I2S with external hardware.
|
||||
HELIX-mp3 and -aac decoder is included.
|
||||
Works with MAX98357A (3 Watt amplifier with DAC), connected three lines (DOUT, BLCK, LRC) to I2S.
|
||||
For stereo are two MAX98357A necessary. AudioI2S works with UDA1334A (Adafruit I2S Stereo Decoder Breakout Board) and PCM5102A.
|
||||
Other HW may work but not tested. Plays also icy-streams and GoogleTTS. Can be compiled with Arduino IDE. [WIKI](https://github.com/schreibfaul1/ESP32-audioI2S/wiki)
|
||||
|
||||
```` c++
|
||||
#include "Arduino.h"
|
||||
#include "WiFi.h"
|
||||
#include "Audio.h"
|
||||
#include "SD.h"
|
||||
#include "FS.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define SD_CS 5
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
Audio audio;
|
||||
|
||||
String ssid = "*******";
|
||||
String password = "*******";
|
||||
|
||||
void setup() {
|
||||
pinMode(SD_CS, OUTPUT); digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
Serial.begin(115200);
|
||||
SD.begin(SD_CS);
|
||||
WiFi.disconnect();
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (WiFi.status() != WL_CONNECTED) delay(1500);
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(21); // 0...21
|
||||
|
||||
// audio.connecttoFS(SD, "/320k_test.mp3");
|
||||
// audio.connecttohost("http://www.wdr.de/wdrlive/media/einslive.m3u");
|
||||
// audio.connecttohost("http://macslons-irish-pub-radio.com/media.asx");
|
||||
// audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.aac"); // 128k aac
|
||||
audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.mp3"); // 128k mp3
|
||||
// audio.connecttohost("https://github.com/schreibfaul1/ESP32-audioI2S/raw/master/additional_info/Testfiles/sample1.m4a"); // m4a
|
||||
// audio.connecttohost("https://github.com/schreibfaul1/ESP32-audioI2S/raw/master/additional_info/Testfiles/test_16bit_stereo.wav"); // wav
|
||||
// audio.connecttospeech("Wenn die Hunde schlafen, kann der Wolf gut Schafe stehlen.", "de");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
audio.loop();
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
Serial.print("station ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
Serial.print("streamtitle ");Serial.println(info);
|
||||
}
|
||||
void audio_bitrate(const char *info){
|
||||
Serial.print("bitrate ");Serial.println(info);
|
||||
}
|
||||
void audio_commercial(const char *info){ //duration in sec
|
||||
Serial.print("commercial ");Serial.println(info);
|
||||
}
|
||||
void audio_icyurl(const char *info){ //homepage
|
||||
Serial.print("icyurl ");Serial.println(info);
|
||||
}
|
||||
void audio_lasthost(const char *info){ //stream URL played
|
||||
Serial.print("lasthost ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_speech(const char *info){
|
||||
Serial.print("eof_speech ");Serial.println(info);
|
||||
}
|
||||
|
||||
````
|
||||
Breadboard
|
||||

|
||||
Wiring
|
||||

|
||||
Impulse diagram
|
||||

|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,156 @@
|
||||
// found this sketch on https://www.mikrocontroller.net/topic/474383
|
||||
// thanks to Michael U. (amiga)
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "WiFi.h"
|
||||
#include "SPI.h"
|
||||
#include "SD.h"
|
||||
#include "FS.h"
|
||||
#include "Wire.h"
|
||||
#include "AC101.h" //https://github.com/schreibfaul1/AC101
|
||||
// #include "ES8388.h" // https://github.com/maditnerd/es8388
|
||||
#include "Audio.h" //https://github.com/schreibfaul1/ESP32-audioI2S
|
||||
|
||||
|
||||
// SPI GPIOs
|
||||
#define SD_CS 13
|
||||
#define SPI_MOSI 15
|
||||
#define SPI_MISO 2
|
||||
#define SPI_SCK 14
|
||||
|
||||
// I2S GPIOs, the names refer on AC101, AS1 Audio Kit V2.2 2379
|
||||
#define I2S_DSIN 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
#define I2S_MCLK 0
|
||||
#define I2S_DOUT 35
|
||||
|
||||
// I2S GPIOs, the names refer on ES8388, AS1 Audio Kit V2.2 3378
|
||||
//#define I2S_DSIN 35
|
||||
//#define I2S_BCLK 27
|
||||
//#define I2S_LRC 25
|
||||
//#define I2S_MCLK 0
|
||||
//#define I2S_DOUT 26
|
||||
|
||||
// I2C GPIOs
|
||||
#define IIC_CLK 32
|
||||
#define IIC_DATA 33
|
||||
|
||||
// buttons
|
||||
// #define BUTTON_2_PIN 13 // shared mit SPI_CS
|
||||
#define BUTTON_3_PIN 19
|
||||
#define BUTTON_4_PIN 23
|
||||
#define BUTTON_5_PIN 18 // Stop
|
||||
#define BUTTON_6_PIN 5 // Play
|
||||
|
||||
// amplifier enable
|
||||
#define GPIO_PA_EN 21
|
||||
|
||||
//Switch S1: 1-OFF, 2-ON, 3-ON, 4-OFF, 5-OFF
|
||||
|
||||
String ssid = "xxxxxxxxx";
|
||||
String password = "xxxxxxxxx";
|
||||
|
||||
static AC101 dac; // AC101
|
||||
// ES8388 dac; // ES8388 (new board)
|
||||
int volume = 40; // 0...100
|
||||
|
||||
Audio audio;
|
||||
|
||||
//#####################################################################
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("\r\nReset");
|
||||
Serial.printf_P(PSTR("Free mem=%d\n"), ESP.getFreeHeap());
|
||||
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
SPI.setFrequency(1000000);
|
||||
|
||||
SD.begin(SD_CS);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.printf_P(PSTR("Connected\r\nRSSI: "));
|
||||
Serial.print(WiFi.RSSI());
|
||||
Serial.print(" IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
Serial.printf("Connect to DAC codec... ");
|
||||
while (not dac.begin(IIC_DATA, IIC_CLK))
|
||||
{
|
||||
Serial.printf("Failed!\n");
|
||||
delay(1000);
|
||||
}
|
||||
Serial.printf("OK\n");
|
||||
|
||||
dac.SetVolumeSpeaker(volume);
|
||||
dac.SetVolumeHeadphone(volume);
|
||||
// ac.DumpRegisters();
|
||||
|
||||
// Enable amplifier
|
||||
pinMode(GPIO_PA_EN, OUTPUT);
|
||||
digitalWrite(GPIO_PA_EN, HIGH);
|
||||
|
||||
// set I2S_MasterClock
|
||||
audio.i2s_mclk_pin_select(I2S_MCLK);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DSIN);
|
||||
audio.setVolume(10); // 0...21
|
||||
|
||||
audio.connecttoFS(SD, "/320k_test.mp3");
|
||||
// audio.connecttoSD("/Banana Boat Song - Harry Belafonte.mp3");
|
||||
// audio.connecttohost("http://mp3channels.webradio.antenne.de:80/oldies-but-goldies");
|
||||
// audio.connecttohost("http://dg-rbb-http-dus-dtag-cdn.cast.addradio.de/rbb/antennebrandenburg/live/mp3/128/stream.mp3");
|
||||
// audio.connecttospeech("Wenn die Hunde schlafen, kann der Wolf gut Schafe stehlen.", "de");
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void loop()
|
||||
{
|
||||
audio.loop();
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
Serial.print("station ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
Serial.print("streamtitle ");Serial.println(info);
|
||||
}
|
||||
void audio_bitrate(const char *info){
|
||||
Serial.print("bitrate ");Serial.println(info);
|
||||
}
|
||||
void audio_commercial(const char *info){ //duration in sec
|
||||
Serial.print("commercial ");Serial.println(info);
|
||||
}
|
||||
void audio_icyurl(const char *info){ //homepage
|
||||
Serial.print("icyurl ");Serial.println(info);
|
||||
}
|
||||
void audio_lasthost(const char *info){ //stream URL played
|
||||
Serial.print("lasthost ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_speech(const char *info){
|
||||
Serial.print("eof_speech ");Serial.println(info);
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
// the pin assignment matches the Olimex ADF board
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "WiFi.h"
|
||||
#include "SPI.h"
|
||||
#include "SD.h"
|
||||
#include "FS.h"
|
||||
#include "Wire.h"
|
||||
#include "ES8388.h" // https://github.com/maditnerd/es8388
|
||||
#include "Audio.h" // https://github.com/schreibfaul1/ESP32-audioI2S
|
||||
|
||||
|
||||
#define SD_CS 21
|
||||
|
||||
// GPIOs for SPI
|
||||
#define SPI_MOSI 13
|
||||
#define SPI_MISO 12
|
||||
#define SPI_SCK 14
|
||||
|
||||
// I2S GPIOs
|
||||
#define I2S_SDOUT 26
|
||||
#define I2S_BCLK 5
|
||||
#define I2S_LRCK 25
|
||||
#define I2S_MCLK 0
|
||||
|
||||
// I2C GPIOs
|
||||
#define IIC_CLK 23
|
||||
#define IIC_DATA 18
|
||||
|
||||
// Amplifier enable
|
||||
#define GPIO_PA_EN 19
|
||||
|
||||
char ssid[] = "xxxxxxxxx";
|
||||
char password[] = "xxxxxxxxx";
|
||||
|
||||
|
||||
int volume = 80; // 0...100
|
||||
|
||||
ES8388 es;
|
||||
Audio audio;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("\r\nReset");
|
||||
Serial.printf_P(PSTR("Free mem=%d\n"), ESP.getFreeHeap());
|
||||
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
SPI.setFrequency(1000000);
|
||||
|
||||
SD.begin(SD_CS);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while(WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.printf_P(PSTR("Connected\r\nRSSI: "));
|
||||
Serial.print(WiFi.RSSI());
|
||||
Serial.print(" IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
Serial.printf("Connect to ES8388 codec... ");
|
||||
while (not es.begin(IIC_DATA, IIC_CLK))
|
||||
{
|
||||
Serial.printf("Failed!\n");
|
||||
delay(1000);
|
||||
}
|
||||
Serial.printf("OK\n");
|
||||
|
||||
es.volume(ES8388::ES_MAIN, volume);
|
||||
es.volume(ES8388::ES_OUT1, volume);
|
||||
es.volume(ES8388::ES_OUT2, volume);
|
||||
es.mute(ES8388::ES_OUT1, false);
|
||||
es.mute(ES8388::ES_OUT2, false);
|
||||
es.mute(ES8388::ES_MAIN, false);
|
||||
|
||||
// Enable amplifier
|
||||
pinMode(GPIO_PA_EN, OUTPUT);
|
||||
digitalWrite(GPIO_PA_EN, HIGH);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRCK, I2S_SDOUT);
|
||||
audio.i2s_mclk_pin_select(I2S_MCLK);
|
||||
audio.setVolume(21); // 0...21
|
||||
|
||||
audio.connecttohost("http://mp3channels.webradio.antenne.de:80/oldies-but-goldies");
|
||||
// audio.connecttoFS(SD, "320k_test.mp3");
|
||||
// audio.connecttospeech("Wenn die Hunde schlafen, kann der Wolf gut Schafe stehlen.", "de");
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void loop()
|
||||
{
|
||||
audio.loop();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
Serial.print("station ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreaminfo(const char *info){
|
||||
Serial.print("streaminfo ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
Serial.print("streamtitle ");Serial.println(info);
|
||||
}
|
||||
void audio_bitrate(const char *info){
|
||||
Serial.print("bitrate ");Serial.println(info);
|
||||
}
|
||||
void audio_commercial(const char *info){ //duration in sec
|
||||
Serial.print("commercial ");Serial.println(info);
|
||||
}
|
||||
void audio_icyurl(const char *info){ //homepage
|
||||
Serial.print("icyurl ");Serial.println(info);
|
||||
}
|
||||
void audio_lasthost(const char *info){ //stream URL played
|
||||
Serial.print("lasthost ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_speech(const char *info){
|
||||
Serial.print("eof_speech ");Serial.println(info);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,100 @@
|
||||
//**********************************************************************************************************
|
||||
//* audioI2S-- I2S audiodecoder for ESP32, *
|
||||
//**********************************************************************************************************
|
||||
//
|
||||
// first release on 11/2018
|
||||
// Version 3 , Jul.02/2020
|
||||
//
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT.
|
||||
// FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR
|
||||
// OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
//
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "WiFiMulti.h"
|
||||
#include "Audio.h"
|
||||
#include "SPI.h"
|
||||
#include "SD.h"
|
||||
#include "FS.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define SD_CS 5
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
Audio audio;
|
||||
WiFiMulti wifiMulti;
|
||||
String ssid = "xxxxx";
|
||||
String password = "xxxxx";
|
||||
|
||||
|
||||
void setup() {
|
||||
pinMode(SD_CS, OUTPUT); digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
SPI.setFrequency(1000000);
|
||||
Serial.begin(115200);
|
||||
SD.begin(SD_CS);
|
||||
WiFi.mode(WIFI_STA);
|
||||
wifiMulti.addAP(ssid.c_str(), password.c_str());
|
||||
wifiMulti.run();
|
||||
if(WiFi.status() != WL_CONNECTED){
|
||||
WiFi.disconnect(true);
|
||||
wifiMulti.run();
|
||||
}
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(12); // 0...21
|
||||
|
||||
// audio.connecttoFS(SD, "/320k_test.mp3");
|
||||
// audio.connecttoFS(SD, "test.wav");
|
||||
// audio.connecttohost("http://www.wdr.de/wdrlive/media/einslive.m3u");
|
||||
// audio.connecttohost("http://macslons-irish-pub-radio.com/media.asx");
|
||||
// audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.aac"); // 128k aac
|
||||
audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.mp3"); // 128k mp3
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
audio.loop();
|
||||
if(Serial.available()){ // put streamURL in serial monitor
|
||||
audio.stopSong();
|
||||
String r=Serial.readString(); r.trim();
|
||||
if(r.length()>5) audio.connecttohost(r.c_str());
|
||||
log_i("free heap=%i", ESP.getFreeHeap());
|
||||
}
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
Serial.print("station ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
Serial.print("streamtitle ");Serial.println(info);
|
||||
}
|
||||
void audio_bitrate(const char *info){
|
||||
Serial.print("bitrate ");Serial.println(info);
|
||||
}
|
||||
void audio_commercial(const char *info){ //duration in sec
|
||||
Serial.print("commercial ");Serial.println(info);
|
||||
}
|
||||
void audio_icyurl(const char *info){ //homepage
|
||||
Serial.print("icyurl ");Serial.println(info);
|
||||
}
|
||||
void audio_lasthost(const char *info){ //stream URL played
|
||||
Serial.print("lasthost ");Serial.println(info);
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
//**********************************************************************************************************
|
||||
//* audioI2S-- I2S audiodecoder for M5Stack Core2 *
|
||||
//**********************************************************************************************************
|
||||
//
|
||||
// first release on May.12/2021
|
||||
//
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT.
|
||||
// FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR
|
||||
// OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
//
|
||||
|
||||
#include <M5Core2.h>
|
||||
#include "Audio.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define SD_CS 4
|
||||
#define SD_MOSI 23
|
||||
#define SD_MISO 38
|
||||
#define SD_SCK 18
|
||||
#define I2S_DOUT 2
|
||||
#define I2S_BCLK 12
|
||||
#define I2S_LRC 0
|
||||
|
||||
Audio audio;
|
||||
String ssid = "xxxxxx";
|
||||
String password = "xxxxxx";
|
||||
|
||||
|
||||
void setup() {
|
||||
M5.begin(true, true, true, true);
|
||||
M5.Axp.SetSpkEnable(true);
|
||||
M5.Lcd.fillScreen(BLACK);
|
||||
M5.Lcd.setTextColor(WHITE);
|
||||
M5.Lcd.setTextSize(2);
|
||||
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SD_SCK, SD_MISO, SD_MOSI);
|
||||
SPI.setFrequency(1000000);
|
||||
SD.begin(SD_CS);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(15); // 0...21
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (!WiFi.isConnected()) {
|
||||
delay(10);
|
||||
}
|
||||
ESP_LOGI(TAG, "Connected");
|
||||
ESP_LOGI(TAG, "Starting MP3...\n");
|
||||
|
||||
// audio.connecttoFS(SD, "/320k_test.mp3");
|
||||
// audio.connecttoFS(SD, "test.wav");
|
||||
audio.connecttohost("http://air.ofr.fm:8008/jazz/mp3/128");
|
||||
// audio.connecttospeech("Миска вареників з картоплею та шкварками, змащених салом!", "uk-UA");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
if(Serial.available()){ // put streamURL in serial monitor
|
||||
audio.stopSong();
|
||||
String r=Serial.readString();
|
||||
r.trim();
|
||||
if(r.length()>5) audio.connecttohost(r.c_str());
|
||||
log_i("free heap=%i", ESP.getFreeHeap());
|
||||
}
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
Serial.print("station ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
Serial.print("streamtitle ");Serial.println(info);
|
||||
}
|
||||
void audio_bitrate(const char *info){
|
||||
Serial.print("bitrate ");Serial.println(info);
|
||||
}
|
||||
void audio_commercial(const char *info){ //duration in sec
|
||||
Serial.print("commercial ");Serial.println(info);
|
||||
}
|
||||
void audio_icyurl(const char *info){ //homepage
|
||||
Serial.print("icyurl ");Serial.println(info);
|
||||
}
|
||||
void audio_lasthost(const char *info){ //stream URL played
|
||||
Serial.print("lasthost ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_speech(const char *info){
|
||||
Serial.print("eof_speech ");Serial.println(info);
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
|
||||
// M5Stack Node support
|
||||
// thanks to Cellie - issue #35 25.Apr.2020
|
||||
// M5Stack board with Node base also need a MCLK signal on GPIO0.
|
||||
|
||||
#include <wm8978.h> /* https://github.com/CelliesProjects/wm8978-esp32 */
|
||||
#include <Audio.h> /* https://github.com/schreibfaul1/ESP32-audioI2S */
|
||||
|
||||
/* M5Stack Node WM8978 I2C pins */
|
||||
#define I2C_SDA 21
|
||||
#define I2C_SCL 22
|
||||
|
||||
/* M5Stack Node I2S pins */
|
||||
#define I2S_BCK 5
|
||||
#define I2S_WS 13
|
||||
#define I2S_DOUT 2
|
||||
#define I2S_DIN 34
|
||||
|
||||
/* M5Stack WM8978 MCLK gpio number */
|
||||
#define I2S_MCLKPIN 0
|
||||
|
||||
WM8978 dac;
|
||||
Audio audio;
|
||||
|
||||
void setup() {
|
||||
/* Setup wm8978 I2C interface */
|
||||
if (!dac.begin(I2C_SDA, I2C_SCL)) {
|
||||
ESP_LOGE(TAG, "Error setting up dac. System halted");
|
||||
while (1) delay(100);
|
||||
}
|
||||
|
||||
/* Setup wm8978 I2S interface */
|
||||
audio.setPinout(I2S_BCK, I2S_WS, I2S_DOUT, I2S_DIN);
|
||||
|
||||
/* Setup wm8978 MCLK - for example M5Stack Node needs MCLK on GPIO 0 */
|
||||
audio.i2s_mclk_pin_select(I2S_MCLKPIN);
|
||||
|
||||
WiFi.begin("xxx", "xxx");
|
||||
while (!WiFi.isConnected()) {
|
||||
delay(10);
|
||||
}
|
||||
ESP_LOGI(TAG, "Connected");
|
||||
|
||||
ESP_LOGI(TAG, "Starting MP3...\n");
|
||||
audio.connecttohost("http://icecast.omroep.nl/3fm-bb-mp3");
|
||||
|
||||
dac.setSPKvol(40); /* max 63 */
|
||||
dac.setHPvol(32, 32);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
//**********************************************************************************************************
|
||||
//* audioI2S-- I2S audiodecoder for M5StickC Plus and SPK HAT *
|
||||
//**********************************************************************************************************
|
||||
//
|
||||
// first release on May.12/2021
|
||||
//
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT.
|
||||
// FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR
|
||||
// OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
//
|
||||
|
||||
#include <M5StickCPlus.h>
|
||||
#include "Audio.h"
|
||||
|
||||
Audio audio = Audio(true);
|
||||
|
||||
String ssid = "xxxxxxxx";
|
||||
String password = "xxxxxxxx";
|
||||
|
||||
|
||||
void setup() {
|
||||
M5.begin(false); //Lcd disabled to reduce noise
|
||||
M5.Axp.ScreenBreath(1); //Lower Lcd backlight
|
||||
pinMode(36, INPUT);
|
||||
gpio_pulldown_dis(GPIO_NUM_25);
|
||||
gpio_pullup_dis(GPIO_NUM_25);
|
||||
M5.Beep.tone(44100); //Built-in buzzer tone
|
||||
M5.Beep.end(); //disabled
|
||||
|
||||
audio.setVolume(15); // 0...21
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (!WiFi.isConnected()) {
|
||||
delay(10);
|
||||
}
|
||||
ESP_LOGI(TAG, "Connected");
|
||||
ESP_LOGI(TAG, "Starting MP3...\n");
|
||||
|
||||
audio.connecttohost("http://air.ofr.fm:8008/jazz/mp3/128");
|
||||
// audio.connecttospeech("Миска вареників з картоплею та шкварками, змащених салом!", "uk-UA");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
if(Serial.available()){ // put streamURL in serial monitor
|
||||
audio.stopSong();
|
||||
String r=Serial.readString();
|
||||
r.trim();
|
||||
if(r.length()>5) audio.connecttohost(r.c_str());
|
||||
log_i("free heap=%i", ESP.getFreeHeap());
|
||||
}
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
Serial.print("station ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
Serial.print("streamtitle ");Serial.println(info);
|
||||
}
|
||||
void audio_bitrate(const char *info){
|
||||
Serial.print("bitrate ");Serial.println(info);
|
||||
}
|
||||
void audio_commercial(const char *info){ //duration in sec
|
||||
Serial.print("commercial ");Serial.println(info);
|
||||
}
|
||||
void audio_icyurl(const char *info){ //homepage
|
||||
Serial.print("icyurl ");Serial.println(info);
|
||||
}
|
||||
void audio_lasthost(const char *info){ //stream URL played
|
||||
Serial.print("lasthost ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_speech(const char *info){
|
||||
Serial.print("eof_speech ");Serial.println(info);
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
#include "Arduino.h"
|
||||
#include "WiFi.h"
|
||||
#include "Audio.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
Audio audio;
|
||||
|
||||
String ssid = "******";
|
||||
String password = "******";
|
||||
|
||||
//****************************************************************************************
|
||||
// A U D I O _ T A S K *
|
||||
//****************************************************************************************
|
||||
|
||||
struct audioMessage{
|
||||
uint8_t cmd;
|
||||
const char* txt;
|
||||
uint32_t value;
|
||||
uint32_t ret;
|
||||
} audioTxMessage, audioRxMessage;
|
||||
|
||||
enum : uint8_t { SET_VOLUME, GET_VOLUME, CONNECTTOHOST, CONNECTTOSD };
|
||||
|
||||
QueueHandle_t audioSetQueue = NULL;
|
||||
QueueHandle_t audioGetQueue = NULL;
|
||||
|
||||
void CreateQueues(){
|
||||
audioSetQueue = xQueueCreate(10, sizeof(struct audioMessage));
|
||||
audioGetQueue = xQueueCreate(10, sizeof(struct audioMessage));
|
||||
}
|
||||
|
||||
void audioTask(void *parameter) {
|
||||
CreateQueues();
|
||||
if(!audioSetQueue || !audioGetQueue){
|
||||
log_e("queues are not initialized");
|
||||
while(true){;} // endless loop
|
||||
}
|
||||
|
||||
struct audioMessage audioRxTaskMessage;
|
||||
struct audioMessage audioTxTaskMessage;
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(15); // 0...21
|
||||
|
||||
while(true){
|
||||
if(xQueueReceive(audioSetQueue, &audioRxTaskMessage, 1) == pdPASS) {
|
||||
if(audioRxTaskMessage.cmd == SET_VOLUME){
|
||||
audioTxTaskMessage.cmd = SET_VOLUME;
|
||||
audio.setVolume(audioRxTaskMessage.value);
|
||||
audioTxTaskMessage.ret = 1;
|
||||
xQueueSend(audioGetQueue, &audioTxTaskMessage, portMAX_DELAY);
|
||||
}
|
||||
else if(audioRxTaskMessage.cmd == CONNECTTOHOST){
|
||||
audioTxTaskMessage.cmd = CONNECTTOHOST;
|
||||
audioTxTaskMessage.ret = audio.connecttohost(audioRxTaskMessage.txt);
|
||||
xQueueSend(audioGetQueue, &audioTxTaskMessage, portMAX_DELAY);
|
||||
}
|
||||
else if(audioRxTaskMessage.cmd == CONNECTTOSD){
|
||||
audioTxTaskMessage.cmd = CONNECTTOSD;
|
||||
audioTxTaskMessage.ret = audio.connecttoSD(audioRxTaskMessage.txt);
|
||||
xQueueSend(audioGetQueue, &audioTxTaskMessage, portMAX_DELAY);
|
||||
}
|
||||
else if(audioRxTaskMessage.cmd == GET_VOLUME){
|
||||
audioTxTaskMessage.cmd = GET_VOLUME;
|
||||
audioTxTaskMessage.ret = audio.getVolume();
|
||||
xQueueSend(audioGetQueue, &audioTxTaskMessage, portMAX_DELAY);
|
||||
}
|
||||
else{
|
||||
log_i("error");
|
||||
}
|
||||
}
|
||||
audio.loop();
|
||||
}
|
||||
}
|
||||
|
||||
void audioInit() {
|
||||
xTaskCreatePinnedToCore(
|
||||
audioTask, /* Function to implement the task */
|
||||
"audioplay", /* Name of the task */
|
||||
5000, /* Stack size in words */
|
||||
NULL, /* Task input parameter */
|
||||
2 | portPRIVILEGE_BIT, /* Priority of the task */
|
||||
NULL, /* Task handle. */
|
||||
1 /* Core where the task should run */
|
||||
);
|
||||
}
|
||||
|
||||
audioMessage transmitReceive(audioMessage msg){
|
||||
xQueueSend(audioSetQueue, &msg, portMAX_DELAY);
|
||||
if(xQueueReceive(audioGetQueue, &audioRxMessage, portMAX_DELAY) == pdPASS){
|
||||
if(msg.cmd != audioRxMessage.cmd){
|
||||
log_e("wrong reply from message queue");
|
||||
}
|
||||
}
|
||||
return audioRxMessage;
|
||||
}
|
||||
|
||||
void audioSetVolume(uint8_t vol){
|
||||
audioTxMessage.cmd = SET_VOLUME;
|
||||
audioTxMessage.value = vol;
|
||||
audioMessage RX = transmitReceive(audioTxMessage);
|
||||
}
|
||||
|
||||
uint8_t audioGetVolume(){
|
||||
audioTxMessage.cmd = GET_VOLUME;
|
||||
audioMessage RX = transmitReceive(audioTxMessage);
|
||||
return RX.ret;
|
||||
}
|
||||
|
||||
bool audioConnecttohost(const char* host){
|
||||
audioTxMessage.cmd = CONNECTTOHOST;
|
||||
audioTxMessage.txt = host;
|
||||
audioMessage RX = transmitReceive(audioTxMessage);
|
||||
return RX.ret;
|
||||
}
|
||||
|
||||
bool audioConnecttoSD(const char* filename){
|
||||
audioTxMessage.cmd = CONNECTTOSD;
|
||||
audioTxMessage.txt = filename;
|
||||
audioMessage RX = transmitReceive(audioTxMessage);
|
||||
return RX.ret;
|
||||
}
|
||||
|
||||
//****************************************************************************************
|
||||
// S E T U P *
|
||||
//****************************************************************************************
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (WiFi.status() != WL_CONNECTED) delay(1500);
|
||||
|
||||
audioInit();
|
||||
|
||||
audioConnecttohost("http://mp3.ffh.de/radioffh/hqlivestream.mp3");
|
||||
audioSetVolume(15);
|
||||
log_i("current volume is: %d", audioGetVolume());
|
||||
}
|
||||
|
||||
//****************************************************************************************
|
||||
// L O O P *
|
||||
//****************************************************************************************
|
||||
|
||||
void loop(){
|
||||
// your own code here
|
||||
}
|
||||
//*****************************************************************************************
|
||||
// E V E N T S *
|
||||
//*****************************************************************************************
|
||||
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
//**********************************************************************************************************
|
||||
//* audioI2S-- I2S audiodecoder for ESP32, SdFat example *
|
||||
//**********************************************************************************************************
|
||||
//
|
||||
// first release on 05/2020
|
||||
// updated on Sep. 27, 2021
|
||||
/*
|
||||
|
||||
⒈ install SdFat V2 from https://github.com/greiman/SdFat
|
||||
⒉ activate "SDFATFS_USED" in Audio.h
|
||||
⒊ activate "#define USE_UTF8_LONG_NAMES 1" in SdFatConfig.h
|
||||
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "SPI.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define SD_CS 5
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
Audio audio;
|
||||
|
||||
void setup() {
|
||||
pinMode(SD_CS, OUTPUT); digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
SPI.setFrequency(1000000);
|
||||
Serial.begin(115200);
|
||||
SD.begin(SD_CS);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(12); // 0...21
|
||||
|
||||
// audio.connecttoFS(SD, "test.mp3");
|
||||
audio.connecttoFS(SD, "良い一日私の友達.mp3");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
audio.loop();
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
#include <Arduino.h>
|
||||
#include <Preferences.h>
|
||||
#include <SPI.h>
|
||||
#include <WiFi.h>
|
||||
#include "tft.h" //see my repository at github "https://github.com/schreibfaul1/ESP32-TFT-Library-ILI9486"
|
||||
#include "Audio.h" //see my repository at github "https://github.com/schreibfaul1/ESP32-audioI2S"
|
||||
|
||||
#define TFT_CS 22
|
||||
#define TFT_DC 21
|
||||
#define TP_CS 14 //16
|
||||
#define TP_IRQ 39
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
Preferences pref;
|
||||
TFT tft;
|
||||
TP tp(TP_CS, TP_IRQ);
|
||||
Audio audio;
|
||||
|
||||
String ssid = "*****";
|
||||
String password = "*****";
|
||||
|
||||
String stations[] ={
|
||||
"0n-80s.radionetz.de:8000/0n-70s.mp3",
|
||||
"mediaserv30.live-streams.nl:8000/stream",
|
||||
"www.surfmusic.de/m3u/100-5-das-hitradio,4529.m3u",
|
||||
"stream.1a-webradio.de/deutsch/mp3-128/vtuner-1a",
|
||||
"mp3.ffh.de/radioffh/hqlivestream.aac", // 128k aac
|
||||
"www.antenne.de/webradio/antenne.m3u",
|
||||
"listen.rusongs.ru/ru-mp3-128",
|
||||
"edge.audio.3qsdn.com/senderkw-mp3",
|
||||
"macslons-irish-pub-radio.com/media.asx",
|
||||
};
|
||||
|
||||
//some global variables
|
||||
|
||||
uint8_t max_volume = 21;
|
||||
uint8_t max_stations = 0; //will be set later
|
||||
uint8_t cur_station = 0; //current station(nr), will be set later
|
||||
uint8_t cur_volume = 0; //will be set from stored preferences
|
||||
int8_t cur_btn =-1; //current button (, -1 means idle)
|
||||
|
||||
enum action{VOLUME_UP=0, VOLUME_DOWN=1, STATION_UP=2, STATION_DOWN=3};
|
||||
enum staus {RELEASED=0, PRESSED=1};
|
||||
|
||||
struct _btns{
|
||||
uint16_t x; //PosX
|
||||
uint16_t y; //PosY
|
||||
uint16_t w; //Width
|
||||
uint16_t h; //Hight
|
||||
uint8_t a; //Action
|
||||
uint8_t s; //Status
|
||||
};
|
||||
typedef _btns btns;
|
||||
|
||||
btns btn[4];
|
||||
|
||||
//**************************************************************************************************
|
||||
// G U I *
|
||||
//**************************************************************************************************
|
||||
void draw_button(btns b){
|
||||
uint16_t color=TFT_BLACK;
|
||||
uint8_t r=4, o=r*3;
|
||||
if(b.s==RELEASED) color=TFT_GOLD;
|
||||
if(b.s==PRESSED) color=TFT_DEEPSKYBLUE;
|
||||
tft.drawRoundRect(b.x, b.y, b.w, b.h, r, color);
|
||||
switch(b.a){
|
||||
case VOLUME_UP:
|
||||
tft.fillTriangle(b.x+b.w/2, b.y+o, b.x+o, b.y+b.h-o, b.x+b.w-o, b.y+b.h-o, color); break;
|
||||
case VOLUME_DOWN:
|
||||
tft.fillTriangle(b.x+o, b.y+o, b.x+b.w/2, b.y+b.h-o, b.x+b.w-o, b.y+o, color); break;
|
||||
case STATION_UP:
|
||||
tft.fillTriangle(b.x+o, b.y+o, b.x+o, b.y+b.h-o, b.x+b.w-o, b.y+b.h/2, color); break;
|
||||
case STATION_DOWN:
|
||||
tft.fillTriangle(b.x+b.w-o, b.y+o, b.x+o, b.y+b.h/2, b.x+b.w-o, b.y+b.h-o, color); break;
|
||||
}
|
||||
}
|
||||
void write_stationNr(uint8_t nr){
|
||||
tft.fillRect(80, 250, 80, 60, TFT_BLACK);
|
||||
String snr = String(nr);
|
||||
if(snr.length()<2) snr = "0"+snr;
|
||||
tft.setCursor(98, 255);
|
||||
tft.setFont(Times_New_Roman66x53);
|
||||
tft.setTextColor(TFT_YELLOW);
|
||||
tft.print(snr);
|
||||
}
|
||||
void write_volume(uint8_t vol){
|
||||
tft.fillRect(320, 250, 80, 60, TFT_BLACK);
|
||||
String svol = String(vol);
|
||||
if(svol.length()<2) svol = "0"+svol;
|
||||
tft.setCursor(338, 255);
|
||||
tft.setFont(Times_New_Roman66x53);
|
||||
tft.setTextColor(TFT_YELLOW);
|
||||
tft.print(svol);
|
||||
}
|
||||
void write_stationName(String sName){
|
||||
tft.fillRect(0, 0, 480, 100, TFT_BLACK);
|
||||
tft.setFont(Times_New_Roman43x35);
|
||||
tft.setTextColor(TFT_CORNSILK);
|
||||
tft.setCursor(20, 20);
|
||||
tft.print(sName);
|
||||
}
|
||||
void write_streamTitle(String sTitle){
|
||||
tft.fillRect(0, 100, 480, 150, TFT_BLACK);
|
||||
tft.setFont(Times_New_Roman43x35);
|
||||
tft.setTextColor(TFT_LIGHTBLUE);
|
||||
tft.setCursor(20, 100);
|
||||
int l = tft.writeText((const uint8_t*) sTitle.c_str(), 100 + 150); // do not write under y=250
|
||||
if(l < sTitle.length()){
|
||||
// sTitle has been shortened, is too long for the display
|
||||
}
|
||||
}
|
||||
//**************************************************************************************************
|
||||
// S E T U P *
|
||||
//**************************************************************************************************
|
||||
void setup() {
|
||||
btn[0].x= 20; btn[0].y=250; btn[0].w=60; btn[0].h=60; btn[0].a=STATION_DOWN; btn[0].s=RELEASED;
|
||||
btn[1].x=160; btn[1].y=250; btn[1].w=60; btn[1].h=60; btn[1].a=STATION_UP; btn[1].s=RELEASED;
|
||||
btn[2].x=260; btn[2].y=250; btn[2].w=60; btn[2].h=60; btn[2].a=VOLUME_UP; btn[2].s=RELEASED;
|
||||
btn[3].x=400; btn[3].y=250; btn[3].w=60; btn[3].h=60; btn[3].a=VOLUME_DOWN; btn[3].s=RELEASED;
|
||||
max_stations= sizeof(stations)/sizeof(stations[0]); log_i("max stations %i", max_stations);
|
||||
Serial.begin(115200);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
pref.begin("WebRadio", false); // instance of preferences for defaults (station, volume ...)
|
||||
if(pref.getShort("volume", 1000) == 1000){ // if that: pref was never been initialized
|
||||
pref.putShort("volume", 10);
|
||||
pref.putShort("station", 0);
|
||||
}
|
||||
else{ // get the stored values
|
||||
cur_station = pref.getShort("station");
|
||||
cur_volume = pref.getShort("volume");
|
||||
}
|
||||
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (WiFi.status() != WL_CONNECTED){
|
||||
delay(2000);
|
||||
Serial.print(".");
|
||||
}
|
||||
log_i("Connect to %s", WiFi.SSID().c_str());
|
||||
tft.begin(TFT_CS, TFT_DC, SPI_MOSI, SPI_MISO, SPI_SCK);
|
||||
tft.setRotation(3);
|
||||
tp.setRotation(3);
|
||||
tft.setFont(Times_New_Roman43x35);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(cur_volume); // 0...21
|
||||
audio.connecttohost(stations[cur_station].c_str());
|
||||
for(uint8_t i=0; i<(sizeof(btn)/sizeof(*btn)); i++) draw_button(btn[i]);
|
||||
write_volume(cur_volume);
|
||||
write_stationNr(cur_station);
|
||||
}
|
||||
//**************************************************************************************************
|
||||
// L O O P *
|
||||
//**************************************************************************************************
|
||||
void loop()
|
||||
{
|
||||
audio.loop();
|
||||
tp.loop();
|
||||
}
|
||||
//**************************************************************************************************
|
||||
// E V E N T S *
|
||||
//**************************************************************************************************
|
||||
void audio_info(const char *info){
|
||||
Serial.print("audio_info: "); Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
write_stationName(String(info));
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
String sinfo=String(info);
|
||||
sinfo.replace("|", "\n");
|
||||
write_streamTitle(sinfo);
|
||||
}
|
||||
|
||||
void tp_pressed(uint16_t x, uint16_t y){
|
||||
for(uint8_t i=0; i<(sizeof(btn)/sizeof(*btn)); i++){
|
||||
if(x>btn[i].x && (x<btn[i].x+btn[i].w)){
|
||||
if(y>btn[i].y && (y<btn[i].y+btn[i].h)){
|
||||
cur_btn=i;
|
||||
btn[cur_btn].s=PRESSED;
|
||||
draw_button(btn[cur_btn]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void tp_released(){
|
||||
if(cur_btn !=-1){
|
||||
btn[cur_btn].s=RELEASED;
|
||||
draw_button(btn[cur_btn]);
|
||||
switch(btn[cur_btn].a){
|
||||
case VOLUME_UP: if(cur_volume<max_volume){
|
||||
cur_volume++;
|
||||
write_volume(cur_volume);
|
||||
audio.setVolume(cur_volume);
|
||||
pref.putShort("volume", cur_volume);} // store the current volume in nvs
|
||||
break;
|
||||
case VOLUME_DOWN: if(cur_volume>0){
|
||||
cur_volume-- ;
|
||||
write_volume(cur_volume);
|
||||
audio.setVolume(cur_volume);
|
||||
pref.putShort("volume", cur_volume);} // store the current volume in nvs
|
||||
break;
|
||||
case STATION_UP: if(cur_station<max_stations-1){
|
||||
cur_station++;
|
||||
write_stationNr(cur_station);
|
||||
tft.fillRect(0, 0, 480, 250, TFT_BLACK);
|
||||
audio.connecttohost(stations[cur_station].c_str());
|
||||
pref.putShort("station", cur_station);} // store the current station in nvs
|
||||
break;
|
||||
case STATION_DOWN:if(cur_station>0){
|
||||
cur_station--;
|
||||
write_stationNr(cur_station);
|
||||
tft.fillRect(0, 0, 480, 250, TFT_BLACK);
|
||||
audio.connecttohost(stations[cur_station].c_str());
|
||||
pref.putShort("station", cur_station);} // store the current station in nvs
|
||||
break;
|
||||
}
|
||||
}
|
||||
cur_btn=-1;
|
||||
}
|
@ -0,0 +1,269 @@
|
||||
// this is the same example as Webradio_I2S.ino only with an IR remote control
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Preferences.h>
|
||||
#include <SPI.h>
|
||||
#include <WiFi.h>
|
||||
#include "tft.h" //see my repository at github "https://github.com/schreibfaul1/ESP32-TFT-Library-ILI9486"
|
||||
#include "Audio.h" //see my repository at github "https://github.com/schreibfaul1/ESP32-audioI2S"
|
||||
#include "IR.h" //see my repository at github "ESP32-IR-Remote-Control"
|
||||
|
||||
|
||||
#define TFT_CS 22
|
||||
#define TFT_DC 21
|
||||
#define TP_CS 16
|
||||
#define TP_IRQ 39
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
#define IR_PIN 34
|
||||
|
||||
Preferences pref;
|
||||
TFT tft; // @suppress("Abstract class cannot be instantiated")
|
||||
TP tp(TP_CS, TP_IRQ);
|
||||
Audio audio;
|
||||
IR ir(IR_PIN); // do not change the objectname, it must be "ir"
|
||||
|
||||
String ssid = "*********";
|
||||
String password = "*********";
|
||||
|
||||
String stations[] ={
|
||||
"0n-80s.radionetz.de:8000/0n-70s.mp3",
|
||||
"mediaserv30.live-streams.nl:8000/stream",
|
||||
"www.surfmusic.de/m3u/100-5-das-hitradio,4529.m3u",
|
||||
"stream.1a-webradio.de/deutsch/mp3-128/vtuner-1a",
|
||||
"mp3.ffh.de/radioffh/hqlivestream.aac", // 128k aac
|
||||
"www.antenne.de/webradio/antenne.m3u",
|
||||
"listen.rusongs.ru/ru-mp3-128",
|
||||
"edge.audio.3qsdn.com/senderkw-mp3",
|
||||
"macslons-irish-pub-radio.com/media.asx",
|
||||
};
|
||||
|
||||
//some global variables
|
||||
|
||||
uint8_t max_volume = 21;
|
||||
uint8_t max_stations = 0; //will be set later
|
||||
uint8_t cur_station = 0; //current station(nr), will be set later
|
||||
uint8_t cur_volume = 0; //will be set from stored preferences
|
||||
int8_t cur_btn =-1; //current button (, -1 means idle)
|
||||
|
||||
enum action{VOLUME_UP=0, VOLUME_DOWN=1, STATION_UP=2, STATION_DOWN=3};
|
||||
enum staus {RELEASED=0, PRESSED=1};
|
||||
|
||||
struct _btns{
|
||||
uint16_t x; //PosX
|
||||
uint16_t y; //PosY
|
||||
uint16_t w; //Width
|
||||
uint16_t h; //Hight
|
||||
uint8_t a; //Action
|
||||
uint8_t s; //Status
|
||||
};
|
||||
typedef _btns btns;
|
||||
|
||||
btns btn[4];
|
||||
|
||||
//**************************************************************************************************
|
||||
// G U I *
|
||||
//**************************************************************************************************
|
||||
void draw_button(btns b){
|
||||
uint16_t color=TFT_BLACK;
|
||||
uint8_t r=4, o=r*3;
|
||||
if(b.s==RELEASED) color=TFT_GOLD;
|
||||
if(b.s==PRESSED) color=TFT_DEEPSKYBLUE;
|
||||
tft.drawRoundRect(b.x, b.y, b.w, b.h, r, color);
|
||||
switch(b.a){
|
||||
case VOLUME_UP:
|
||||
tft.fillTriangle(b.x+b.w/2, b.y+o, b.x+o, b.y+b.h-o, b.x+b.w-o, b.y+b.h-o, color); break;
|
||||
case VOLUME_DOWN:
|
||||
tft.fillTriangle(b.x+o, b.y+o, b.x+b.w/2, b.y+b.h-o, b.x+b.w-o, b.y+o, color); break;
|
||||
case STATION_UP:
|
||||
tft.fillTriangle(b.x+o, b.y+o, b.x+o, b.y+b.h-o, b.x+b.w-o, b.y+b.h/2, color); break;
|
||||
case STATION_DOWN:
|
||||
tft.fillTriangle(b.x+b.w-o, b.y+o, b.x+o, b.y+b.h/2, b.x+b.w-o, b.y+b.h-o, color); break;
|
||||
}
|
||||
}
|
||||
void write_stationNr(uint8_t nr){
|
||||
tft.fillRect(80, 250, 80, 60, TFT_BLACK);
|
||||
String snr = String(nr);
|
||||
if(snr.length()<2) snr = "0"+snr;
|
||||
tft.setCursor(98, 255);
|
||||
tft.setFont(Times_New_Roman66x53);
|
||||
tft.setTextColor(TFT_YELLOW);
|
||||
tft.print(snr);
|
||||
}
|
||||
void write_volume(uint8_t vol){
|
||||
tft.fillRect(320, 250, 80, 60, TFT_BLACK);
|
||||
String svol = String(vol);
|
||||
if(svol.length()<2) svol = "0"+svol;
|
||||
tft.setCursor(338, 255);
|
||||
tft.setFont(Times_New_Roman66x53);
|
||||
tft.setTextColor(TFT_YELLOW);
|
||||
tft.print(svol);
|
||||
}
|
||||
void write_stationName(String sName){
|
||||
tft.fillRect(0, 0, 480, 100, TFT_BLACK);
|
||||
tft.setFont(Times_New_Roman43x35);
|
||||
tft.setTextColor(TFT_CORNSILK);
|
||||
tft.setCursor(20, 20);
|
||||
tft.print(sName);
|
||||
}
|
||||
void write_streamTitle(String sTitle){
|
||||
tft.fillRect(0, 100, 480, 150, TFT_BLACK);
|
||||
tft.setFont(Times_New_Roman43x35);
|
||||
tft.setTextColor(TFT_LIGHTBLUE);
|
||||
tft.setCursor(20, 100);
|
||||
tft.print(sTitle);
|
||||
}
|
||||
void volume_up(){
|
||||
if(cur_volume < max_volume){
|
||||
cur_volume++;
|
||||
write_volume(cur_volume);
|
||||
audio.setVolume(cur_volume);
|
||||
pref.putShort("volume", cur_volume);} // store the current volume in nvs
|
||||
}
|
||||
void volume_down(){
|
||||
if(cur_volume>0){
|
||||
cur_volume-- ;
|
||||
write_volume(cur_volume);
|
||||
audio.setVolume(cur_volume);
|
||||
pref.putShort("volume", cur_volume);} // store the current volume in nvs
|
||||
}
|
||||
void station_up(){
|
||||
if(cur_station < max_stations-1){
|
||||
cur_station++;
|
||||
write_stationNr(cur_station);
|
||||
tft.fillRect(0, 0, 480, 250, TFT_BLACK);
|
||||
audio.connecttohost(stations[cur_station].c_str());
|
||||
pref.putShort("station", cur_station);} // store the current station in nvs
|
||||
}
|
||||
void station_down(){
|
||||
if(cur_station > 0){
|
||||
cur_station--;
|
||||
write_stationNr(cur_station);
|
||||
tft.fillRect(0, 0, 480, 250, TFT_BLACK);
|
||||
audio.connecttohost(stations[cur_station].c_str());
|
||||
pref.putShort("station", cur_station);} // store the current station in nvs
|
||||
}
|
||||
|
||||
|
||||
//**************************************************************************************************
|
||||
// S E T U P *
|
||||
//**************************************************************************************************
|
||||
void setup() {
|
||||
btn[0].x= 20; btn[0].y=250; btn[0].w=60; btn[0].h=60; btn[0].a=STATION_DOWN; btn[0].s=RELEASED;
|
||||
btn[1].x=160; btn[1].y=250; btn[1].w=60; btn[1].h=60; btn[1].a=STATION_UP; btn[1].s=RELEASED;
|
||||
btn[2].x=260; btn[2].y=250; btn[2].w=60; btn[2].h=60; btn[2].a=VOLUME_UP; btn[2].s=RELEASED;
|
||||
btn[3].x=400; btn[3].y=250; btn[3].w=60; btn[3].h=60; btn[3].a=VOLUME_DOWN; btn[3].s=RELEASED;
|
||||
max_stations= sizeof(stations)/sizeof(stations[0]); log_i("max stations %i", max_stations);
|
||||
Serial.begin(115200);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
pref.begin("WebRadio", false); // instance of preferences for defaults (station, volume ...)
|
||||
if(pref.getShort("volume", 1000) == 1000){ // if that: pref was never been initialized
|
||||
pref.putShort("volume", 10);
|
||||
pref.putShort("station", 0);
|
||||
}
|
||||
else{ // get the stored values
|
||||
cur_station = pref.getShort("station");
|
||||
cur_volume = pref.getShort("volume");
|
||||
}
|
||||
WiFi.disconnect();
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (WiFi.status() != WL_CONNECTED) {delay(1500); Serial.print(".");}
|
||||
log_i("Connected to %s", WiFi.SSID().c_str());
|
||||
tft.begin(TFT_CS, TFT_DC, SPI_MOSI, SPI_MISO, SPI_SCK);
|
||||
tft.setFrequency(20000000);
|
||||
tft.setRotation(3);
|
||||
tp.setRotation(3);
|
||||
tft.setFont(Times_New_Roman43x35);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
ir.begin(); // Init InfraredDecoder
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(cur_volume); // 0...21
|
||||
audio.connecttohost(stations[cur_station].c_str());
|
||||
for(uint8_t i=0; i<(sizeof(btn)/sizeof(*btn)); i++) draw_button(btn[i]);
|
||||
write_volume(cur_volume);
|
||||
write_stationNr(cur_station);
|
||||
}
|
||||
//**************************************************************************************************
|
||||
// L O O P *
|
||||
//**************************************************************************************************
|
||||
void loop()
|
||||
{
|
||||
audio.loop();
|
||||
tp.loop();
|
||||
ir.loop();
|
||||
}
|
||||
//**************************************************************************************************
|
||||
// E V E N T S *
|
||||
//**************************************************************************************************
|
||||
void audio_info(const char *info){
|
||||
Serial.print("audio_info: "); Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
write_stationName(String(info));
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
String sinfo=String(info);
|
||||
sinfo.replace("|", "\n");
|
||||
write_streamTitle(sinfo);
|
||||
}
|
||||
|
||||
void tp_pressed(uint16_t x, uint16_t y){
|
||||
for(uint8_t i=0; i<(sizeof(btn)/sizeof(*btn)); i++){
|
||||
if(x>btn[i].x && (x<btn[i].x+btn[i].w)){
|
||||
if(y>btn[i].y && (y<btn[i].y+btn[i].h)){
|
||||
cur_btn=i;
|
||||
btn[cur_btn].s=PRESSED;
|
||||
draw_button(btn[cur_btn]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void tp_released(){
|
||||
if(cur_btn !=-1){
|
||||
btn[cur_btn].s=RELEASED;
|
||||
draw_button(btn[cur_btn]);
|
||||
switch(btn[cur_btn].a){
|
||||
case VOLUME_UP: volume_up(); break;
|
||||
case VOLUME_DOWN: volume_down(); break;
|
||||
case STATION_UP: station_up(); break;
|
||||
case STATION_DOWN: station_down(); break;
|
||||
}
|
||||
}
|
||||
cur_btn=-1;
|
||||
}
|
||||
// Events from IR Library
|
||||
void ir_res(uint32_t res){
|
||||
if(res < max_stations){
|
||||
cur_station = res;
|
||||
write_stationNr(cur_station);
|
||||
tft.fillRect(0, 0, 480, 250, TFT_BLACK);
|
||||
audio.connecttohost(stations[cur_station].c_str());
|
||||
pref.putShort("station", cur_station);} // store the current station in nvs
|
||||
else{
|
||||
tft.fillRect(0, 0, 480, 250, TFT_BLACK);
|
||||
audio.connecttohost(stations[cur_station].c_str());
|
||||
}
|
||||
}
|
||||
void ir_number(const char* num){
|
||||
tft.fillRect(0, 0, 480, 250, TFT_BLACK);
|
||||
tft.setTextSize(7);
|
||||
tft.setTextColor(TFT_CORNSILK);
|
||||
tft.setCursor(50, 70);
|
||||
tft.print(num);
|
||||
}
|
||||
void ir_key(const char* key){
|
||||
switch(key[0]){
|
||||
case 'k': break; // OK
|
||||
case 'r': volume_up(); break; // right
|
||||
case 'l': volume_down(); break; // left
|
||||
case 'u': station_up(); break; // up
|
||||
case 'd': station_down(); break; // down
|
||||
case '#': break; // #
|
||||
case '*': break; // *
|
||||
default: break;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
// Copied from https://github.com/LilyGO/TTGO-TAudio/issues/12
|
||||
|
||||
|
||||
// Required Libraries (Download zips and add to the Arduino IDE library).
|
||||
#include "Arduino.h"
|
||||
#include <WM8978.h> // https://github.com/CelliesProjects/wm8978-esp32
|
||||
#include <Audio.h> // https://github.com/schreibfaul1/ESP32-audioI2S
|
||||
|
||||
// T-Audio 1.6 WM8978 I2C pins.
|
||||
#define I2C_SDA 19
|
||||
#define I2C_SCL 18
|
||||
|
||||
// T-Audio 1.6 WM8978 I2S pins.
|
||||
#define I2S_BCK 33
|
||||
#define I2S_WS 25
|
||||
#define I2S_DOUT 26
|
||||
|
||||
// T-Audio 1.6 WM8978 MCLK gpio number
|
||||
#define I2S_MCLKPIN 0
|
||||
|
||||
Audio audio;
|
||||
WM8978 dac;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Setup wm8978 I2C interface.
|
||||
if (!dac.begin(I2C_SDA, I2C_SCL)) {
|
||||
ESP_LOGE(TAG, "Error setting up dac: System halted.");
|
||||
while (1) delay(100);
|
||||
}
|
||||
|
||||
// Select I2S pins
|
||||
audio.setPinout(I2S_BCK, I2S_WS, I2S_DOUT);
|
||||
audio.i2s_mclk_pin_select(I2S_MCLKPIN);
|
||||
|
||||
// WiFi Settings here.
|
||||
WiFi.begin("EnterSSIDHere", "EnterPasswordHere");
|
||||
while (!WiFi.isConnected()) {
|
||||
delay(10);
|
||||
}
|
||||
ESP_LOGI(TAG, "Connected. Starting MP3...");
|
||||
// Enter your Icecast station URL here.
|
||||
audio.setVolume(21);
|
||||
audio.connecttohost("http://hestia2.cdnstream.com/1458_128");
|
||||
// Volume control.
|
||||
dac.setSPKvol(63); // Change volume here for board speaker output (Max 63).
|
||||
dac.setHPvol(63, 63); // Change volume here for headphone jack left, right channel.
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Start the stream.
|
||||
audio.loop();
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
Serial.print("station ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
Serial.print("streamtitle ");Serial.println(info);
|
||||
}
|
||||
void audio_bitrate(const char *info){
|
||||
Serial.print("bitrate ");Serial.println(info);
|
||||
}
|
||||
void audio_commercial(const char *info){ //duration in sec
|
||||
Serial.print("commercial ");Serial.println(info);
|
||||
}
|
||||
void audio_icyurl(const char *info){ //homepage
|
||||
Serial.print("icyurl ");Serial.println(info);
|
||||
}
|
||||
void audio_lasthost(const char *info){ //stream URL played
|
||||
Serial.print("lasthost ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_speech(const char *info){
|
||||
Serial.print("eof_speech ");Serial.println(info);
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "SD.h"
|
||||
#include "SPI.h"
|
||||
#include "FS.h"
|
||||
#include "Ticker.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define SD_CS 5
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
Audio audio;
|
||||
Ticker ticker;
|
||||
struct tm timeinfo;
|
||||
time_t now;
|
||||
|
||||
uint8_t hour = 6;
|
||||
uint8_t minute = 59;
|
||||
uint8_t sec = 45;
|
||||
|
||||
bool f_time = false;
|
||||
int8_t timefile = -1;
|
||||
char chbuf[100];
|
||||
|
||||
void tckr1s(){
|
||||
sec++;
|
||||
if(sec > 59) {sec = 0; minute++;}
|
||||
if(minute > 59){minute = 0; hour++;}
|
||||
if(hour > 23) {hour = 0;}
|
||||
if(minute == 59 && sec == 50) f_time = true; // flag will be set 10s before full hour
|
||||
Serial.printf("%02d:%02d:%02d\n", hour, minute, sec);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
SD.begin(SD_CS);
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(10); // 0...21
|
||||
ticker.attach(1, tckr1s);
|
||||
}
|
||||
|
||||
void loop(){
|
||||
audio.loop();
|
||||
if(f_time == true){
|
||||
f_time = false;
|
||||
timefile = 3;
|
||||
uint8_t next_hour = hour + 1;
|
||||
if(next_hour == 25) next_hour = 1;
|
||||
sprintf(chbuf, "/voice_time/%03d.mp3", next_hour);
|
||||
audio.connecttoFS(SD, chbuf);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
//Serial.printf("file :%s\n", info);
|
||||
if(timefile>0){
|
||||
if(timefile==1){audio.connecttoFS(SD, "/voice_time/080.mp3"); timefile--;} // stroke
|
||||
if(timefile==2){audio.connecttoFS(SD, "/voice_time/200.mp3"); timefile--;} // precisely
|
||||
if(timefile==3){audio.connecttoFS(SD, "/voice_time/O'clock.mp3"); timefile--;}
|
||||
}
|
||||
}
|
||||
|
60
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/keywords.txt
Normal file
60
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/keywords.txt
Normal file
@ -0,0 +1,60 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Ultrasound
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
Audio KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
audio_bitrate KEYWORD2
|
||||
audio_commercial KEYWORD2
|
||||
audio_eof_mp3 KEYWORD2
|
||||
audio_eof_speech KEYWORD2
|
||||
audio_icyurl KEYWORD2
|
||||
audio_info KEYWORD2
|
||||
audio_id3data KEYWORD2
|
||||
audio_lasthost KEYWORD2
|
||||
audio_showstreamtitle KEYWORD2
|
||||
audio_showstation KEYWORD2
|
||||
audio_eof_stream KEYWORD2
|
||||
audio_id3image KEYWORD2
|
||||
connecttoSD KEYWORD2
|
||||
connecttoFS KEYWORD2
|
||||
connecttohost KEYWORD2
|
||||
getAudioCurrentTime KEYWORD2
|
||||
getAudioFileDuration KEYWORD2
|
||||
getDatamode KEYWORD2
|
||||
getFilePos KEYWORD2
|
||||
getFileSize KEYWORD2
|
||||
getVolume KEYWORD2
|
||||
loop KEYWORD2
|
||||
pauseResume KEYWORD2
|
||||
setDatamode KEYWORD2
|
||||
setFilePos KEYWORD2
|
||||
setPinout KEYWORD2
|
||||
setVolume KEYWORD2
|
||||
stopSong KEYWORD2
|
||||
streamavail KEYWORD2
|
||||
isRunning KEYWORD2
|
||||
inBufferFilled KEYWORD2
|
||||
inBufferFree KEYWORD2
|
||||
i2s_mclk_pin_select KEYWORD2
|
||||
setFileLoop KEYWORD2
|
||||
setTone KEYWORD2
|
||||
setBalance KEYWORD2
|
||||
forceMono KEYWORD2
|
||||
setInternalDAC KEYWORD2
|
||||
setI2SCommFMT_LSB KEYWORD2
|
||||
setTimeOffset KEYWORD2
|
||||
|
||||
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
23
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/library.json
Normal file
23
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/library.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "ESP32-audioI2S",
|
||||
"version": "2.1.0",
|
||||
"description": "With this library You can easily build a WebRadio with a ESP32 board and a I2S-module",
|
||||
"keywords": "audio, i2s, esp32",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/esphome/ESP32-audioI2S.git"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "schreibfaul1"
|
||||
},
|
||||
{
|
||||
"name": "jesserockz"
|
||||
}
|
||||
],
|
||||
"license": "GPL-3.0",
|
||||
"homepage": "https://github.com/esphome/ESP32-audioI2S",
|
||||
"dependencies": {},
|
||||
"frameworks": "arduino",
|
||||
"platforms": "espressif32"
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
name=ESP32-audioI2S-master
|
||||
version=2.1.0
|
||||
author=schreibfaul1
|
||||
maintainer=schreibfaul1
|
||||
sentence=With this library You can easily build a WebRadio with a ESP32 board and a I2S-module.
|
||||
paragraph=Plays webradio, playlists can be m3u, pls or asx. Data format can be only mp3, aac, flac or m4a. It can also play files from a SD Card.
|
||||
category=Device Control
|
||||
url=https://github.com/schreibfaul1/ESP32-audioI2S
|
||||
architectures=esp32
|
4487
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/src/Audio.cpp
Normal file
4487
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/src/Audio.cpp
Normal file
File diff suppressed because it is too large
Load Diff
496
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/src/Audio.h
Normal file
496
.pio/libdeps/esp32-s3-devkitc-1/ESP32-audioI2S/src/Audio.h
Normal file
@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Audio.h
|
||||
*
|
||||
* Created on: Oct 26,2018
|
||||
* Updated on: May 19,2022
|
||||
* Author: Wolle (schreibfaul1)
|
||||
*/
|
||||
|
||||
//#define SDFATFS_USED // activate for SdFat
|
||||
|
||||
|
||||
#pragma once
|
||||
#pragma GCC optimize ("Ofast")
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <libb64/cencode.h>
|
||||
|
||||
#include <driver/i2s.h>
|
||||
|
||||
#ifndef AUDIO_NO_SD_FS
|
||||
#include <SPI.h>
|
||||
#ifdef SDFATFS_USED
|
||||
#include <SdFat.h> // https://github.com/greiman/SdFat
|
||||
#else
|
||||
#include <SD.h>
|
||||
#include <SD_MMC.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <FS.h>
|
||||
#include <FFat.h>
|
||||
#endif // SDFATFS_USED
|
||||
|
||||
|
||||
#ifdef SDFATFS_USED
|
||||
//typedef File32 File;
|
||||
typedef FsFile File;
|
||||
|
||||
namespace fs {
|
||||
class FS : public SdFat {
|
||||
public:
|
||||
bool begin(SdCsPin_t csPin = SS, uint32_t maxSck = SD_SCK_MHZ(25)) { return SdFat::begin(csPin, maxSck); }
|
||||
};
|
||||
|
||||
class SDFATFS : public fs::FS {
|
||||
public:
|
||||
// sdcard_type_t cardType();
|
||||
uint64_t cardSize() {
|
||||
return totalBytes();
|
||||
}
|
||||
uint64_t usedBytes() {
|
||||
// set SdFatConfig MAINTAIN_FREE_CLUSTER_COUNT non-zero. Then only the first call will take time.
|
||||
return (uint64_t)(clusterCount() - freeClusterCount()) * (uint64_t)bytesPerCluster();
|
||||
}
|
||||
uint64_t totalBytes() {
|
||||
return (uint64_t)clusterCount() * (uint64_t)bytesPerCluster();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
extern fs::SDFATFS SD_SDFAT;
|
||||
|
||||
using namespace fs;
|
||||
#define SD SD_SDFAT
|
||||
#endif //SDFATFS_USED
|
||||
#endif // AUDIO_NO_SD_FS
|
||||
|
||||
extern __attribute__((weak)) void audio_info(const char*);
|
||||
extern __attribute__((weak)) void audio_id3data(const char*); //ID3 metadata
|
||||
#ifndef AUDIO_NO_SD_FS
|
||||
extern __attribute__((weak)) void audio_id3image(File& file, const size_t pos, const size_t size); //ID3 metadata image
|
||||
#endif // AUDIO_NO_SD_FS
|
||||
extern __attribute__((weak)) void audio_eof_mp3(const char*); //end of mp3 file
|
||||
extern __attribute__((weak)) void audio_showstreamtitle(const char*);
|
||||
extern __attribute__((weak)) void audio_showstation(const char*);
|
||||
extern __attribute__((weak)) void audio_bitrate(const char*);
|
||||
extern __attribute__((weak)) void audio_commercial(const char*);
|
||||
extern __attribute__((weak)) void audio_icyurl(const char*);
|
||||
extern __attribute__((weak)) void audio_icydescription(const char*);
|
||||
extern __attribute__((weak)) void audio_lasthost(const char*);
|
||||
extern __attribute__((weak)) void audio_eof_speech(const char*);
|
||||
extern __attribute__((weak)) void audio_eof_stream(const char*); // The webstream comes to an end
|
||||
extern __attribute__((weak)) void audio_process_extern(int16_t* buff, uint16_t len, bool *continueI2S); // record audiodata or send via BT
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class AudioBuffer {
|
||||
// AudioBuffer will be allocated in PSRAM, If PSRAM not available or has not enough space AudioBuffer will be
|
||||
// allocated in FlashRAM with reduced size
|
||||
//
|
||||
// m_buffer m_readPtr m_writePtr m_endPtr
|
||||
// | |<------dataLength------->|<------ writeSpace ----->|
|
||||
// ▼ ▼ ▼ ▼
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// | <--m_buffSize--> | <--m_resBuffSize --> |
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// |<-----freeSpace------->| |<------freeSpace-------->|
|
||||
//
|
||||
//
|
||||
//
|
||||
// if the space between m_readPtr and buffend < m_resBuffSize copy data from the beginning to resBuff
|
||||
// so that the mp3/aac/flac frame is always completed
|
||||
//
|
||||
// m_buffer m_writePtr m_readPtr m_endPtr
|
||||
// | |<-------writeSpace------>|<--dataLength-->|
|
||||
// ▼ ▼ ▼ ▼
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// | <--m_buffSize--> | <--m_resBuffSize --> |
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// |<--- ------dataLength-- ------>|<-------freeSpace------->|
|
||||
//
|
||||
//
|
||||
|
||||
public:
|
||||
AudioBuffer(size_t maxBlockSize = 0); // constructor
|
||||
~AudioBuffer(); // frees the buffer
|
||||
size_t init(); // set default values
|
||||
bool isInitialized() { return m_f_init; };
|
||||
void setBufsize(int ram, int psram);
|
||||
void changeMaxBlockSize(uint16_t mbs); // is default 1600 for mp3 and aac, set 16384 for FLAC
|
||||
uint16_t getMaxBlockSize(); // returns maxBlockSize
|
||||
size_t freeSpace(); // number of free bytes to overwrite
|
||||
size_t writeSpace(); // space fom writepointer to bufferend
|
||||
size_t bufferFilled(); // returns the number of filled bytes
|
||||
void bytesWritten(size_t bw); // update writepointer
|
||||
void bytesWasRead(size_t br); // update readpointer
|
||||
uint8_t* getWritePtr(); // returns the current writepointer
|
||||
uint8_t* getReadPtr(); // returns the current readpointer
|
||||
uint32_t getWritePos(); // write position relative to the beginning
|
||||
uint32_t getReadPos(); // read position relative to the beginning
|
||||
void resetBuffer(); // restore defaults
|
||||
bool havePSRAM() { return m_f_psram; };
|
||||
|
||||
protected:
|
||||
size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes
|
||||
size_t m_buffSizeRAM = 1600 * 5;
|
||||
size_t m_buffSize = 0;
|
||||
size_t m_freeSpace = 0;
|
||||
size_t m_writeSpace = 0;
|
||||
size_t m_dataLength = 0;
|
||||
size_t m_resBuffSizeRAM = 1600; // reserved buffspace, >= one mp3 frame
|
||||
size_t m_resBuffSizePSRAM = 4096 * 4; // reserved buffspace, >= one flac frame
|
||||
size_t m_maxBlockSize = 1600;
|
||||
uint8_t* m_buffer = NULL;
|
||||
uint8_t* m_writePtr = NULL;
|
||||
uint8_t* m_readPtr = NULL;
|
||||
uint8_t* m_endPtr = NULL;
|
||||
bool m_f_start = true;
|
||||
bool m_f_init = false;
|
||||
bool m_f_psram = false; // PSRAM is available (and used...)
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class Audio : private AudioBuffer{
|
||||
|
||||
AudioBuffer InBuff; // instance of input buffer
|
||||
|
||||
public:
|
||||
Audio(bool internalDAC = false, uint8_t channelEnabled = 3, uint8_t i2sPort = I2S_NUM_0); // #99
|
||||
~Audio();
|
||||
void setBufsize(int rambuf_sz, int psrambuf_sz);
|
||||
bool connecttohost(const char* host, const char* user = "", const char* pwd = "");
|
||||
bool connecttospeech(const char* speech, const char* lang);
|
||||
#ifndef AUDIO_NO_SD_FS
|
||||
bool connecttoFS(fs::FS &fs, const char* path, uint32_t resumeFilePos = 0);
|
||||
bool connecttoSD(const char* path, uint32_t resumeFilePos = 0);
|
||||
#endif // AUDIO_NO_SD_FS
|
||||
bool setFileLoop(bool input);//TEST loop
|
||||
void setConnectionTimeout(uint16_t timeout_ms, uint16_t timeout_ms_ssl);
|
||||
bool setAudioPlayPosition(uint16_t sec);
|
||||
bool setFilePos(uint32_t pos);
|
||||
bool audioFileSeek(const float speed);
|
||||
bool setTimeOffset(int sec);
|
||||
bool setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t DIN=I2S_PIN_NO_CHANGE);
|
||||
bool pauseResume();
|
||||
bool isRunning() {return m_f_running;}
|
||||
void loop();
|
||||
uint32_t stopSong();
|
||||
void forceMono(bool m);
|
||||
void setBalance(int8_t bal = 0);
|
||||
void setVolume(uint8_t vol);
|
||||
uint8_t getVolume();
|
||||
uint8_t getI2sPort();
|
||||
|
||||
uint32_t getAudioDataStartPos();
|
||||
uint32_t getFileSize();
|
||||
uint32_t getFilePos();
|
||||
uint32_t getSampleRate();
|
||||
uint8_t getBitsPerSample();
|
||||
uint8_t getChannels();
|
||||
uint32_t getBitRate(bool avg = false);
|
||||
uint32_t getAudioFileDuration();
|
||||
uint32_t getAudioCurrentTime();
|
||||
uint32_t getTotalPlayingTime();
|
||||
|
||||
esp_err_t i2s_mclk_pin_select(const uint8_t pin);
|
||||
uint32_t inBufferFilled(); // returns the number of stored bytes in the inputbuffer
|
||||
uint32_t inBufferFree(); // returns the number of free bytes in the inputbuffer
|
||||
void setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass);
|
||||
void setI2SCommFMT_LSB(bool commFMT);
|
||||
int getCodec() {return m_codec;}
|
||||
const char *getCodecname() {return codecname[m_codec];}
|
||||
enum : int { CODEC_NONE, CODEC_WAV, CODEC_MP3, CODEC_AAC, CODEC_M4A, CODEC_FLAC, CODEC_OGG,
|
||||
CODEC_OGG_FLAC, CODEC_OGG_OPUS};
|
||||
|
||||
private:
|
||||
void UTF8toASCII(char* str);
|
||||
bool latinToUTF8(char* buff, size_t bufflen);
|
||||
void httpPrint(const char* url);
|
||||
void setDefaults(); // free buffers and set defaults
|
||||
void initInBuff();
|
||||
#ifndef AUDIO_NO_SD_FS
|
||||
void processLocalFile();
|
||||
#endif // AUDIO_NO_SD_FS
|
||||
void processWebStream();
|
||||
void processPlayListData();
|
||||
void processM3U8entries(uint8_t nrOfEntries = 0, uint32_t seqNr = 0, uint8_t pos = 0, uint16_t targetDuration = 0);
|
||||
bool STfromEXTINF(char* str);
|
||||
void showCodecParams();
|
||||
int findNextSync(uint8_t* data, size_t len);
|
||||
int sendBytes(uint8_t* data, size_t len);
|
||||
void compute_audioCurrentTime(int bd);
|
||||
void printDecodeError(int r);
|
||||
void showID3Tag(const char* tag, const char* val);
|
||||
void unicode2utf8(char* buff, uint32_t len);
|
||||
int read_WAV_Header(uint8_t* data, size_t len);
|
||||
int read_FLAC_Header(uint8_t *data, size_t len);
|
||||
int read_MP3_Header(uint8_t* data, size_t len);
|
||||
int read_M4A_Header(uint8_t* data, size_t len);
|
||||
int read_OGG_Header(uint8_t *data, size_t len);
|
||||
bool setSampleRate(uint32_t hz);
|
||||
bool setBitsPerSample(int bits);
|
||||
bool setChannels(int channels);
|
||||
bool setBitrate(int br);
|
||||
bool playChunk();
|
||||
bool playSample(int16_t sample[2]) ;
|
||||
void playI2Sremains();
|
||||
int32_t Gain(int16_t s[2]);
|
||||
bool fill_InputBuf();
|
||||
void showstreamtitle(const char* ml);
|
||||
bool parseContentType(const char* ct);
|
||||
void processAudioHeaderData();
|
||||
bool readMetadata(uint8_t b, bool first = false);
|
||||
esp_err_t I2Sstart(uint8_t i2s_num);
|
||||
esp_err_t I2Sstop(uint8_t i2s_num);
|
||||
void urlencode(char* buff, uint16_t buffLen, bool spacesOnly = false);
|
||||
int16_t* IIR_filterChain0(int16_t iir_in[2], bool clear = false);
|
||||
int16_t* IIR_filterChain1(int16_t* iir_in, bool clear = false);
|
||||
int16_t* IIR_filterChain2(int16_t* iir_in, bool clear = false);
|
||||
inline void setDatamode(uint8_t dm){m_datamode=dm;}
|
||||
inline uint8_t getDatamode(){return m_datamode;}
|
||||
inline uint32_t streamavail(){ return _client ? _client->available() : 0;}
|
||||
void IIR_calculateCoefficients(int8_t G1, int8_t G2, int8_t G3);
|
||||
|
||||
// implement several function with respect to the index of string
|
||||
void trim(char *s) {
|
||||
//fb trim in place
|
||||
char *pe;
|
||||
char *p = s;
|
||||
while ( isspace(*p) ) p++; //left
|
||||
pe = p; //right
|
||||
while ( *pe != '\0' ) pe++;
|
||||
do {
|
||||
pe--;
|
||||
} while ( (pe > p) && isspace(*pe) );
|
||||
if (p == s) {
|
||||
*++pe = '\0';
|
||||
} else { //move
|
||||
while ( p <= pe ) *s++ = *p++;
|
||||
*s = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool startsWith (const char* base, const char* str) {
|
||||
//fb
|
||||
char c;
|
||||
while ( (c = *str++) != '\0' )
|
||||
if (c != *base++) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool endsWith (const char* base, const char* str) {
|
||||
//fb
|
||||
int slen = strlen(str) - 1;
|
||||
const char *p = base + strlen(base) - 1;
|
||||
while(p > base && isspace(*p)) p--; // rtrim
|
||||
p -= slen;
|
||||
if (p < base) return false;
|
||||
return (strncmp(p, str, slen) == 0);
|
||||
}
|
||||
|
||||
int indexOf (const char* base, const char* str, int startIndex) {
|
||||
//fb
|
||||
const char *p = base;
|
||||
for (; startIndex > 0; startIndex--)
|
||||
if (*p++ == '\0') return -1;
|
||||
char* pos = strstr(p, str);
|
||||
if (pos == nullptr) return -1;
|
||||
return pos - base;
|
||||
}
|
||||
|
||||
int indexOf (const char* base, char ch, int startIndex) {
|
||||
//fb
|
||||
const char *p = base;
|
||||
for (; startIndex > 0; startIndex--)
|
||||
if (*p++ == '\0') return -1;
|
||||
char *pos = strchr(p, ch);
|
||||
if (pos == nullptr) return -1;
|
||||
return pos - base;
|
||||
}
|
||||
|
||||
int lastIndexOf(const char* haystack, const char* needle) {
|
||||
//fb
|
||||
int nlen = strlen(needle);
|
||||
if (nlen == 0) return -1;
|
||||
const char *p = haystack - nlen + strlen(haystack);
|
||||
while (p >= haystack) {
|
||||
int i = 0;
|
||||
while (needle[i] == p[i])
|
||||
if (++i == nlen) return p - haystack;
|
||||
p--;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lastIndexOf(const char* haystack, const char needle) {
|
||||
//fb
|
||||
const char *p = strrchr(haystack, needle);
|
||||
return (p ? p - haystack : -1);
|
||||
}
|
||||
|
||||
int specialIndexOf (uint8_t* base, const char* str, int baselen, bool exact = false){
|
||||
int result; // seek for str in buffer or in header up to baselen, not nullterninated
|
||||
if (strlen(str) > baselen) return -1; // if exact == true seekstr in buffer must have "\0" at the end
|
||||
for (int i = 0; i < baselen - strlen(str); i++){
|
||||
result = i;
|
||||
for (int j = 0; j < strlen(str) + exact; j++){
|
||||
if (*(base + i + j) != *(str + j)){
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result >= 0) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
size_t bigEndian(uint8_t* base, uint8_t numBytes, uint8_t shiftLeft = 8){
|
||||
size_t result = 0;
|
||||
if(numBytes < 1 or numBytes > 4) return 0;
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
result += *(base + i) << (numBytes -i - 1) * shiftLeft;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool b64encode(const char* source, uint16_t sourceLength, char* dest){
|
||||
size_t size = base64_encode_expected_len(sourceLength) + 1;
|
||||
char * buffer = (char *) malloc(size);
|
||||
if(buffer) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block(&source[0], sourceLength, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
memcpy(dest, buffer, strlen(buffer));
|
||||
dest[strlen(buffer)] = '\0';
|
||||
free(buffer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
size_t urlencode_expected_len(const char* source){
|
||||
size_t expectedLen = strlen(source);
|
||||
for(int i = 0; i < strlen(source); i++) {
|
||||
if(isalnum(source[i])){;}
|
||||
else expectedLen += 2;
|
||||
}
|
||||
return expectedLen;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *codecname[9] = {"unknown", "WAV", "MP3", "AAC", "M4A", "FLAC", "OGG", "OGG FLAC", "OPUS"};
|
||||
enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
|
||||
enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
|
||||
enum : int { FORMAT_NONE = 0, FORMAT_M3U = 1, FORMAT_PLS = 2, FORMAT_ASX = 3, FORMAT_M3U8 = 4};
|
||||
enum : int { AUDIO_NONE, AUDIO_HEADER, AUDIO_DATA,
|
||||
AUDIO_PLAYLISTINIT, AUDIO_PLAYLISTHEADER, AUDIO_PLAYLISTDATA};
|
||||
enum : int { FLAC_BEGIN = 0, FLAC_MAGIC = 1, FLAC_MBH =2, FLAC_SINFO = 3, FLAC_PADDING = 4, FLAC_APP = 5,
|
||||
FLAC_SEEK = 6, FLAC_VORBIS = 7, FLAC_CUESHEET = 8, FLAC_PICTURE = 9, FLAC_OKAY = 100};
|
||||
enum : int { M4A_BEGIN = 0, M4A_FTYP = 1, M4A_CHK = 2, M4A_MOOV = 3, M4A_FREE = 4, M4A_TRAK = 5, M4A_MDAT = 6,
|
||||
M4A_ILST = 7, M4A_MP4A = 8, M4A_AMRDY = 99, M4A_OKAY = 100};
|
||||
enum : int { OGG_BEGIN = 0, OGG_MAGIC = 1, OGG_HEADER = 2, OGG_FIRST = 3, OGG_AMRDY = 99, OGG_OKAY = 100};
|
||||
typedef enum { LEFTCHANNEL=0, RIGHTCHANNEL=1 } SampleIndex;
|
||||
typedef enum { LOWSHELF = 0, PEAKEQ = 1, HIFGSHELF =2 } FilterType;
|
||||
|
||||
const uint8_t volumetable[22]={ 0, 1, 2, 3, 4 , 6 , 8, 10, 12, 14, 17,
|
||||
20, 23, 27, 30 ,34, 38, 43 ,48, 52, 58, 64}; //22 elements
|
||||
|
||||
typedef struct _filter{
|
||||
float a0;
|
||||
float a1;
|
||||
float a2;
|
||||
float b1;
|
||||
float b2;
|
||||
} filter_t;
|
||||
|
||||
#ifndef AUDIO_NO_SD_FS
|
||||
File audiofile; // @suppress("Abstract class cannot be instantiated")
|
||||
#endif // AUDIO_NO_SD_FS
|
||||
WiFiClient client; // @suppress("Abstract class cannot be instantiated")
|
||||
WiFiClientSecure clientsecure; // @suppress("Abstract class cannot be instantiated")
|
||||
WiFiClient* _client = nullptr;
|
||||
i2s_config_t m_i2s_config; // stores values for I2S driver
|
||||
i2s_pin_config_t m_pin_config;
|
||||
|
||||
const size_t m_frameSizeWav = 1600;
|
||||
const size_t m_frameSizeMP3 = 1600;
|
||||
const size_t m_frameSizeAAC = 1600;
|
||||
const size_t m_frameSizeFLAC = 4096 * 4;
|
||||
|
||||
char chbuf[512 + 128]; // must be greater than m_lastHost #254
|
||||
char m_lastHost[512]; // Store the last URL to a webstream
|
||||
char* m_playlistBuff = NULL; // stores playlistdata
|
||||
const uint16_t m_plsBuffEntryLen = 256; // length of each entry in playlistBuff
|
||||
filter_t m_filter[3]; // digital filters
|
||||
int m_LFcount = 0; // Detection of end of header
|
||||
uint32_t m_sampleRate=16000;
|
||||
uint32_t m_bitRate=0; // current bitrate given fom decoder
|
||||
uint32_t m_avr_bitrate = 0; // average bitrate, median computed by VBR
|
||||
int m_readbytes=0; // bytes read
|
||||
int m_metalen=0; // Number of bytes in metadata
|
||||
int m_controlCounter = 0; // Status within readID3data() and readWaveHeader()
|
||||
int8_t m_balance = 0; // -16 (mute left) ... +16 (mute right)
|
||||
uint8_t m_vol=64; // volume
|
||||
uint8_t m_bitsPerSample = 16; // bitsPerSample
|
||||
uint8_t m_channels=2;
|
||||
uint8_t m_i2s_num = I2S_NUM_0; // I2S_NUM_0 or I2S_NUM_1
|
||||
uint8_t m_playlistFormat = 0; // M3U, PLS, ASX
|
||||
uint8_t m_m3u8codec = CODEC_NONE; // M4A
|
||||
uint8_t m_codec = CODEC_NONE; //
|
||||
uint8_t m_filterType[2]; // lowpass, highpass
|
||||
int16_t m_outBuff[2048*2]; // Interleaved L/R
|
||||
int16_t m_validSamples = 0;
|
||||
int16_t m_curSample = 0;
|
||||
uint16_t m_datamode = 0; // Statemaschine
|
||||
uint16_t m_streamTitleHash = 0; // remember streamtitle, ignore multiple occurence in metadata
|
||||
uint16_t m_streamUrlHash = 0; // remember streamURL, ignore multiple occurence in metadata
|
||||
uint16_t m_timeout_ms = 250;
|
||||
uint16_t m_timeout_ms_ssl = 2700;
|
||||
uint8_t m_flacBitsPerSample = 0; // bps should be 16
|
||||
uint8_t m_flacNumChannels = 0; // can be read out in the FLAC file header
|
||||
uint32_t m_flacSampleRate = 0; // can be read out in the FLAC file header
|
||||
uint16_t m_flacMaxFrameSize = 0; // can be read out in the FLAC file header
|
||||
uint16_t m_flacMaxBlockSize = 0; // can be read out in the FLAC file header
|
||||
uint32_t m_flacTotalSamplesInStream = 0; // can be read out in the FLAC file header
|
||||
uint32_t m_metaint = 0; // Number of databytes between metadata
|
||||
uint32_t m_chunkcount = 0 ; // Counter for chunked transfer
|
||||
uint32_t m_t0 = 0; // store millis(), is needed for a small delay
|
||||
uint32_t m_contentlength = 0; // Stores the length if the stream comes from fileserver
|
||||
uint32_t m_bytesNotDecoded = 0; // pictures or something else that comes with the stream
|
||||
uint32_t m_PlayingStartTime = 0; // Stores the milliseconds after the start of the audio
|
||||
uint32_t m_resumeFilePos = 0; // the return value from stopSong() can be entered here
|
||||
bool m_f_swm = true; // Stream without metadata
|
||||
bool m_f_unsync = false; // set within ID3 tag but not used
|
||||
bool m_f_exthdr = false; // ID3 extended header
|
||||
bool m_f_localfile = false ; // Play from local mp3-file
|
||||
bool m_f_webstream = false ; // Play from URL
|
||||
bool m_f_ssl = false;
|
||||
bool m_f_running = false;
|
||||
bool m_f_firstCall = false; // InitSequence for processWebstream and processLokalFile
|
||||
bool m_f_ctseen = false; // First line of header seen or not
|
||||
bool m_f_chunked = false ; // Station provides chunked transfer
|
||||
bool m_f_firstmetabyte = false; // True if first metabyte (counter)
|
||||
bool m_f_playing = false; // valid mp3 stream recognized
|
||||
bool m_f_webfile = false; // assume it's a radiostream, not a podcast
|
||||
bool m_f_tts = false; // text to speech
|
||||
bool m_f_loop = false; // Set if audio file should loop
|
||||
bool m_f_forceMono = false; // if true stereo -> mono
|
||||
bool m_f_internalDAC = false; // false: output vis I2S, true output via internal DAC
|
||||
bool m_f_rtsp = false; // set if RTSP is used (m3u8 stream)
|
||||
bool m_f_m3u8data = false; // used in processM3U8entries
|
||||
bool m_f_Log = true; // if m3u8: log is cancelled
|
||||
bool m_f_continue = false; // next m3u8 chunk is available
|
||||
uint8_t m_f_channelEnabled = 3; // internal DAC, both channels
|
||||
uint32_t m_audioFileDuration = 0;
|
||||
float m_audioCurrentTime = 0;
|
||||
uint32_t m_audioDataStart = 0; // in bytes
|
||||
size_t m_audioDataSize = 0; //
|
||||
float m_filterBuff[3][2][2][2]; // IIR filters memory for Audio DSP
|
||||
size_t m_i2s_bytesWritten = 0; // set in i2s_write() but not used
|
||||
size_t m_file_size = 0; // size of the file
|
||||
uint16_t m_filterFrequency[2];
|
||||
int8_t m_gain0 = 0; // cut or boost filters (EQ)
|
||||
int8_t m_gain1 = 0;
|
||||
int8_t m_gain2 = 0;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,586 @@
|
||||
// based on helix aac decoder
|
||||
#pragma once
|
||||
//#pragma GCC optimize ("O3")
|
||||
//#pragma GCC diagnostic ignored "-Wnarrowing"
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define AAC_ENABLE_MPEG4
|
||||
|
||||
#if (defined CONFIG_IDF_TARGET_ESP32S3 && defined BOARD_HAS_PSRAM)
|
||||
#define AAC_ENABLE_SBR // needs additional 60KB DRAM,
|
||||
#endif
|
||||
|
||||
#define ASSERT(x) /* do nothing */
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) std::max(a,b)
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) std::min(a,b)
|
||||
#endif
|
||||
|
||||
|
||||
/* AAC file format */
|
||||
enum {
|
||||
AAC_FF_Unknown = 0, /* should be 0 on init */
|
||||
AAC_FF_ADTS = 1,
|
||||
AAC_FF_ADIF = 2,
|
||||
AAC_FF_RAW = 3
|
||||
};
|
||||
|
||||
/* syntactic element type */
|
||||
enum {
|
||||
AAC_ID_INVALID = -1,
|
||||
AAC_ID_SCE = 0,
|
||||
AAC_ID_CPE = 1,
|
||||
AAC_ID_CCE = 2,
|
||||
AAC_ID_LFE = 3,
|
||||
AAC_ID_DSE = 4,
|
||||
AAC_ID_PCE = 5,
|
||||
AAC_ID_FIL = 6,
|
||||
AAC_ID_END = 7
|
||||
};
|
||||
|
||||
enum {
|
||||
ERR_AAC_NONE = 0,
|
||||
ERR_AAC_INDATA_UNDERFLOW = -1,
|
||||
ERR_AAC_NULL_POINTER = -2,
|
||||
ERR_AAC_INVALID_ADTS_HEADER = -3,
|
||||
ERR_AAC_INVALID_ADIF_HEADER = -4,
|
||||
ERR_AAC_INVALID_FRAME = -5,
|
||||
ERR_AAC_MPEG4_UNSUPPORTED = -6,
|
||||
ERR_AAC_CHANNEL_MAP = -7,
|
||||
ERR_AAC_SYNTAX_ELEMENT = -8,
|
||||
ERR_AAC_DEQUANT = -9,
|
||||
ERR_AAC_STEREO_PROCESS = -10,
|
||||
ERR_AAC_PNS = -11,
|
||||
ERR_AAC_SHORT_BLOCK_DEINT = -12,
|
||||
ERR_AAC_TNS = -13,
|
||||
ERR_AAC_IMDCT = -14,
|
||||
ERR_AAC_NCHANS_TOO_HIGH = -15,
|
||||
ERR_AAC_SBR_INIT = -16,
|
||||
ERR_AAC_SBR_BITSTREAM = -17,
|
||||
ERR_AAC_SBR_DATA = -18,
|
||||
ERR_AAC_SBR_PCM_FORMAT = -19,
|
||||
ERR_AAC_SBR_NCHANS_TOO_HIGH = -20,
|
||||
ERR_AAC_SBR_SINGLERATE_UNSUPPORTED = -21,
|
||||
ERR_AAC_RAWBLOCK_PARAMS = -22,
|
||||
ERR_AAC_UNKNOWN = -9999
|
||||
};
|
||||
|
||||
enum {
|
||||
SBR_GRID_FIXFIX = 0,
|
||||
SBR_GRID_FIXVAR = 1,
|
||||
SBR_GRID_VARFIX = 2,
|
||||
SBR_GRID_VARVAR = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
HuffTabSBR_tEnv15 = 0,
|
||||
HuffTabSBR_fEnv15 = 1,
|
||||
HuffTabSBR_tEnv15b = 2,
|
||||
HuffTabSBR_fEnv15b = 3,
|
||||
HuffTabSBR_tEnv30 = 4,
|
||||
HuffTabSBR_fEnv30 = 5,
|
||||
HuffTabSBR_tEnv30b = 6,
|
||||
HuffTabSBR_fEnv30b = 7,
|
||||
HuffTabSBR_tNoise30 = 8,
|
||||
HuffTabSBR_fNoise30 = 5,
|
||||
HuffTabSBR_tNoise30b = 9,
|
||||
HuffTabSBR_fNoise30b = 7
|
||||
};
|
||||
|
||||
typedef struct _AACDecInfo_t {
|
||||
/* raw decoded data, before rounding to 16-bit PCM (for postprocessing such as SBR) */
|
||||
void *rawSampleBuf[2];
|
||||
int rawSampleBytes;
|
||||
int rawSampleFBits;
|
||||
/* fill data (can be used for processing SBR or other extensions) */
|
||||
uint8_t *fillBuf;
|
||||
int fillCount;
|
||||
int fillExtType;
|
||||
int prevBlockID; /* block information */
|
||||
int currBlockID;
|
||||
int currInstTag;
|
||||
int sbDeinterleaveReqd[2]; // [MAX_NCHANS_ELEM]
|
||||
int adtsBlocksLeft;
|
||||
int bitRate; /* user-accessible info */
|
||||
int nChans;
|
||||
int sampRate;
|
||||
float compressionRatio;
|
||||
int id; /* 0: MPEG-4, 1: MPEG2 */
|
||||
int profile; /* 0: Main profile, 1: LowComplexity (LC), 2: ScalableSamplingRate (SSR), 3: reserved */
|
||||
int format;
|
||||
int sbrEnabled;
|
||||
int tnsUsed;
|
||||
int pnsUsed;
|
||||
int frameCount;
|
||||
} AACDecInfo_t;
|
||||
|
||||
|
||||
typedef struct _aac_BitStreamInfo_t {
|
||||
uint8_t *bytePtr;
|
||||
unsigned int iCache;
|
||||
int cachedBits;
|
||||
int nBytes;
|
||||
} aac_BitStreamInfo_t;
|
||||
|
||||
typedef union _U64 {
|
||||
int64_t w64;
|
||||
struct {
|
||||
unsigned int lo32;
|
||||
signed int hi32;
|
||||
} r;
|
||||
} U64;
|
||||
|
||||
typedef struct _AACFrameInfo_t {
|
||||
int bitRate;
|
||||
int nChans;
|
||||
int sampRateCore;
|
||||
int sampRateOut;
|
||||
int bitsPerSample;
|
||||
int outputSamps;
|
||||
int profile;
|
||||
int tnsUsed;
|
||||
int pnsUsed;
|
||||
} AACFrameInfo_t;
|
||||
|
||||
typedef struct _HuffInfo_t {
|
||||
int maxBits; /* number of bits in longest codeword */
|
||||
uint8_t count[20]; /* count[MAX_HUFF_BITS] = number of codes with length i+1 bits */
|
||||
int offset; /* offset into symbol table */
|
||||
} HuffInfo_t;
|
||||
|
||||
typedef struct _PulseInfo_t {
|
||||
uint8_t pulseDataPresent;
|
||||
uint8_t numPulse;
|
||||
uint8_t startSFB;
|
||||
uint8_t offset[4]; // [MAX_PULSES]
|
||||
uint8_t amp[4]; // [MAX_PULSES]
|
||||
} PulseInfo_t;
|
||||
|
||||
typedef struct _TNSInfo_t {
|
||||
uint8_t tnsDataPresent;
|
||||
uint8_t numFilt[8]; // [MAX_TNS_FILTERS] max 1 filter each for 8 short windows, or 3 filters for 1 long window
|
||||
uint8_t coefRes[8]; // [MAX_TNS_FILTERS]
|
||||
uint8_t length[8]; // [MAX_TNS_FILTERS]
|
||||
uint8_t order[8]; // [MAX_TNS_FILTERS]
|
||||
uint8_t dir[8]; // [MAX_TNS_FILTERS]
|
||||
int8_t coef[60]; // [MAX_TNS_COEFS] max 3 filters * 20 coefs for 1 long window,
|
||||
// or 1 filter * 7 coefs for each of 8 short windows
|
||||
} TNSInfo_t;
|
||||
|
||||
typedef struct _GainControlInfo_t {
|
||||
uint8_t gainControlDataPresent;
|
||||
uint8_t maxBand;
|
||||
uint8_t adjNum[3][8]; // [MAX_GAIN_BANDS][MAX_GAIN_WIN]
|
||||
uint8_t alevCode[3][8][7]; // [MAX_GAIN_BANDS][MAX_GAIN_WIN][MAX_GAIN_ADJUST]
|
||||
uint8_t alocCode[3][8][7]; // [MAX_GAIN_BANDS][MAX_GAIN_WIN][MAX_GAIN_ADJUST]
|
||||
} GainControlInfo_t;
|
||||
|
||||
typedef struct _ICSInfo_t {
|
||||
uint8_t icsResBit;
|
||||
uint8_t winSequence;
|
||||
uint8_t winShape;
|
||||
uint8_t maxSFB;
|
||||
uint8_t sfGroup;
|
||||
uint8_t predictorDataPresent;
|
||||
uint8_t predictorReset;
|
||||
uint8_t predictorResetGroupNum;
|
||||
uint8_t predictionUsed[41]; // [MAX_PRED_SFB]
|
||||
uint8_t numWinGroup;
|
||||
uint8_t winGroupLen[8]; // [MAX_WIN_GROUPS]
|
||||
} ICSInfo_t;
|
||||
|
||||
typedef struct _ADTSHeader_t {
|
||||
/* fixed */
|
||||
uint8_t id; /* MPEG bit - should be 1 */
|
||||
uint8_t layer; /* MPEG layer - should be 0 */
|
||||
uint8_t protectBit; /* 0 = CRC word follows, 1 = no CRC word */
|
||||
uint8_t profile; /* 0 = main, 1 = LC, 2 = SSR, 3 = reserved */
|
||||
uint8_t sampRateIdx; /* sample rate index range = [0, 11] */
|
||||
uint8_t privateBit; /* ignore */
|
||||
uint8_t channelConfig; /* 0 = implicit, >0 = use default table */
|
||||
uint8_t origCopy; /* 0 = copy, 1 = original */
|
||||
uint8_t home; /* ignore */
|
||||
/* variable */
|
||||
uint8_t copyBit; /* 1 bit of the 72-bit copyright ID (transmitted as 1 bit per frame) */
|
||||
uint8_t copyStart; /* 1 = this bit starts the 72-bit ID, 0 = it does not */
|
||||
int frameLength; /* length of frame */
|
||||
int bufferFull; /* number of 32-bit words left in enc buffer, 0x7FF = VBR */
|
||||
uint8_t numRawDataBlocks; /* number of raw data blocks in frame */
|
||||
/* CRC */
|
||||
int crcCheckWord; /* 16-bit CRC check word (present if protectBit == 0) */
|
||||
} ADTSHeader_t;
|
||||
|
||||
typedef struct _ADIFHeader_t {
|
||||
uint8_t copyBit; /* 0 = no copyright ID, 1 = 72-bit copyright ID follows immediately */
|
||||
uint8_t origCopy; /* 0 = copy, 1 = original */
|
||||
uint8_t home; /* ignore */
|
||||
uint8_t bsType; /* bitstream type: 0 = CBR, 1 = VBR */
|
||||
int bitRate; /* bitRate: CBR = bits/sec, VBR = peak bits/frame, 0 = unknown */
|
||||
uint8_t numPCE; /* number of program config elements (max = 16) */
|
||||
int bufferFull; /* bits left in bit reservoir */
|
||||
uint8_t copyID[9]; /* [ADIF_COPYID_SIZE] optional 72-bit copyright ID */
|
||||
} ADIFHeader_t;
|
||||
|
||||
/* sizeof(ProgConfigElement_t) = 82 bytes (if KEEP_PCE_COMMENTS not defined) */
|
||||
typedef struct _ProgConfigElement_t {
|
||||
uint8_t elemInstTag; /* element instance tag */
|
||||
uint8_t profile; /* 0 = main, 1 = LC, 2 = SSR, 3 = reserved */
|
||||
uint8_t sampRateIdx; /* sample rate index range = [0, 11] */
|
||||
uint8_t numFCE; /* number of front channel elements (max = 15) */
|
||||
uint8_t numSCE; /* number of side channel elements (max = 15) */
|
||||
uint8_t numBCE; /* number of back channel elements (max = 15) */
|
||||
uint8_t numLCE; /* number of LFE channel elements (max = 3) */
|
||||
uint8_t numADE; /* number of associated data elements (max = 7) */
|
||||
uint8_t numCCE; /* number of valid channel coupling elements (max = 15) */
|
||||
uint8_t monoMixdown; /* mono mixdown: bit 4 = present flag, bits 3-0 = element number */
|
||||
uint8_t stereoMixdown; /* stereo mixdown: bit 4 = present flag, bits 3-0 = element number */
|
||||
uint8_t matrixMixdown; /* bit 4 = present flag, bit 3 = unused,bits 2-1 = index, bit 0 = pseudo-surround enable */
|
||||
uint8_t fce[15]; /* [MAX_NUM_FCE] front element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */
|
||||
uint8_t sce[15]; /* [MAX_NUM_SCE] side element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */
|
||||
uint8_t bce[15]; /* [MAX_NUM_BCE] back element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */
|
||||
uint8_t lce[3]; /* [MAX_NUM_LCE] instance tag for LFE elements */
|
||||
uint8_t ade[7]; /* [MAX_NUM_ADE] instance tag for ADE elements */
|
||||
uint8_t cce[15]; /* [MAX_NUM_BCE] channel coupling elements: bit 4 = switching flag, bits 3-0 = inst tag */
|
||||
} ProgConfigElement_t;
|
||||
|
||||
typedef struct _SBRHeader {
|
||||
int count;
|
||||
|
||||
uint8_t ampRes;
|
||||
uint8_t startFreq;
|
||||
uint8_t stopFreq;
|
||||
uint8_t crossOverBand;
|
||||
uint8_t resBitsHdr;
|
||||
uint8_t hdrExtra1;
|
||||
uint8_t hdrExtra2;
|
||||
|
||||
uint8_t freqScale;
|
||||
uint8_t alterScale;
|
||||
uint8_t noiseBands;
|
||||
|
||||
uint8_t limiterBands;
|
||||
uint8_t limiterGains;
|
||||
uint8_t interpFreq;
|
||||
uint8_t smoothMode;
|
||||
} SBRHeader;
|
||||
|
||||
/* need one SBRGrid per channel, updated every frame */
|
||||
typedef struct _SBRGrid {
|
||||
uint8_t frameClass;
|
||||
uint8_t ampResFrame;
|
||||
uint8_t pointer;
|
||||
|
||||
uint8_t numEnv; /* L_E */
|
||||
uint8_t envTimeBorder[5 + 1]; // [MAX_NUM_ENV+1] /* t_E */
|
||||
uint8_t freqRes[5]; // [MAX_NUM_ENV]/* r */
|
||||
uint8_t numNoiseFloors; /* L_Q */
|
||||
uint8_t noiseTimeBorder[2 + 1]; // [MAX_NUM_NOISE_FLOORS+1] /* t_Q */
|
||||
|
||||
uint8_t numEnvPrev;
|
||||
uint8_t numNoiseFloorsPrev;
|
||||
uint8_t freqResPrev;
|
||||
} SBRGrid;
|
||||
|
||||
/* need one SBRFreq per element (SCE/CPE/LFE), updated only on header reset */
|
||||
typedef struct _SBRFreq {
|
||||
int kStart; /* k_x */
|
||||
int nMaster;
|
||||
int nHigh;
|
||||
int nLow;
|
||||
int nLimiter; /* N_l */
|
||||
int numQMFBands; /* M */
|
||||
int numNoiseFloorBands; /* Nq */
|
||||
int kStartPrev;
|
||||
int numQMFBandsPrev;
|
||||
uint8_t freqMaster[48 + 1]; // [MAX_QMF_BANDS + 1] /* not necessary to save this after derived tables are generated */
|
||||
uint8_t freqHigh[48 + 1]; // [MAX_QMF_BANDS + 1]
|
||||
uint8_t freqLow[48 / 2 + 1]; // [MAX_QMF_BANDS / 2 + 1] /* nLow = nHigh - (nHigh >> 1) */
|
||||
uint8_t freqNoise[5 + 1]; // [MAX_NUM_NOISE_FLOOR_BANDS+1]
|
||||
uint8_t freqLimiter[48 / 2 + 5];// [MAX_QMF_BANDS / 2 + MAX_NUM_PATCHES] /* max (intermediate) size = nLow + numPatches - 1 */
|
||||
|
||||
uint8_t numPatches;
|
||||
uint8_t patchNumSubbands[5 + 1]; // [MAX_NUM_PATCHES + 1]
|
||||
uint8_t patchStartSubband[5 + 1]; // [MAX_NUM_PATCHES + 1]
|
||||
} SBRFreq;
|
||||
|
||||
typedef struct _SBRChan {
|
||||
int reset;
|
||||
uint8_t deltaFlagEnv[5]; // [MAX_NUM_ENV]
|
||||
uint8_t deltaFlagNoise[2]; // [MAX_NUM_NOISE_FLOORS]
|
||||
int8_t envDataQuant[5][48]; // [MAX_NUM_ENV][MAX_QMF_BANDS] /* range = [0, 127] */
|
||||
int8_t noiseDataQuant[2][5]; // [MAX_NUM_NOISE_FLOORS][MAX_NUM_NOISE_FLOOR_BANDS]
|
||||
|
||||
uint8_t invfMode[2][5]; // [2][MAX_NUM_NOISE_FLOOR_BANDS] /* invfMode[0/1][band] = prev/curr */
|
||||
int chirpFact[5]; // [MAX_NUM_NOISE_FLOOR_BANDS] /* bwArray */
|
||||
uint8_t addHarmonicFlag[2]; /* addHarmonicFlag[0/1] = prev/curr */
|
||||
uint8_t addHarmonic[2][64]; /* addHarmonic[0/1][band] = prev/curr */
|
||||
|
||||
int gbMask[2]; /* gbMask[0/1] = XBuf[0-31]/XBuf[32-39] */
|
||||
int8_t laPrev;
|
||||
|
||||
int noiseTabIndex;
|
||||
int sinIndex;
|
||||
int gainNoiseIndex;
|
||||
int gTemp[5][48]; // [MAX_NUM_SMOOTH_COEFS][MAX_QMF_BANDS]
|
||||
int qTemp[5][48]; // [MAX_NUM_SMOOTH_COEFS][MAX_QMF_BANDS]
|
||||
|
||||
} SBRChan;
|
||||
|
||||
|
||||
/* state info struct for baseline (MPEG-4 LC) decoding */
|
||||
typedef struct _PSInfoBase_t {
|
||||
int dataCount;
|
||||
uint8_t dataBuf[510]; // [DATA_BUF_SIZE]
|
||||
int fillCount;
|
||||
uint8_t fillBuf[269]; //[FILL_BUF_SIZE]
|
||||
/* state information which is the same throughout whole frame */
|
||||
int nChans;
|
||||
int useImpChanMap;
|
||||
int sampRateIdx;
|
||||
/* state information which can be overwritten by subsequent elements within frame */
|
||||
ICSInfo_t icsInfo[2]; // [MAX_NCHANS_ELEM]
|
||||
int commonWin;
|
||||
short scaleFactors[2][15*8]; // [MAX_NCHANS_ELEM][MAX_SF_BANDS]
|
||||
uint8_t sfbCodeBook[2][15*8]; // [MAX_NCHANS_ELEM][MAX_SF_BANDS]
|
||||
int msMaskPresent;
|
||||
uint8_t msMaskBits[(15 * 8 + 7) >> 3]; // [MAX_MS_MASK_BYTES]
|
||||
int pnsUsed[2]; // [MAX_NCHANS_ELEM]
|
||||
int pnsLastVal;
|
||||
int intensityUsed[2]; // [MAX_NCHANS_ELEM]
|
||||
// PulseInfo_t pulseInfo[2]; // [MAX_NCHANS_ELEM]
|
||||
TNSInfo_t tnsInfo[2]; // [MAX_NCHANS_ELEM]
|
||||
int tnsLPCBuf[20]; // [MAX_TNS_ORDER]
|
||||
int tnsWorkBuf[20]; //[MAX_TNS_ORDER]
|
||||
GainControlInfo_t gainControlInfo[2]; // [MAX_NCHANS_ELEM]
|
||||
int gbCurrent[2]; // [MAX_NCHANS_ELEM]
|
||||
int coef[2][1024]; // [MAX_NCHANS_ELEM][AAC_MAX_NSAMPS]
|
||||
#ifdef AAC_ENABLE_SBR
|
||||
int sbrWorkBuf[2][1024]; // [MAX_NCHANS_ELEM][AAC_MAX_NSAMPS];
|
||||
#endif
|
||||
/* state information which must be saved for each element and used in next frame */
|
||||
int overlap[2][1024]; // [AAC_MAX_NCHANS][AAC_MAX_NSAMPS]
|
||||
int prevWinShape[2]; // [AAC_MAX_NCHANS]
|
||||
} PSInfoBase_t;
|
||||
|
||||
typedef struct _PSInfoSBR {
|
||||
/* save for entire file */
|
||||
int frameCount;
|
||||
int sampRateIdx;
|
||||
|
||||
/* state info that must be saved for each channel */
|
||||
SBRHeader sbrHdr[2];
|
||||
SBRGrid sbrGrid[2];
|
||||
SBRFreq sbrFreq[2];
|
||||
SBRChan sbrChan[2];
|
||||
|
||||
/* temp variables, no need to save between blocks */
|
||||
uint8_t dataExtra;
|
||||
uint8_t resBitsData;
|
||||
uint8_t extendedDataPresent;
|
||||
int extendedDataSize;
|
||||
|
||||
int8_t envDataDequantScale[2][5]; // [MAX_NCHANS_ELEM][MAX_NUM_ENV
|
||||
int envDataDequant[2][5][48]; // [MAX_NCHANS_ELEM][MAX_NUM_ENV][MAX_QMF_BANDS
|
||||
int noiseDataDequant[2][2][5]; // [MAX_NCHANS_ELEM][MAX_NUM_NOISE_FLOORS][MAX_NUM_NOISE_FLOOR_BANDS]
|
||||
|
||||
int eCurr[48]; // [MAX_QMF_BANDS]
|
||||
uint8_t eCurrExp[48]; // [MAX_QMF_BANDS]
|
||||
uint8_t eCurrExpMax;
|
||||
int8_t la;
|
||||
|
||||
int crcCheckWord;
|
||||
int couplingFlag;
|
||||
int envBand;
|
||||
int eOMGainMax;
|
||||
int gainMax;
|
||||
int gainMaxFBits;
|
||||
int noiseFloorBand;
|
||||
int qp1Inv;
|
||||
int qqp1Inv;
|
||||
int sMapped;
|
||||
int sBand;
|
||||
int highBand;
|
||||
|
||||
int sumEOrigMapped;
|
||||
int sumECurrGLim;
|
||||
int sumSM;
|
||||
int sumQM;
|
||||
int gLimBoost[48];
|
||||
int qmLimBoost[48];
|
||||
int smBoost[48];
|
||||
|
||||
int smBuf[48];
|
||||
int qmLimBuf[48];
|
||||
int gLimBuf[48];
|
||||
int gLimFbits[48];
|
||||
|
||||
int gFiltLast[48];
|
||||
int qFiltLast[48];
|
||||
|
||||
/* large buffers */
|
||||
int delayIdxQMFA[2]; // [AAC_MAX_NCHANS]
|
||||
int delayQMFA[2][10 * 32]; // [AAC_MAX_NCHANS][DELAY_SAMPS_QMFA]
|
||||
int delayIdxQMFS[2]; // [AAC_MAX_NCHANS]
|
||||
int delayQMFS[2][10 * 128]; // [AAC_MAX_NCHANS][DELAY_SAMPS_QMFS]
|
||||
int XBufDelay[2][8][64][2]; // [AAC_MAX_NCHANS][HF_GEN][64][2]
|
||||
int XBuf[32+8][64][2];
|
||||
} PSInfoSBR_t;
|
||||
|
||||
bool AACDecoder_AllocateBuffers(void);
|
||||
int AACFlushCodec();
|
||||
void AACDecoder_FreeBuffers(void);
|
||||
bool AACDecoder_IsInit(void);
|
||||
int AACFindSyncWord(uint8_t *buf, int nBytes);
|
||||
int AACSetRawBlockParams(int copyLast, int nChans, int sampRateCore, int profile);
|
||||
int AACDecode(uint8_t *inbuf, int *bytesLeft, short *outbuf);
|
||||
int AACGetSampRate();
|
||||
int AACGetChannels();
|
||||
int AACGetID(); // 0-MPEG4, 1-MPEG2
|
||||
uint8_t AACGetProfile(); // 0-Main, 1-LC, 2-SSR, 3-reserved
|
||||
uint8_t AACGetFormat(); // 0-unknown 1-ADTS 2-ADIF, 3-RAW
|
||||
int AACGetBitsPerSample();
|
||||
int AACGetBitrate();
|
||||
int AACGetOutputSamps();
|
||||
int AACGetBitrate();
|
||||
void DecodeLPCCoefs(int order, int res, int8_t *filtCoef, int *a, int *b);
|
||||
int FilterRegion(int size, int dir, int order, int *audioCoef, int *a, int *hist);
|
||||
int TNSFilter(int ch);
|
||||
int DecodeSingleChannelElement();
|
||||
int DecodeChannelPairElement();
|
||||
int DecodeLFEChannelElement();
|
||||
int DecodeDataStreamElement();
|
||||
int DecodeProgramConfigElement(uint8_t idx);
|
||||
int DecodeFillElement();
|
||||
int DecodeNextElement(uint8_t **buf, int *bitOffset, int *bitsAvail);
|
||||
void PreMultiply(int tabidx, int *zbuf1);
|
||||
void PostMultiply(int tabidx, int *fft1);
|
||||
void PreMultiplyRescale(int tabidx, int *zbuf1, int es);
|
||||
void PostMultiplyRescale(int tabidx, int *fft1, int es);
|
||||
void DCT4(int tabidx, int *coef, int gb);
|
||||
void BitReverse(int *inout, int tabidx);
|
||||
void R4FirstPass(int *x, int bg);
|
||||
void R8FirstPass(int *x, int bg);
|
||||
void R4Core(int *x, int bg, int gp, int *wtab);
|
||||
void R4FFT(int tabidx, int *x);
|
||||
void UnpackZeros(int nVals, int *coef);
|
||||
void UnpackQuads(int cb, int nVals, int *coef);
|
||||
void UnpackPairsNoEsc(int cb, int nVals, int *coef);
|
||||
void UnpackPairsEsc(int cb, int nVals, int *coef);
|
||||
void DecodeSpectrumLong(int ch);
|
||||
void DecodeSpectrumShort(int ch);
|
||||
void DecWindowOverlap(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapLongStart(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapLongStop(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapShort(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev);
|
||||
int IMDCT(int ch, int chOut, short *outbuf);
|
||||
void DecodeICSInfo(ICSInfo_t *icsInfo, int sampRateIdx);
|
||||
void DecodeSectionData(int winSequence, int numWinGrp, int maxSFB, uint8_t *sfbCodeBook);
|
||||
int DecodeOneScaleFactor();
|
||||
void DecodeScaleFactors(int numWinGrp, int maxSFB, int globalGain, uint8_t *sfbCodeBook, short *scaleFactors);
|
||||
void DecodePulseInfo(uint8_t ch);
|
||||
void DecodeTNSInfo(int winSequence, TNSInfo_t *ti, int8_t *tnsCoef);
|
||||
void DecodeGainControlInfo(int winSequence, GainControlInfo_t *gi);
|
||||
void DecodeICS(int ch);
|
||||
int DecodeNoiselessData(uint8_t **buf, int *bitOffset, int *bitsAvail, int ch);
|
||||
int DecodeHuffmanScalar(const signed short *huffTab, const HuffInfo_t *huffTabInfo, unsigned int bitBuf, int32_t *val);
|
||||
int UnpackADTSHeader(uint8_t **buf, int *bitOffset, int *bitsAvail);
|
||||
int GetADTSChannelMapping(uint8_t *buf, int bitOffset, int bitsAvail);
|
||||
int GetNumChannelsADIF(int nPCE);
|
||||
int GetSampleRateIdxADIF(int nPCE);
|
||||
int UnpackADIFHeader(uint8_t **buf, int *bitOffset, int *bitsAvail);
|
||||
int SetRawBlockParams(int copyLast, int nChans, int sampRate, int profile);
|
||||
int PrepareRawBlock();
|
||||
int DequantBlock(int *inbuf, int nSamps, int scale);
|
||||
int AACDequantize(int ch);
|
||||
int DeinterleaveShortBlocks(int ch);
|
||||
unsigned int Get32BitVal(unsigned int *last);
|
||||
int InvRootR(int r);
|
||||
int ScaleNoiseVector(int *coef, int nVals, int sf);
|
||||
void GenerateNoiseVector(int *coef, int *last, int nVals);
|
||||
void CopyNoiseVector(int *coefL, int *coefR, int nVals);
|
||||
int PNS(int ch);
|
||||
int GetSampRateIdx(int sampRate);
|
||||
void StereoProcessGroup(int *coefL, int *coefR, const uint16_t *sfbTab, int msMaskPres, uint8_t *msMaskPtr,
|
||||
int msMaskOffset, int maxSFB, uint8_t *cbRight, short *sfRight, int *gbCurrent);
|
||||
int StereoProcess();
|
||||
int RatioPowInv(int a, int b, int c);
|
||||
int SqrtFix(int q, int fBitsIn, int *fBitsOut);
|
||||
int InvRNormalized(int r);
|
||||
void BitReverse32(int *inout);
|
||||
void R8FirstPass32(int *r0);
|
||||
void R4Core32(int *r0);
|
||||
void FFT32C(int *x);
|
||||
void CVKernel1(int *XBuf, int *accBuf);
|
||||
void CVKernel2(int *XBuf, int *accBuf);
|
||||
void SetBitstreamPointer(int nBytes, uint8_t *buf);
|
||||
inline void RefillBitstreamCache();
|
||||
unsigned int GetBits(int nBits);
|
||||
unsigned int GetBitsNoAdvance(int nBits);
|
||||
void AdvanceBitstream(int nBits);
|
||||
int CalcBitsUsed(uint8_t *startBuf, int startOffset);
|
||||
void ByteAlignBitstream();
|
||||
// SBR
|
||||
void InitSBRState();
|
||||
int DecodeSBRBitstream(int chBase);
|
||||
int DecodeSBRData(int chBase, short *outbuf);
|
||||
int FlushCodecSBR();
|
||||
void BubbleSort(uint8_t *v, int nItems);
|
||||
uint8_t VMin(uint8_t *v, int nItems);
|
||||
uint8_t VMax(uint8_t *v, int nItems);
|
||||
int CalcFreqMasterScaleZero(uint8_t *freqMaster, int alterScale, int k0, int k2);
|
||||
int CalcFreqMaster(uint8_t *freqMaster, int freqScale, int alterScale, int k0, int k2);
|
||||
int CalcFreqHigh(uint8_t *freqHigh, uint8_t *freqMaster, int nMaster, int crossOverBand);
|
||||
int CalcFreqLow(uint8_t *freqLow, uint8_t *freqHigh, int nHigh);
|
||||
int CalcFreqNoise(uint8_t *freqNoise, uint8_t *freqLow, int nLow, int kStart, int k2, int noiseBands);
|
||||
int BuildPatches(uint8_t *patchNumSubbands, uint8_t *patchStartSubband, uint8_t *freqMaster, int nMaster, int k0,
|
||||
int kStart, int numQMFBands, int sampRateIdx);
|
||||
int FindFreq(uint8_t *freq, int nFreq, uint8_t val);
|
||||
void RemoveFreq(uint8_t *freq, int nFreq, int removeIdx);
|
||||
int CalcFreqLimiter(uint8_t *freqLimiter, uint8_t *patchNumSubbands, uint8_t *freqLow, int nLow, int kStart,
|
||||
int limiterBands, int numPatches);
|
||||
int CalcFreqTables(SBRHeader *sbrHdr, SBRFreq *sbrFreq, int sampRateIdx);
|
||||
void EstimateEnvelope(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, int env);
|
||||
int GetSMapped(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int env, int band, int la);
|
||||
void CalcMaxGain(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, int ch, int env, int lim, int fbitsDQ);
|
||||
void CalcNoiseDivFactors(int q, int *qp1Inv, int *qqp1Inv);
|
||||
void CalcComponentGains(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch, int env, int lim, int fbitsDQ);
|
||||
void ApplyBoost(SBRFreq *sbrFreq, int lim, int fbitsDQ);
|
||||
void CalcGain(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch, int env);
|
||||
void MapHF(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int env, int hfReset);
|
||||
void AdjustHighFreq(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch);
|
||||
int CalcCovariance1(int *XBuf, int *p01reN, int *p01imN, int *p12reN, int *p12imN, int *p11reN, int *p22reN);
|
||||
int CalcCovariance2(int *XBuf, int *p02reN, int *p02imN);
|
||||
void CalcLPCoefs(int *XBuf, int *a0re, int *a0im, int *a1re, int *a1im, int gb);
|
||||
void GenerateHighFreq(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch);
|
||||
int DecodeHuffmanScalar(const signed int *huffTab, const HuffInfo_t *huffTabInfo, unsigned int bitBuf, signed int *val);
|
||||
int DecodeOneSymbol(int huffTabIndex);
|
||||
int DequantizeEnvelope(int nBands, int ampRes, int8_t *envQuant, int *envDequant);
|
||||
void DequantizeNoise(int nBands, int8_t *noiseQuant, int *noiseDequant);
|
||||
void DecodeSBREnvelope(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch);
|
||||
void DecodeSBRNoise(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch);
|
||||
void UncoupleSBREnvelope(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChanR);
|
||||
void UncoupleSBRNoise(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChanR);
|
||||
void DecWindowOverlapNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapLongStartNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapLongStopNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapShortNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev);
|
||||
void PreMultiply64(int *zbuf1);
|
||||
void PostMultiply64(int *fft1, int nSampsOut);
|
||||
void QMFAnalysisConv(int *cTab, int *delay, int dIdx, int *uBuf);
|
||||
int QMFAnalysis(int *inbuf, int *delay, int *XBuf, int fBitsIn, int *delayIdx, int qmfaBands);
|
||||
void QMFSynthesisConv(int *cPtr, int *delay, int dIdx, short *outbuf, int nChans);
|
||||
void QMFSynthesis(int *inbuf, int *delay, int *delayIdx, int qmfsBands, short *outbuf, int nChans);
|
||||
int UnpackSBRHeader(SBRHeader *sbrHdr);
|
||||
void UnpackSBRGrid(SBRHeader *sbrHdr, SBRGrid *sbrGrid);
|
||||
void UnpackDeltaTimeFreq(int numEnv, uint8_t *deltaFlagEnv, int numNoiseFloors, uint8_t *deltaFlagNoise);
|
||||
void UnpackInverseFilterMode(int numNoiseFloorBands, uint8_t *mode);
|
||||
void UnpackSinusoids(int nHigh, int addHarmonicFlag, uint8_t *addHarmonic);
|
||||
void CopyCouplingGrid(SBRGrid *sbrGridLeft, SBRGrid *sbrGridRight);
|
||||
void CopyCouplingInverseFilterMode(int numNoiseFloorBands, uint8_t *modeLeft, uint8_t *modeRight);
|
||||
void UnpackSBRSingleChannel(int chBase);
|
||||
void UnpackSBRChannelPair(int chBase);
|
@ -0,0 +1,556 @@
|
||||
/*
|
||||
* flac_decoder.cpp
|
||||
* Java source code from https://www.nayuki.io/page/simple-flac-implementation
|
||||
* adapted to ESP32
|
||||
*
|
||||
* Created on: Jul 03,2020
|
||||
* Updated on: Jul 03,2021
|
||||
*
|
||||
* Author: Wolle
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include "flac_decoder.h"
|
||||
#include "vector"
|
||||
using namespace std;
|
||||
|
||||
|
||||
FLACFrameHeader_t *FLACFrameHeader;
|
||||
FLACMetadataBlock_t *FLACMetadataBlock;
|
||||
FLACsubFramesBuff_t *FLACsubFramesBuff;
|
||||
|
||||
vector<int32_t>coefs;
|
||||
const uint16_t outBuffSize = 2048;
|
||||
uint16_t m_blockSize=0;
|
||||
uint16_t m_blockSizeLeft = 0;
|
||||
uint16_t m_validSamples = 0;
|
||||
uint8_t m_status = 0;
|
||||
uint8_t* m_inptr;
|
||||
int16_t m_bytesAvail;
|
||||
int16_t m_bytesDecoded = 0;
|
||||
float m_compressionRatio = 0;
|
||||
uint16_t m_rIndex=0;
|
||||
uint64_t m_bitBuffer = 0;
|
||||
uint8_t m_bitBufferLen = 0;
|
||||
bool m_f_OggS_found = false;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// FLAC INI SECTION
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool FLACDecoder_AllocateBuffers(void){
|
||||
if(psramFound()) {
|
||||
// PSRAM found, Buffer will be allocated in PSRAM
|
||||
if(!FLACFrameHeader) {FLACFrameHeader = (FLACFrameHeader_t*) ps_malloc(sizeof(FLACFrameHeader_t));}
|
||||
if(!FLACMetadataBlock) {FLACMetadataBlock = (FLACMetadataBlock_t*) ps_malloc(sizeof(FLACMetadataBlock_t));}
|
||||
if(!FLACsubFramesBuff) {FLACsubFramesBuff = (FLACsubFramesBuff_t*) ps_malloc(sizeof(FLACsubFramesBuff_t));}
|
||||
}
|
||||
else {
|
||||
if(!FLACFrameHeader) {FLACFrameHeader = (FLACFrameHeader_t*) malloc(sizeof(FLACFrameHeader_t));}
|
||||
if(!FLACMetadataBlock) {FLACMetadataBlock = (FLACMetadataBlock_t*) malloc(sizeof(FLACMetadataBlock_t));}
|
||||
if(!FLACsubFramesBuff) {FLACsubFramesBuff = (FLACsubFramesBuff_t*) malloc(sizeof(FLACsubFramesBuff_t));}
|
||||
}
|
||||
if(!FLACFrameHeader || !FLACMetadataBlock || !FLACsubFramesBuff ){
|
||||
log_e("not enough memory to allocate flacdecoder buffers");
|
||||
return false;
|
||||
}
|
||||
FLACDecoder_ClearBuffer();
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void FLACDecoder_ClearBuffer(){
|
||||
memset(FLACFrameHeader, 0, sizeof(FLACFrameHeader_t));
|
||||
memset(FLACMetadataBlock, 0, sizeof(FLACMetadataBlock_t));
|
||||
memset(FLACsubFramesBuff, 0, sizeof(FLACsubFramesBuff_t));
|
||||
m_status = DECODE_FRAME;
|
||||
return;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void FLACDecoder_FreeBuffers(){
|
||||
if(FLACFrameHeader) {free(FLACFrameHeader); FLACFrameHeader = NULL;}
|
||||
if(FLACMetadataBlock) {free(FLACMetadataBlock); FLACMetadataBlock = NULL;}
|
||||
if(FLACsubFramesBuff) {free(FLACsubFramesBuff); FLACsubFramesBuff = NULL;}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// B I T R E A D E R
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t readUint(uint8_t nBits){
|
||||
while (m_bitBufferLen < nBits){
|
||||
uint8_t temp = *(m_inptr + m_rIndex);
|
||||
m_rIndex++;
|
||||
m_bytesAvail--;
|
||||
if(m_bytesAvail < 0) { log_i("error in bitreader"); }
|
||||
m_bitBuffer = (m_bitBuffer << 8) | temp;
|
||||
m_bitBufferLen += 8;
|
||||
}
|
||||
m_bitBufferLen -= nBits;
|
||||
uint32_t result = m_bitBuffer >> m_bitBufferLen;
|
||||
if (nBits < 32)
|
||||
result &= (1 << nBits) - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t readSignedInt(int nBits){
|
||||
int32_t temp = readUint(nBits) << (32 - nBits);
|
||||
temp = temp >> (32 - nBits); // The C++ compiler uses the sign bit to fill vacated bit positions
|
||||
return temp;
|
||||
}
|
||||
|
||||
int64_t readRiceSignedInt(uint8_t param){
|
||||
long val = 0;
|
||||
while (readUint(1) == 0)
|
||||
val++;
|
||||
val = (val << param) | readUint(param);
|
||||
return (val >> 1) ^ -(val & 1);
|
||||
}
|
||||
|
||||
void alignToByte() {
|
||||
m_bitBufferLen -= m_bitBufferLen % 8;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// F L A C - D E C O D E R
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void FLACSetRawBlockParams(uint8_t Chans, uint32_t SampRate, uint8_t BPS, uint32_t tsis, uint32_t AuDaLength){
|
||||
FLACMetadataBlock->numChannels = Chans;
|
||||
FLACMetadataBlock->sampleRate = SampRate;
|
||||
FLACMetadataBlock->bitsPerSample = BPS;
|
||||
FLACMetadataBlock->totalSamples = tsis; // total samples in stream
|
||||
FLACMetadataBlock->audioDataLength = AuDaLength;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void FLACDecoderReset(){ // set var to default
|
||||
m_status = DECODE_FRAME;
|
||||
m_bitBuffer = 0;
|
||||
m_bitBufferLen = 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int FLACFindSyncWord(unsigned char *buf, int nBytes) {
|
||||
int i;
|
||||
|
||||
/* find byte-aligned syncword - need 13 matching bits */
|
||||
for (i = 0; i < nBytes - 1; i++) {
|
||||
if ((buf[i + 0] & 0xFF) == 0xFF && (buf[i + 1] & 0xF8) == 0xF8) {
|
||||
FLACDecoderReset();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int FLACFindOggSyncWord(unsigned char *buf, int nBytes){
|
||||
int i;
|
||||
|
||||
/* find byte-aligned syncword - need 13 matching bits */
|
||||
for (i = 0; i < nBytes - 1; i++) {
|
||||
if ((buf[i + 0] & 0xFF) == 0xFF && (buf[i + 1] & 0xF8) == 0xF8) {
|
||||
FLACDecoderReset();
|
||||
log_i("FLAC sync found");
|
||||
return i;
|
||||
}
|
||||
}
|
||||
/* find byte-aligned OGG Magic - OggS */
|
||||
for (i = 0; i < nBytes - 1; i++) {
|
||||
if ((buf[i + 0] == 'O') && (buf[i + 1] == 'g') && (buf[i + 2] == 'g') && (buf[i + 3] == 'S')) {
|
||||
FLACDecoderReset();
|
||||
log_i("OggS found");
|
||||
m_f_OggS_found = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int FLACparseOggHeader(unsigned char *buf){
|
||||
uint8_t i = 0;
|
||||
uint8_t ssv = *(buf + i); // stream_structure_version
|
||||
(void)ssv;
|
||||
i++;
|
||||
uint8_t htf = *(buf + i); // header_type_flag
|
||||
(void)htf;
|
||||
i++;
|
||||
uint32_t tmp = 0; // absolute granule position
|
||||
for (int j = 0; j < 4; j++) {
|
||||
tmp += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint64_t agp = (uint64_t) tmp << 32;
|
||||
for (int j = 0; j < 4; j++) {
|
||||
agp += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint32_t ssnr = 0; // stream serial number
|
||||
for (int j = 0; j < 4; j++) {
|
||||
ssnr += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint32_t psnr = 0; // page sequence no
|
||||
for (int j = 0; j < 4; j++) {
|
||||
psnr += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint32_t pchk = 0; // page checksum
|
||||
for (int j = 0; j < 4; j++) {
|
||||
pchk += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint8_t psegm = *(buf + i);
|
||||
i++;
|
||||
uint8_t psegmBuff[256];
|
||||
uint32_t pageLen = 0;
|
||||
for(uint8_t j = 0; j < psegm; j++){
|
||||
psegmBuff[j] = *(buf + i);
|
||||
pageLen += psegmBuff[j];
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t FLACDecode(uint8_t *inbuf, int *bytesLeft, short *outbuf){
|
||||
|
||||
if(m_f_OggS_found == true){
|
||||
m_f_OggS_found = false;
|
||||
*bytesLeft -= FLACparseOggHeader(inbuf);
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
|
||||
if(m_status != OUT_SAMPLES){
|
||||
m_rIndex = 0;
|
||||
m_bytesAvail = (*bytesLeft);
|
||||
m_inptr = inbuf;
|
||||
}
|
||||
|
||||
if(m_status == DECODE_FRAME){ // Read a ton of header fields, and ignore most of them
|
||||
|
||||
if ((inbuf[0] == 'O') && (inbuf[1] == 'g') && (inbuf[2] == 'g') && (inbuf[3] == 'S')){
|
||||
*bytesLeft -= 4;
|
||||
m_f_OggS_found = true;
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
|
||||
uint32_t temp = readUint(8);
|
||||
uint16_t sync = temp << 6 |readUint(6);
|
||||
if (sync != 0x3FFE){
|
||||
log_i("Sync code expected 0x3FFE but received %X", sync);
|
||||
return ERR_FLAC_SYNC_CODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
readUint(1);
|
||||
FLACFrameHeader->blockingStrategy = readUint(1);
|
||||
FLACFrameHeader->blockSizeCode = readUint(4);
|
||||
FLACFrameHeader->sampleRateCode = readUint(4);
|
||||
FLACFrameHeader->chanAsgn = readUint(4);
|
||||
FLACFrameHeader->sampleSizeCode = readUint(3);
|
||||
|
||||
if(!FLACMetadataBlock->numChannels){
|
||||
if(FLACFrameHeader->chanAsgn == 0) FLACMetadataBlock->numChannels = 1;
|
||||
if(FLACFrameHeader->chanAsgn == 1) FLACMetadataBlock->numChannels = 2;
|
||||
if(FLACFrameHeader->chanAsgn > 7) FLACMetadataBlock->numChannels = 2;
|
||||
}
|
||||
if(FLACMetadataBlock->numChannels < 1) return ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT;
|
||||
|
||||
if(!FLACMetadataBlock->bitsPerSample){
|
||||
if(FLACFrameHeader->sampleSizeCode == 1) FLACMetadataBlock->bitsPerSample = 8;
|
||||
if(FLACFrameHeader->sampleSizeCode == 2) FLACMetadataBlock->bitsPerSample = 12;
|
||||
if(FLACFrameHeader->sampleSizeCode == 4) FLACMetadataBlock->bitsPerSample = 16;
|
||||
if(FLACFrameHeader->sampleSizeCode == 5) FLACMetadataBlock->bitsPerSample = 20;
|
||||
if(FLACFrameHeader->sampleSizeCode == 6) FLACMetadataBlock->bitsPerSample = 24;
|
||||
}
|
||||
if(FLACMetadataBlock->bitsPerSample > 16) return ERR_FLAC_BITS_PER_SAMPLE_TOO_BIG;
|
||||
if(FLACMetadataBlock->bitsPerSample < 8 ) return ERR_FLAG_BITS_PER_SAMPLE_UNKNOWN;
|
||||
|
||||
if(!FLACMetadataBlock->sampleRate){
|
||||
if(FLACFrameHeader->sampleRateCode == 1) FLACMetadataBlock->sampleRate = 88200;
|
||||
if(FLACFrameHeader->sampleRateCode == 2) FLACMetadataBlock->sampleRate = 176400;
|
||||
if(FLACFrameHeader->sampleRateCode == 3) FLACMetadataBlock->sampleRate = 192000;
|
||||
if(FLACFrameHeader->sampleRateCode == 4) FLACMetadataBlock->sampleRate = 8000;
|
||||
if(FLACFrameHeader->sampleRateCode == 5) FLACMetadataBlock->sampleRate = 16000;
|
||||
if(FLACFrameHeader->sampleRateCode == 6) FLACMetadataBlock->sampleRate = 22050;
|
||||
if(FLACFrameHeader->sampleRateCode == 7) FLACMetadataBlock->sampleRate = 24000;
|
||||
if(FLACFrameHeader->sampleRateCode == 8) FLACMetadataBlock->sampleRate = 32000;
|
||||
if(FLACFrameHeader->sampleRateCode == 9) FLACMetadataBlock->sampleRate = 44100;
|
||||
if(FLACFrameHeader->sampleRateCode == 10) FLACMetadataBlock->sampleRate = 48000;
|
||||
if(FLACFrameHeader->sampleRateCode == 11) FLACMetadataBlock->sampleRate = 96000;
|
||||
}
|
||||
|
||||
readUint(1);
|
||||
temp = (readUint(8) << 24);
|
||||
temp = ~temp;
|
||||
|
||||
uint32_t shift = 0x80000000; // Number of leading zeros
|
||||
int8_t count = 0;
|
||||
for(int i=0; i<32; i++){
|
||||
if((temp & shift) == 0) {count++; shift >>= 1;}
|
||||
else break;
|
||||
}
|
||||
count--;
|
||||
for (int i = 0; i < count; i++) readUint(8);
|
||||
m_blockSize = 0;
|
||||
|
||||
if (FLACFrameHeader->blockSizeCode == 1)
|
||||
m_blockSize = 192;
|
||||
else if (2 <= FLACFrameHeader->blockSizeCode && FLACFrameHeader->blockSizeCode <= 5)
|
||||
m_blockSize = 576 << (FLACFrameHeader->blockSizeCode - 2);
|
||||
else if (FLACFrameHeader->blockSizeCode == 6)
|
||||
m_blockSize = readUint(8) + 1;
|
||||
else if (FLACFrameHeader->blockSizeCode == 7)
|
||||
m_blockSize = readUint(16) + 1;
|
||||
else if (8 <= FLACFrameHeader->blockSizeCode && FLACFrameHeader->blockSizeCode <= 15)
|
||||
m_blockSize = 256 << (FLACFrameHeader->blockSizeCode - 8);
|
||||
else{
|
||||
return ERR_FLAC_RESERVED_BLOCKSIZE_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if(m_blockSize > 8192){
|
||||
log_e("Error: blockSize too big");
|
||||
return ERR_FLAC_BLOCKSIZE_TOO_BIG;
|
||||
}
|
||||
|
||||
if(FLACFrameHeader->sampleRateCode == 12)
|
||||
readUint(8);
|
||||
else if (FLACFrameHeader->sampleRateCode == 13 || FLACFrameHeader->sampleRateCode == 14){
|
||||
readUint(16);
|
||||
}
|
||||
readUint(8);
|
||||
m_status = DECODE_SUBFRAMES;
|
||||
*bytesLeft = m_bytesAvail;
|
||||
m_blockSizeLeft = m_blockSize;
|
||||
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
|
||||
if(m_status == DECODE_SUBFRAMES){
|
||||
|
||||
// Decode each channel's subframe, then skip footer
|
||||
int ret = decodeSubframes();
|
||||
if(ret != 0) return ret;
|
||||
m_status = OUT_SAMPLES;
|
||||
}
|
||||
|
||||
if(m_status == OUT_SAMPLES){ // Write the decoded samples
|
||||
// blocksize can be much greater than outbuff, so we can't stuff all in once
|
||||
// therefore we need often more than one loop (split outputblock into pieces)
|
||||
uint16_t blockSize;
|
||||
static uint16_t offset = 0;
|
||||
if(m_blockSize < outBuffSize + offset) blockSize = m_blockSize - offset;
|
||||
else blockSize = outBuffSize;
|
||||
|
||||
|
||||
for (int i = 0; i < blockSize; i++) {
|
||||
for (int j = 0; j < FLACMetadataBlock->numChannels; j++) {
|
||||
int val = FLACsubFramesBuff->samplesBuffer[j][i + offset];
|
||||
if (FLACMetadataBlock->bitsPerSample == 8) val += 128;
|
||||
outbuf[2*i+j] = val;
|
||||
}
|
||||
}
|
||||
|
||||
m_validSamples = blockSize * FLACMetadataBlock->numChannels;
|
||||
offset += blockSize;
|
||||
|
||||
if(offset != m_blockSize) return GIVE_NEXT_LOOP;
|
||||
offset = 0;
|
||||
if(offset > m_blockSize) { log_e("offset has a wrong value"); }
|
||||
}
|
||||
|
||||
alignToByte();
|
||||
readUint(16);
|
||||
m_bytesDecoded = *bytesLeft - m_bytesAvail;
|
||||
// log_i("m_bytesDecoded %i", m_bytesDecoded);
|
||||
// m_compressionRatio = (float)m_bytesDecoded / (float)m_blockSize * FLACMetadataBlock->numChannels * (16/8);
|
||||
// log_i("m_compressionRatio % f", m_compressionRatio);
|
||||
*bytesLeft = m_bytesAvail;
|
||||
m_status = DECODE_FRAME;
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint16_t FLACGetOutputSamps(){
|
||||
int vs = m_validSamples;
|
||||
m_validSamples=0;
|
||||
return vs;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint64_t FLACGetTotoalSamplesInStream(){
|
||||
return FLACMetadataBlock->totalSamples;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint8_t FLACGetBitsPerSample(){
|
||||
return FLACMetadataBlock->bitsPerSample;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint8_t FLACGetChannels(){
|
||||
return FLACMetadataBlock->numChannels;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t FLACGetSampRate(){
|
||||
return FLACMetadataBlock->sampleRate;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t FLACGetBitRate(){
|
||||
if(FLACMetadataBlock->totalSamples){
|
||||
float BitsPerSamp = (float)FLACMetadataBlock->audioDataLength / (float)FLACMetadataBlock->totalSamples * 8;
|
||||
return ((uint32_t)BitsPerSamp * FLACMetadataBlock->sampleRate);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t FLACGetAudioFileDuration() {
|
||||
if(FLACGetSampRate()){
|
||||
uint32_t afd = FLACGetTotoalSamplesInStream()/ FLACGetSampRate(); // AudioFileDuration
|
||||
return afd;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeSubframes(){
|
||||
if(FLACFrameHeader->chanAsgn <= 7) {
|
||||
for (int ch = 0; ch < FLACMetadataBlock->numChannels; ch++)
|
||||
decodeSubframe(FLACMetadataBlock->bitsPerSample, ch);
|
||||
}
|
||||
else if (8 <= FLACFrameHeader->chanAsgn && FLACFrameHeader->chanAsgn <= 10) {
|
||||
decodeSubframe(FLACMetadataBlock->bitsPerSample + (FLACFrameHeader->chanAsgn == 9 ? 1 : 0), 0);
|
||||
decodeSubframe(FLACMetadataBlock->bitsPerSample + (FLACFrameHeader->chanAsgn == 9 ? 0 : 1), 1);
|
||||
if(FLACFrameHeader->chanAsgn == 8) {
|
||||
for (int i = 0; i < m_blockSize; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[1][i] = (
|
||||
FLACsubFramesBuff->samplesBuffer[0][i] -
|
||||
FLACsubFramesBuff->samplesBuffer[1][i]);
|
||||
}
|
||||
else if (FLACFrameHeader->chanAsgn == 9) {
|
||||
for (int i = 0; i < m_blockSize; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[0][i] += FLACsubFramesBuff->samplesBuffer[1][i];
|
||||
}
|
||||
else if (FLACFrameHeader->chanAsgn == 10) {
|
||||
for (int i = 0; i < m_blockSize; i++) {
|
||||
long side = FLACsubFramesBuff->samplesBuffer[1][i];
|
||||
long right = FLACsubFramesBuff->samplesBuffer[0][i] - (side >> 1);
|
||||
FLACsubFramesBuff->samplesBuffer[1][i] = right;
|
||||
FLACsubFramesBuff->samplesBuffer[0][i] = right + side;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_e("unknown channel assignment");
|
||||
return ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT;
|
||||
}
|
||||
}
|
||||
else{
|
||||
log_e("Reserved channel assignment");
|
||||
return ERR_FLAC_RESERVED_CHANNEL_ASSIGNMENT;
|
||||
}
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeSubframe(uint8_t sampleDepth, uint8_t ch) {
|
||||
int8_t ret = 0;
|
||||
readUint(1);
|
||||
uint8_t type = readUint(6);
|
||||
int shift = readUint(1);
|
||||
if (shift == 1) {
|
||||
while (readUint(1) == 0)
|
||||
shift++;
|
||||
}
|
||||
sampleDepth -= shift;
|
||||
|
||||
if(type == 0){ // Constant coding
|
||||
int16_t s= readSignedInt(sampleDepth);
|
||||
for(int i=0; i < m_blockSize; i++){
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] = s;
|
||||
}
|
||||
}
|
||||
else if (type == 1) { // Verbatim coding
|
||||
for (int i = 0; i < m_blockSize; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] = readSignedInt(sampleDepth);
|
||||
}
|
||||
else if (8 <= type && type <= 12){
|
||||
ret = decodeFixedPredictionSubframe(type - 8, sampleDepth, ch);
|
||||
if(ret) return ret;
|
||||
}
|
||||
else if (32 <= type && type <= 63){
|
||||
ret = decodeLinearPredictiveCodingSubframe(type - 31, sampleDepth, ch);
|
||||
if(ret) return ret;
|
||||
}
|
||||
else{
|
||||
return ERR_FLAC_RESERVED_SUB_TYPE;
|
||||
}
|
||||
if(shift>0){
|
||||
for (int i = 0; i < m_blockSize; i++){
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] <<= shift;
|
||||
}
|
||||
}
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeFixedPredictionSubframe(uint8_t predOrder, uint8_t sampleDepth, uint8_t ch) {
|
||||
uint8_t ret = 0;
|
||||
for(uint8_t i = 0; i < predOrder; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] = readSignedInt(sampleDepth);
|
||||
ret = decodeResiduals(predOrder, ch);
|
||||
if(ret) return ret;
|
||||
coefs.clear();
|
||||
if(predOrder == 0) coefs.resize(0);
|
||||
if(predOrder == 1) coefs.push_back(1); // FIXED_PREDICTION_COEFFICIENTS
|
||||
if(predOrder == 2){coefs.push_back(2); coefs.push_back(-1);}
|
||||
if(predOrder == 3){coefs.push_back(3); coefs.push_back(-3); coefs.push_back(1);}
|
||||
if(predOrder == 4){coefs.push_back(4); coefs.push_back(-6); coefs.push_back(4); coefs.push_back(-1);}
|
||||
if(predOrder > 4) return ERR_FLAC_PREORDER_TOO_BIG; // Error: preorder > 4"
|
||||
restoreLinearPrediction(ch, 0);
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeLinearPredictiveCodingSubframe(int lpcOrder, int sampleDepth, uint8_t ch){
|
||||
int8_t ret = 0;
|
||||
for (int i = 0; i < lpcOrder; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] = readSignedInt(sampleDepth);
|
||||
int precision = readUint(4) + 1;
|
||||
int shift = readSignedInt(5);
|
||||
coefs.resize(0);
|
||||
for (uint8_t i = 0; i < lpcOrder; i++)
|
||||
coefs.push_back(readSignedInt(precision));
|
||||
ret = decodeResiduals(lpcOrder, ch);
|
||||
if(ret) return ret;
|
||||
restoreLinearPrediction(ch, shift);
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeResiduals(uint8_t warmup, uint8_t ch) {
|
||||
|
||||
int method = readUint(2);
|
||||
if (method >= 2)
|
||||
return ERR_FLAC_RESERVED_RESIDUAL_CODING; // Reserved residual coding method
|
||||
uint8_t paramBits = method == 0 ? 4 : 5;
|
||||
int escapeParam = (method == 0 ? 0xF : 0x1F);
|
||||
int partitionOrder = readUint(4);
|
||||
|
||||
int numPartitions = 1 << partitionOrder;
|
||||
if (m_blockSize % numPartitions != 0)
|
||||
return ERR_FLAC_WRONG_RICE_PARTITION_NR; //Error: Block size not divisible by number of Rice partitions
|
||||
int partitionSize = m_blockSize/ numPartitions;
|
||||
|
||||
for (int i = 0; i < numPartitions; i++) {
|
||||
int start = i * partitionSize + (i == 0 ? warmup : 0);
|
||||
int end = (i + 1) * partitionSize;
|
||||
|
||||
int param = readUint(paramBits);
|
||||
if (param < escapeParam) {
|
||||
for (int j = start; j < end; j++){
|
||||
FLACsubFramesBuff->samplesBuffer[ch][j] = readRiceSignedInt(param);
|
||||
}
|
||||
} else {
|
||||
int numBits = readUint(5);
|
||||
for (int j = start; j < end; j++){
|
||||
FLACsubFramesBuff->samplesBuffer[ch][j] = readSignedInt(numBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void restoreLinearPrediction(uint8_t ch, uint8_t shift) {
|
||||
|
||||
for (int i = coefs.size(); i < m_blockSize; i++) {
|
||||
int32_t sum = 0;
|
||||
for (int j = 0; j < coefs.size(); j++){
|
||||
sum += FLACsubFramesBuff->samplesBuffer[ch][i - 1 - j] * coefs[j];
|
||||
}
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] += (sum >> shift);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* flac_decoder.h
|
||||
*
|
||||
* Created on: Jul 03,2020
|
||||
* Updated on: Apr 27,2021
|
||||
*
|
||||
* Author: wolle
|
||||
*
|
||||
* Restrictions:
|
||||
* blocksize must not exceed 8192
|
||||
* bits per sample must be 8 or 16
|
||||
* num Channels must be 1 or 2
|
||||
*
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#pragma GCC optimize ("Ofast")
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define MAX_CHANNELS 2
|
||||
#define MAX_BLOCKSIZE 8192
|
||||
#define APLL_DISABLE 0
|
||||
#define EXTERNAL_I2S 0
|
||||
|
||||
|
||||
typedef struct FLACsubFramesBuff_t{
|
||||
int32_t samplesBuffer[MAX_CHANNELS][MAX_BLOCKSIZE];
|
||||
}FLACsubframesBuffer_t;
|
||||
|
||||
enum : uint8_t {FLACDECODER_INIT, FLACDECODER_READ_IN, FLACDECODER_WRITE_OUT};
|
||||
enum : uint8_t {DECODE_FRAME, DECODE_SUBFRAMES, OUT_SAMPLES};
|
||||
enum : int8_t {GIVE_NEXT_LOOP = +1,
|
||||
ERR_FLAC_NONE = 0,
|
||||
ERR_FLAC_BLOCKSIZE_TOO_BIG = -1,
|
||||
ERR_FLAC_RESERVED_BLOCKSIZE_UNSUPPORTED = -2,
|
||||
ERR_FLAC_SYNC_CODE_NOT_FOUND = -3,
|
||||
ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT = -4,
|
||||
ERR_FLAC_RESERVED_CHANNEL_ASSIGNMENT = -5,
|
||||
ERR_FLAC_RESERVED_SUB_TYPE = -6,
|
||||
ERR_FLAC_PREORDER_TOO_BIG = -7,
|
||||
ERR_FLAC_RESERVED_RESIDUAL_CODING = -8,
|
||||
ERR_FLAC_WRONG_RICE_PARTITION_NR = -9,
|
||||
ERR_FLAC_BITS_PER_SAMPLE_TOO_BIG = -10,
|
||||
ERR_FLAG_BITS_PER_SAMPLE_UNKNOWN = 11};
|
||||
|
||||
typedef struct FLACMetadataBlock_t{
|
||||
// METADATA_BLOCK_STREAMINFO
|
||||
uint16_t minblocksize; // The minimum block size (in samples) used in the stream.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// The maximum block size (in samples) used in the stream.
|
||||
uint16_t maxblocksize; // (Minimum blocksize == maximum blocksize) implies a fixed-blocksize stream.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// The minimum frame size (in bytes) used in the stream.
|
||||
uint32_t minframesize; // May be 0 to imply the value is not known.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// The maximum frame size (in bytes) used in the stream.
|
||||
uint32_t maxframesize; // May be 0 to imply the value is not known.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Sample rate in Hz. Though 20 bits are available,
|
||||
// the maximum sample rate is limited by the structure of frame headers to 655350Hz.
|
||||
uint32_t sampleRate; // Also, a value of 0 is invalid.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Number of channels FLAC supports from 1 to 8 channels
|
||||
uint8_t numChannels; // 000 : 1 channel .... 111 : 8 channels
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Sample size in bits:
|
||||
// 000 : get from STREAMINFO metadata block
|
||||
// 001 : 8 bits per sample
|
||||
// 010 : 12 bits per sample
|
||||
// 011 : reserved
|
||||
// 100 : 16 bits per sample
|
||||
// 101 : 20 bits per sample
|
||||
// 110 : 24 bits per sample
|
||||
uint8_t bitsPerSample; // 111 : reserved
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Total samples in stream. 'Samples' means inter-channel sample,
|
||||
// i.e. one second of 44.1Khz audio will have 44100 samples regardless of the number
|
||||
uint64_t totalSamples; // of channels. A value of zero here means the number of total samples is unknown.
|
||||
//----------------------------------------------------------------------------------------
|
||||
uint32_t audioDataLength;// is not the filelength, is only the length of the audio datablock in bytes
|
||||
|
||||
|
||||
|
||||
}FLACMetadataBlock_t;
|
||||
|
||||
|
||||
typedef struct FLACFrameHeader_t {
|
||||
// 0 : fixed-blocksize stream; frame header encodes the frame number
|
||||
uint8_t blockingStrategy; // 1 : variable-blocksize stream; frame header encodes the sample number
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Block size in inter-channel samples:
|
||||
// 0000 : reserved
|
||||
// 0001 : 192 samples
|
||||
// 0010-0101 : 576 * (2^(n-2)) samples, i.e. 576/1152/2304/4608
|
||||
// 0110 : get 8 bit (blocksize-1) from end of header
|
||||
// 0111 : get 16 bit (blocksize-1) from end of header
|
||||
uint8_t blockSizeCode; // 1000-1111 : 256 * (2^(n-8)) samples, i.e. 256/512/1024/2048/4096/8192/16384/32768
|
||||
//----------------------------------------------------------------------------------------
|
||||
// 0000 : get from STREAMINFO metadata block
|
||||
// 0001 : 88.2kHz
|
||||
// 0010 : 176.4kHz
|
||||
// 0011 : 192kHz
|
||||
// 0100 : 8kHz
|
||||
// 0101 : 16kHz
|
||||
// 0110 : 22.05kHz
|
||||
// 0111 : 24kHz
|
||||
// 1000 : 32kHz
|
||||
// 1001 : 44.1kHz
|
||||
// 1010 : 48kHz
|
||||
// 1011 : 96kHz
|
||||
// 1100 : get 8 bit sample rate (in kHz) from end of header
|
||||
// 1101 : get 16 bit sample rate (in Hz) from end of header
|
||||
// 1110 : get 16 bit sample rate (in tens of Hz) from end of header
|
||||
uint8_t sampleRateCode; // 1111 : invalid, to prevent sync-fooling string of 1s
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Channel assignment
|
||||
// 0000 1 channel: mono
|
||||
// 0001 2 channels: left, right
|
||||
// 0010 3 channels
|
||||
// 0011 4 channels
|
||||
// 0100 5 channels
|
||||
// 0101 6 channels
|
||||
// 0110 7 channels
|
||||
// 0111 8 channels
|
||||
// 1000 : left/side stereo: channel 0 is the left channel, channel 1 is the side(difference) channel
|
||||
// 1001 : right/side stereo: channel 0 is the side(difference) channel, channel 1 is the right channel
|
||||
// 1010 : mid/side stereo: channel 0 is the mid(average) channel, channel 1 is the side(difference) channel
|
||||
uint8_t chanAsgn; // 1011-1111 : reserved
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Sample size in bits:
|
||||
// 000 : get from STREAMINFO metadata block
|
||||
// 001 : 8 bits per sample
|
||||
// 010 : 12 bits per sample
|
||||
// 011 : reserved
|
||||
// 100 : 16 bits per sample
|
||||
// 101 : 20 bits per sample
|
||||
// 110 : 24 bits per sample
|
||||
uint8_t sampleSizeCode; // 111 : reserved
|
||||
//----------------------------------------------------------------------------------------
|
||||
uint32_t totalSamples; // totalSamplesInStream
|
||||
//----------------------------------------------------------------------------------------
|
||||
uint32_t bitrate; // bitrate
|
||||
|
||||
|
||||
}FLACFrameHeader_t;
|
||||
|
||||
int FLACFindSyncWord(unsigned char *buf, int nBytes);
|
||||
int FLACFindOggSyncWord(unsigned char *buf, int nBytes);
|
||||
int FLACparseOggHeader(unsigned char *buf);
|
||||
bool FLACDecoder_AllocateBuffers(void);
|
||||
void FLACDecoder_ClearBuffer();
|
||||
void FLACDecoder_FreeBuffers();
|
||||
void FLACSetRawBlockParams(uint8_t Chans, uint32_t SampRate, uint8_t BPS, uint32_t tsis, uint32_t AuDaLength);
|
||||
void FLACDecoderReset();
|
||||
int8_t FLACDecode(uint8_t *inbuf, int *bytesLeft, short *outbuf);
|
||||
uint16_t FLACGetOutputSamps();
|
||||
uint64_t FLACGetTotoalSamplesInStream();
|
||||
uint8_t FLACGetBitsPerSample();
|
||||
uint8_t FLACGetChannels();
|
||||
uint32_t FLACGetSampRate();
|
||||
uint32_t FLACGetBitRate();
|
||||
uint32_t FLACGetAudioFileDuration();
|
||||
uint32_t readUint(uint8_t nBits);
|
||||
int32_t readSignedInt(int nBits);
|
||||
int64_t readRiceSignedInt(uint8_t param);
|
||||
void alignToByte();
|
||||
int8_t decodeSubframes();
|
||||
int8_t decodeSubframe(uint8_t sampleDepth, uint8_t ch);
|
||||
int8_t decodeFixedPredictionSubframe(uint8_t predOrder, uint8_t sampleDepth, uint8_t ch);
|
||||
int8_t decodeLinearPredictiveCodingSubframe(int lpcOrder, int sampleDepth, uint8_t ch);
|
||||
int8_t decodeResiduals(uint8_t warmup, uint8_t ch);
|
||||
void restoreLinearPrediction(uint8_t ch, uint8_t shift);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,513 @@
|
||||
// based om helix mp3 decoder
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "assert.h"
|
||||
|
||||
static const uint8_t m_HUFF_PAIRTABS =32;
|
||||
static const uint8_t m_BLOCK_SIZE =18;
|
||||
static const uint8_t m_NBANDS =32;
|
||||
static const uint8_t m_MAX_REORDER_SAMPS =(192-126)*3; // largest critical band for short blocks (see sfBandTable)
|
||||
static const uint16_t m_VBUF_LENGTH =17*2* m_NBANDS; // for double-sized vbuf FIFO
|
||||
static const uint8_t m_MAX_SCFBD =4; // max scalefactor bands per channel
|
||||
static const uint16_t m_MAINBUF_SIZE =1940;
|
||||
static const uint8_t m_MAX_NGRAN =2; // max granules
|
||||
static const uint8_t m_MAX_NCHAN =2; // max channels
|
||||
static const uint16_t m_MAX_NSAMP =576; // max samples per channel, per granule
|
||||
|
||||
enum {
|
||||
ERR_MP3_NONE = 0,
|
||||
ERR_MP3_INDATA_UNDERFLOW = -1,
|
||||
ERR_MP3_MAINDATA_UNDERFLOW = -2,
|
||||
ERR_MP3_FREE_BITRATE_SYNC = -3,
|
||||
ERR_MP3_OUT_OF_MEMORY = -4,
|
||||
ERR_MP3_NULL_POINTER = -5,
|
||||
ERR_MP3_INVALID_FRAMEHEADER = -6,
|
||||
ERR_MP3_INVALID_SIDEINFO = -7,
|
||||
ERR_MP3_INVALID_SCALEFACT = -8,
|
||||
ERR_MP3_INVALID_HUFFCODES = -9,
|
||||
ERR_MP3_INVALID_DEQUANTIZE = -10,
|
||||
ERR_MP3_INVALID_IMDCT = -11,
|
||||
ERR_MP3_INVALID_SUBBAND = -12,
|
||||
|
||||
ERR_UNKNOWN = -9999
|
||||
};
|
||||
|
||||
typedef struct MP3FrameInfo {
|
||||
int bitrate;
|
||||
int nChans;
|
||||
int samprate;
|
||||
int bitsPerSample;
|
||||
int outputSamps;
|
||||
int layer;
|
||||
int version;
|
||||
} MP3FrameInfo_t;
|
||||
|
||||
typedef struct SFBandTable {
|
||||
int/*short*/ l[23];
|
||||
int/*short*/ s[14];
|
||||
} SFBandTable_t;
|
||||
|
||||
typedef struct BitStreamInfo {
|
||||
unsigned char *bytePtr;
|
||||
unsigned int iCache;
|
||||
int cachedBits;
|
||||
int nBytes;
|
||||
} BitStreamInfo_t;
|
||||
|
||||
typedef enum { /* map these to the corresponding 2-bit values in the frame header */
|
||||
Stereo = 0x00, /* two independent channels, but L and R frames might have different # of bits */
|
||||
Joint = 0x01, /* coupled channels - layer III: mix of M-S and intensity, Layers I/II: intensity and direct coding only */
|
||||
Dual = 0x02, /* two independent channels, L and R always have exactly 1/2 the total bitrate */
|
||||
Mono = 0x03 /* one channel */
|
||||
} StereoMode_t;
|
||||
|
||||
typedef enum { /* map to 0,1,2 to make table indexing easier */
|
||||
MPEG1 = 0,
|
||||
MPEG2 = 1,
|
||||
MPEG25 = 2
|
||||
} MPEGVersion_t;
|
||||
|
||||
typedef struct FrameHeader {
|
||||
int layer; /* layer index (1, 2, or 3) */
|
||||
int crc; /* CRC flag: 0 = disabled, 1 = enabled */
|
||||
int brIdx; /* bitrate index (0 - 15) */
|
||||
int srIdx; /* sample rate index (0 - 2) */
|
||||
int paddingBit; /* padding flag: 0 = no padding, 1 = single pad byte */
|
||||
int privateBit; /* unused */
|
||||
int modeExt; /* used to decipher joint stereo mode */
|
||||
int copyFlag; /* copyright flag: 0 = no, 1 = yes */
|
||||
int origFlag; /* original flag: 0 = copy, 1 = original */
|
||||
int emphasis; /* deemphasis mode */
|
||||
int CRCWord; /* CRC word (16 bits, 0 if crc not enabled) */
|
||||
} FrameHeader_t;
|
||||
|
||||
typedef struct SideInfoSub {
|
||||
int part23Length; /* number of bits in main data */
|
||||
int nBigvals; /* 2x this = first set of Huffman cw's (maximum amplitude can be > 1) */
|
||||
int globalGain; /* overall gain for dequantizer */
|
||||
int sfCompress; /* unpacked to figure out number of bits in scale factors */
|
||||
int winSwitchFlag; /* window switching flag */
|
||||
int blockType; /* block type */
|
||||
int mixedBlock; /* 0 = regular block (all short or long), 1 = mixed block */
|
||||
int tableSelect[3]; /* index of Huffman tables for the big values regions */
|
||||
int subBlockGain[3]; /* subblock gain offset, relative to global gain */
|
||||
int region0Count; /* 1+region0Count = num scale factor bands in first region of bigvals */
|
||||
int region1Count; /* 1+region1Count = num scale factor bands in second region of bigvals */
|
||||
int preFlag; /* for optional high frequency boost */
|
||||
int sfactScale; /* scaling of the scalefactors */
|
||||
int count1TableSelect; /* index of Huffman table for quad codewords */
|
||||
} SideInfoSub_t;
|
||||
|
||||
typedef struct SideInfo {
|
||||
int mainDataBegin;
|
||||
int privateBits;
|
||||
int scfsi[m_MAX_NCHAN][m_MAX_SCFBD]; /* 4 scalefactor bands per channel */
|
||||
} SideInfo_t;
|
||||
|
||||
typedef struct {
|
||||
int cbType; /* pure long = 0, pure short = 1, mixed = 2 */
|
||||
int cbEndS[3]; /* number nonzero short cb's, per subbblock */
|
||||
int cbEndSMax; /* max of cbEndS[] */
|
||||
int cbEndL; /* number nonzero long cb's */
|
||||
} CriticalBandInfo_t;
|
||||
|
||||
typedef struct DequantInfo {
|
||||
int workBuf[m_MAX_REORDER_SAMPS]; /* workbuf for reordering short blocks */
|
||||
} DequantInfo_t;
|
||||
|
||||
typedef struct HuffmanInfo {
|
||||
int huffDecBuf[m_MAX_NCHAN][m_MAX_NSAMP]; /* used both for decoded Huffman values and dequantized coefficients */
|
||||
int nonZeroBound[m_MAX_NCHAN]; /* number of coeffs in huffDecBuf[ch] which can be > 0 */
|
||||
int gb[m_MAX_NCHAN]; /* minimum number of guard bits in huffDecBuf[ch] */
|
||||
} HuffmanInfo_t;
|
||||
|
||||
typedef enum HuffTabType {
|
||||
noBits,
|
||||
oneShot,
|
||||
loopNoLinbits,
|
||||
loopLinbits,
|
||||
quadA,
|
||||
quadB,
|
||||
invalidTab
|
||||
} HuffTabType_t;
|
||||
|
||||
typedef struct HuffTabLookup {
|
||||
int linBits;
|
||||
int tabType; /*HuffTabType*/
|
||||
} HuffTabLookup_t;
|
||||
|
||||
typedef struct IMDCTInfo {
|
||||
int outBuf[m_MAX_NCHAN][m_BLOCK_SIZE][m_NBANDS]; /* output of IMDCT */
|
||||
int overBuf[m_MAX_NCHAN][m_MAX_NSAMP / 2]; /* overlap-add buffer (by symmetry, only need 1/2 size) */
|
||||
int numPrevIMDCT[m_MAX_NCHAN]; /* how many IMDCT's calculated in this channel on prev. granule */
|
||||
int prevType[m_MAX_NCHAN];
|
||||
int prevWinSwitch[m_MAX_NCHAN];
|
||||
int gb[m_MAX_NCHAN];
|
||||
} IMDCTInfo_t;
|
||||
|
||||
typedef struct BlockCount {
|
||||
int nBlocksLong;
|
||||
int nBlocksTotal;
|
||||
int nBlocksPrev;
|
||||
int prevType;
|
||||
int prevWinSwitch;
|
||||
int currWinSwitch;
|
||||
int gbIn;
|
||||
int gbOut;
|
||||
} BlockCount_t;
|
||||
|
||||
typedef struct ScaleFactorInfoSub { /* max bits in scalefactors = 5, so use char's to save space */
|
||||
char l[23]; /* [band] */
|
||||
char s[13][3]; /* [band][window] */
|
||||
} ScaleFactorInfoSub_t;
|
||||
|
||||
typedef struct ScaleFactorJS { /* used in MPEG 2, 2.5 intensity (joint) stereo only */
|
||||
int intensityScale;
|
||||
int slen[4];
|
||||
int nr[4];
|
||||
} ScaleFactorJS_t;
|
||||
|
||||
/* NOTE - could get by with smaller vbuf if memory is more important than speed
|
||||
* (in Subband, instead of replicating each block in FDCT32 you would do a memmove on the
|
||||
* last 15 blocks to shift them down one, a hardware style FIFO)
|
||||
*/
|
||||
typedef struct SubbandInfo {
|
||||
int vbuf[m_MAX_NCHAN * m_VBUF_LENGTH]; /* vbuf for fast DCT-based synthesis PQMF - double size for speed (no modulo indexing) */
|
||||
int vindex; /* internal index for tracking position in vbuf */
|
||||
} SubbandInfo_t;
|
||||
|
||||
typedef struct MP3DecInfo {
|
||||
/* buffer which must be large enough to hold largest possible main_data section */
|
||||
unsigned char mainBuf[m_MAINBUF_SIZE];
|
||||
/* special info for "free" bitrate files */
|
||||
int freeBitrateFlag;
|
||||
int freeBitrateSlots;
|
||||
/* user-accessible info */
|
||||
int bitrate;
|
||||
int nChans;
|
||||
int samprate;
|
||||
int nGrans; /* granules per frame */
|
||||
int nGranSamps; /* samples per granule */
|
||||
int nSlots;
|
||||
int layer;
|
||||
|
||||
int mainDataBegin;
|
||||
int mainDataBytes;
|
||||
int part23Length[m_MAX_NGRAN][m_MAX_NCHAN];
|
||||
} MP3DecInfo_t;
|
||||
|
||||
|
||||
|
||||
|
||||
/* format = Q31
|
||||
* #define M_PI 3.14159265358979323846
|
||||
* double u = 2.0 * M_PI / 9.0;
|
||||
* float c0 = sqrt(3.0) / 2.0;
|
||||
* float c1 = cos(u);
|
||||
* float c2 = cos(2*u);
|
||||
* float c3 = sin(u);
|
||||
* float c4 = sin(2*u);
|
||||
*/
|
||||
|
||||
const int c9_0 = 0x6ed9eba1;
|
||||
const int c9_1 = 0x620dbe8b;
|
||||
const int c9_2 = 0x163a1a7e;
|
||||
const int c9_3 = 0x5246dd49;
|
||||
const int c9_4 = 0x7e0e2e32;
|
||||
|
||||
|
||||
|
||||
const int c3_0 = 0x6ed9eba1; /* format = Q31, cos(pi/6) */
|
||||
const int c6[3] = { 0x7ba3751d, 0x5a82799a, 0x2120fb83 }; /* format = Q31, cos(((0:2) + 0.5) * (pi/6)) */
|
||||
|
||||
/* format = Q31
|
||||
* cos(((0:8) + 0.5) * (pi/18))
|
||||
*/
|
||||
const uint32_t c18[9] = { 0x7f834ed0, 0x7ba3751d, 0x7401e4c1, 0x68d9f964, 0x5a82799a, 0x496af3e2, 0x36185aee, 0x2120fb83, 0x0b27eb5c};
|
||||
|
||||
/* scale factor lengths (num bits) */
|
||||
const char m_SFLenTab[16][2] = { {0, 0}, {0, 1}, {0, 2}, {0, 3}, {3, 0}, {1, 1}, {1, 2}, {1, 3},
|
||||
{2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}, {4, 2}, {4, 3}};
|
||||
|
||||
/* NRTab[size + 3*is_right][block type][partition]
|
||||
* block type index: 0 = (bt0,bt1,bt3), 1 = bt2 non-mixed, 2 = bt2 mixed
|
||||
* partition: scale factor groups (sfb1 through sfb4)
|
||||
* for block type = 2 (mixed or non-mixed) / by 3 is rolled into this table
|
||||
* (for 3 short blocks per long block)
|
||||
* see 2.4.3.2 in MPEG 2 (low sample rate) spec
|
||||
* stuff rolled into this table:
|
||||
* NRTab[x][1][y] --> (NRTab[x][1][y]) / 3
|
||||
* NRTab[x][2][>=1] --> (NRTab[x][2][>=1]) / 3 (first partition is long block)
|
||||
*/
|
||||
const char NRTab[6][3][4] = {
|
||||
{{ 6, 5, 5, 5}, {3, 3, 3, 3}, {6, 3, 3, 3}},
|
||||
{{ 6, 5, 7, 3}, {3, 3, 4, 2}, {6, 3, 4, 2}},
|
||||
{{11, 10, 0, 0}, {6, 6, 0, 0}, {6, 3, 6, 0}},
|
||||
{{ 7, 7, 7, 0}, {4, 4, 4, 0}, {6, 5, 4, 0}},
|
||||
{{ 6, 6, 6, 3}, {4, 3, 3, 2}, {6, 4, 3, 2}},
|
||||
{{ 8, 8, 5, 0}, {5, 4, 3, 0}, {6, 6, 3, 0}}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* optional pre-emphasis for high-frequency scale factor bands */
|
||||
const char preTab[22] = { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0 };
|
||||
|
||||
/* pow(2,-i/4) for i=0..3, Q31 format */
|
||||
const int pow14[4] PROGMEM = {
|
||||
0x7fffffff, 0x6ba27e65, 0x5a82799a, 0x4c1bf829
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Minimax polynomial approximation to pow(x, 4/3), over the range
|
||||
* poly43lo: x = [0.5, 0.7071]
|
||||
* poly43hi: x = [0.7071, 1.0]
|
||||
*
|
||||
* Relative error < 1E-7
|
||||
* Coefs are scaled by 4, 2, 1, 0.5, 0.25
|
||||
*/
|
||||
const unsigned int poly43lo[5] PROGMEM = { 0x29a0bda9, 0xb02e4828, 0x5957aa1b, 0x236c498d, 0xff581859 };
|
||||
const unsigned int poly43hi[5] PROGMEM = { 0x10852163, 0xd333f6a4, 0x46e9408b, 0x27c2cef0, 0xfef577b4 };
|
||||
|
||||
/* pow(2, i*4/3) as exp and frac */
|
||||
const int pow2exp[8] PROGMEM = { 14, 13, 11, 10, 9, 7, 6, 5 };
|
||||
|
||||
const int pow2frac[8] PROGMEM = {
|
||||
0x6597fa94, 0x50a28be6, 0x7fffffff, 0x6597fa94,
|
||||
0x50a28be6, 0x7fffffff, 0x6597fa94, 0x50a28be6
|
||||
};
|
||||
|
||||
const uint16_t m_HUFF_OFFSET_01= 0;
|
||||
const uint16_t m_HUFF_OFFSET_02= 9 + m_HUFF_OFFSET_01;
|
||||
const uint16_t m_HUFF_OFFSET_03= 65 + m_HUFF_OFFSET_02;
|
||||
const uint16_t m_HUFF_OFFSET_05= 65 + m_HUFF_OFFSET_03;
|
||||
const uint16_t m_HUFF_OFFSET_06=257 + m_HUFF_OFFSET_05;
|
||||
const uint16_t m_HUFF_OFFSET_07=129 + m_HUFF_OFFSET_06;
|
||||
const uint16_t m_HUFF_OFFSET_08=110 + m_HUFF_OFFSET_07;
|
||||
const uint16_t m_HUFF_OFFSET_09=280 + m_HUFF_OFFSET_08;
|
||||
const uint16_t m_HUFF_OFFSET_10= 93 + m_HUFF_OFFSET_09;
|
||||
const uint16_t m_HUFF_OFFSET_11=320 + m_HUFF_OFFSET_10;
|
||||
const uint16_t m_HUFF_OFFSET_12=296 + m_HUFF_OFFSET_11;
|
||||
const uint16_t m_HUFF_OFFSET_13=185 + m_HUFF_OFFSET_12;
|
||||
const uint16_t m_HUFF_OFFSET_15=497 + m_HUFF_OFFSET_13;
|
||||
const uint16_t m_HUFF_OFFSET_16=580 + m_HUFF_OFFSET_15;
|
||||
const uint16_t m_HUFF_OFFSET_24=651 + m_HUFF_OFFSET_16;
|
||||
|
||||
const int huffTabOffset[m_HUFF_PAIRTABS] PROGMEM = {
|
||||
0, m_HUFF_OFFSET_01, m_HUFF_OFFSET_02, m_HUFF_OFFSET_03,
|
||||
0, m_HUFF_OFFSET_05, m_HUFF_OFFSET_06, m_HUFF_OFFSET_07,
|
||||
m_HUFF_OFFSET_08, m_HUFF_OFFSET_09, m_HUFF_OFFSET_10, m_HUFF_OFFSET_11,
|
||||
m_HUFF_OFFSET_12, m_HUFF_OFFSET_13, 0, m_HUFF_OFFSET_15,
|
||||
m_HUFF_OFFSET_16, m_HUFF_OFFSET_16, m_HUFF_OFFSET_16, m_HUFF_OFFSET_16,
|
||||
m_HUFF_OFFSET_16, m_HUFF_OFFSET_16, m_HUFF_OFFSET_16, m_HUFF_OFFSET_16,
|
||||
m_HUFF_OFFSET_24, m_HUFF_OFFSET_24, m_HUFF_OFFSET_24, m_HUFF_OFFSET_24,
|
||||
m_HUFF_OFFSET_24, m_HUFF_OFFSET_24, m_HUFF_OFFSET_24, m_HUFF_OFFSET_24,};
|
||||
|
||||
const HuffTabLookup_t huffTabLookup[m_HUFF_PAIRTABS] PROGMEM = {
|
||||
{ 0, noBits },
|
||||
{ 0, oneShot },
|
||||
{ 0, oneShot },
|
||||
{ 0, oneShot },
|
||||
{ 0, invalidTab },
|
||||
{ 0, oneShot },
|
||||
{ 0, oneShot },
|
||||
{ 0, loopNoLinbits },
|
||||
{ 0, loopNoLinbits },
|
||||
{ 0, loopNoLinbits },
|
||||
{ 0, loopNoLinbits },
|
||||
{ 0, loopNoLinbits },
|
||||
{ 0, loopNoLinbits },
|
||||
{ 0, loopNoLinbits },
|
||||
{ 0, invalidTab },
|
||||
{ 0, loopNoLinbits },
|
||||
{ 1, loopLinbits },
|
||||
{ 2, loopLinbits },
|
||||
{ 3, loopLinbits },
|
||||
{ 4, loopLinbits },
|
||||
{ 6, loopLinbits },
|
||||
{ 8, loopLinbits },
|
||||
{ 10, loopLinbits },
|
||||
{ 13, loopLinbits },
|
||||
{ 4, loopLinbits },
|
||||
{ 5, loopLinbits },
|
||||
{ 6, loopLinbits },
|
||||
{ 7, loopLinbits },
|
||||
{ 8, loopLinbits },
|
||||
{ 9, loopLinbits },
|
||||
{ 11, loopLinbits },
|
||||
{ 13, loopLinbits },
|
||||
};
|
||||
|
||||
|
||||
const int quadTabOffset[2] PROGMEM = {0, 64};
|
||||
const int quadTabMaxBits[2] PROGMEM = {6, 4};
|
||||
|
||||
/* indexing = [version][samplerate index]
|
||||
* sample rate of frame (Hz)
|
||||
*/
|
||||
const int samplerateTab[3][3] PROGMEM = {
|
||||
{ 44100, 48000, 32000 }, /* MPEG-1 */
|
||||
{ 22050, 24000, 16000 }, /* MPEG-2 */
|
||||
{ 11025, 12000, 8000 }, /* MPEG-2.5 */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* indexing = [version][layer]
|
||||
* number of samples in one frame (per channel)
|
||||
*/
|
||||
const int/*short*/samplesPerFrameTab[3][3] PROGMEM = { { 384, 1152, 1152 }, /* MPEG1 */
|
||||
{ 384, 1152, 576 }, /* MPEG2 */
|
||||
{ 384, 1152, 576 }, /* MPEG2.5 */
|
||||
};
|
||||
|
||||
/* layers 1, 2, 3 */
|
||||
const short bitsPerSlotTab[3] = { 32, 8, 8 };
|
||||
|
||||
/* indexing = [version][mono/stereo]
|
||||
* number of bytes in side info section of bitstream
|
||||
*/
|
||||
const int/*short*/sideBytesTab[3][2] PROGMEM = { { 17, 32 }, /* MPEG-1: mono, stereo */
|
||||
{ 9, 17 }, /* MPEG-2: mono, stereo */
|
||||
{ 9, 17 }, /* MPEG-2.5: mono, stereo */
|
||||
};
|
||||
|
||||
/* indexing = [version][sampleRate][long (.l) or short (.s) block]
|
||||
* sfBandTable[v][s].l[cb] = index of first bin in critical band cb (long blocks)
|
||||
* sfBandTable[v][s].s[cb] = index of first bin in critical band cb (short blocks)
|
||||
*/
|
||||
const SFBandTable_t sfBandTable[3][3] PROGMEM = {
|
||||
{ /* MPEG-1 (44, 48, 32 kHz) */
|
||||
{ {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576 },
|
||||
{0, 4, 8, 12, 16, 22, 30, 40, 52, 66, 84, 106, 136, 192} },
|
||||
{ {0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576 },
|
||||
{0, 4, 8, 12, 16, 22, 28, 38, 50, 64, 80, 100, 126, 192} },
|
||||
{ {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576 },
|
||||
{0, 4, 8, 12, 16, 22, 30, 42, 58, 78, 104, 138, 180, 192} } },
|
||||
{ /* MPEG-2 (22, 24, 16 kHz) */
|
||||
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
|
||||
{0, 4, 8, 12, 18, 24, 32, 42, 56, 74, 100, 132, 174, 192} },
|
||||
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 332, 394, 464, 540, 576 },
|
||||
{0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 136, 180, 192} },
|
||||
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
|
||||
{0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192} }, },
|
||||
{ /* MPEG-2.5 (11, 12, 8 kHz) */
|
||||
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
|
||||
{0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192 } },
|
||||
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
|
||||
{0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192 } },
|
||||
{ {0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, 232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576 },
|
||||
{0, 8, 16, 24, 36, 52, 72, 96, 124, 160, 162, 164, 166, 192 } }, },
|
||||
};
|
||||
|
||||
|
||||
/* indexing = [intensity scale on/off][left/right]
|
||||
* format = Q30, range = [0.0, 1.414]
|
||||
*
|
||||
* illegal intensity position scalefactors (see comments on ISFMpeg1)
|
||||
*/
|
||||
const int ISFIIP[2][2] PROGMEM = {
|
||||
{0x40000000, 0x00000000}, /* mid-side off */
|
||||
{0x40000000, 0x40000000}, /* mid-side on */
|
||||
};
|
||||
|
||||
const unsigned char uniqueIDTab[8] = {0x5f, 0x4b, 0x43, 0x5f, 0x5f, 0x4a, 0x52, 0x5f};
|
||||
|
||||
/* anti-alias coefficients - see spec Annex B, table 3-B.9
|
||||
* csa[0][i] = CSi, csa[1][i] = CAi
|
||||
* format = Q31
|
||||
*/
|
||||
const uint32_t csa[8][2] PROGMEM = {
|
||||
{0x6dc253f0, 0xbe2500aa},
|
||||
{0x70dcebe4, 0xc39e4949},
|
||||
{0x798d6e73, 0xd7e33f4a},
|
||||
{0x7ddd40a7, 0xe8b71176},
|
||||
{0x7f6d20b7, 0xf3e4fe2f},
|
||||
{0x7fe47e40, 0xfac1a3c7},
|
||||
{0x7ffcb263, 0xfe2ebdc6},
|
||||
{0x7fffc694, 0xff86c25d},
|
||||
};
|
||||
|
||||
/* format = Q30, right shifted by 12 (sign bits only in top 12 - undo this when rounding to short)
|
||||
* this is to enable early-terminating multiplies on ARM
|
||||
* range = [-1.144287109, 1.144989014]
|
||||
* max gain of filter (per output sample) ~= 2.731
|
||||
*
|
||||
* new (properly sign-flipped) values
|
||||
* - these actually are correct to 32 bits, (floating-pt coefficients in spec
|
||||
* chosen such that only ~20 bits are required)
|
||||
*
|
||||
* Reordering - see table 3-B.3 in spec (appendix B)
|
||||
*
|
||||
* polyCoef[i] =
|
||||
* D[ 0, 32, 64, ... 480], i = [ 0, 15]
|
||||
* D[ 1, 33, 65, ... 481], i = [ 16, 31]
|
||||
* D[ 2, 34, 66, ... 482], i = [ 32, 47]
|
||||
* ...
|
||||
* D[15, 47, 79, ... 495], i = [240,255]
|
||||
*
|
||||
* also exploits symmetry: D[i] = -D[512 - i], for i = [1, 255]
|
||||
*
|
||||
* polyCoef[256, 257, ... 263] are for special case of sample 16 (out of 0)
|
||||
* see PolyphaseStereo() and PolyphaseMono()
|
||||
*/
|
||||
|
||||
// prototypes
|
||||
bool MP3Decoder_AllocateBuffers(void);
|
||||
void MP3Decoder_FreeBuffers();
|
||||
int MP3Decode( unsigned char *inbuf, int *bytesLeft, short *outbuf, int useSize);
|
||||
void MP3GetLastFrameInfo();
|
||||
int MP3GetNextFrameInfo(unsigned char *buf);
|
||||
int MP3FindSyncWord(unsigned char *buf, int nBytes);
|
||||
int MP3GetSampRate();
|
||||
int MP3GetChannels();
|
||||
int MP3GetBitsPerSample();
|
||||
int MP3GetBitrate();
|
||||
int MP3GetOutputSamps();
|
||||
|
||||
//internally used
|
||||
void MP3Decoder_ClearBuffer(void);
|
||||
void PolyphaseMono(short *pcm, int *vbuf, const uint32_t *coefBase);
|
||||
void PolyphaseStereo(short *pcm, int *vbuf, const uint32_t *coefBase);
|
||||
void SetBitstreamPointer(BitStreamInfo_t *bsi, int nBytes, unsigned char *buf);
|
||||
unsigned int GetBits(BitStreamInfo_t *bsi, int nBits);
|
||||
int CalcBitsUsed(BitStreamInfo_t *bsi, unsigned char *startBuf, int startOffset);
|
||||
int DequantChannel(int *sampleBuf, int *workBuf, int *nonZeroBound, SideInfoSub_t *sis, ScaleFactorInfoSub_t *sfis, CriticalBandInfo_t *cbi);
|
||||
void MidSideProc(int x[m_MAX_NCHAN][m_MAX_NSAMP], int nSamps, int mOut[2]);
|
||||
void IntensityProcMPEG1(int x[m_MAX_NCHAN][m_MAX_NSAMP], int nSamps, ScaleFactorInfoSub_t *sfis, CriticalBandInfo_t *cbi, int midSideFlag, int mixFlag, int mOut[2]);
|
||||
void IntensityProcMPEG2(int x[m_MAX_NCHAN][m_MAX_NSAMP], int nSamps, ScaleFactorInfoSub_t *sfis, CriticalBandInfo_t *cbi, ScaleFactorJS_t *sfjs, int midSideFlag, int mixFlag, int mOut[2]);
|
||||
void FDCT32(int *x, int *d, int offset, int oddBlock, int gb);// __attribute__ ((section (".data")));
|
||||
void FreeBuffers();
|
||||
int CheckPadBit();
|
||||
int UnpackFrameHeader(unsigned char *buf);
|
||||
int UnpackSideInfo(unsigned char *buf);
|
||||
int DecodeHuffman( unsigned char *buf, int *bitOffset, int huffBlockBits, int gr, int ch);
|
||||
int MP3Dequantize( int gr);
|
||||
int IMDCT( int gr, int ch);
|
||||
int UnpackScaleFactors( unsigned char *buf, int *bitOffset, int bitsAvail, int gr, int ch);
|
||||
int Subband(short *pcmBuf);
|
||||
short ClipToShort(int x, int fracBits);
|
||||
void RefillBitstreamCache(BitStreamInfo_t *bsi);
|
||||
void UnpackSFMPEG1(BitStreamInfo_t *bsi, SideInfoSub_t *sis, ScaleFactorInfoSub_t *sfis, int *scfsi, int gr, ScaleFactorInfoSub_t *sfisGr0);
|
||||
void UnpackSFMPEG2(BitStreamInfo_t *bsi, SideInfoSub_t *sis, ScaleFactorInfoSub_t *sfis, int gr, int ch, int modeExt, ScaleFactorJS_t *sfjs);
|
||||
int MP3FindFreeSync(unsigned char *buf, unsigned char firstFH[4], int nBytes);
|
||||
void MP3ClearBadFrame( short *outbuf);
|
||||
int DecodeHuffmanPairs(int *xy, int nVals, int tabIdx, int bitsLeft, unsigned char *buf, int bitOffset);
|
||||
int DecodeHuffmanQuads(int *vwxy, int nVals, int tabIdx, int bitsLeft, unsigned char *buf, int bitOffset);
|
||||
int DequantBlock(int *inbuf, int *outbuf, int num, int scale);
|
||||
void AntiAlias(int *x, int nBfly);
|
||||
void WinPrevious(int *xPrev, int *xPrevWin, int btPrev);
|
||||
int FreqInvertRescale(int *y, int *xPrev, int blockIdx, int es);
|
||||
void idct9(int *x);
|
||||
int IMDCT36(int *xCurr, int *xPrev, int *y, int btCurr, int btPrev, int blockIdx, int gb);
|
||||
void imdct12(int *x, int *out);
|
||||
int IMDCT12x3(int *xCurr, int *xPrev, int *y, int btPrev, int blockIdx, int gb);
|
||||
int HybridTransform(int *xCurr, int *xPrev, int y[m_BLOCK_SIZE][m_NBANDS], SideInfoSub_t *sis, BlockCount_t *bc);
|
||||
inline uint64_t SAR64(uint64_t x, int n) {return x >> n;}
|
||||
inline int MULSHIFT32(int x, int y) { int z; z = (uint64_t) x * (uint64_t) y >> 32; return z;}
|
||||
inline uint64_t MADD64(uint64_t sum64, int x, int y) {sum64 += (uint64_t) x * (uint64_t) y; return sum64;}/* returns 64-bit value in [edx:eax] */
|
||||
inline uint64_t xSAR64(uint64_t x, int n){return x >> n;}
|
||||
inline int FASTABS(int x){ return __builtin_abs(x);} //xtensa has a fast abs instruction //fb
|
||||
#define CLZ(x) __builtin_clz(x) //fb
|
Reference in New Issue
Block a user