diff --git a/.gitmodules b/.gitmodules index aa05213..84ec047 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,7 +16,7 @@ branch = main [submodule "components/esp32-component-appfs"] path = components/appfs - url = https://github.com/badgeteam/esp32-component-appfs.git + url = git@github.com:hnzlmnn/esp32-component-appfs.git branch = master [submodule "components/ws2812"] path = components/ws2812 @@ -51,3 +51,6 @@ [submodule "components/pax-keyboard"] path = components/pax-keyboard url = https://github.com/robotman2412/pax-keyboard.git +[submodule "components/spi-st77xx"] + path = components/spi-st77xx + url = git@github.com:badgeteam/eps32-component-spi-st77xx.git diff --git a/README.md b/README.md index 4ddf4b0..19c1c52 100644 --- a/README.md +++ b/README.md @@ -14,22 +14,24 @@ The source code contained in this repository is licensed under terms of the MIT Some source code is licensed separately, please check the following table for details. -| Location | Version | License | Author | -|-------------------------|---------|-----------------------------------|-------------------------------------------------------------------------------------------------| -| esp-idf | 4.4.7 | Apache License 2.0 | Espressif Systems (Shanghai) CO LTD | -| components/appfs | | THE BEER-WARE LICENSE Revision 42 | Jeroen Domburg | -| components/bus-i2c | | MIT | Nicolai Electronics | -| components/i2c-pca9555 | | MIT | Renze Nicolai and Malte Heinzelmann | -| components/keyboard | | MIT | Malte Heinzelmann | -| components/pax-graphics | | MIT | Julian Scheffers | -| components/pax-keyboard | | MIT | Julian Scheffers | -| components/sdcard | | MIT | Nicolai Electronics | -| components/spi-ili9341 | | MIT | Nicolai Electronics | -| components/spi-st25r3911b/en.STSW-ST25RFAL001 | 2.10.0 | SLA0051 MyLiberty | STMicroelectronics | -| components/ws2812 | | MIT | Unlicense / Public domain | -| tools/[libusb-1.0.dll] | | GNU LGPL 2.1 | See the [AUTHORS](https://github.com/libusb/libusb/blob/master/AUTHORS) document of the project | +| Location | Version | License | Author | +|-----------------------------------------------|---------|-----------------------------------|-------------------------------------------------------------------------------------------------| +| esp-idf | 4.4.7 | Apache License 2.0 | Espressif Systems (Shanghai) CO LTD | +| components/appfs | | THE BEER-WARE LICENSE Revision 42 | Jeroen Domburg | +| components/bus-i2c | | MIT | Nicolai Electronics | +| components/i2c-pca9555 | | MIT | Renze Nicolai and Malte Heinzelmann | +| components/keyboard | | MIT | Malte Heinzelmann | +| components/pax-graphics | | MIT | Julian Scheffers | +| components/pax-keyboard | | MIT | Julian Scheffers | +| components/sdcard | | MIT | Nicolai Electronics | +| components/spi-ili9341 | | MIT | Nicolai Electronics | +| components/spi-st25r3911b/en.STSW-ST25RFAL001 | 2.10.0 | SLA0051 MyLiberty | STMicroelectronics | +| components/ws2812 | | MIT | Unlicense / Public domain | +| components/[qrcode] | | MIT | Project Nayuki | +| tools/[libusb-1.0.dll] | | GNU LGPL 2.1 | See the [AUTHORS](https://github.com/libusb/libusb/blob/master/AUTHORS) document of the project | [libusb-1.0.dll]: https://libusb.info +[qrcode]: https://www.nayuki.io/page/qr-code-generator-library Some of the icons in `resources/icons` are licensed under MIT license `Copyright (c) 2019-2021 The Bootstrap Authors`. The source files for these icons can be found at https://icons.getbootstrap.com/. diff --git a/burn_id_manual.sh b/burn_id_manual.sh new file mode 100755 index 0000000..9f689bf --- /dev/null +++ b/burn_id_manual.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Always fail hard +set -e + +i="$1" + +read -p "Ready to burn $i. Press any key to continue..." + +verify=`espefuse.py dump | grep BLOCK2 | cut -c 56-60 | python -c 'import sys,struct; print(struct.unpack("H", int(sys.argv[1])) + bytes([0]*30)); f.close()' $i + +# Burn fuses +espefuse.py burn_block_data BLOCK2 id.bin --do-not-confirm > /dev/null + +# Verify fuses +verify=`espefuse.py dump | grep BLOCK2 | cut -c 56-60 | python -c 'import sys,struct; print(struct.unpack(" +#include + +struct Config { + uint8_t volume; +} config; + +static QueueHandle_t soundQueue; +static int soundRunning = 0; + +void driver_i2s_sound_start(i2s_pin_config_t *pin_config) { + config.volume = 128; + + i2s_config_t cfg = {.mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .dma_buf_count = 8, + .dma_buf_len = 256, + .intr_alloc_flags = 0, + .use_apll = false, + .bits_per_chan = I2S_BITS_PER_SAMPLE_16BIT}; + + i2s_driver_install(0, &cfg, 4, &soundQueue); + i2s_set_pin(0, pin_config); + soundRunning = 1; +} + +void driver_i2s_sound_stop() { i2s_driver_uninstall(0); } + +#define SND_CHUNKSZ 32 +void driver_i2s_sound_push(int16_t *buf, int len, int stereo_input) { + int16_t tmpb[SND_CHUNKSZ * 2]; + int i = 0; + while (i < len) { + int plen = len - i; + if (plen > SND_CHUNKSZ) { + plen = SND_CHUNKSZ; + } + for (int sample = 0; sample < plen; sample++) { + int32_t s[2] = {0, 0}; + if (stereo_input) { + s[0] = buf[(i + sample) * 2 + 0]; + s[1] = buf[(i + sample) * 2 + 1]; + } else { + s[0] = s[1] = buf[i + sample]; + } + + // Multiply with volume/volume_max, resulting in signed integers with range [INT16_MIN:INT16_MAX] +// s[0] = (s[0] * config.volume / 255); +// s[1] = (s[1] * config.volume / 255); + +//#ifdef CONFIG_DRIVER_SNDMIXER_I2S_DATA_FORMAT_UNSIGNED + // Offset to [0:UINT16_MAX] store as unsigned integers +// s[0] -= INT16_MIN; +// s[1] -= INT16_MIN; +//#endif + tmpb[(i + sample) * 2 + 0] = s[0]; + tmpb[(i + sample) * 2 + 1] = s[1]; + + } + size_t bytes_written; + i2s_write(0, (char *) tmpb, plen * 2 * sizeof(tmpb[0]), &bytes_written, portMAX_DELAY); + i += plen; + } +} + +void driver_i2s_set_volume(uint8_t new_volume) { + // xSemaphoreTake(configMux, portMAX_DELAY); + config.volume = new_volume; + // xSemaphoreGive(configMux); +} + +uint8_t driver_i2s_get_volume() { return config.volume; } + +void driver_i2s_sound_mute(int doMute) { + if (doMute) { + dac_i2s_disable(); + } else { + dac_i2s_enable(); + } +} diff --git a/components/driver_sndmixer/driver_i2s.h b/components/driver_sndmixer/driver_i2s.h new file mode 100644 index 0000000..b8c5b72 --- /dev/null +++ b/components/driver_sndmixer/driver_i2s.h @@ -0,0 +1,31 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "driver/i2s.h" +#include "esp_sleep.h" + +#include "driver/gpio.h" +#include "driver/adc.h" +#include "driver/dac.h" +#include "soc/rtc_cntl_reg.h" + +// Start audio output driver +void driver_i2s_sound_start(i2s_pin_config_t* pin_config); + +// Stop audio output driver +void driver_i2s_sound_stop(); + +// Push audio to the driver +void driver_i2s_sound_push(int16_t *buf, int len, int stereo); + +// Set the volume (0-255) +void driver_i2s_set_volume(uint8_t new_volume); + +// Get the volume +uint8_t driver_i2s_get_volume(); + +// Mute audio output +void driver_i2s_sound_mute(int doMute); diff --git a/components/driver_sndmixer/libhelix-mp3/LICENSE.txt b/components/driver_sndmixer/libhelix-mp3/LICENSE.txt new file mode 100644 index 0000000..12e5372 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/LICENSE.txt @@ -0,0 +1,30 @@ + Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + + The contents of this directory, and (except where otherwise + indicated) the directories included within this directory, are + subject to the current version of the RealNetworks Public Source + License (the "RPSL") available at RPSL.txt in this directory, unless + you have licensed the directory under the current version of the + RealNetworks Community Source License (the "RCSL") available at + RCSL.txt in this directory, in which case the RCSL will apply. You + may also obtain the license terms directly from RealNetworks. You + may not use the files in this directory except in compliance with the + RPSL or, if you have a valid RCSL with RealNetworks applicable to + this directory, the RCSL. Please see the applicable RPSL or RCSL for + the rights, obligations and limitations governing use of the contents + of the directory. + + This directory is part of the Helix DNA Technology. RealNetworks is + the developer of the Original Code and owns the copyrights in the + portions it created. + + This directory, and the directories included with this directory, are + distributed and made available on an 'AS IS' basis, WITHOUT WARRANTY + OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY + DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, + QUIET ENJOYMENT OR NON-INFRINGEMENT. + + Technology Compatibility Kit Test Suite(s) Location: + http://www.helixcommunity.org/content/tck + diff --git a/components/driver_sndmixer/libhelix-mp3/RCSL.txt b/components/driver_sndmixer/libhelix-mp3/RCSL.txt new file mode 100644 index 0000000..a809759 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/RCSL.txt @@ -0,0 +1,948 @@ +The RCSL is made up of a base agreement and a few Attachments. + +For Research and Development use, you agree to the terms of the +RCSL R&D License (base RCSL and Attachments A, B, and C) + +For Commercial Use (either distribution or internal commercial +deployment) of the Helix DNA with or without support for RealNetworks' +RealAudio and RealVideo Add-on Technology, you agree to the +terms of the same RCSL R&D license +and execute one or more additional Commercial Use License attachments +. + +------------------------------------------------------------------------ + + + REALNETWORKS COMMUNITY SOURCE LICENSE + +Version 1.2 (Rev. Date: January 22, 2003). + + + RECITALS + +Original Contributor has developed Specifications, Source Code +implementations and Executables of certain Technology; and + +Original Contributor desires to license the Technology to a large +community to facilitate research, innovation and product development +while maintaining compatibility of such products with the Technology as +delivered by Original Contributor; and + +Original Contributor desires to license certain Trademarks for the +purpose of branding products that are compatible with the relevant +Technology delivered by Original Contributor; and + +You desire to license the Technology and possibly certain Trademarks +from Original Contributor on the terms and conditions specified in this +License. + +In consideration for the mutual covenants contained herein, You and +Original Contributor agree as follows: + + + AGREEMENT + +*1. Introduction.* + +The RealNetworks Community Source License ("RCSL") and effective +attachments ("License") may include five distinct licenses: + +i) Research Use license -- License plus Attachments A, B and C only. + +ii) Commercial Use and Trademark License, which may be for Internal +Deployment Use or external distribution, or both -- License plus +Attachments A, B, C, and D. + +iii) Technology Compatibility Kit (TCK) license -- Attachment C. + +iv) Add-On Technology License (Executable) Commercial Use License +-Attachment F. + +v) Add-On Technology Source Code Porting and Optimization +License-Attachment G. + +The Research Use license is effective when You click and accept this +License. The TCK is effective when You click and accept this License, +unless otherwise specified in the TCK attachments. The Commercial Use +and Trademark, Add-On Technology License, and the Add-On Technology +Source Code Porting and Optimization licenses must each be signed by You +and Original Contributor to become effective. Once effective, these +licenses and the associated requirements and responsibilities are +cumulative. Capitalized terms used in this License are defined in the +Glossary. + +*2. License Grants.* + +2.1 Original Contributor Grant. + +Subject to Your compliance with Sections 3, 8.10 and Attachment A of +this License, Original Contributor grants to You a worldwide, +royalty-free, non-exclusive license, to the extent of Original +Contributor's Intellectual Property Rights covering the Original Code, +Upgraded Code and Specifications, to do the following: + +(a) Research Use License: + +(i) use, reproduce and modify the Original Code, Upgraded Code and +Specifications to create Modifications and Reformatted Specifications +for Research Use by You; + +(ii) publish and display Original Code, Upgraded Code and Specifications +with, or as part of Modifications, as permitted under Section 3.1(b) below; + +(iii) reproduce and distribute copies of Original Code and Upgraded Code +to Licensees and students for Research Use by You; + +(iv) compile, reproduce and distribute Original Code and Upgraded Code +in Executable form, and Reformatted Specifications to anyone for +Research Use by You. + +(b) Other than the licenses expressly granted in this License, Original +Contributor retains all right, title, and interest in Original Code and +Upgraded Code and Specifications. + +2.2 Your Grants. + +(a) To Other Licensees. You hereby grant to each Licensee a license to +Your Error Corrections and Shared Modifications, of the same scope and +extent as Original Contributor's licenses under Section 2.1 a) above +relative to Research Use and Attachment D relative to Commercial Use. + +(b) To Original Contributor. You hereby grant to Original Contributor a +worldwide, royalty-free, non-exclusive, perpetual and irrevocable +license, to the extent of Your Intellectual Property Rights covering +Your Error Corrections, Shared Modifications and Reformatted +Specifications, to use, reproduce, modify, display and distribute Your +Error Corrections, Shared Modifications and Reformatted Specifications, +in any form, including the right to sublicense such rights through +multiple tiers of distribution. + +(c) Other than the licenses expressly granted in Sections 2.2(a) and (b) +above, and the restrictions set forth in Section 3.1(d)(iv) below, You +retain all right, title, and interest in Your Error Corrections, Shared +Modifications and Reformatted Specifications. + +2.3 Contributor Modifications. + +You may use, reproduce, modify, display and distribute Contributor Error +Corrections, Shared Modifications and Reformatted Specifications, +obtained by You under this License, to the same scope and extent as with +Original Code, Upgraded Code and Specifications. + +2.4 Subcontracting. + +You may deliver the Source Code of Covered Code to other Licensees +having at least a Research Use license, for the sole purpose of +furnishing development services to You in connection with Your rights +granted in this License. All such Licensees must execute appropriate +documents with respect to such work consistent with the terms of this +License, and acknowledging their work-made-for-hire status or assigning +exclusive right to the work product and associated Intellectual Property +Rights to You. + +*3. Requirements and Responsibilities*. + +3.1 Research Use License. + +As a condition of exercising the rights granted under Section 2.1(a) +above, You agree to comply with the following: + +(a) Your Contribution to the Community. All Error Corrections and Shared +Modifications which You create or contribute to are automatically +subject to the licenses granted under Section 2.2 above. You are +encouraged to license all of Your other Modifications under Section 2.2 +as Shared Modifications, but are not required to do so. You agree to +notify Original Contributor of any errors in the Specification. + +(b) Source Code Availability. You agree to provide all Your Error +Corrections to Original Contributor as soon as reasonably practicable +and, in any event, prior to Internal Deployment Use or Commercial Use, +if applicable. Original Contributor may, at its discretion, post Source +Code for Your Error Corrections and Shared Modifications on the +Community Webserver. You may also post Error Corrections and Shared +Modifications on a web-server of Your choice; provided, that You must +take reasonable precautions to ensure that only Licensees have access to +such Error Corrections and Shared Modifications. Such precautions shall +include, without limitation, a password protection scheme limited to +Licensees and a click-on, download certification of Licensee status +required of those attempting to download from the server. An example of +an acceptable certification is attached as Attachment A-2. + +(c) Notices. All Error Corrections and Shared Modifications You create +or contribute to must include a file documenting the additions and +changes You made and the date of such additions and changes. You must +also include the notice set forth in Attachment A-1 in the file header. +If it is not possible to put the notice in a particular Source Code file +due to its structure, then You must include the notice in a location +(such as a relevant directory file), where a recipient would be most +likely to look for such a notice. + +(d) Redistribution. + +(i) Source. Covered Code may be distributed in Source Code form only to +another Licensee (except for students as provided below). You may not +offer or impose any terms on any Covered Code that alter the rights, +requirements, or responsibilities of such Licensee. You may distribute +Covered Code to students for use in connection with their course work +and research projects undertaken at accredited educational institutions. +Such students need not be Licensees, but must be given a copy of the +notice set forth in Attachment A-3 and such notice must also be included +in a file header or prominent location in the Source Code made available +to such students. + +(ii) Executable. You may distribute Executable version(s) of Covered +Code to Licensees and other third parties only for the purpose of +evaluation and comment in connection with Research Use by You and under +a license of Your choice, but which limits use of such Executable +version(s) of Covered Code only to that purpose. + +(iii) Modified Class, Interface and Package Naming. In connection with +Research Use by You only, You may use Original Contributor's class, +Interface and package names only to accurately reference or invoke the +Source Code files You modify. Original Contributor grants to You a +limited license to the extent necessary for such purposes. + +(iv) You expressly agree that any distribution, in whole or in part, of +Modifications developed by You shall only be done pursuant to the terms +and conditions of this License. + +(e) Extensions. + +(i) Covered Code. You may not include any Source Code of Community Code +in any Extensions. You may include the compiled Header Files of +Community Code in an Extension provided that Your use of the Covered +Code, including Heading Files, complies with the Commercial Use License, +the TCK and all other terms of this License. + +(ii) Publication. No later than the date on which You first distribute +such Extension for Commercial Use, You must publish to the industry, on +a non-confidential basis and free of all copyright restrictions with +respect to reproduction and use, an accurate and current specification +for any Extension. In addition, You must make available an appropriate +test suite, pursuant to the same rights as the specification, +sufficiently detailed to allow any third party reasonably skilled in the +technology to produce implementations of the Extension compatible with +the specification. Such test suites must be made available as soon as +reasonably practicable but, in no event, later than ninety (90) days +after Your first Commercial Use of the Extension. You must use +reasonable efforts to promptly clarify and correct the specification and +the test suite upon written request by Original Contributor. + +(iii) Open. You agree to refrain from enforcing any Intellectual +Property Rights You may have covering any interface(s) of Your +Extension, which would prevent the implementation of such interface(s) +by Original Contributor or any Licensee. This obligation does not +prevent You from enforcing any Intellectual Property Right You have that +would otherwise be infringed by an implementation of Your Extension. + +(iv) Interface Modifications and Naming. You may not modify or add to +the GUID space * * "xxxxxxxx-0901-11d1-8B06-00A024406D59" or any other +GUID space designated by Original Contributor. You may not modify any +Interface prefix provided with the Covered Code or any other prefix +designated by Original Contributor.* * + +* * + +(f) You agree that any Specifications provided to You by Original +Contributor are confidential and proprietary information of Original +Contributor. You must maintain the confidentiality of the Specifications +and may not disclose them to any third party without Original +Contributor's prior written consent. You may only use the Specifications +under the terms of this License and only for the purpose of implementing +the terms of this License with respect to Covered Code. You agree not +use, copy or distribute any such Specifications except as provided in +writing by Original Contributor. + +3.2 Commercial Use License. + +You may not make Commercial Use of any Covered Code unless You and +Original Contributor have executed a copy of the Commercial Use and +Trademark License attached as Attachment D. + +*4. Versions of the License.* + +4.1 License Versions. + +Original Contributor may publish revised versions of the License from +time to time. Each version will be given a distinguishing version number. + +4.2 Effect. + +Once a particular version of Covered Code has been provided under a +version of the License, You may always continue to use such Covered Code +under the terms of that version of the License. You may also choose to +use such Covered Code under the terms of any subsequent version of the +License. No one other than Original Contributor has the right to +promulgate License versions. + +4.3 Multiple-Licensed Code. + +Original Contributor may designate portions of the Covered Code as +"Multiple-Licensed." "Multiple-Licensed" means that the Original +Contributor permits You to utilize those designated portions of the +Covered Code under Your choice of this License or the alternative +license(s), if any, specified by the Original Contributor in an +Attachment to this License. + +*5. Disclaimer of Warranty.* + +5.1 COVERED CODE PROVIDED AS IS. + +COVERED CODE IS PROVIDED UNDER THIS LICENSE "AS IS," WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, +WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT +FOR A PARTICULAR PURPOSE OR NON-INFRINGING. YOU AGREE TO BEAR THE ENTIRE +RISK IN CONNECTION WITH YOUR USE AND DISTRIBUTION OF COVERED CODE UNDER +THIS LICENSE. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART +OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER +EXCEPT SUBJECT TO THIS DISCLAIMER. + +5.2 Not Designed for High Risk Activities. + +You acknowledge that Original Code, Upgraded Code and Specifications are +not designed or intended for use in high risk activities including, but +not limited to: (i) on-line control of aircraft, air traffic, aircraft +navigation or aircraft communications; or (ii) in the design, +construction, operation or maintenance of any nuclear facility. Original +Contributor disclaims any express or implied warranty of fitness for +such uses. + +*6. Termination.* + +6.1 By You. + +You may terminate this Research Use license at anytime by providing +written notice to Original Contributor. + +6.2 By Original Contributor. + +This License and the rights granted hereunder will terminate: + +(i) automatically if You fail to comply with the terms of this License +and fail to cure such breach within 30 days of receipt of written notice +of the breach; + +(ii) immediately in the event of circumstances specified in Sections 7.1 +and 8.4; or + +(iii) at Original Contributor's discretion upon any action initiated by +You (including by cross-claim or counter claim) alleging that use or +distribution by Original Contributor or any Licensee, of Original Code, +Upgraded Code, Error Corrections, Shared Modifications or Specifications +infringe a patent owned or controlled by You. + +6.3 Effective of Termination. + +Upon termination, You agree to discontinue use of and destroy all copies +of Covered Code in Your possession. All sublicenses to the Covered Code +which You have properly granted shall survive any termination of this +License. Provisions that, by their nature, should remain in effect +beyond the termination of this License shall survive including, without +limitation, Sections 2.2, 3, 5, 7 and 8. + +6.4 No Compensation. + +Each party waives and releases the other from any claim to compensation +or indemnity for permitted or lawful termination of the business +relationship established by this License. + +*7. Liability.* + +7.1 Infringement. Should any of the Original Code, Upgraded Code, TCK or +Specifications ("Materials") become the subject of a claim of +infringement, Original Contributor may, at its sole option, (i) attempt +to procure the rights necessary for You to continue using the Materials, +(ii) modify the Materials so that they are no longer infringing, or +(iii) terminate Your right to use the Materials, immediately upon +written notice, and refund to You the amount, if any, having then +actually been paid by You to Original Contributor for the Original Code, +Upgraded Code and TCK, depreciated on a straight line, five year basis. + +7.2 LIMITATION OF LIABILITY. TO THE FULL EXTENT ALLOWED BY APPLICABLE +LAW, ORIGINAL CONTRIBUTOR'S LIABILITY TO YOU FOR CLAIMS RELATING TO THIS +LICENSE, WHETHER FOR BREACH OR IN TORT, SHALL BE LIMITED TO ONE HUNDRED +PERCENT (100%) OF THE AMOUNT HAVING THEN ACTUALLY BEEN PAID BY YOU TO +ORIGINAL CONTRIBUTOR FOR ALL COPIES LICENSED HEREUNDER OF THE PARTICULAR +ITEMS GIVING RISE TO SUCH CLAIM, IF ANY, DURING THE TWELVE MONTHS +PRECEDING THE CLAIMED BREACH. IN NO EVENT WILL YOU (RELATIVE TO YOUR +SHARED MODIFICATIONS OR ERROR CORRECTIONS) OR ORIGINAL CONTRIBUTOR BE +LIABLE FOR ANY INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES IN CONNECTION WITH OR RISING OUT OF THIS LICENSE (INCLUDING, +WITHOUT LIMITATION, LOSS OF PROFITS, USE, DATA, OR OTHER ECONOMIC +ADVANTAGE), HOWEVER IT ARISES AND ON ANY THEORY OF LIABILITY, WHETHER IN +AN ACTION FOR CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE) +OR OTHERWISE, WHETHER OR NOT YOU OR ORIGINAL CONTRIBUTOR HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE AND NOTWITHSTANDING THE +FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. + +*8. Miscellaneous.* + +8.1 Trademark. + +You shall not use any Trademark unless You and Original Contributor +execute a copy of the Commercial Use and Trademark License Agreement +attached hereto as Attachment D. Except as expressly provided in the +License, You are granted no right, title or license to, or interest in, +any Trademarks. Whether or not You and Original Contributor enter into +the Trademark License, You agree not to (i) challenge Original +Contributor's ownership or use of Trademarks; (ii) attempt to register +any Trademarks, or any mark or logo substantially similar thereto; or +(iii) incorporate any Trademarks into Your own trademarks, product +names, service marks, company names, or domain names. + +8.2 Integration. + +This License represents the complete agreement concerning the subject +matter hereof. + +8.3 Assignment. + +Original Contributor may assign this License, and its rights and +obligations hereunder, in its sole discretion. You may assign the +Research Use portions of this License and the TCK license to a third +party upon prior written notice to Original Contributor (which may be +provided electronically via the Community Web-Server). You may not +assign the Commercial Use and Trademark license, the Add-On Technology +License, or the Add-On Technology Source Code Porting License, including +by way of merger (regardless of whether You are the surviving entity) or +acquisition, without Original Contributor's prior written consent. + +8.4 Severability. + +If any provision of this License is held to be unenforceable, such +provision shall be reformed only to the extent necessary to make it +enforceable. Notwithstanding the foregoing, if You are prohibited by law +from fully and specifically complying with Sections 2.2 or 3, this +License will immediately terminate and You must immediately discontinue +any use of Covered Code. + +8.5 Governing Law. + +This License shall be governed by the laws of the United States and the +State of Washington, as applied to contracts entered into and to be +performed in Washington between Washington residents. The application of +the United Nations Convention on Contracts for the International Sale of +Goods is expressly excluded. You agree that the state and federal courts +located in Seattle, Washington have exclusive jurisdiction over any +claim relating to the License, including contract and tort claims. + +8.6 Dispute Resolution. + +a) Arbitration. Any dispute arising out of or relating to this License +shall be finally settled by arbitration as set out herein, except that +either party may bring any action, in a court of competent jurisdiction +(which jurisdiction shall be exclusive), with respect to any dispute +relating to such party's Intellectual Property Rights or with respect to +Your compliance with the TCK license. Arbitration shall be administered: +(i) by the American Arbitration Association (AAA), (ii) in accordance +with the rules of the United Nations Commission on International Trade +Law (UNCITRAL) (the "Rules") in effect at the time of arbitration as +modified herein; and (iii) the arbitrator will apply the substantive +laws of Washington and the United States. Judgment upon the award +rendered by the arbitrator may be entered in any court having +jurisdiction to enforce such award. + +b) Arbitration language, venue and damages. All arbitration proceedings +shall be conducted in English by a single arbitrator selected in +accordance with the Rules, who must be fluent in English and be either a +retired judge or practicing attorney having at least ten (10) years +litigation experience and be reasonably familiar with the technology +matters relative to the dispute. Unless otherwise agreed, arbitration +venue shall be in Seattle, Washington. The arbitrator may award monetary +damages only and nothing shall preclude either party from seeking +provisional or emergency relief from a court of competent jurisdiction. +The arbitrator shall have no authority to award damages in excess of +those permitted in this License and any such award in excess is void. +All awards will be payable in U.S. dollars and may include, for the +prevailing party (i) pre-judgment award interest, (ii) reasonable +attorneys' fees incurred in connection with the arbitration, and (iii) +reasonable costs and expenses incurred in enforcing the award. The +arbitrator will order each party to produce identified documents and +respond to no more than twenty-five single question interrogatories. + +8.7 Construction. + +Any law or regulation, which provides that the language of a contract +shall be construed against the drafter, shall not apply to this License. + +8.8 U.S. Government End Users. + +The Covered Code is a "commercial item," as that term is defined in 48 +C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" +and "commercial computer software documentation," as such terms are used +in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and +48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government +End Users acquire Covered Code with only those rights set forth herein. +You agree to pass this notice to our licensees. + +8.9 Marketing Activities. + +Licensee hereby grants Original Contributor a non-exclusive, +non-transferable, limited license to use the Licensee's company name and +logo ("Licensee Marks") in any presentations, press releases, or +marketing materials solely for the purpose of identifying Licensee as a +member of the Helix Community. Licensee shall provide samples of +Licensee Marks to Original Contributor upon request by Original +Contributor. Original Contributor acknowledges that the Licensee Marks +are the trademarks of Licensee. Original Contributor shall not use the +Licensee Marks in a way that may imply that Original Contributor is an +agency or branch of Licensee. Original Contributor understands and +agrees that the use of any Licensee Marks in connection with this +Agreement shall not create any right, title or interest, in, or to the +Licensee Marks or any Licensee trademarks and that all such use and +goodwill associated with any such trademarks will inure to the benefit +of Licensee. Further the Original Contributor will stop usage of the +Licensee Marks upon Licensee's request. + +8.10 Press Announcements. + +You may make press announcements or other public statements regarding +this License without the prior written consent of the Original +Contributor, if Your statement is limited to announcing the licensing of +the Covered Code or the availability of Your Product and its +compatibility with the Covered Code. All other public announcements +regarding this license require the prior written consent of the Original +Contributor. Consent requests are welcome at press@helixcommunity.org. + +8.11 International Use. + +a) Export/Import laws. Covered Code is subject to U.S. export control +laws and may be subject to export or import regulations in other +countries. Each party agrees to comply strictly with all such laws and +regulations and acknowledges their responsibility to obtain such +licenses to export, re-export, or import as may be required. You agree +to pass these obligations to Your licensees. + +b) Intellectual Property Protection. Due to limited intellectual +property protection and enforcement in certain countries, You agree not +to redistribute the Original Code, Upgraded Code, TCK and Specifications +to any country on the list of restricted countries on the Community Web +Server. + +8.12 Language. + +This License is in the English language only, which language shall be +controlling in all respects, and all versions of this License in any +other language shall be for accommodation only and shall not be binding +on the parties to this License. All communications and notices made or +given pursuant to this License, and all documentation and support to be +provided, unless otherwise noted, shall be in the English language. + +PLEASE READ THE TERMS OF THIS LICENSE CAREFULLY. BY CLICKING ON THE +"ACCEPT" BUTTON BELOW YOU ARE ACCEPTING AND AGREEING TO THE TERMS AND +CONDITIONS OF THIS LICENSE WITH REALNETWORKS, INC. IF YOU ARE AGREEING +TO THIS LICENSE ON BEHALF OF A COMPANY, YOU REPRESENT THAT YOU ARE +AUTHORIZED TO BIND THE COMPANY TO SUCH A LICENSE. WHETHER YOU ARE ACTING +ON YOUR OWN BEHALF, OR REPRESENTING A COMPANY, YOU MUST BE OF MAJORITY +AGE AND BE OTHERWISE COMPETENT TO ENTER INTO CONTRACTS. IF YOU DO NOT +MEET THIS CRITERIA OR YOU DO NOT AGREE TO ANY OF THE TERMS AND +CONDITIONS OF THIS LICENSE, CLICK ON THE REJECT BUTTON TO EXIT. + + + GLOSSARY + +1. *"Added Value"* means code which: + +(i) has a principal purpose which is substantially different from that +of the stand-alone Technology; + +(ii) represents a significant functional and value enhancement to the +Technology; + +(iii) operates in conjunction with the Technology; and + +(iv) is not marketed as a technology which replaces or substitutes for +the Technology + +2. "*Applicable Patent Rights*" mean: (a) in the case where Original +Contributor is the grantor of rights, claims of patents that (i) are now +or hereafter acquired, owned by or assigned to Original Contributor and +(ii) are necessarily infringed by using or making the Original Code or +Upgraded Code, including Modifications provided by Original Contributor, +alone and not in combination with other software or hardware; and (b) in +the case where Licensee is the grantor of rights, claims of patents that +(i) are now or hereafter acquired, owned by or assigned to Licensee and +(ii) are infringed (directly or indirectly) by using or making +Licensee's Modifications or Error Corrections, taken alone or in +combination with Covered Code. + +3. "*Application Programming Interfaces (APIs)"* means the interfaces, +associated header files, service provider interfaces, and protocols that +enable a device, application, Operating System, or other program to +obtain services from or make requests of (or provide services in +response to requests from) other programs, and to use, benefit from, or +rely on the resources, facilities, and capabilities of the relevant +programs using the APIs. APIs includes the technical documentation +describing the APIs, the Source Code constituting the API, and any +Header Files used with the APIs. + +4. "*Commercial Use*" means any use (internal or external), copying, +sublicensing or distribution (internal or external), directly or +indirectly of Covered Code by You other than Your Research Use of +Covered Code within Your business or organization or in conjunction with +other Licensees with equivalent Research Use rights. Commercial Use +includes any use of the Covered Code for direct or indirect commercial +or strategic gain, advantage or other business purpose. Any Commercial +Use requires execution of Attachment D by You and Original Contributor. + +5. "*Community Code*" means the Original Code, Upgraded Code, Error +Corrections, Shared Modifications, or any combination thereof. + +6. "*Community Webserver(s)"* means the webservers designated by +Original Contributor for access to the Original Code, Upgraded Code, TCK +and Specifications and for posting Error Corrections and Shared +Modifications. + +7. "*Compliant Covered Code*" means Covered Code that complies with the +requirements of the TCK. + +8. "*Contributor*" means each Licensee that creates or contributes to +the creation of any Error Correction or Shared Modification. + +9. "*Covered Code*" means the Original Code, Upgraded Code, +Modifications, or any combination thereof. + +10. "*Error Correction*" means any change made to Community Code which +conforms to the Specification and corrects the adverse effect of a +failure of Community Code to perform any function set forth in or +required by the Specifications. + +11. "*Executable*" means Covered Code that has been converted from +Source Code to the preferred form for execution by a computer or digital +processor (e.g. binary form). + +12. "*Extension(s)"* means any additional Interfaces developed by or for +You which: (i) are designed for use with the Technology; (ii) constitute +an API for a library of computing functions or services; and (iii) are +disclosed or otherwise made available to third party software developers +for the purpose of developing software which invokes such additional +Interfaces. The foregoing shall not apply to software developed by Your +subcontractors to be exclusively used by You. + +13. "*Header File(s)"* means that portion of the Source Code that +provides the names and types of member functions, data members, class +definitions, and interface definitions necessary to implement the APIs +for the Covered Code. Header Files include, files specifically +designated by Original Contributor as Header Files. Header Files do not +include the code necessary to implement the functionality underlying the +Interface. + +14. *"Helix DNA Server Technology"* means the program(s) that implement +the Helix Universal Server streaming engine for the Technology as +defined in the Specification. + +15. *"Helix DNA Client Technology"* means the Covered Code that +implements the RealOne Player engine as defined in the Specification. + +16. *"Helix DNA Producer Technology"* means the Covered Code that +implements the Helix Producer engine as defined in the Specification. + +17. *"Helix DNA Technology"* means the Helix DNA Server Technology, the +Helix DNA Client Technology, the Helix DNA Producer Technology and other +Helix technologies designated by Original Contributor. + +18. "*Intellectual Property Rights*" means worldwide statutory and +common law rights associated solely with (i) Applicable Patent Rights; +(ii) works of authorship including copyrights, copyright applications, +copyright registrations and "moral rights"; (iii) the protection of +trade and industrial secrets and confidential information; and (iv) +divisions, continuations, renewals, and re-issuances of the foregoing +now existing or acquired in the future. + +19. *"Interface*" means interfaces, functions, properties, class +definitions, APIs, Header Files, GUIDs, V-Tables, and/or protocols +allowing one piece of software, firmware or hardware to communicate or +interoperate with another piece of software, firmware or hardware. + +20. "*Internal Deployment Use*" means use of Compliant Covered Code +(excluding Research Use) within Your business or organization only by +Your employees and/or agents on behalf of Your business or organization, +but not to provide services, including content distribution, to third +parties, subject to execution of Attachment D by You and Original +Contributor, if required. + +21. "*Licensee*" means any party that has entered into and has in effect +a version of this License with Original Contributor. + +22. "*MIME type*" means a description of what type of media or other +content is in a file, including by way of example but not limited to +'audio/x-pn-realaudio-plugin.' + +23. "*Modification(s)"* means (i) any addition to, deletion from and/or +change to the substance and/or structure of the Covered Code, including +Interfaces; (ii) the combination of any Covered Code and any previous +Modifications; (iii) any new file or other representation of computer +program statements that contains any portion of Covered Code; and/or +(iv) any new Source Code implementing any portion of the Specifications. + +24. "*MP3 Patents*" means any patents necessary to make, use or sell +technology implementing any portion of the specification developed by +the Moving Picture Experts Group known as MPEG-1 Audio Layer-3 or MP3, +including but not limited to all past and future versions, profiles, +extensions, parts and amendments relating to the MP3 specification. + +25. "*MPEG-4 Patents*" means any patents necessary to make, use or sell +technology implementing any portion of the specification developed by +the Moving Pictures Experts Group known as MPEG-4, including but not +limited to all past and future versions, profiles, extensions, parts and +amendments relating to the MPEG-4 specification. + +26. "*Original Code*" means the initial Source Code for the Technology +as described on the Community Web Server. + +27. "*Original Contributor*" means RealNetworks, Inc., its affiliates +and its successors and assigns. + +28. "*Original Contributor MIME Type*" means the MIME registry, browser +preferences, or local file/protocol associations invoking any Helix DNA +Client-based application, including the RealOne Player, for playback of +RealAudio, RealVideo, other RealMedia MIME types or datatypes (e.g., +.ram, .rnx, .rpm, .ra, .rm, .rp, .rt, .rf, .prx, .mpe, .rmp, .rmj, .rav, +.rjs, .rmx, .rjt, .rms), and any other Original Contributor-specific or +proprietary MIME types that Original Contributor may introduce in the +future. + +29. "*Personal Use*" means use of Covered Code by an individual solely +for his or her personal, private and non-commercial purposes. An +individual's use of Covered Code in his or her capacity as an officer, +employee, member, independent contractor or agent of a corporation, +business or organization (commercial or non-commercial) does not qualify +as Personal Use. + +30. "*RealMedia File Format*" means the file format designed and +developed by RealNetworks for storing multimedia data and used to store +RealAudio and RealVideo encoded streams. Valid RealMedia File Format +extensions include: .rm, .rmj, .rmc, .rmvb, .rms. + +31. "*RCSL Webpage*" means the RealNetworks Community Source License +webpage located at https://www.helixcommunity.org/content/rcsl or such +other URL that Original Contributor may designate from time to time. + +32. "*Reformatted Specifications*" means any revision to the +Specifications which translates or reformats the Specifications (as for +example in connection with Your documentation) but which does not alter, +subset or superset * *the functional or operational aspects of the +Specifications. + +33. "*Research Use*" means use and distribution of Covered Code only for +Your Personal Use, research or development use and expressly excludes +Internal Deployment Use and Commercial Use. Research Use also includes +use of Covered Code to teach individuals how to use Covered Code. + +34. "*Shared Modifications*" means Modifications that You distribute or +use for a Commercial Use, in addition to any Modifications provided by +You, at Your option, pursuant to Section 2.2, or received by You from a +Contributor pursuant to Section 2.3. + +35. "*Source Code*" means the preferred form of the Covered Code for +making modifications to it, including all modules it contains, plus any +associated interface definition files, scripts used to control +compilation and installation of an Executable, or source code +differential comparisons against either the Original Code or another +well known, available Covered Code of the Contributor's choice. The +Source Code can be in a compressed or archival form, provided the +appropriate decompression or de-archiving software is widely available +for no charge. + +36. "*Specifications*" means the specifications for the Technology and +other documentation, as designated on the Community Web Server, as may +be revised by Original Contributor from time to time. + +37. "*Trademarks*" means Original Contributor's trademarks and logos, +including, but not limited to, RealNetworks, RealAudio, RealVideo, +RealOne, RealSystem, SureStream, Helix, Helix DNA and other trademarks +whether now used or adopted in the future. + +38. "*Technology*" means the technology described in Attachment B, and +Upgrades. + +39. "*Technology Compatibility Kit"* or *"TCK*" means the test programs, +procedures, acceptance criteria and/or other requirements, designated by +Original Contributor for use in verifying compliance of Covered Code +with the Specifications, in conjunction with the Original Code and +Upgraded Code. Original Contributor may, in its sole discretion and from +time to time, revise a TCK to correct errors and/or omissions and in +connection with Upgrades. + +40. "*Upgrade(s)"* means new versions of Technology designated +exclusively by Original Contributor as an "Upgrade" and released by +Original Contributor from time to time under the terms of the License. + +41. "*Upgraded Code*" means the Source Code and/or Executables for +Upgrades, possibly including Modifications made by Contributors. + +42. *"User's Guide"* means the users guide for the TCK which Original +Contributor makes available to You to provide direction in how to run +the TCK and properly interpret the results, as may be revised by +Original Contributor from time to time. + +43. "*You(r)*" means an individual, or a legal entity acting by and +through an individual or individuals, exercising rights either under +this License or under a future version of this License issued pursuant +to Section 4.1. For legal entities, "You(r)" includes any entity that by +majority voting interest controls, is controlled by, or is under common +control with You. + +44. "*Your Products*" means any (i) hardware products You distribute +integrating the Covered Code; (ii) any software products You distribute +with the Covered Code that utilize the APIs of the Covered Code; or +(iii) any services You provide using the Covered Code. + + + ATTACHMENT A + +REQUIRED NOTICES + + + ATTACHMENT A-1 + +REQUIRED IN ALL CASES + +Notice to be included in header file of all Error Corrections and Shared +Modifications: + +Portions Copyright 1994-2003 © RealNetworks, Inc. All rights reserved. + +The contents of this file, and the files included with this file, are +subject to the current version of RealNetworks Community Source License +Version 1.1 (the "License"). You may not use this file except in +compliance with the License executed by both You and RealNetworks. You +may obtain a copy of the License at * +https://www.helixcommunity.org/content/rcsl.* You may also obtain a copy +of the License by contacting RealNetworks directly. Please see the +License for the rights, obligations and limitations governing use of the +contents of the file. + +This file is part of the Helix DNA technology. RealNetworks, Inc., is +the developer of the Original code and owns the copyrights in the +portions it created. + +This file, and the files included with this file, are distributed on an +'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, +AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT +LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + +Contributor(s): + +_______________________________________________ + +Technology Compatibility Kit Test Suite(s) Location: + +________________________________ + + + ATTACHMENT A-2 + +SAMPLE LICENSEE CERTIFICATION + +"By clicking the `Agree' button below, You certify that You are a +Licensee in good standing under the RealNetworks Community Source +License, ("License") and that Your access, use and distribution of code +and information You may obtain at this site is subject to the License. +If You are not a Licensee under the RealNetworks Community Source +License You agree not to download, copy or use the Helix DNA technology. + + + ATTACHMENT A-3 + +REQUIRED STUDENT NOTIFICATION + +"This software and related documentation has been obtained by Your +educational institution subject to the RealNetworks Community Source +License. You have been provided access to the software and related +documentation for use only in connection with your course work and +research activities as a matriculated student of Your educational +institution. Any other use is expressly prohibited. + +THIS SOFTWARE AND RELATED DOCUMENTATION CONTAINS PROPRIETARY MATERIAL OF +REALNETWORKS, INC, WHICH ARE PROTECTED BY VARIOUS INTELLECTUAL PROPERTY +RIGHTS. + +You may not use this file except in compliance with the License. You may +obtain a copy of the License on the web at +https://www.helixcommunity.org/content/rcsl. + +* +* + + + ATTACHMENT B + +Description of Technology + +Helix DNA, which consists of Helix DNA Client, Helix DNA Server and +Helix DNA Producer. + +Description of "Technology" + +Helix DNA Technology v1.0 as described on the Community Web Server. + + + ATTACHMENT C + +TECHNOLOGY COMPATIBILITY KIT LICENSE + +The following license is effective for the *Helix DNA* Technology +Compatibility Kit - as described on the Community Web Server. The +Technology Compatibility Kit(s) for the Technology specified in +Attachment B may be accessed at the Community Web Server. + +1. TCK License. + +1.1 Grants to use TCK + +Subject to the terms and restrictions set forth below and the +RealNetworks Community Source License, and the Research Use license, +Original Contributor grants to You a worldwide, non-exclusive, +non-transferable license, to the extent of Original Contributor's +Intellectual Property Rights in the TCK (without the right to +sublicense), to use the TCK to develop and test Covered Code. + +1.2 TCK Use Restrictions. + +You are not authorized to create derivative works of the TCK or use the +TCK to test any implementation of the Specification that is not Covered +Code. You may not publish Your test results or make claims of +comparative compatibility with respect to other implementations of the +Specification. In consideration for the license grant in Section 1.1 +above You agree not to develop Your own tests that are intended to +validate conformation with the Specification. + +2. Test Results. + +You agree to provide to Original Contributor or the third party test +facility if applicable, Your test results that demonstrate that Covered +Code is Compliant Covered Code and that Original Contributor may publish +or otherwise distribute such test results. + +PLEASE READ THE TERMS OF THIS LICENSE CAREFULLY. BY CLICKING ON THE +"ACCEPT" BUTTON BELOW YOU ARE ACCEPTING AND AGREEING TO THE TERMS AND +CONDITIONS OF THIS LICENSE WITH THE ORIGINAL CONTRIBUTOR, REALNETWORKS, +INC. IF YOU ARE AGREEING TO THIS LICENSE ON BEHALF OF A COMPANY, YOU +REPRESENT THAT YOU ARE AUTHORIZED TO BIND THE COMPANY TO SUCH A LICENSE. +WHETHER YOU ARE ACTING ON YOUR OWN BEHALF, OR REPRESENTING A COMPANY, +YOU MUST BE OF MAJORITY AGE AND BE OTHERWISE COMPETENT TO ENTER INTO +CONTRACTS. IF YOU DO NOT MEET THIS CRITERIA OR YOU DO NOT AGREE TO ANY +OF THE TERMS AND CONDITIONS OF THIS LICENSE, CLICK ON THE REJECT BUTTON +TO EXIT. + +*ACCEPT / REJECT +* + +* +* + +*To agree to the R&D/academic terms of this license, please register + on the site -- +you will then be given a chance to agree to the clickwrap RCSL + +R&D License + +and gain access to the RCSL-licensed source code. To build or deploy +commercial applications based on the RCSL, you will need to agree to the +Commercial Use license attachments +* + + + diff --git a/components/driver_sndmixer/libhelix-mp3/RPSL.txt b/components/driver_sndmixer/libhelix-mp3/RPSL.txt new file mode 100644 index 0000000..d040a45 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/RPSL.txt @@ -0,0 +1,518 @@ +RealNetworks Public Source License Version 1.0 +(Rev. Date October 28, 2002) + +1. General Definitions. This License applies to any program or other work which +RealNetworks, Inc., or any other entity that elects to use this license, +("Licensor") makes publicly available and which contains a notice placed by +Licensor identifying such program or work as "Original Code" and stating that it +is subject to the terms of this RealNetworks Public Source License version 1.0 +(or subsequent version thereof) ("License"). You are not required to accept this +License. However, nothing else grants You permission to use, copy, modify or +distribute the software or its derivative works. These actions are prohibited by +law if You do not accept this License. Therefore, by modifying, copying or +distributing the software (or any work based on the software), You indicate your +acceptance of this License to do so, and all its terms and conditions. In +addition, you agree to the terms of this License by clicking the Accept button +or downloading the software. As used in this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Licensor is the +grantor of rights, claims of patents that (i) are now or hereafter acquired, +owned by or assigned to Licensor and (ii) are necessarily infringed by using or +making the Original Code alone and not in combination with other software or +hardware; and (b) in the case where You are the grantor of rights, claims of +patents that (i) are now or hereafter acquired, owned by or assigned to You and +(ii) are infringed (directly or indirectly) by using or making Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Compatible Source License" means any one of the licenses listed on Exhibit +B or at https://www.helixcommunity.org/content/complicense or other licenses +specifically identified by Licensor in writing. Notwithstanding any term to the +contrary in any Compatible Source License, any code covered by any Compatible +Source License that is used with Covered Code must be made readily available in +Source Code format for royalty-free use under the terms of the Compatible Source +License or this License. + +1.3 "Contributor" means any person or entity that creates or contributes to the +creation of Modifications. + +1.4 "Covered Code" means the Original Code, Modifications, the combination of +Original Code and any Modifications, and/or any respective portions thereof. + +1.5 "Deploy" means to use, sublicense or distribute Covered Code other than for +Your internal research and development (R&D) and/or Personal Use, and includes +without limitation, any and all internal use or distribution of Covered Code +within Your business or organization except for R&D use and/or Personal Use, as +well as direct or indirect sublicensing or distribution of Covered Code by You +to any third party in any form or manner. + +1.6 "Derivative Work" means either the Covered Code or any derivative work under +United States copyright law, and including any work containing or including any +portion of the Covered Code or Modifications, either verbatim or with +modifications and/or translated into another language. Derivative Work also +includes any work which combines any portion of Covered Code or Modifications +with code not otherwise governed by the terms of this License. + +1.7 "Externally Deploy" means to Deploy the Covered Code in any way that may be +accessed or used by anyone other than You, used to provide any services to +anyone other than You, or used in any way to deliver any content to anyone other +than You, whether the Covered Code is distributed to those parties, made +available as an application intended for use over a computer network, or used to +provide services or otherwise deliver content to anyone other than You. + +1.8. "Interface" means interfaces, functions, properties, class definitions, +APIs, header files, GUIDs, V-Tables, and/or protocols allowing one piece of +software, firmware or hardware to communicate or interoperate with another piece +of software, firmware or hardware. + +1.9 "Modifications" mean any addition to, deletion from, and/or change to, the +substance and/or structure of the Original Code, any previous Modifications, the +combination of Original Code and any previous Modifications, and/or any +respective portions thereof. When code is released as a series of files, a +Modification is: (a) any addition to or deletion from the contents of a file +containing Covered Code; and/or (b) any new file or other representation of +computer program statements that contains any part of Covered Code. + +1.10 "Original Code" means (a) the Source Code of a program or other work as +originally made available by Licensor under this License, including the Source +Code of any updates or upgrades to such programs or works made available by +Licensor under this License, and that has been expressly identified by Licensor +as such in the header file(s) of such work; and (b) the object code compiled +from such Source Code and originally made available by Licensor under this +License. + +1.11 "Personal Use" means use of Covered Code by an individual solely for his or +her personal, private and non-commercial purposes. An individual's use of +Covered Code in his or her capacity as an officer, employee, member, independent +contractor or agent of a corporation, business or organization (commercial or +non-commercial) does not qualify as Personal Use. + +1.12 "Source Code" means the human readable form of a program or other work that +is suitable for making modifications to it, including all modules it contains, +plus any associated interface definition files, scripts used to control +compilation and installation of an executable (object code). + +1.13 "You" or "Your" means an individual or a legal entity exercising rights +under this License. For legal entities, "You" or "Your" includes any entity +which controls, is controlled by, or is under common control with, You, where +"control" means (a) the power, direct or indirect, to cause the direction or +management of such entity, whether by contract or otherwise, or (b) ownership of +fifty percent (50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms and +conditions of this License, Licensor hereby grants You, effective on the date +You accept this License (via downloading or using Covered Code or otherwise +indicating your acceptance of this License), a worldwide, royalty-free, +non-exclusive copyright license, to the extent of Licensor's copyrights cover +the Original Code, to do the following: + +2.1 You may reproduce, display, perform, modify and Deploy Covered Code, +provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the copyright +and other proprietary notices and disclaimers of Licensor as they appear in the +Original Code, and keep intact all notices in the Original Code that refer to +this License; + +(b) You must include a copy of this License with every copy of Source Code of +Covered Code and documentation You distribute, and You may not offer or impose +any terms on such Source Code that alter or restrict this License or the +recipients' rights hereunder, except as permitted under Section 6; + +(c) You must duplicate, to the extent it does not already exist, the notice in +Exhibit A in each file of the Source Code of all Your Modifications, and cause +the modified files to carry prominent notices stating that You changed the files +and the date of any change; + +(d) You must make Source Code of all Your Externally Deployed Modifications +publicly available under the terms of this License, including the license grants +set forth in Section 3 below, for as long as you Deploy the Covered Code or +twelve (12) months from the date of initial Deployment, whichever is longer. You +should preferably distribute the Source Code of Your Deployed Modifications +electronically (e.g. download from a web site); and + +(e) if You Deploy Covered Code in object code, executable form only, You must +include a prominent notice, in the code itself as well as in related +documentation, stating that Source Code of the Covered Code is available under +the terms of this License with information on how and where to obtain such +Source Code. You must also include the Object Code Notice set forth in Exhibit A +in the "about" box or other appropriate place where other copyright notices are +placed, including any packaging materials. + +2.2 You expressly acknowledge and agree that although Licensor and each +Contributor grants the licenses to their respective portions of the Covered Code +set forth herein, no assurances are provided by Licensor or any Contributor that +the Covered Code does not infringe the patent or other intellectual property +rights of any other entity. Licensor and each Contributor disclaim any liability +to You for claims brought by any other entity based on infringement of +intellectual property rights or otherwise. As a condition to exercising the +rights and licenses granted hereunder, You hereby assume sole responsibility to +secure any other intellectual property rights needed, if any. For example, if a +third party patent license is required to allow You to make, use, sell, import +or offer for sale the Covered Code, it is Your responsibility to acquire such +license(s). + +2.3 Subject to the terms and conditions of this License, Licensor hereby grants +You, effective on the date You accept this License (via downloading or using +Covered Code or otherwise indicating your acceptance of this License), a +worldwide, royalty-free, perpetual, non-exclusive patent license under +Licensor's Applicable Patent Rights to make, use, sell, offer for sale and +import the Covered Code, provided that in each instance you comply with the +terms of this License. + +3. Your Grants. In consideration of, and as a condition to, the licenses granted +to You under this License: + +(a) You grant to Licensor and all third parties a non-exclusive, perpetual, +irrevocable, royalty free license under Your Applicable Patent Rights and other +intellectual property rights owned or controlled by You, to make, sell, offer +for sale, use, import, reproduce, display, perform, modify, distribute and +Deploy Your Modifications of the same scope and extent as Licensor's licenses +under Sections 2.1 and 2.2; and + +(b) You grant to Licensor and its subsidiaries a non-exclusive, worldwide, +royalty-free, perpetual and irrevocable license, under Your Applicable Patent +Rights and other intellectual property rights owned or controlled by You, to +make, use, sell, offer for sale, import, reproduce, display, perform, +distribute, modify or have modified (for Licensor and/or its subsidiaries), +sublicense and distribute Your Modifications, in any form and for any purpose, +through multiple tiers of distribution. + +(c) You agree not use any information derived from Your use and review of the +Covered Code, including but not limited to any algorithms or inventions that may +be contained in the Covered Code, for the purpose of asserting any of Your +patent rights, or assisting a third party to assert any of its patent rights, +against Licensor or any Contributor. + +4. Derivative Works. You may create a Derivative Work by combining Covered Code +with other code not otherwise governed by the terms of this License and +distribute the Derivative Work as an integrated product. In each such instance, +You must make sure the requirements of this License are fulfilled for the +Covered Code or any portion thereof, including all Modifications. + +4.1 You must cause any Derivative Work that you distribute, publish or +Externally Deploy, that in whole or in part contains or is derived from the +Covered Code or any part thereof, to be licensed as a whole at no charge to all +third parties under the terms of this License and no other license except as +provided in Section 4.2. You also must make Source Code available for the +Derivative Work under the same terms as Modifications, described in Sections 2 +and 3, above. + +4.2 Compatible Source Licenses. Software modules that have been independently +developed without any use of Covered Code and which contain no portion of the +Covered Code, Modifications or other Derivative Works, but are used or combined +in any way wtih the Covered Code or any Derivative Work to form a larger +Derivative Work, are exempt from the conditions described in Section 4.1 but +only to the extent that: the software module, including any software that is +linked to, integrated with, or part of the same applications as, the software +module by any method must be wholly subject to one of the Compatible Source +Licenses. Notwithstanding the foregoing, all Covered Code must be subject to the +terms of this License. Thus, the entire Derivative Work must be licensed under a +combination of the RPSL (for Covered Code) and a Compatible Source License for +any independently developed software modules within the Derivative Work. The +foregoing requirement applies even if the Compatible Source License would +ordinarily allow the software module to link with, or form larger works with, +other software that is not subject to the Compatible Source License. For +example, although the Mozilla Public License v1.1 allows Mozilla code to be +combined with proprietary software that is not subject to the MPL, if +MPL-licensed code is used with Covered Code the MPL-licensed code could not be +combined or linked with any code not governed by the MPL. The general intent of +this section 4.2 is to enable use of Covered Code with applications that are +wholly subject to an acceptable open source license. You are responsible for +determining whether your use of software with Covered Code is allowed under Your +license to such software. + +4.3 Mere aggregation of another work not based on the Covered Code with the +Covered Code (or with a work based on the Covered Code) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. If You deliver the Covered Code for combination and/or integration with +an application previously provided by You (for example, via automatic updating +technology), such combination and/or integration constitutes a Derivative Work +subject to the terms of this License. + +5. Exclusions From License Grant. Nothing in this License shall be deemed to +grant any rights to trademarks, copyrights, patents, trade secrets or any other +intellectual property of Licensor or any Contributor except as expressly stated +herein. No right is granted to the trademarks of Licensor or any Contributor +even if such marks are included in the Covered Code. Nothing in this License +shall be interpreted to prohibit Licensor from licensing under different terms +from this License any code that Licensor otherwise would have a right to +license. Modifications, Derivative Works and/or any use or combination of +Covered Code with other technology provided by Licensor or third parties may +require additional patent licenses from Licensor which Licensor may grant in its +sole discretion. No patent license is granted separate from the Original Code or +combinations of the Original Code with other software or hardware. + +5.1. Trademarks. This License does not grant any rights to use the trademarks or +trade names owned by Licensor ("Licensor Marks" defined in Exhibit C) or to any +trademark or trade name belonging to any Contributor. No Licensor Marks may be +used to endorse or promote products derived from the Original Code other than as +permitted by the Licensor Trademark Policy defined in Exhibit C. + +6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, +support, indemnity or liability obligations and/or other rights consistent with +the scope of the license granted herein ("Additional Terms") to one or more +recipients of Covered Code. However, You may do so only on Your own behalf and +as Your sole responsibility, and not on behalf of Licensor or any Contributor. +You must obtain the recipient's agreement that any such Additional Terms are +offered by You alone, and You hereby agree to indemnify, defend and hold +Licensor and every Contributor harmless for any liability incurred by or claims +asserted against Licensor or such Contributor by reason of any such Additional +Terms. + +7. Versions of the License. Licensor may publish revised and/or new versions of +this License from time to time. Each version will be given a distinguishing +version number. Once Original Code has been published under a particular version +of this License, You may continue to use it under the terms of that version. You +may also choose to use such Original Code under the terms of any subsequent +version of this License published by Licensor. No one other than Licensor has +the right to modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part +pre-release, untested, or not fully tested works. The Covered Code may contain +errors that could cause failures or loss of data, and may be incomplete or +contain inaccuracies. You expressly acknowledge and agree that use of the +Covered Code, or any portion thereof, is at Your sole and entire risk. THE +COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF +ANY KIND AND LICENSOR AND LICENSOR'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS +"LICENSOR" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY +DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF +SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET +ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. LICENSOR AND EACH +CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE +COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR +REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR +WRITTEN DOCUMENTATION, INFORMATION OR ADVICE GIVEN BY LICENSOR, A LICENSOR +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You +acknowledge that the Covered Code is not intended for use in high risk +activities, including, but not limited to, the design, construction, operation +or maintenance of nuclear facilities, aircraft navigation, aircraft +communication systems, or air traffic control machines in which case the failure +of the Covered Code could lead to death, personal injury, or severe physical or +environmental damage. Licensor disclaims any express or implied warranty of +fitness for such uses. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT +SHALL LICENSOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR +YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER +UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE OR STRICT +LIABILITY), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF LICENSOR OR SUCH +CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND +NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME +JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR +CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event +shall Licensor's total liability to You for all damages (other than as may be +required by applicable law) under this License exceed the amount of ten dollars +($10.00). + +10. Ownership. Subject to the licenses granted under this License, each +Contributor retains all rights, title and interest in and to any Modifications +made by such Contributor. Licensor retains all rights, title and interest in and +to the Original Code and any Modifications made by or on behalf of Licensor +("Licensor Modifications"), and such Licensor Modifications will not be +automatically subject to this License. Licensor may, at its sole discretion, +choose to license such Licensor Modifications under this License, or on +different terms from those contained in this License or may choose not to +license them at all. + +11. Termination. + +11.1 Term and Termination. The term of this License is perpetual unless +terminated as provided below. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Licensor if You fail to comply with any +term(s) of this License and fail to cure such breach within 30 days of becoming +aware of such breach; + +(b) immediately in the event of the circumstances described in Section 12.5(b); +or + +(c) automatically without notice from Licensor if You, at any time during the +term of this License, commence an action for patent infringement against +Licensor (including by cross-claim or counter claim in a lawsuit); + +(d) upon written notice from Licensor if You, at any time during the term of +this License, commence an action for patent infringement against any third party +alleging that the Covered Code itself (excluding combinations with other +software or hardware) infringes any patent (including by cross-claim or counter +claim in a lawsuit). + +11.2 Effect of Termination. Upon termination, You agree to immediately stop any +further use, reproduction, modification, sublicensing and distribution of the +Covered Code and to destroy all copies of the Covered Code that are in your +possession or control. All sublicenses to the Covered Code which have been +properly granted prior to termination shall survive any termination of this +License. Provisions which, by their nature, should remain in effect beyond the +termination of this License shall survive, including but not limited to Sections +3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for +compensation, indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this License will +be without prejudice to any other right or remedy of any party. + +12. Miscellaneous. + +12.1 Government End Users. The Covered Code is a "commercial item" as defined in +FAR 2.101. Government software and technical data rights in the Covered Code +include only those rights customarily provided to the public as defined in this +License. This customary commercial license in technical data and software is +provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer +Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical +Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software +or Computer Software Documentation). Accordingly, all U.S. Government End Users +acquire Covered Code with only those rights set forth herein. + +12.2 Relationship of Parties. This License will not be construed as creating an +agency, partnership, joint venture or any other form of legal association +between or among You, Licensor or any Contributor, and You will not represent to +the contrary, whether expressly, by implication, appearance or otherwise. + +12.3 Independent Development. Nothing in this License will impair Licensor's +right to acquire, license, develop, have others develop for it, market and/or +distribute technology or products that perform the same or similar functions as, +or otherwise compete with, Modifications, Derivative Works, technology or +products that You may develop, produce, market or distribute. + +12.4 Waiver; Construction. Failure by Licensor or any Contributor to enforce any +provision of this License will not be deemed a waiver of future enforcement of +that or any other provision. Any law or regulation which provides that the +language of a contract shall be construed against the drafter will not apply to +this License. + +12.5 Severability. (a) If for any reason a court of competent jurisdiction finds +any provision of this License, or portion thereof, to be unenforceable, that +provision of the License will be enforced to the maximum extent permissible so +as to effect the economic benefits and intent of the parties, and the remainder +of this License will continue in full force and effect. (b) Notwithstanding the +foregoing, if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the enforceability +of either of those Sections, this License will immediately terminate and You +must immediately discontinue any use of the Covered Code and destroy all copies +of it that are in your possession or control. + +12.6 Dispute Resolution. Any litigation or other dispute resolution between You +and Licensor relating to this License shall take place in the Seattle, +Washington, and You and Licensor hereby consent to the personal jurisdiction of, +and venue in, the state and federal courts within that District with respect to +this License. The application of the United Nations Convention on Contracts for +the International Sale of Goods is expressly excluded. + +12.7 Export/Import Laws. This software is subject to all export and import laws +and restrictions and regulations of the country in which you receive the Covered +Code and You are solely responsible for ensuring that You do not export, +re-export or import the Covered Code or any direct product thereof in violation +of any such restrictions, laws or regulations, or without all necessary +authorizations. + +12.8 Entire Agreement; Governing Law. This License constitutes the entire +agreement between the parties with respect to the subject matter hereof. This +License shall be governed by the laws of the United States and the State of +Washington. + +Where You are located in the province of Quebec, Canada, the following clause +applies: The parties hereby confirm that they have requested that this License +and all related documents be drafted in English. Les parties ont exigé +que le présent contrat et tous les documents connexes soient +rédigés en anglais. + + EXHIBIT A. + +"Copyright © 1995-2002 +RealNetworks, Inc. and/or its licensors. All Rights Reserved. + +The contents of this file, and the files included with this file, are subject to +the current version of the RealNetworks Public Source License Version 1.0 (the +"RPSL") available at https://www.helixcommunity.org/content/rpsl unless you have +licensed the file under the RealNetworks Community Source License Version 1.0 +(the "RCSL") available at https://www.helixcommunity.org/content/rcsl, in which +case the RCSL will apply. You may also obtain the license terms directly from +RealNetworks. You may not use this file except in compliance with the RPSL or, +if you have a valid RCSL with RealNetworks applicable to this file, the RCSL. +Please see the applicable RPSL or RCSL for the rights, obligations and +limitations governing use of the contents of the file. + +This file is part of the Helix DNA Technology. RealNetworks is the developer of +the Original code and owns the copyrights in the portions it created. + +This file, and the files included with this file, is distributed and made +available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR +IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING +WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + +Contributor(s): ____________________________________ + +Technology Compatibility Kit Test +Suite(s) Location (if licensed under the RCSL): ______________________________ + +Object Code Notice: Helix DNA Client technology included. Copyright (c) +RealNetworks, Inc., 1995-2002. All rights reserved. + + + EXHIBIT B + +Compatible Source Licenses for the RealNetworks Public Source License. The +following list applies to the most recent version of the license as of October +25, 2002, unless otherwise indicated. + +* Academic Free License +* Apache Software License +* Apple Public Source License +* Artistic license +* Attribution Assurance Licenses +* BSD license +* Common Public License (1) +* Eiffel Forum License +* GNU General Public License (GPL) (1) +* GNU Library or "Lesser" General Public License (LGPL) (1) +* IBM Public License +* Intel Open Source License +* Jabber Open Source License +* MIT license +* MITRE Collaborative Virtual Workspace License (CVW License) +* Motosoto License +* Mozilla Public License 1.0 (MPL) +* Mozilla Public License 1.1 (MPL) +* Nokia Open Source License +* Open Group Test Suite License +* Python Software Foundation License +* Ricoh Source Code Public License +* Sun Industry Standards Source License (SISSL) +* Sun Public License +* University of Illinois/NCSA Open Source License +* Vovida Software License v. 1.0 +* W3C License +* X.Net License +* Zope Public License +* zlib/libpng license + +(1) Note: because this license contains certain reciprocal licensing terms that +purport to extend to independently developed code, You may be prohibited under +the terms of this otherwise compatible license from using code licensed under +its terms with Covered Code because Covered Code may only be licensed under the +RealNetworks Public Source License. Any attempt to apply non RPSL license terms, +including without limitation the GPL, to Covered Code is expressly forbidden. +You are responsible for ensuring that Your use of Compatible Source Licensed +code does not violate either the RPSL or the Compatible Source License. + +The latest version of this list can be found at: +https://www.helixcommunity.org/content/complicense + + EXHIBIT C + +RealNetworks' Trademark policy. + +RealNetworks defines the following trademarks collectively as "Licensor +Trademarks": "RealNetworks", "RealPlayer", "RealJukebox", "RealSystem", +"RealAudio", "RealVideo", "RealOne Player", "RealMedia", "Helix" or any other +trademarks or trade names belonging to RealNetworks. + +RealNetworks "Licensor Trademark Policy" forbids any use of Licensor Trademarks +except as permitted by and in strict compliance at all times with RealNetworks' +third party trademark usage guidelines which are posted at +http://www.realnetworks.com/info/helixlogo.html. + diff --git a/components/driver_sndmixer/libhelix-mp3/assembly.h b/components/driver_sndmixer/libhelix-mp3/assembly.h new file mode 100644 index 0000000..f5ab875 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/assembly.h @@ -0,0 +1,107 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * assembly.h - assembly language functions and prototypes for supported platforms + * + * - inline rountines with access to 64-bit multiply results + * - x86 (_WIN32) and ARM (ARM_ADS, _WIN32_WCE) versions included + * - some inline functions are mix of asm and C for speed + * - some functions are in native asm files, so only the prototype is given here + * + * MULSHIFT32(x, y) signed multiply of two 32-bit integers (x and y), returns top 32 bits of 64-bit result + * FASTABS(x) branchless absolute value of signed integer x + * CLZ(x) count leading zeros in x + * MADD64(sum, x, y) (Windows only) sum [64-bit] += x [32-bit] * y [32-bit] + * SHL64(sum, x, y) (Windows only) 64-bit left shift using __int64 + * SAR64(sum, x, y) (Windows only) 64-bit right shift using __int64 + */ + +#ifndef _ASSEMBLY_H +#define _ASSEMBLY_H + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return (sizeof(int) * 8); + + numZeros = 0; + while (!(x & 0x80000000)) { + numZeros++; + x <<= 1; + } + + return numZeros; +} + +/* returns 64-bit value in [edx:eax] */ +static __inline Word64 MADD64(Word64 sum64, int x, int y) +{ + sum64 += (Word64)x * (Word64)y; + return sum64; +} + +static __inline__ int MULSHIFT32(int x, int y) +{ + int z; + + z = (Word64)x * (Word64)y >> 32; + + return z; +} + +static __inline Word64 SAR64(Word64 x, int n) +{ + return x >> n; +} + +#endif /* _ASSEMBLY_H */ diff --git a/components/driver_sndmixer/libhelix-mp3/bitstream.c b/components/driver_sndmixer/libhelix-mp3/bitstream.c new file mode 100644 index 0000000..608c39c --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/bitstream.c @@ -0,0 +1,389 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * bitstream.c - bitstream unpacking, frame header parsing, side info parsing + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: SetBitstreamPointer + * + * Description: initialize bitstream reader + * + * Inputs: pointer to BitStreamInfo struct + * number of bytes in bitstream + * pointer to byte-aligned buffer of data to read from + * + * Outputs: filled bitstream info struct + * + * Return: none + **************************************************************************************/ +void SetBitstreamPointer(BitStreamInfo *bsi, int nBytes, unsigned char *buf) +{ + /* init bitstream */ + bsi->bytePtr = buf; + bsi->iCache = 0; /* 4-byte unsigned int */ + bsi->cachedBits = 0; /* i.e. zero bits in cache */ + bsi->nBytes = nBytes; +} + +/************************************************************************************** + * Function: RefillBitstreamCache + * + * Description: read new data from bitstream buffer into bsi cache + * + * Inputs: pointer to initialized BitStreamInfo struct + * + * Outputs: updated bitstream info struct + * + * Return: none + * + * Notes: only call when iCache is completely drained (resets bitOffset to 0) + * always loads 4 new bytes except when bsi->nBytes < 4 (end of buffer) + * stores data as big-endian in cache, regardless of machine endian-ness + * + * TODO: optimize for ARM + * possibly add little/big-endian modes for doing 32-bit loads + **************************************************************************************/ +static __inline void RefillBitstreamCache(BitStreamInfo *bsi) +{ + int nBytes = bsi->nBytes; + + /* optimize for common case, independent of machine endian-ness */ + if (nBytes >= 4) { + bsi->iCache = (*bsi->bytePtr++) << 24; + bsi->iCache |= (*bsi->bytePtr++) << 16; + bsi->iCache |= (*bsi->bytePtr++) << 8; + bsi->iCache |= (*bsi->bytePtr++); + bsi->cachedBits = 32; + bsi->nBytes -= 4; + } else { + bsi->iCache = 0; + while (nBytes--) { + bsi->iCache |= (*bsi->bytePtr++); + bsi->iCache <<= 8; + } + bsi->iCache <<= ((3 - bsi->nBytes)*8); + bsi->cachedBits = 8*bsi->nBytes; + bsi->nBytes = 0; + } +} + +/************************************************************************************** + * Function: GetBits + * + * Description: get bits from bitstream, advance bitstream pointer + * + * Inputs: pointer to initialized BitStreamInfo struct + * number of bits to get from bitstream + * + * Outputs: updated bitstream info struct + * + * Return: the next nBits bits of data from bitstream buffer + * + * Notes: nBits must be in range [0, 31], nBits outside this range masked by 0x1f + * for speed, does not indicate error if you overrun bit buffer + * if nBits = 0, returns 0 (useful for scalefactor unpacking) + * + * TODO: optimize for ARM + **************************************************************************************/ +unsigned int GetBits(BitStreamInfo *bsi, int nBits) +{ + unsigned int data, lowBits; + + nBits &= 0x1f; /* nBits mod 32 to avoid unpredictable results like >> by negative amount */ + data = bsi->iCache >> (31 - nBits); /* unsigned >> so zero-extend */ + data >>= 1; /* do as >> 31, >> 1 so that nBits = 0 works okay (returns 0) */ + bsi->iCache <<= nBits; /* left-justify cache */ + bsi->cachedBits -= nBits; /* how many bits have we drawn from the cache so far */ + + /* if we cross an int boundary, refill the cache */ + if (bsi->cachedBits < 0) { + lowBits = -bsi->cachedBits; + RefillBitstreamCache(bsi); + data |= bsi->iCache >> (32 - lowBits); /* get the low-order bits */ + + bsi->cachedBits -= lowBits; /* how many bits have we drawn from the cache so far */ + bsi->iCache <<= lowBits; /* left-justify cache */ + } + + return data; +} + +/************************************************************************************** + * Function: CalcBitsUsed + * + * Description: calculate how many bits have been read from bitstream + * + * Inputs: pointer to initialized BitStreamInfo struct + * pointer to start of bitstream buffer + * bit offset into first byte of startBuf (0-7) + * + * Outputs: none + * + * Return: number of bits read from bitstream, as offset from startBuf:startOffset + **************************************************************************************/ +int CalcBitsUsed(BitStreamInfo *bsi, unsigned char *startBuf, int startOffset) +{ + int bitsUsed; + + bitsUsed = (bsi->bytePtr - startBuf) * 8; + bitsUsed -= bsi->cachedBits; + bitsUsed -= startOffset; + + return bitsUsed; +} + +/************************************************************************************** + * Function: CheckPadBit + * + * Description: check whether padding byte is present in an MP3 frame + * + * Inputs: MP3DecInfo struct with valid FrameHeader struct + * (filled by UnpackFrameHeader()) + * + * Outputs: none + * + * Return: 1 if pad bit is set, 0 if not, -1 if null input pointer + **************************************************************************************/ +int CheckPadBit(MP3DecInfo *mp3DecInfo) +{ + FrameHeader *fh; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS) + return -1; + + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + + return (fh->paddingBit ? 1 : 0); +} + +/************************************************************************************** + * Function: UnpackFrameHeader + * + * Description: parse the fields of the MP3 frame header + * + * Inputs: buffer pointing to a complete MP3 frame header (4 bytes, plus 2 if CRC) + * + * Outputs: filled frame header info in the MP3DecInfo structure + * updated platform-specific FrameHeader struct + * + * Return: length (in bytes) of frame header (for caller to calculate offset to + * first byte following frame header) + * -1 if null frameHeader or invalid header + * + * TODO: check for valid modes, depending on capabilities of decoder + * test CRC on actual stream (verify no endian problems) + **************************************************************************************/ +int UnpackFrameHeader(MP3DecInfo *mp3DecInfo, unsigned char *buf) +{ + + int verIdx; + FrameHeader *fh; + + /* validate pointers and sync word */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || (buf[0] & SYNCWORDH) != SYNCWORDH || (buf[1] & SYNCWORDL) != SYNCWORDL) + return -1; + + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + + /* read header fields - use bitmasks instead of GetBits() for speed, since format never varies */ + verIdx = (buf[1] >> 3) & 0x03; + fh->ver = (MPEGVersion)( verIdx == 0 ? MPEG25 : ((verIdx & 0x01) ? MPEG1 : MPEG2) ); + fh->layer = 4 - ((buf[1] >> 1) & 0x03); /* easy mapping of index to layer number, 4 = error */ + fh->crc = 1 - ((buf[1] >> 0) & 0x01); + fh->brIdx = (buf[2] >> 4) & 0x0f; + fh->srIdx = (buf[2] >> 2) & 0x03; + fh->paddingBit = (buf[2] >> 1) & 0x01; + fh->privateBit = (buf[2] >> 0) & 0x01; + fh->sMode = (StereoMode)((buf[3] >> 6) & 0x03); /* maps to correct enum (see definition) */ + fh->modeExt = (buf[3] >> 4) & 0x03; + fh->copyFlag = (buf[3] >> 3) & 0x01; + fh->origFlag = (buf[3] >> 2) & 0x01; + fh->emphasis = (buf[3] >> 0) & 0x03; + + /* check parameters to avoid indexing tables with bad values */ + if (fh->srIdx == 3 || fh->layer == 4 || fh->brIdx == 15) + return -1; + + fh->sfBand = &sfBandTable[fh->ver][fh->srIdx]; /* for readability (we reference sfBandTable many times in decoder) */ + if (fh->sMode != Joint) /* just to be safe (dequant, stproc check fh->modeExt) */ + fh->modeExt = 0; + + /* init user-accessible data */ + mp3DecInfo->nChans = (fh->sMode == Mono ? 1 : 2); + mp3DecInfo->samprate = samplerateTab[fh->ver][fh->srIdx]; + mp3DecInfo->nGrans = (fh->ver == MPEG1 ? NGRANS_MPEG1 : NGRANS_MPEG2); + mp3DecInfo->nGranSamps = ((int)samplesPerFrameTab[fh->ver][fh->layer - 1]) / mp3DecInfo->nGrans; + mp3DecInfo->layer = fh->layer; + mp3DecInfo->version = fh->ver; + + /* get bitrate and nSlots from table, unless brIdx == 0 (free mode) in which case caller must figure it out himself + * question - do we want to overwrite mp3DecInfo->bitrate with 0 each time if it's free mode, and + * copy the pre-calculated actual free bitrate into it in mp3dec.c (according to the spec, + * this shouldn't be necessary, since it should be either all frames free or none free) + */ + if (fh->brIdx) { + mp3DecInfo->bitrate = ((int)bitrateTab[fh->ver][fh->layer - 1][fh->brIdx]) * 1000; + + /* nSlots = total frame bytes (from table) - sideInfo bytes - header - CRC (if present) + pad (if present) */ + mp3DecInfo->nSlots = (int)slotTab[fh->ver][fh->srIdx][fh->brIdx] - + (int)sideBytesTab[fh->ver][(fh->sMode == Mono ? 0 : 1)] - + 4 - (fh->crc ? 2 : 0) + (fh->paddingBit ? 1 : 0); + } + + /* load crc word, if enabled, and return length of frame header (in bytes) */ + if (fh->crc) { + fh->CRCWord = ((int)buf[4] << 8 | (int)buf[5] << 0); + return 6; + } else { + fh->CRCWord = 0; + return 4; + } +} + +/************************************************************************************** + * Function: UnpackSideInfo + * + * Description: parse the fields of the MP3 side info header + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader() + * buffer pointing to the MP3 side info data + * + * Outputs: updated mainDataBegin in MP3DecInfo struct + * updated private (platform-specific) SideInfo struct + * + * Return: length (in bytes) of side info data + * -1 if null input pointers + **************************************************************************************/ +int UnpackSideInfo(MP3DecInfo *mp3DecInfo, unsigned char *buf) +{ + int gr, ch, bd, nBytes; + BitStreamInfo bitStreamInfo, *bsi; + FrameHeader *fh; + SideInfo *si; + SideInfoSub *sis; + + /* validate pointers and sync word */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS) + return -1; + + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + si = ((SideInfo *)(mp3DecInfo->SideInfoPS)); + + bsi = &bitStreamInfo; + if (fh->ver == MPEG1) { + /* MPEG 1 */ + nBytes = (fh->sMode == Mono ? SIBYTES_MPEG1_MONO : SIBYTES_MPEG1_STEREO); + SetBitstreamPointer(bsi, nBytes, buf); + si->mainDataBegin = GetBits(bsi, 9); + si->privateBits = GetBits(bsi, (fh->sMode == Mono ? 5 : 3)); + + for (ch = 0; ch < mp3DecInfo->nChans; ch++) + for (bd = 0; bd < MAX_SCFBD; bd++) + si->scfsi[ch][bd] = GetBits(bsi, 1); + } else { + /* MPEG 2, MPEG 2.5 */ + nBytes = (fh->sMode == Mono ? SIBYTES_MPEG2_MONO : SIBYTES_MPEG2_STEREO); + SetBitstreamPointer(bsi, nBytes, buf); + si->mainDataBegin = GetBits(bsi, 8); + si->privateBits = GetBits(bsi, (fh->sMode == Mono ? 1 : 2)); + } + + for(gr =0; gr < mp3DecInfo->nGrans; gr++) { + for (ch = 0; ch < mp3DecInfo->nChans; ch++) { + sis = &si->sis[gr][ch]; /* side info subblock for this granule, channel */ + + sis->part23Length = GetBits(bsi, 12); + sis->nBigvals = GetBits(bsi, 9); + sis->globalGain = GetBits(bsi, 8); + sis->sfCompress = GetBits(bsi, (fh->ver == MPEG1 ? 4 : 9)); + sis->winSwitchFlag = GetBits(bsi, 1); + + if(sis->winSwitchFlag) { + /* this is a start, stop, short, or mixed block */ + sis->blockType = GetBits(bsi, 2); /* 0 = normal, 1 = start, 2 = short, 3 = stop */ + sis->mixedBlock = GetBits(bsi, 1); /* 0 = not mixed, 1 = mixed */ + sis->tableSelect[0] = GetBits(bsi, 5); + sis->tableSelect[1] = GetBits(bsi, 5); + sis->tableSelect[2] = 0; /* unused */ + sis->subBlockGain[0] = GetBits(bsi, 3); + sis->subBlockGain[1] = GetBits(bsi, 3); + sis->subBlockGain[2] = GetBits(bsi, 3); + + /* TODO - check logic */ + if (sis->blockType == 0) { + /* this should not be allowed, according to spec */ + sis->nBigvals = 0; + sis->part23Length = 0; + sis->sfCompress = 0; + } else if (sis->blockType == 2 && sis->mixedBlock == 0) { + /* short block, not mixed */ + sis->region0Count = 8; + } else { + /* start, stop, or short-mixed */ + sis->region0Count = 7; + } + sis->region1Count = 20 - sis->region0Count; + } else { + /* this is a normal block */ + sis->blockType = 0; + sis->mixedBlock = 0; + sis->tableSelect[0] = GetBits(bsi, 5); + sis->tableSelect[1] = GetBits(bsi, 5); + sis->tableSelect[2] = GetBits(bsi, 5); + sis->region0Count = GetBits(bsi, 4); + sis->region1Count = GetBits(bsi, 3); + } + sis->preFlag = (fh->ver == MPEG1 ? GetBits(bsi, 1) : 0); + sis->sfactScale = GetBits(bsi, 1); + sis->count1TableSelect = GetBits(bsi, 1); + } + } + mp3DecInfo->mainDataBegin = si->mainDataBegin; /* needed by main decode loop */ + + ASSERT(nBytes == CalcBitsUsed(bsi, buf, 0) >> 3); + + return nBytes; +} + diff --git a/components/driver_sndmixer/libhelix-mp3/buffers.c b/components/driver_sndmixer/libhelix-mp3/buffers.c new file mode 100644 index 0000000..52b9bcf --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/buffers.c @@ -0,0 +1,177 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * buffers.c - allocation and freeing of internal MP3 decoder buffers + * + * All memory allocation for the codec is done in this file, so if you don't want + * to use other the default system malloc() and free() for heap management this is + * the only file you'll need to change. + **************************************************************************************/ + +//#include "hlxclib/stdlib.h" /* for malloc, free */ +#include +#include +#include "coder.h" + +/************************************************************************************** + * Function: ClearBuffer + * + * Description: fill buffer with 0's + * + * Inputs: pointer to buffer + * number of bytes to fill with 0 + * + * Outputs: cleared buffer + * + * Return: none + * + * Notes: slow, platform-independent equivalent to memset(buf, 0, nBytes) + **************************************************************************************/ +#define ClearBuffer(buf, nBytes) memset(buf, 0, nBytes) //fb +/* +static void ClearBuffer(void *buf, int nBytes) +{ + int i; + unsigned char *cbuf = (unsigned char *)buf; + + for (i = 0; i < nBytes; i++) + cbuf[i] = 0; + + //fb + memset(buf, 0, nBytes) + + return; +} +*/ +/************************************************************************************** + * Function: AllocateBuffers + * + * Description: allocate all the memory needed for the MP3 decoder + * + * Inputs: none + * + * Outputs: none + * + * Return: pointer to MP3DecInfo structure (initialized with pointers to all + * the internal buffers needed for decoding, all other members of + * MP3DecInfo structure set to 0) + * + * Notes: if one or more mallocs fail, function frees any buffers already + * allocated before returning + **************************************************************************************/ +MP3DecInfo *AllocateBuffers(void) +{ + MP3DecInfo *mp3DecInfo; + FrameHeader *fh; + SideInfo *si; + ScaleFactorInfo *sfi; + HuffmanInfo *hi; + DequantInfo *di; + IMDCTInfo *mi; + SubbandInfo *sbi; + + mp3DecInfo = (MP3DecInfo *)malloc(sizeof(MP3DecInfo)); + if (!mp3DecInfo) + return 0; + ClearBuffer(mp3DecInfo, sizeof(MP3DecInfo)); + + fh = (FrameHeader *) malloc(sizeof(FrameHeader)); + si = (SideInfo *) malloc(sizeof(SideInfo)); + sfi = (ScaleFactorInfo *) malloc(sizeof(ScaleFactorInfo)); + hi = (HuffmanInfo *) malloc(sizeof(HuffmanInfo)); + di = (DequantInfo *) malloc(sizeof(DequantInfo)); + mi = (IMDCTInfo *) malloc(sizeof(IMDCTInfo)); + sbi = (SubbandInfo *) malloc(sizeof(SubbandInfo)); + + mp3DecInfo->FrameHeaderPS = (void *)fh; + mp3DecInfo->SideInfoPS = (void *)si; + mp3DecInfo->ScaleFactorInfoPS = (void *)sfi; + mp3DecInfo->HuffmanInfoPS = (void *)hi; + mp3DecInfo->DequantInfoPS = (void *)di; + mp3DecInfo->IMDCTInfoPS = (void *)mi; + mp3DecInfo->SubbandInfoPS = (void *)sbi; + + if (!fh || !si || !sfi || !hi || !di || !mi || !sbi) { + FreeBuffers(mp3DecInfo); /* safe to call - only frees memory that was successfully allocated */ + return 0; + } + + /* important to do this - DSP primitives assume a bunch of state variables are 0 on first use */ + ClearBuffer(fh, sizeof(FrameHeader)); + ClearBuffer(si, sizeof(SideInfo)); + ClearBuffer(sfi, sizeof(ScaleFactorInfo)); + ClearBuffer(hi, sizeof(HuffmanInfo)); + ClearBuffer(di, sizeof(DequantInfo)); + ClearBuffer(mi, sizeof(IMDCTInfo)); + ClearBuffer(sbi, sizeof(SubbandInfo)); + + return mp3DecInfo; +} + +#define SAFE_FREE(x) {if (x) free(x); (x) = 0;} /* helper macro */ + +/************************************************************************************** + * Function: FreeBuffers + * + * Description: frees all the memory used by the MP3 decoder + * + * Inputs: pointer to initialized MP3DecInfo structure + * + * Outputs: none + * + * Return: none + * + * Notes: safe to call even if some buffers were not allocated (uses SAFE_FREE) + **************************************************************************************/ +void FreeBuffers(MP3DecInfo *mp3DecInfo) +{ + if (!mp3DecInfo) + return; + + SAFE_FREE(mp3DecInfo->FrameHeaderPS); + SAFE_FREE(mp3DecInfo->SideInfoPS); + SAFE_FREE(mp3DecInfo->ScaleFactorInfoPS); + SAFE_FREE(mp3DecInfo->HuffmanInfoPS); + SAFE_FREE(mp3DecInfo->DequantInfoPS); + SAFE_FREE(mp3DecInfo->IMDCTInfoPS); + SAFE_FREE(mp3DecInfo->SubbandInfoPS); + + SAFE_FREE(mp3DecInfo); +} diff --git a/components/driver_sndmixer/libhelix-mp3/coder.h b/components/driver_sndmixer/libhelix-mp3/coder.h new file mode 100644 index 0000000..5cc3ae4 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/coder.h @@ -0,0 +1,309 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * coder.h - private, implementation-specific header file + **************************************************************************************/ + +#ifndef _CODER_H +#define _CODER_H + +#pragma GCC optimize ("O3") + +#include "mp3common.h" + +#if defined(ASSERT) +#undef ASSERT +#endif +#if defined(_WIN32) && defined(_M_IX86) && (defined (_DEBUG) || defined (REL_ENABLE_ASSERTS)) +#define ASSERT(x) if (!(x)) __asm int 3; +#else +#define ASSERT(x) /* do nothing */ +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* clip to range [-2^n, 2^n - 1] */ +#define CLIP_2N(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (n)) { \ + (y) = sign ^ ((1 << (n)) - 1); \ + } \ +} + +#define SIBYTES_MPEG1_MONO 17 +#define SIBYTES_MPEG1_STEREO 32 +#define SIBYTES_MPEG2_MONO 9 +#define SIBYTES_MPEG2_STEREO 17 + +/* number of fraction bits for pow43Tab (see comments there) */ +#define POW43_FRACBITS_LOW 22 +#define POW43_FRACBITS_HIGH 12 + +#define DQ_FRACBITS_OUT 25 /* number of fraction bits in output of dequant */ +#define IMDCT_SCALE 2 /* additional scaling (by sqrt(2)) for fast IMDCT36 */ + +#define HUFF_PAIRTABS 32 +#define BLOCK_SIZE 18 +#define NBANDS 32 +#define MAX_REORDER_SAMPS ((192-126)*3) /* largest critical band for short blocks (see sfBandTable) */ +#define VBUF_LENGTH (17 * 2 * NBANDS) /* for double-sized vbuf FIFO */ + +/* additional external symbols to name-mangle for static linking */ +#define SetBitstreamPointer STATNAME(SetBitstreamPointer) +#define GetBits STATNAME(GetBits) +#define CalcBitsUsed STATNAME(CalcBitsUsed) +#define DequantChannel STATNAME(DequantChannel) +#define MidSideProc STATNAME(MidSideProc) +#define IntensityProcMPEG1 STATNAME(IntensityProcMPEG1) +#define IntensityProcMPEG2 STATNAME(IntensityProcMPEG2) +#define PolyphaseMono STATNAME(PolyphaseMono) +#define PolyphaseStereo STATNAME(PolyphaseStereo) +#define FDCT32 STATNAME(FDCT32) + +#define ISFMpeg1 STATNAME(ISFMpeg1) +#define ISFMpeg2 STATNAME(ISFMpeg2) +#define ISFIIP STATNAME(ISFIIP) +#define uniqueIDTab STATNAME(uniqueIDTab) +#define coef32 STATNAME(coef32) +#define polyCoef STATNAME(polyCoef) +#define csa STATNAME(csa) +#define imdctWin STATNAME(imdctWin) + +#define huffTable STATNAME(huffTable) +#define huffTabOffset STATNAME(huffTabOffset) +#define huffTabLookup STATNAME(huffTabLookup) +#define quadTable STATNAME(quadTable) +#define quadTabOffset STATNAME(quadTabOffset) +#define quadTabMaxBits STATNAME(quadTabMaxBits) + +/* map these to the corresponding 2-bit values in the frame header */ +typedef enum { + 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; + +typedef struct _BitStreamInfo { + unsigned char *bytePtr; + unsigned int iCache; + int cachedBits; + int nBytes; +} BitStreamInfo; + +typedef struct _FrameHeader { + MPEGVersion ver; /* version ID */ + 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 */ + StereoMode sMode; /* mono/stereo mode */ + 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) */ + + const SFBandTable *sfBand; +} FrameHeader; + +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; + +typedef struct _SideInfo { + int mainDataBegin; + int privateBits; + int scfsi[MAX_NCHAN][MAX_SCFBD]; /* 4 scalefactor bands per channel */ + + SideInfoSub sis[MAX_NGRAN][MAX_NCHAN]; +} SideInfo; + +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; + +typedef struct _DequantInfo { + int workBuf[MAX_REORDER_SAMPS]; /* workbuf for reordering short blocks */ + CriticalBandInfo cbi[MAX_NCHAN]; /* filled in dequantizer, used in joint stereo reconstruction */ +} DequantInfo; + +typedef struct _HuffmanInfo { + int huffDecBuf[MAX_NCHAN][MAX_NSAMP]; /* used both for decoded Huffman values and dequantized coefficients */ + int nonZeroBound[MAX_NCHAN]; /* number of coeffs in huffDecBuf[ch] which can be > 0 */ + int gb[MAX_NCHAN]; /* minimum number of guard bits in huffDecBuf[ch] */ +} HuffmanInfo; + +typedef enum _HuffTabType { + noBits, + oneShot, + loopNoLinbits, + loopLinbits, + quadA, + quadB, + invalidTab +} HuffTabType; + +typedef struct _HuffTabLookup { + int linBits; + int /*HuffTabType*/ tabType; +} HuffTabLookup; + +typedef struct _IMDCTInfo { + int outBuf[MAX_NCHAN][BLOCK_SIZE][NBANDS]; /* output of IMDCT */ + int overBuf[MAX_NCHAN][MAX_NSAMP / 2]; /* overlap-add buffer (by symmetry, only need 1/2 size) */ + int numPrevIMDCT[MAX_NCHAN]; /* how many IMDCT's calculated in this channel on prev. granule */ + int prevType[MAX_NCHAN]; + int prevWinSwitch[MAX_NCHAN]; + int gb[MAX_NCHAN]; +} IMDCTInfo; + +typedef struct _BlockCount { + int nBlocksLong; + int nBlocksTotal; + int nBlocksPrev; + int prevType; + int prevWinSwitch; + int currWinSwitch; + int gbIn; + int gbOut; +} BlockCount; + +/* max bits in scalefactors = 5, so use char's to save space */ +typedef struct _ScaleFactorInfoSub { + char l[23]; /* [band] */ + char s[13][3]; /* [band][window] */ +} ScaleFactorInfoSub; + +/* used in MPEG 2, 2.5 intensity (joint) stereo only */ +typedef struct _ScaleFactorJS { + int intensityScale; + int slen[4]; + int nr[4]; +} ScaleFactorJS; + +typedef struct _ScaleFactorInfo { + ScaleFactorInfoSub sfis[MAX_NGRAN][MAX_NCHAN]; + ScaleFactorJS sfjs; +} ScaleFactorInfo; + +/* 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[MAX_NCHAN * 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; + +/* bitstream.c */ +void SetBitstreamPointer(BitStreamInfo *bsi, int nBytes, unsigned char *buf); +unsigned int GetBits(BitStreamInfo *bsi, int nBits); +int CalcBitsUsed(BitStreamInfo *bsi, unsigned char *startBuf, int startOffset); + +/* dequant.c, dqchan.c, stproc.c */ +int DequantChannel(int *sampleBuf, int *workBuf, int *nonZeroBound, FrameHeader *fh, SideInfoSub *sis, + ScaleFactorInfoSub *sfis, CriticalBandInfo *cbi); +void MidSideProc(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, int mOut[2]); +void IntensityProcMPEG1(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, FrameHeader *fh, ScaleFactorInfoSub *sfis, + CriticalBandInfo *cbi, int midSideFlag, int mixFlag, int mOut[2]); +void IntensityProcMPEG2(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, FrameHeader *fh, ScaleFactorInfoSub *sfis, + CriticalBandInfo *cbi, ScaleFactorJS *sfjs, int midSideFlag, int mixFlag, int mOut[2]); + +/* dct32.c */ +// about 1 ms faster in RAM, but very large +void FDCT32(int *x, int *d, int offset, int oddBlock, int gb);// __attribute__ ((section (".data"))); + +/* hufftabs.c */ +extern const HuffTabLookup huffTabLookup[HUFF_PAIRTABS]; +extern const int huffTabOffset[HUFF_PAIRTABS]; +extern const unsigned short huffTable[]; +extern const unsigned char quadTable[64+16]; +extern const int quadTabOffset[2]; +extern const int quadTabMaxBits[2]; + +/* polyphase.c (or asmpoly.s) + * some platforms require a C++ compile of all source files, + * so if we're compiling C as C++ and using native assembly + * for these functions we need to prevent C++ name mangling. + */ +#ifdef __cplusplus +extern "C" { +#endif +void PolyphaseMono(short *pcm, int *vbuf, const int *coefBase); +void PolyphaseStereo(short *pcm, int *vbuf, const int *coefBase); +#ifdef __cplusplus +} +#endif + +/* trigtabs.c */ +extern const int imdctWin[4][36]; +extern const int ISFMpeg1[2][7]; +extern const int ISFMpeg2[2][2][16]; +extern const int ISFIIP[2][2]; +extern const int csa[8][2]; +extern const int coef32[31]; +extern const int polyCoef[264]; + +#endif /* _CODER_H */ diff --git a/components/driver_sndmixer/libhelix-mp3/dct32.c b/components/driver_sndmixer/libhelix-mp3/dct32.c new file mode 100644 index 0000000..e0761a7 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/dct32.c @@ -0,0 +1,280 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * dct32.c - optimized implementations of 32-point DCT for matrixing stage of + * polyphase filter + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +#define COS0_0 0x4013c251 /* Q31 */ +#define COS0_1 0x40b345bd /* Q31 */ +#define COS0_2 0x41fa2d6d /* Q31 */ +#define COS0_3 0x43f93421 /* Q31 */ +#define COS0_4 0x46cc1bc4 /* Q31 */ +#define COS0_5 0x4a9d9cf0 /* Q31 */ +#define COS0_6 0x4fae3711 /* Q31 */ +#define COS0_7 0x56601ea7 /* Q31 */ +#define COS0_8 0x5f4cf6eb /* Q31 */ +#define COS0_9 0x6b6fcf26 /* Q31 */ +#define COS0_10 0x7c7d1db3 /* Q31 */ +#define COS0_11 0x4ad81a97 /* Q30 */ +#define COS0_12 0x5efc8d96 /* Q30 */ +#define COS0_13 0x41d95790 /* Q29 */ +#define COS0_14 0x6d0b20cf /* Q29 */ +#define COS0_15 0x518522fb /* Q27 */ + +#define COS1_0 0x404f4672 /* Q31 */ +#define COS1_1 0x42e13c10 /* Q31 */ +#define COS1_2 0x48919f44 /* Q31 */ +#define COS1_3 0x52cb0e63 /* Q31 */ +#define COS1_4 0x64e2402e /* Q31 */ +#define COS1_5 0x43e224a9 /* Q30 */ +#define COS1_6 0x6e3c92c1 /* Q30 */ +#define COS1_7 0x519e4e04 /* Q28 */ + +#define COS2_0 0x4140fb46 /* Q31 */ +#define COS2_1 0x4cf8de88 /* Q31 */ +#define COS2_2 0x73326bbf /* Q31 */ +#define COS2_3 0x52036742 /* Q29 */ + +#define COS3_0 0x4545e9ef /* Q31 */ +#define COS3_1 0x539eba45 /* Q30 */ + +#define COS4_0 0x5a82799a /* Q31 */ + +// faster in ROM +static const int dcttab[48] = { + /* first pass */ + COS0_0, COS0_15, COS1_0, /* 31, 27, 31 */ + COS0_1, COS0_14, COS1_1, /* 31, 29, 31 */ + COS0_2, COS0_13, COS1_2, /* 31, 29, 31 */ + COS0_3, COS0_12, COS1_3, /* 31, 30, 31 */ + COS0_4, COS0_11, COS1_4, /* 31, 30, 31 */ + COS0_5, COS0_10, COS1_5, /* 31, 31, 30 */ + COS0_6, COS0_9, COS1_6, /* 31, 31, 30 */ + COS0_7, COS0_8, COS1_7, /* 31, 31, 28 */ + /* second pass */ + COS2_0, COS2_3, COS3_0, /* 31, 29, 31 */ + COS2_1, COS2_2, COS3_1, /* 31, 31, 30 */ + -COS2_0, -COS2_3, COS3_0, /* 31, 29, 31 */ + -COS2_1, -COS2_2, COS3_1, /* 31, 31, 30 */ + COS2_0, COS2_3, COS3_0, /* 31, 29, 31 */ + COS2_1, COS2_2, COS3_1, /* 31, 31, 30 */ + -COS2_0, -COS2_3, COS3_0, /* 31, 29, 31 */ + -COS2_1, -COS2_2, COS3_1, /* 31, 31, 30 */ +}; + +#define D32FP(i, s0, s1, s2) { \ + a0 = buf[i]; a3 = buf[31-i]; \ + a1 = buf[15-i]; a2 = buf[16+i]; \ + b0 = a0 + a3; b3 = MULSHIFT32(*cptr++, a0 - a3) << (s0); \ + b1 = a1 + a2; b2 = MULSHIFT32(*cptr++, a1 - a2) << (s1); \ + buf[i] = b0 + b1; buf[15-i] = MULSHIFT32(*cptr, b0 - b1) << (s2); \ + buf[16+i] = b2 + b3; buf[31-i] = MULSHIFT32(*cptr++, b3 - b2) << (s2); \ +} + +/************************************************************************************** + * Function: FDCT32 + * + * Description: Ken's highly-optimized 32-point DCT (radix-4 + radix-8) + * + * Inputs: input buffer, length = 32 samples + * require at least 6 guard bits in input vector x to avoid possibility + * of overflow in internal calculations (see bbtest_imdct test app) + * buffer offset and oddblock flag for polyphase filter input buffer + * number of guard bits in input + * + * Outputs: output buffer, data copied and interleaved for polyphase filter + * no guarantees about number of guard bits in output + * + * Return: none + * + * Notes: number of muls = 4*8 + 12*4 = 80 + * final stage of DCT is hardcoded to shuffle data into the proper order + * for the polyphase filterbank + * fully unrolled stage 1, for max precision (scale the 1/cos() factors + * differently, depending on magnitude) + * guard bit analysis verified by exhaustive testing of all 2^32 + * combinations of max pos/max neg values in x[] + * + * TODO: code organization and optimization for ARM + * possibly interleave stereo (cut # of coef loads in half - may not have + * enough registers) + **************************************************************************************/ +// about 1ms faster in RAM +/* attribute__ ((section (".data"))) */ void FDCT32(int *buf, int *dest, int offset, int oddBlock, int gb) +{ + int i, s, tmp, es; + const int *cptr = dcttab; + int a0, a1, a2, a3, a4, a5, a6, a7; + int b0, b1, b2, b3, b4, b5, b6, b7; + int *d; + + /* scaling - ensure at least 6 guard bits for DCT + * (in practice this is already true 99% of time, so this code is + * almost never triggered) + */ + es = 0; + if (gb < 6) { + es = 6 - gb; + for (i = 0; i < 32; i++) + buf[i] >>= es; + } + + /* first pass */ + D32FP(0, 1, 5, 1); + D32FP(1, 1, 3, 1); + D32FP(2, 1, 3, 1); + D32FP(3, 1, 2, 1); + D32FP(4, 1, 2, 1); + D32FP(5, 1, 1, 2); + D32FP(6, 1, 1, 2); + D32FP(7, 1, 1, 4); + + /* second pass */ + for (i = 4; i > 0; i--) { + a0 = buf[0]; a7 = buf[7]; a3 = buf[3]; a4 = buf[4]; + b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; + b3 = a3 + a4; b4 = MULSHIFT32(*cptr++, a3 - a4) << 3; + a0 = b0 + b3; a3 = MULSHIFT32(*cptr, b0 - b3) << 1; + a4 = b4 + b7; a7 = MULSHIFT32(*cptr++, b7 - b4) << 1; + + a1 = buf[1]; a6 = buf[6]; a2 = buf[2]; a5 = buf[5]; + b1 = a1 + a6; b6 = MULSHIFT32(*cptr++, a1 - a6) << 1; + b2 = a2 + a5; b5 = MULSHIFT32(*cptr++, a2 - a5) << 1; + a1 = b1 + b2; a2 = MULSHIFT32(*cptr, b1 - b2) << 2; + a5 = b5 + b6; a6 = MULSHIFT32(*cptr++, b6 - b5) << 2; + + b0 = a0 + a1; b1 = MULSHIFT32(COS4_0, a0 - a1) << 1; + b2 = a2 + a3; b3 = MULSHIFT32(COS4_0, a3 - a2) << 1; + buf[0] = b0; buf[1] = b1; + buf[2] = b2 + b3; buf[3] = b3; + + b4 = a4 + a5; b5 = MULSHIFT32(COS4_0, a4 - a5) << 1; + b6 = a6 + a7; b7 = MULSHIFT32(COS4_0, a7 - a6) << 1; + b6 += b7; + buf[4] = b4 + b6; buf[5] = b5 + b7; + buf[6] = b5 + b6; buf[7] = b7; + + buf += 8; + } + buf -= 32; /* reset */ + + /* sample 0 - always delayed one block */ + d = dest + 64*16 + ((offset - oddBlock) & 7) + (oddBlock ? 0 : VBUF_LENGTH); + s = buf[ 0]; d[0] = d[8] = s; + + /* samples 16 to 31 */ + d = dest + offset + (oddBlock ? VBUF_LENGTH : 0); + + s = buf[ 1]; d[0] = d[8] = s; d += 64; + + tmp = buf[25] + buf[29]; + s = buf[17] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 9] + buf[13]; d[0] = d[8] = s; d += 64; + s = buf[21] + tmp; d[0] = d[8] = s; d += 64; + + tmp = buf[29] + buf[27]; + s = buf[ 5]; d[0] = d[8] = s; d += 64; + s = buf[21] + tmp; d[0] = d[8] = s; d += 64; + s = buf[13] + buf[11]; d[0] = d[8] = s; d += 64; + s = buf[19] + tmp; d[0] = d[8] = s; d += 64; + + tmp = buf[27] + buf[31]; + s = buf[ 3]; d[0] = d[8] = s; d += 64; + s = buf[19] + tmp; d[0] = d[8] = s; d += 64; + s = buf[11] + buf[15]; d[0] = d[8] = s; d += 64; + s = buf[23] + tmp; d[0] = d[8] = s; d += 64; + + tmp = buf[31]; + s = buf[ 7]; d[0] = d[8] = s; d += 64; + s = buf[23] + tmp; d[0] = d[8] = s; d += 64; + s = buf[15]; d[0] = d[8] = s; d += 64; + s = tmp; d[0] = d[8] = s; + + /* samples 16 to 1 (sample 16 used again) */ + d = dest + 16 + ((offset - oddBlock) & 7) + (oddBlock ? 0 : VBUF_LENGTH); + + s = buf[ 1]; d[0] = d[8] = s; d += 64; + + tmp = buf[30] + buf[25]; + s = buf[17] + tmp; d[0] = d[8] = s; d += 64; + s = buf[14] + buf[ 9]; d[0] = d[8] = s; d += 64; + s = buf[22] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 6]; d[0] = d[8] = s; d += 64; + + tmp = buf[26] + buf[30]; + s = buf[22] + tmp; d[0] = d[8] = s; d += 64; + s = buf[10] + buf[14]; d[0] = d[8] = s; d += 64; + s = buf[18] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 2]; d[0] = d[8] = s; d += 64; + + tmp = buf[28] + buf[26]; + s = buf[18] + tmp; d[0] = d[8] = s; d += 64; + s = buf[12] + buf[10]; d[0] = d[8] = s; d += 64; + s = buf[20] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 4]; d[0] = d[8] = s; d += 64; + + tmp = buf[24] + buf[28]; + s = buf[20] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 8] + buf[12]; d[0] = d[8] = s; d += 64; + s = buf[16] + tmp; d[0] = d[8] = s; + + /* this is so rarely invoked that it's not worth making two versions of the output + * shuffle code (one for no shift, one for clip + variable shift) like in IMDCT + * here we just load, clip, shift, and store on the rare instances that es != 0 + */ + if (es) { + d = dest + 64*16 + ((offset - oddBlock) & 7) + (oddBlock ? 0 : VBUF_LENGTH); + s = d[0]; CLIP_2N(s, 31 - es); d[0] = d[8] = (s << es); + + d = dest + offset + (oddBlock ? VBUF_LENGTH : 0); + for (i = 16; i <= 31; i++) { + s = d[0]; CLIP_2N(s, 31 - es); d[0] = d[8] = (s << es); d += 64; + } + + d = dest + 16 + ((offset - oddBlock) & 7) + (oddBlock ? 0 : VBUF_LENGTH); + for (i = 15; i >= 0; i--) { + s = d[0]; CLIP_2N(s, 31 - es); d[0] = d[8] = (s << es); d += 64; + } + } +} diff --git a/components/driver_sndmixer/libhelix-mp3/dequant.c b/components/driver_sndmixer/libhelix-mp3/dequant.c new file mode 100644 index 0000000..b989b7d --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/dequant.c @@ -0,0 +1,158 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * dequant.c - dequantization, stereo processing (intensity, mid-side), short-block + * coefficient reordering + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: Dequantize + * + * Description: dequantize coefficients, decode stereo, reorder short blocks + * (one granule-worth) + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader(), UnpackSideInfo(), + * UnpackScaleFactors(), and DecodeHuffman() (for this granule) + * index of current granule + * + * Outputs: dequantized and reordered coefficients in hi->huffDecBuf + * (one granule-worth, all channels), format = Q26 + * operates in-place on huffDecBuf but also needs di->workBuf + * updated hi->nonZeroBound index for both channels + * + * Return: 0 on success, -1 if null input pointers + * + * Notes: In calling output Q(DQ_FRACBITS_OUT), we assume an implicit bias + * of 2^15. Some (floating-point) reference implementations factor this + * into the 2^(0.25 * gain) scaling explicitly. But to avoid precision + * loss, we don't do that. Instead take it into account in the final + * round to PCM (>> by 15 less than we otherwise would have). + * Equivalently, we can think of the dequantized coefficients as + * Q(DQ_FRACBITS_OUT - 15) with no implicit bias. + **************************************************************************************/ +int Dequantize(MP3DecInfo *mp3DecInfo, int gr) +{ + int i, ch, nSamps, mOut[2]; + FrameHeader *fh; + SideInfo *si; + ScaleFactorInfo *sfi; + HuffmanInfo *hi; + DequantInfo *di; + CriticalBandInfo *cbi; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS || !mp3DecInfo->ScaleFactorInfoPS || + !mp3DecInfo->HuffmanInfoPS || !mp3DecInfo->DequantInfoPS) + return -1; + + fh = (FrameHeader *)(mp3DecInfo->FrameHeaderPS); + + /* si is an array of up to 4 structs, stored as gr0ch0, gr0ch1, gr1ch0, gr1ch1 */ + si = (SideInfo *)(mp3DecInfo->SideInfoPS); + sfi = (ScaleFactorInfo *)(mp3DecInfo->ScaleFactorInfoPS); + hi = (HuffmanInfo *)mp3DecInfo->HuffmanInfoPS; + di = (DequantInfo *)mp3DecInfo->DequantInfoPS; + cbi = di->cbi; + mOut[0] = mOut[1] = 0; + + /* dequantize all the samples in each channel */ + for (ch = 0; ch < mp3DecInfo->nChans; ch++) { + hi->gb[ch] = DequantChannel(hi->huffDecBuf[ch], di->workBuf, &hi->nonZeroBound[ch], fh, + &si->sis[gr][ch], &sfi->sfis[gr][ch], &cbi[ch]); + } + + /* joint stereo processing assumes one guard bit in input samples + * it's extremely rare not to have at least one gb, so if this is the case + * just make a pass over the data and clip to [-2^30+1, 2^30-1] + * in practice this may never happen + */ + if (fh->modeExt && (hi->gb[0] < 1 || hi->gb[1] < 1)) { + for (i = 0; i < hi->nonZeroBound[0]; i++) { + if (hi->huffDecBuf[0][i] < -0x3fffffff) hi->huffDecBuf[0][i] = -0x3fffffff; + if (hi->huffDecBuf[0][i] > 0x3fffffff) hi->huffDecBuf[0][i] = 0x3fffffff; + } + for (i = 0; i < hi->nonZeroBound[1]; i++) { + if (hi->huffDecBuf[1][i] < -0x3fffffff) hi->huffDecBuf[1][i] = -0x3fffffff; + if (hi->huffDecBuf[1][i] > 0x3fffffff) hi->huffDecBuf[1][i] = 0x3fffffff; + } + } + + /* do mid-side stereo processing, if enabled */ + if (fh->modeExt >> 1) { + if (fh->modeExt & 0x01) { + /* intensity stereo enabled - run mid-side up to start of right zero region */ + if (cbi[1].cbType == 0) + nSamps = fh->sfBand->l[cbi[1].cbEndL + 1]; + else + nSamps = 3 * fh->sfBand->s[cbi[1].cbEndSMax + 1]; + } else { + /* intensity stereo disabled - run mid-side on whole spectrum */ + nSamps = MAX(hi->nonZeroBound[0], hi->nonZeroBound[1]); + } + MidSideProc(hi->huffDecBuf, nSamps, mOut); + } + + /* do intensity stereo processing, if enabled */ + if (fh->modeExt & 0x01) { + nSamps = hi->nonZeroBound[0]; + if (fh->ver == MPEG1) { + IntensityProcMPEG1(hi->huffDecBuf, nSamps, fh, &sfi->sfis[gr][1], di->cbi, + fh->modeExt >> 1, si->sis[gr][1].mixedBlock, mOut); + } else { + IntensityProcMPEG2(hi->huffDecBuf, nSamps, fh, &sfi->sfis[gr][1], di->cbi, &sfi->sfjs, + fh->modeExt >> 1, si->sis[gr][1].mixedBlock, mOut); + } + } + + /* adjust guard bit count and nonZeroBound if we did any stereo processing */ + if (fh->modeExt) { + hi->gb[0] = CLZ(mOut[0]) - 1; + hi->gb[1] = CLZ(mOut[1]) - 1; + nSamps = MAX(hi->nonZeroBound[0], hi->nonZeroBound[1]); + hi->nonZeroBound[0] = nSamps; + hi->nonZeroBound[1] = nSamps; + } + + /* output format Q(DQ_FRACBITS_OUT) */ + return 0; +} diff --git a/components/driver_sndmixer/libhelix-mp3/dqchan.c b/components/driver_sndmixer/libhelix-mp3/dqchan.c new file mode 100644 index 0000000..2847f0d --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/dqchan.c @@ -0,0 +1,376 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * August 2003 + * + * dqchan.c - dequantization of transform coefficients + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +typedef int ARRAY3[3]; /* for short-block reordering */ + +/* optional pre-emphasis for high-frequency scale factor bands */ +static 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] = { + 0x7fffffff, 0x6ba27e65, 0x5a82799a, 0x4c1bf829 +}; + +/* pow(2,-i/4) * pow(j,4/3) for i=0..3 j=0..15, Q25 format */ +const int pow43_14[4][16] = { +{ 0x00000000, 0x10000000, 0x285145f3, 0x453a5cdb, /* Q28 */ + 0x0cb2ff53, 0x111989d6, 0x15ce31c8, 0x1ac7f203, + 0x20000000, 0x257106b9, 0x2b16b4a3, 0x30ed74b4, + 0x36f23fa5, 0x3d227bd3, 0x437be656, 0x49fc823c, }, + +{ 0x00000000, 0x0d744fcd, 0x21e71f26, 0x3a36abd9, + 0x0aadc084, 0x0e610e6e, 0x12560c1d, 0x168523cf, + 0x1ae89f99, 0x1f7c03a4, 0x243bae49, 0x29249c67, + 0x2e34420f, 0x33686f85, 0x38bf3dff, 0x3e370182, }, + +{ 0x00000000, 0x0b504f33, 0x1c823e07, 0x30f39a55, + 0x08facd62, 0x0c176319, 0x0f6b3522, 0x12efe2ad, + 0x16a09e66, 0x1a79a317, 0x1e77e301, 0x2298d5b4, + 0x26da56fc, 0x2b3a902a, 0x2fb7e7e7, 0x3450f650, }, + +{ 0x00000000, 0x09837f05, 0x17f910d7, 0x2929c7a9, + 0x078d0dfa, 0x0a2ae661, 0x0cf73154, 0x0fec91cb, + 0x1306fe0a, 0x16434a6c, 0x199ee595, 0x1d17ae3d, + 0x20abd76a, 0x2459d551, 0x28204fbb, 0x2bfe1808, }, +}; + +/* pow(j,4/3) for j=16..63, Q23 format */ +const int pow43[] = { + 0x1428a2fa, 0x15db1bd6, 0x1796302c, 0x19598d85, + 0x1b24e8bb, 0x1cf7fcfa, 0x1ed28af2, 0x20b4582a, + 0x229d2e6e, 0x248cdb55, 0x26832fda, 0x28800000, + 0x2a832287, 0x2c8c70a8, 0x2e9bc5d8, 0x30b0ff99, + 0x32cbfd4a, 0x34eca001, 0x3712ca62, 0x393e6088, + 0x3b6f47e0, 0x3da56717, 0x3fe0a5fc, 0x4220ed72, + 0x44662758, 0x46b03e7c, 0x48ff1e87, 0x4b52b3f3, + 0x4daaebfd, 0x5007b497, 0x5268fc62, 0x54ceb29c, + 0x5738c721, 0x59a72a59, 0x5c19cd35, 0x5e90a129, + 0x610b9821, 0x638aa47f, 0x660db90f, 0x6894c90b, + 0x6b1fc80c, 0x6daeaa0d, 0x70416360, 0x72d7e8b0, + 0x75722ef9, 0x78102b85, 0x7ab1d3ec, 0x7d571e09, +}; + +/* sqrt(0.5) in Q31 format */ +#define SQRTHALF 0x5a82799a + +/* + * 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 + */ +static const unsigned int poly43lo[5] = { 0x29a0bda9, 0xb02e4828, 0x5957aa1b, 0x236c498d, 0xff581859 }; +static const unsigned int poly43hi[5] = { 0x10852163, 0xd333f6a4, 0x46e9408b, 0x27c2cef0, 0xfef577b4 }; + +/* pow(2, i*4/3) as exp and frac */ +const int pow2exp[8] = { 14, 13, 11, 10, 9, 7, 6, 5 }; + +const int pow2frac[8] = { + 0x6597fa94, 0x50a28be6, 0x7fffffff, 0x6597fa94, + 0x50a28be6, 0x7fffffff, 0x6597fa94, 0x50a28be6 +}; + +/************************************************************************************** + * Function: DequantBlock + * + * Description: Ken's highly-optimized, low memory dequantizer performing the operation + * y = pow(x, 4.0/3.0) * pow(2, 25 - scale/4.0) + * + * Inputs: input buffer of decode Huffman codewords (signed-magnitude) + * output buffer of same length (in-place (outbuf = inbuf) is allowed) + * number of samples + * + * Outputs: dequantized samples in Q25 format + * + * Return: bitwise-OR of the unsigned outputs (for guard bit calculations) + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static int DequantBlock(int *inbuf, int *outbuf, int num, int scale) +{ + int tab4[4]; + int scalef, scalei, shift; + int sx, x, y; + int mask = 0; + const int *tab16; + const unsigned int *coef; + + tab16 = pow43_14[scale & 0x3]; + scalef = pow14[scale & 0x3]; + scalei = MIN(scale >> 2, 31); /* smallest input scale = -47, so smallest scalei = -12 */ + + /* cache first 4 values */ + shift = MIN(scalei + 3, 31); + shift = MAX(shift, 0); + tab4[0] = 0; + tab4[1] = tab16[1] >> shift; + tab4[2] = tab16[2] >> shift; + tab4[3] = tab16[3] >> shift; + + do { + + sx = *inbuf++; + x = sx & 0x7fffffff; /* sx = sign|mag */ + + if (x < 4) { + + y = tab4[x]; + + } else if (x < 16) { + + y = tab16[x]; + y = (scalei < 0) ? y << -scalei : y >> scalei; + + } else { + + if (x < 64) { + + y = pow43[x-16]; + + /* fractional scale */ + y = MULSHIFT32(y, scalef); + shift = scalei - 3; + + } else { + + /* normalize to [0x40000000, 0x7fffffff] */ + x <<= 17; + shift = 0; + if (x < 0x08000000) + x <<= 4, shift += 4; + if (x < 0x20000000) + x <<= 2, shift += 2; + if (x < 0x40000000) + x <<= 1, shift += 1; + + coef = (x < SQRTHALF) ? poly43lo : poly43hi; + + /* polynomial */ + y = coef[0]; + y = MULSHIFT32(y, x) + coef[1]; + y = MULSHIFT32(y, x) + coef[2]; + y = MULSHIFT32(y, x) + coef[3]; + y = MULSHIFT32(y, x) + coef[4]; + y = MULSHIFT32(y, pow2frac[shift]) << 3; + + /* fractional scale */ + y = MULSHIFT32(y, scalef); + shift = scalei - pow2exp[shift]; + } + + /* integer scale */ + if (shift < 0) { + shift = -shift; + if (y > (0x7fffffff >> shift)) + y = 0x7fffffff; /* clip */ + else + y <<= shift; + } else { + y >>= shift; + } + } + + /* sign and store */ + mask |= y; + *outbuf++ = (sx < 0) ? -y : y; + + } while (--num); + + return mask; +} + +/************************************************************************************** + * Function: DequantChannel + * + * Description: dequantize one granule, one channel worth of decoded Huffman codewords + * + * Inputs: sample buffer (decoded Huffman codewords), length = MAX_NSAMP samples + * work buffer for reordering short-block, length = MAX_REORDER_SAMPS + * samples (3 * width of largest short-block critical band) + * non-zero bound for this channel/granule + * valid FrameHeader, SideInfoSub, ScaleFactorInfoSub, and CriticalBandInfo + * structures for this channel/granule + * + * Outputs: MAX_NSAMP dequantized samples in sampleBuf + * updated non-zero bound (indicating which samples are != 0 after DQ) + * filled-in cbi structure indicating start and end critical bands + * + * Return: minimum number of guard bits in dequantized sampleBuf + * + * Notes: dequantized samples in Q(DQ_FRACBITS_OUT) format + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ int DequantChannel(int *sampleBuf, int *workBuf, int *nonZeroBound, FrameHeader *fh, SideInfoSub *sis, + ScaleFactorInfoSub *sfis, CriticalBandInfo *cbi) +{ + int i, j, w, cb; + int /* cbStartL, */ cbEndL, cbStartS, cbEndS; + int nSamps, nonZero, sfactMultiplier, gbMask; + int globalGain, gainI; + int cbMax[3]; + ARRAY3 *buf; /* short block reorder */ + + /* set default start/end points for short/long blocks - will update with non-zero cb info */ + if (sis->blockType == 2) { + // cbStartL = 0; + if (sis->mixedBlock) { + cbEndL = (fh->ver == MPEG1 ? 8 : 6); + cbStartS = 3; + } else { + cbEndL = 0; + cbStartS = 0; + } + cbEndS = 13; + } else { + /* long block */ + //cbStartL = 0; + cbEndL = 22; + cbStartS = 13; + cbEndS = 13; + } + cbMax[2] = cbMax[1] = cbMax[0] = 0; + gbMask = 0; + i = 0; + + /* sfactScale = 0 --> quantizer step size = 2 + * sfactScale = 1 --> quantizer step size = sqrt(2) + * so sfactMultiplier = 2 or 4 (jump through globalGain by powers of 2 or sqrt(2)) + */ + sfactMultiplier = 2 * (sis->sfactScale + 1); + + /* offset globalGain by -2 if midSide enabled, for 1/sqrt(2) used in MidSideProc() + * (DequantBlock() does 0.25 * gainI so knocking it down by two is the same as + * dividing every sample by sqrt(2) = multiplying by 2^-.5) + */ + globalGain = sis->globalGain; + if (fh->modeExt >> 1) + globalGain -= 2; + globalGain += IMDCT_SCALE; /* scale everything by sqrt(2), for fast IMDCT36 */ + + /* long blocks */ + for (cb = 0; cb < cbEndL; cb++) { + + nonZero = 0; + nSamps = fh->sfBand->l[cb + 1] - fh->sfBand->l[cb]; + gainI = 210 - globalGain + sfactMultiplier * (sfis->l[cb] + (sis->preFlag ? (int)preTab[cb] : 0)); + + nonZero |= DequantBlock(sampleBuf + i, sampleBuf + i, nSamps, gainI); + i += nSamps; + + /* update highest non-zero critical band */ + if (nonZero) + cbMax[0] = cb; + gbMask |= nonZero; + + if (i >= *nonZeroBound) + break; + } + + /* set cbi (Type, EndS[], EndSMax will be overwritten if we proceed to do short blocks) */ + cbi->cbType = 0; /* long only */ + cbi->cbEndL = cbMax[0]; + cbi->cbEndS[0] = cbi->cbEndS[1] = cbi->cbEndS[2] = 0; + cbi->cbEndSMax = 0; + + /* early exit if no short blocks */ + if (cbStartS >= 12) + return CLZ(gbMask) - 1; + + /* short blocks */ + cbMax[2] = cbMax[1] = cbMax[0] = cbStartS; + for (cb = cbStartS; cb < cbEndS; cb++) { + + nSamps = fh->sfBand->s[cb + 1] - fh->sfBand->s[cb]; + for (w = 0; w < 3; w++) { + nonZero = 0; + gainI = 210 - globalGain + 8*sis->subBlockGain[w] + sfactMultiplier*(sfis->s[cb][w]); + + nonZero |= DequantBlock(sampleBuf + i + nSamps*w, workBuf + nSamps*w, nSamps, gainI); + + /* update highest non-zero critical band */ + if (nonZero) + cbMax[w] = cb; + gbMask |= nonZero; + } + + /* reorder blocks */ + buf = (ARRAY3 *)(sampleBuf + i); + i += 3*nSamps; + for (j = 0; j < nSamps; j++) { + buf[j][0] = workBuf[0*nSamps + j]; + buf[j][1] = workBuf[1*nSamps + j]; + buf[j][2] = workBuf[2*nSamps + j]; + } + + ASSERT(3*nSamps <= MAX_REORDER_SAMPS); + + if (i >= *nonZeroBound) + break; + } + + /* i = last non-zero INPUT sample processed, which corresponds to highest possible non-zero + * OUTPUT sample (after reorder) + * however, the original nzb is no longer necessarily true + * for each cb, buf[][] is updated with 3*nSamps samples (i increases 3*nSamps each time) + * (buf[j + 1][0] = 3 (input) samples ahead of buf[j][0]) + * so update nonZeroBound to i + */ + *nonZeroBound = i; + + ASSERT(*nonZeroBound <= MAX_NSAMP); + + cbi->cbType = (sis->mixedBlock ? 2 : 1); /* 2 = mixed short/long, 1 = short only */ + + cbi->cbEndS[0] = cbMax[0]; + cbi->cbEndS[1] = cbMax[1]; + cbi->cbEndS[2] = cbMax[2]; + + cbi->cbEndSMax = cbMax[0]; + cbi->cbEndSMax = MAX(cbi->cbEndSMax, cbMax[1]); + cbi->cbEndSMax = MAX(cbi->cbEndSMax, cbMax[2]); + + return CLZ(gbMask) - 1; +} + diff --git a/components/driver_sndmixer/libhelix-mp3/huffman.c b/components/driver_sndmixer/libhelix-mp3/huffman.c new file mode 100644 index 0000000..82a813e --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/huffman.c @@ -0,0 +1,461 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * July 2003 + * + * huffman.c - Huffman decoding of transform coefficients + **************************************************************************************/ + +#include "coder.h" + +/* helper macros - see comments in hufftabs.c about the format of the huffman tables */ +#define GetMaxbits(x) ((int)( (((unsigned short)(x)) >> 0) & 0x000f)) +#define GetHLen(x) ((int)( (((unsigned short)(x)) >> 12) & 0x000f)) +#define GetCWY(x) ((int)( (((unsigned short)(x)) >> 8) & 0x000f)) +#define GetCWX(x) ((int)( (((unsigned short)(x)) >> 4) & 0x000f)) +#define GetSignBits(x) ((int)( (((unsigned short)(x)) >> 0) & 0x000f)) + +#define GetHLenQ(x) ((int)( (((unsigned char)(x)) >> 4) & 0x0f)) +#define GetCWVQ(x) ((int)( (((unsigned char)(x)) >> 3) & 0x01)) +#define GetCWWQ(x) ((int)( (((unsigned char)(x)) >> 2) & 0x01)) +#define GetCWXQ(x) ((int)( (((unsigned char)(x)) >> 1) & 0x01)) +#define GetCWYQ(x) ((int)( (((unsigned char)(x)) >> 0) & 0x01)) + +/* apply sign of s to the positive number x (save in MSB, will do two's complement in dequant) */ +#define ApplySign(x, s) { (x) |= ((s) & 0x80000000); } + +/************************************************************************************** + * Function: DecodeHuffmanPairs + * + * Description: decode 2-way vector Huffman codes in the "bigValues" region of spectrum + * + * Inputs: valid BitStreamInfo struct, pointing to start of pair-wise codes + * pointer to xy buffer to received decoded values + * number of codewords to decode + * index of Huffman table to use + * number of bits remaining in bitstream + * + * Outputs: pairs of decoded coefficients in vwxy + * updated BitStreamInfo struct + * + * Return: number of bits used, or -1 if out of bits + * + * Notes: assumes that nVals is an even number + * si_huff.bit tests every Huffman codeword in every table (though not + * necessarily all linBits outputs for x,y > 15) + **************************************************************************************/ +// no improvement with section=data +static int DecodeHuffmanPairs(int *xy, int nVals, int tabIdx, int bitsLeft, unsigned char *buf, int bitOffset) +{ + int i, x, y; + int cachedBits, padBits, len, startBits, linBits, maxBits, minBits; + HuffTabType tabType; + unsigned short cw, *tBase, *tCurr; + unsigned int cache; + + if(nVals <= 0) + return 0; + + if (bitsLeft < 0) + return -1; + startBits = bitsLeft; + + tBase = (unsigned short *)(huffTable + huffTabOffset[tabIdx]); + linBits = huffTabLookup[tabIdx].linBits; + tabType = huffTabLookup[tabIdx].tabType; + + ASSERT(!(nVals & 0x01)); + ASSERT(tabIdx < HUFF_PAIRTABS); + ASSERT(tabIdx >= 0); + ASSERT(tabType != invalidTab); + + /* initially fill cache with any partial byte */ + cache = 0; + cachedBits = (8 - bitOffset) & 0x07; + if (cachedBits) + cache = (unsigned int)(*buf++) << (32 - cachedBits); + bitsLeft -= cachedBits; + + if (tabType == noBits) { + /* table 0, no data, x = y = 0 */ + for (i = 0; i < nVals; i+=2) { + xy[i+0] = 0; + xy[i+1] = 0; + } + return 0; + } else if (tabType == oneShot) { + /* single lookup, no escapes */ + maxBits = GetMaxbits(tBase[0]); + tBase++; + padBits = 0; + while (nVals > 0) { + /* refill cache - assumes cachedBits <= 16 */ + if (bitsLeft >= 16) { + /* load 2 new bytes into left-justified cache */ + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += 16; + bitsLeft -= 16; + } else { + /* last time through, pad cache with zeros and drain cache */ + if (cachedBits + bitsLeft <= 0) return -1; + if (bitsLeft > 0) cache |= (unsigned int)(*buf++) << (24 - cachedBits); + if (bitsLeft > 8) cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += bitsLeft; + bitsLeft = 0; + + cache &= (signed int)0x80000000 >> (cachedBits - 1); + padBits = 11; + cachedBits += padBits; /* okay if this is > 32 (0's automatically shifted in from right) */ + } + + /* largest maxBits = 9, plus 2 for sign bits, so make sure cache has at least 11 bits */ + while (nVals > 0 && cachedBits >= 11 ) { + cw = tBase[cache >> (32 - maxBits)]; + len = GetHLen(cw); + cachedBits -= len; + cache <<= len; + + x = GetCWX(cw); if (x) {ApplySign(x, cache); cache <<= 1; cachedBits--;} + y = GetCWY(cw); if (y) {ApplySign(y, cache); cache <<= 1; cachedBits--;} + + /* ran out of bits - should never have consumed padBits */ + if (cachedBits < padBits) + return -1; + + *xy++ = x; + *xy++ = y; + nVals -= 2; + } + } + bitsLeft += (cachedBits - padBits); + return (startBits - bitsLeft); + } else if (tabType == loopLinbits || tabType == loopNoLinbits) { + tCurr = tBase; + padBits = 0; + while (nVals > 0) { + /* refill cache - assumes cachedBits <= 16 */ + if (bitsLeft >= 16) { + /* load 2 new bytes into left-justified cache */ + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += 16; + bitsLeft -= 16; + } else { + /* last time through, pad cache with zeros and drain cache */ + if (cachedBits + bitsLeft <= 0) return -1; + if (bitsLeft > 0) cache |= (unsigned int)(*buf++) << (24 - cachedBits); + if (bitsLeft > 8) cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += bitsLeft; + bitsLeft = 0; + + cache &= (signed int)0x80000000 >> (cachedBits - 1); + padBits = 11; + cachedBits += padBits; /* okay if this is > 32 (0's automatically shifted in from right) */ + } + + /* largest maxBits = 9, plus 2 for sign bits, so make sure cache has at least 11 bits */ + while (nVals > 0 && cachedBits >= 11 ) { + maxBits = GetMaxbits(tCurr[0]); + cw = tCurr[(cache >> (32 - maxBits)) + 1]; + len = GetHLen(cw); + if (!len) { + cachedBits -= maxBits; + cache <<= maxBits; + tCurr += cw; + continue; + } + cachedBits -= len; + cache <<= len; + + x = GetCWX(cw); + y = GetCWY(cw); + + if (x == 15 && tabType == loopLinbits) { + minBits = linBits + 1 + (y ? 1 : 0); + if (cachedBits + bitsLeft < minBits) + return -1; + while (cachedBits < minBits) { + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cachedBits += 8; + bitsLeft -= 8; + } + if (bitsLeft < 0) { + cachedBits += bitsLeft; + bitsLeft = 0; + cache &= (signed int)0x80000000 >> (cachedBits - 1); + } + x += (int)(cache >> (32 - linBits)); + cachedBits -= linBits; + cache <<= linBits; + } + if (x) {ApplySign(x, cache); cache <<= 1; cachedBits--;} + + if (y == 15 && tabType == loopLinbits) { + minBits = linBits + 1; + if (cachedBits + bitsLeft < minBits) + return -1; + while (cachedBits < minBits) { + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cachedBits += 8; + bitsLeft -= 8; + } + if (bitsLeft < 0) { + cachedBits += bitsLeft; + bitsLeft = 0; + cache &= (signed int)0x80000000 >> (cachedBits - 1); + } + y += (int)(cache >> (32 - linBits)); + cachedBits -= linBits; + cache <<= linBits; + } + if (y) {ApplySign(y, cache); cache <<= 1; cachedBits--;} + + /* ran out of bits - should never have consumed padBits */ + if (cachedBits < padBits) + return -1; + + *xy++ = x; + *xy++ = y; + nVals -= 2; + tCurr = tBase; + } + } + bitsLeft += (cachedBits - padBits); + return (startBits - bitsLeft); + } + + /* error in bitstream - trying to access unused Huffman table */ + return -1; +} + +/************************************************************************************** + * Function: DecodeHuffmanQuads + * + * Description: decode 4-way vector Huffman codes in the "count1" region of spectrum + * + * Inputs: valid BitStreamInfo struct, pointing to start of quadword codes + * pointer to vwxy buffer to received decoded values + * maximum number of codewords to decode + * index of quadword table (0 = table A, 1 = table B) + * number of bits remaining in bitstream + * + * Outputs: quadruples of decoded coefficients in vwxy + * updated BitStreamInfo struct + * + * Return: index of the first "zero_part" value (index of the first sample + * of the quad word after which all samples are 0) + * + * Notes: si_huff.bit tests every vwxy output in both quad tables + **************************************************************************************/ +// no improvement with section=data +static int DecodeHuffmanQuads(int *vwxy, int nVals, int tabIdx, int bitsLeft, unsigned char *buf, int bitOffset) +{ + int i, v, w, x, y; + int len, maxBits, cachedBits, padBits; + unsigned int cache; + unsigned char cw, *tBase; + + if (bitsLeft <= 0) + return 0; + + tBase = (unsigned char *)quadTable + quadTabOffset[tabIdx]; + maxBits = quadTabMaxBits[tabIdx]; + + /* initially fill cache with any partial byte */ + cache = 0; + cachedBits = (8 - bitOffset) & 0x07; + if (cachedBits) + cache = (unsigned int)(*buf++) << (32 - cachedBits); + bitsLeft -= cachedBits; + + i = padBits = 0; + while (i < (nVals - 3)) { + /* refill cache - assumes cachedBits <= 16 */ + if (bitsLeft >= 16) { + /* load 2 new bytes into left-justified cache */ + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += 16; + bitsLeft -= 16; + } else { + /* last time through, pad cache with zeros and drain cache */ + if (cachedBits + bitsLeft <= 0) return i; + if (bitsLeft > 0) cache |= (unsigned int)(*buf++) << (24 - cachedBits); + if (bitsLeft > 8) cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += bitsLeft; + bitsLeft = 0; + + cache &= (signed int)0x80000000 >> (cachedBits - 1); + padBits = 10; + cachedBits += padBits; /* okay if this is > 32 (0's automatically shifted in from right) */ + } + + /* largest maxBits = 6, plus 4 for sign bits, so make sure cache has at least 10 bits */ + while (i < (nVals - 3) && cachedBits >= 10 ) { + cw = tBase[cache >> (32 - maxBits)]; + len = GetHLenQ(cw); + cachedBits -= len; + cache <<= len; + + v = GetCWVQ(cw); if(v) {ApplySign(v, cache); cache <<= 1; cachedBits--;} + w = GetCWWQ(cw); if(w) {ApplySign(w, cache); cache <<= 1; cachedBits--;} + x = GetCWXQ(cw); if(x) {ApplySign(x, cache); cache <<= 1; cachedBits--;} + y = GetCWYQ(cw); if(y) {ApplySign(y, cache); cache <<= 1; cachedBits--;} + + /* ran out of bits - okay (means we're done) */ + if (cachedBits < padBits) + return i; + + *vwxy++ = v; + *vwxy++ = w; + *vwxy++ = x; + *vwxy++ = y; + i += 4; + } + } + + /* decoded max number of quad values */ + return i; +} + +/************************************************************************************** + * Function: DecodeHuffman + * + * Description: decode one granule, one channel worth of Huffman codes + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader(), UnpackSideInfo(), + * and UnpackScaleFactors() (for this granule) + * buffer pointing to start of Huffman data in MP3 frame + * pointer to bit offset (0-7) indicating starting bit in buf[0] + * number of bits in the Huffman data section of the frame + * (could include padding bits) + * index of current granule and channel + * + * Outputs: decoded coefficients in hi->huffDecBuf[ch] (hi pointer in mp3DecInfo) + * updated bitOffset + * + * Return: length (in bytes) of Huffman codes + * bitOffset also returned in parameter (0 = MSB, 7 = LSB of + * byte located at buf + offset) + * -1 if null input pointers, huffBlockBits < 0, or decoder runs + * out of bits prematurely (invalid bitstream) + **************************************************************************************/ +// .data about 1ms faster per frame +/* __attribute__ ((section (".data"))) */ int DecodeHuffman(MP3DecInfo *mp3DecInfo, unsigned char *buf, int *bitOffset, int huffBlockBits, int gr, int ch) +{ + int r1Start, r2Start, rEnd[4]; /* region boundaries */ + int i, w, bitsUsed, bitsLeft; + unsigned char *startBuf = buf; + + FrameHeader *fh; + SideInfo *si; + SideInfoSub *sis; + //ScaleFactorInfo *sfi; + HuffmanInfo *hi; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS || !mp3DecInfo->ScaleFactorInfoPS || !mp3DecInfo->HuffmanInfoPS) + return -1; + + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + si = ((SideInfo *)(mp3DecInfo->SideInfoPS)); + sis = &si->sis[gr][ch]; + //sfi = ((ScaleFactorInfo *)(mp3DecInfo->ScaleFactorInfoPS)); + hi = (HuffmanInfo*)(mp3DecInfo->HuffmanInfoPS); + + if (huffBlockBits < 0) + return -1; + + /* figure out region boundaries (the first 2*bigVals coefficients divided into 3 regions) */ + if (sis->winSwitchFlag && sis->blockType == 2) { + if (sis->mixedBlock == 0) { + r1Start = fh->sfBand->s[(sis->region0Count + 1)/3] * 3; + } else { + if (fh->ver == MPEG1) { + r1Start = fh->sfBand->l[sis->region0Count + 1]; + } else { + /* see MPEG2 spec for explanation */ + w = fh->sfBand->s[4] - fh->sfBand->s[3]; + r1Start = fh->sfBand->l[6] + 2*w; + } + } + r2Start = MAX_NSAMP; /* short blocks don't have region 2 */ + } else { + r1Start = fh->sfBand->l[sis->region0Count + 1]; + r2Start = fh->sfBand->l[sis->region0Count + 1 + sis->region1Count + 1]; + } + + /* offset rEnd index by 1 so first region = rEnd[1] - rEnd[0], etc. */ + rEnd[3] = MIN(MAX_NSAMP, 2 * sis->nBigvals); + rEnd[2] = MIN(r2Start, rEnd[3]); + rEnd[1] = MIN(r1Start, rEnd[3]); + rEnd[0] = 0; + + /* rounds up to first all-zero pair (we don't check last pair for (x,y) == (non-zero, zero)) */ + hi->nonZeroBound[ch] = rEnd[3]; + + /* decode Huffman pairs (rEnd[i] are always even numbers) */ + bitsLeft = huffBlockBits; + for (i = 0; i < 3; i++) { + bitsUsed = DecodeHuffmanPairs(hi->huffDecBuf[ch] + rEnd[i], rEnd[i+1] - rEnd[i], sis->tableSelect[i], bitsLeft, buf, *bitOffset); + if (bitsUsed < 0 || bitsUsed > bitsLeft) /* error - overran end of bitstream */ + return -1; + + /* update bitstream position */ + buf += (bitsUsed + *bitOffset) >> 3; + *bitOffset = (bitsUsed + *bitOffset) & 0x07; + bitsLeft -= bitsUsed; + } + + /* decode Huffman quads (if any) */ + hi->nonZeroBound[ch] += DecodeHuffmanQuads(hi->huffDecBuf[ch] + rEnd[3], MAX_NSAMP - rEnd[3], sis->count1TableSelect, bitsLeft, buf, *bitOffset); + + ASSERT(hi->nonZeroBound[ch] <= MAX_NSAMP); + for (i = hi->nonZeroBound[ch]; i < MAX_NSAMP; i++) + hi->huffDecBuf[ch][i] = 0; + + /* If bits used for 576 samples < huffBlockBits, then the extras are considered + * to be stuffing bits (throw away, but need to return correct bitstream position) + */ + buf += (bitsLeft + *bitOffset) >> 3; + *bitOffset = (bitsLeft + *bitOffset) & 0x07; + + return (buf - startBuf); +} + diff --git a/components/driver_sndmixer/libhelix-mp3/hufftabs.c b/components/driver_sndmixer/libhelix-mp3/hufftabs.c new file mode 100644 index 0000000..6a38919 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/hufftabs.c @@ -0,0 +1,754 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * hufftabs.c - compressed Huffman code tables + **************************************************************************************/ +#include "coder.h" + +/* NOTE - regenerated tables to use shorts instead of ints + * (all needed data can fit in 16 bits - see below) + * + * format 0xABCD + * A = length of codeword + * B = y value + * C = x value + * D = number of sign bits (0, 1, or 2) + * + * to read a CW, the code reads maxbits from the stream (dep. on + * table index), but doesn't remove them from the bitstream reader + * then it gets the correct CW by direct lookup into the table + * of length (2^maxbits) (more complicated for non-oneShot...) + * for CW's with hlen < maxbits, there are multiple entries in the + * table (extra bits are don't cares) + * the bitstream reader then "purges" (or removes) only the correct + * number of bits for the chosen CW + * + * entries starting with F are special: D (signbits) is maxbits, + * so the decoder always checks huffTableXX[0] first, gets the + * signbits, and reads that many bits from the bitstream + * (sometimes it takes > 1 read to get the value, so maxbits is + * can get updated by jumping to another value starting with 0xF) + * entries starting with 0 are also special: A = hlen = 0, rest of + * value is an offset to jump higher in the table (for tables of + * type loopNoLinbits or loopLinbits) + */ + +/* store Huffman codes as one big table plus table of offsets, since some platforms + * don't properly support table-of-tables (table of pointers to other const tables) + */ +const unsigned short huffTable[] = { + /* huffTable01[9] */ + 0xf003, 0x3112, 0x3101, 0x2011, 0x2011, 0x1000, 0x1000, 0x1000, + 0x1000, + + /* huffTable02[65] */ + 0xf006, 0x6222, 0x6201, 0x5212, 0x5212, 0x5122, 0x5122, 0x5021, + 0x5021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, + + /* huffTable03[65] */ + 0xf006, 0x6222, 0x6201, 0x5212, 0x5212, 0x5122, 0x5122, 0x5021, + 0x5021, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, + 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, + 0x2101, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, + + /* huffTable05[257] */ + 0xf008, 0x8332, 0x8322, 0x7232, 0x7232, 0x6132, 0x6132, 0x6132, + 0x6132, 0x7312, 0x7312, 0x7301, 0x7301, 0x7031, 0x7031, 0x7222, + 0x7222, 0x6212, 0x6212, 0x6212, 0x6212, 0x6122, 0x6122, 0x6122, + 0x6122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, + 0x6021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, + + /* huffTable06[129] */ + 0xf007, 0x7332, 0x7301, 0x6322, 0x6322, 0x6232, 0x6232, 0x6031, + 0x6031, 0x5312, 0x5312, 0x5312, 0x5312, 0x5132, 0x5132, 0x5132, + 0x5132, 0x5222, 0x5222, 0x5222, 0x5222, 0x5201, 0x5201, 0x5201, + 0x5201, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, + 0x4122, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021, + 0x4021, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, + + /* huffTable07[110] */ + 0xf006, 0x0041, 0x0052, 0x005b, 0x0060, 0x0063, 0x0068, 0x006b, + 0x6212, 0x5122, 0x5122, 0x6201, 0x6021, 0x4112, 0x4112, 0x4112, + 0x4112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0xf004, 0x4552, 0x4542, 0x4452, 0x4352, 0x3532, 0x3532, + 0x3442, 0x3442, 0x3522, 0x3522, 0x3252, 0x3252, 0x2512, 0x2512, + 0x2512, 0x2512, 0xf003, 0x2152, 0x2152, 0x3501, 0x3432, 0x2051, + 0x2051, 0x3342, 0x3332, 0xf002, 0x2422, 0x2242, 0x1412, 0x1412, + 0xf001, 0x1142, 0x1041, 0xf002, 0x2401, 0x2322, 0x2232, 0x2301, + 0xf001, 0x1312, 0x1132, 0xf001, 0x1031, 0x1222, + + /* huffTable08[280] */ + 0xf008, 0x0101, 0x010a, 0x010f, 0x8512, 0x8152, 0x0112, 0x0115, + 0x8422, 0x8242, 0x8412, 0x7142, 0x7142, 0x8401, 0x8041, 0x8322, + 0x8232, 0x8312, 0x8132, 0x8301, 0x8031, 0x6222, 0x6222, 0x6222, + 0x6222, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, + 0x6021, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, + 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, + 0x4122, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0xf003, 0x3552, 0x3452, 0x2542, 0x2542, 0x1352, 0x1352, + 0x1352, 0x1352, 0xf002, 0x2532, 0x2442, 0x1522, 0x1522, 0xf001, + 0x1252, 0x1501, 0xf001, 0x1432, 0x1342, 0xf001, 0x1051, 0x1332, + + /* huffTable09[93] */ + 0xf006, 0x0041, 0x004a, 0x004f, 0x0052, 0x0057, 0x005a, 0x6412, + 0x6142, 0x6322, 0x6232, 0x5312, 0x5312, 0x5132, 0x5132, 0x6301, + 0x6031, 0x5222, 0x5222, 0x5201, 0x5201, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4021, 0x4021, 0x4021, + 0x4021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0xf003, 0x3552, 0x3542, 0x2532, 0x2532, 0x2352, 0x2352, + 0x3452, 0x3501, 0xf002, 0x2442, 0x2522, 0x2252, 0x2512, 0xf001, + 0x1152, 0x1432, 0xf002, 0x1342, 0x1342, 0x2051, 0x2401, 0xf001, + 0x1422, 0x1242, 0xf001, 0x1332, 0x1041, + + /* huffTable10[320] */ + 0xf008, 0x0101, 0x010a, 0x010f, 0x0118, 0x011b, 0x0120, 0x0125, + 0x8712, 0x8172, 0x012a, 0x012d, 0x0132, 0x8612, 0x8162, 0x8061, + 0x0137, 0x013a, 0x013d, 0x8412, 0x8142, 0x8041, 0x8322, 0x8232, + 0x8301, 0x7312, 0x7312, 0x7132, 0x7132, 0x7031, 0x7031, 0x7222, + 0x7222, 0x6212, 0x6212, 0x6212, 0x6212, 0x6122, 0x6122, 0x6122, + 0x6122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, + 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0xf003, 0x3772, 0x3762, 0x3672, 0x3752, 0x3572, 0x3662, + 0x2742, 0x2742, 0xf002, 0x2472, 0x2652, 0x2562, 0x2732, 0xf003, + 0x2372, 0x2372, 0x2642, 0x2642, 0x3552, 0x3452, 0x2362, 0x2362, + 0xf001, 0x1722, 0x1272, 0xf002, 0x2462, 0x2701, 0x1071, 0x1071, + 0xf002, 0x1262, 0x1262, 0x2542, 0x2532, 0xf002, 0x1601, 0x1601, + 0x2352, 0x2442, 0xf001, 0x1632, 0x1622, 0xf002, 0x2522, 0x2252, + 0x1512, 0x1512, 0xf002, 0x1152, 0x1152, 0x2432, 0x2342, 0xf001, + 0x1501, 0x1051, 0xf001, 0x1422, 0x1242, 0xf001, 0x1332, 0x1401, + + /* huffTable11[296] */ + 0xf008, 0x0101, 0x0106, 0x010f, 0x0114, 0x0117, 0x8722, 0x8272, + 0x011c, 0x7172, 0x7172, 0x8712, 0x8071, 0x8632, 0x8362, 0x8061, + 0x011f, 0x0122, 0x8512, 0x7262, 0x7262, 0x8622, 0x8601, 0x7612, + 0x7612, 0x7162, 0x7162, 0x8152, 0x8432, 0x8051, 0x0125, 0x8422, + 0x8242, 0x8412, 0x8142, 0x8401, 0x8041, 0x7322, 0x7322, 0x7232, + 0x7232, 0x6312, 0x6312, 0x6312, 0x6312, 0x6132, 0x6132, 0x6132, + 0x6132, 0x7301, 0x7301, 0x7031, 0x7031, 0x6222, 0x6222, 0x6222, + 0x6222, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, + 0x5122, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, + 0x5201, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, + 0x5021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0xf002, 0x2772, 0x2762, 0x2672, 0x2572, 0xf003, 0x2662, + 0x2662, 0x2742, 0x2742, 0x2472, 0x2472, 0x3752, 0x3552, 0xf002, + 0x2652, 0x2562, 0x1732, 0x1732, 0xf001, 0x1372, 0x1642, 0xf002, + 0x2542, 0x2452, 0x2532, 0x2352, 0xf001, 0x1462, 0x1701, 0xf001, + 0x1442, 0x1522, 0xf001, 0x1252, 0x1501, 0xf001, 0x1342, 0x1332, + + /* huffTable12[185] */ + 0xf007, 0x0081, 0x008a, 0x008f, 0x0092, 0x0097, 0x009a, 0x009d, + 0x00a2, 0x00a5, 0x00a8, 0x7622, 0x7262, 0x7162, 0x00ad, 0x00b0, + 0x00b3, 0x7512, 0x7152, 0x7432, 0x7342, 0x00b6, 0x7422, 0x7242, + 0x7412, 0x6332, 0x6332, 0x6142, 0x6142, 0x6322, 0x6322, 0x6232, + 0x6232, 0x7041, 0x7301, 0x6031, 0x6031, 0x5312, 0x5312, 0x5312, + 0x5312, 0x5132, 0x5132, 0x5132, 0x5132, 0x5222, 0x5222, 0x5222, + 0x5222, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, + 0x4122, 0x5201, 0x5201, 0x5201, 0x5201, 0x5021, 0x5021, 0x5021, + 0x5021, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0xf003, 0x3772, 0x3762, 0x2672, 0x2672, 0x2752, 0x2752, + 0x2572, 0x2572, 0xf002, 0x2662, 0x2742, 0x2472, 0x2562, 0xf001, + 0x1652, 0x1732, 0xf002, 0x2372, 0x2552, 0x1722, 0x1722, 0xf001, + 0x1272, 0x1642, 0xf001, 0x1462, 0x1712, 0xf002, 0x1172, 0x1172, + 0x2701, 0x2071, 0xf001, 0x1632, 0x1362, 0xf001, 0x1542, 0x1452, + 0xf002, 0x1442, 0x1442, 0x2601, 0x2501, 0xf001, 0x1612, 0x1061, + 0xf001, 0x1532, 0x1352, 0xf001, 0x1522, 0x1252, 0xf001, 0x1051, + 0x1401, + + /* huffTable13[497] */ + 0xf006, 0x0041, 0x0082, 0x00c3, 0x00e4, 0x0105, 0x0116, 0x011f, + 0x0130, 0x0139, 0x013e, 0x0143, 0x0146, 0x6212, 0x6122, 0x6201, + 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4101, 0x4101, 0x4101, + 0x4101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0xf006, 0x0108, 0x0111, 0x011a, 0x0123, 0x012c, 0x0131, + 0x0136, 0x013f, 0x0144, 0x0147, 0x014c, 0x0151, 0x0156, 0x015b, + 0x6f12, 0x61f2, 0x60f1, 0x0160, 0x0163, 0x0166, 0x62e2, 0x0169, + 0x6e12, 0x61e2, 0x016c, 0x016f, 0x0172, 0x0175, 0x0178, 0x017b, + 0x66c2, 0x6d32, 0x017e, 0x6d22, 0x62d2, 0x6d12, 0x67b2, 0x0181, + 0x0184, 0x63c2, 0x0187, 0x6b42, 0x51d2, 0x51d2, 0x6d01, 0x60d1, + 0x6a82, 0x68a2, 0x6c42, 0x64c2, 0x6b62, 0x66b2, 0x5c32, 0x5c32, + 0x5c22, 0x5c22, 0x52c2, 0x52c2, 0x5b52, 0x5b52, 0x65b2, 0x6982, + 0x5c12, 0x5c12, 0xf006, 0x51c2, 0x51c2, 0x6892, 0x6c01, 0x50c1, + 0x50c1, 0x64b2, 0x6a62, 0x66a2, 0x6972, 0x5b32, 0x5b32, 0x53b2, + 0x53b2, 0x6882, 0x6a52, 0x5b22, 0x5b22, 0x65a2, 0x6962, 0x54a2, + 0x54a2, 0x6872, 0x6782, 0x5492, 0x5492, 0x6772, 0x6672, 0x42b2, + 0x42b2, 0x42b2, 0x42b2, 0x4b12, 0x4b12, 0x4b12, 0x4b12, 0x41b2, + 0x41b2, 0x41b2, 0x41b2, 0x5b01, 0x5b01, 0x50b1, 0x50b1, 0x5692, + 0x5692, 0x5a42, 0x5a42, 0x5a32, 0x5a32, 0x53a2, 0x53a2, 0x5952, + 0x5952, 0x5592, 0x5592, 0x4a22, 0x4a22, 0x4a22, 0x4a22, 0x42a2, + 0x42a2, 0x42a2, 0x42a2, 0xf005, 0x4a12, 0x4a12, 0x41a2, 0x41a2, + 0x5a01, 0x5862, 0x40a1, 0x40a1, 0x5682, 0x5942, 0x4392, 0x4392, + 0x5932, 0x5852, 0x5582, 0x5762, 0x4922, 0x4922, 0x4292, 0x4292, + 0x5752, 0x5572, 0x4832, 0x4832, 0x4382, 0x4382, 0x5662, 0x5742, + 0x5472, 0x5652, 0x5562, 0x5372, 0xf005, 0x3912, 0x3912, 0x3912, + 0x3912, 0x3192, 0x3192, 0x3192, 0x3192, 0x4901, 0x4901, 0x4091, + 0x4091, 0x4842, 0x4842, 0x4482, 0x4482, 0x4272, 0x4272, 0x5642, + 0x5462, 0x3822, 0x3822, 0x3822, 0x3822, 0x3282, 0x3282, 0x3282, + 0x3282, 0x3812, 0x3812, 0x3812, 0x3812, 0xf004, 0x4732, 0x4722, + 0x3712, 0x3712, 0x3172, 0x3172, 0x4552, 0x4701, 0x4071, 0x4632, + 0x4362, 0x4542, 0x4452, 0x4622, 0x4262, 0x4532, 0xf003, 0x2182, + 0x2182, 0x3801, 0x3081, 0x3612, 0x3162, 0x3601, 0x3061, 0xf004, + 0x4352, 0x4442, 0x3522, 0x3522, 0x3252, 0x3252, 0x3501, 0x3501, + 0x2512, 0x2512, 0x2512, 0x2512, 0x2152, 0x2152, 0x2152, 0x2152, + 0xf003, 0x3432, 0x3342, 0x3051, 0x3422, 0x3242, 0x3332, 0x2412, + 0x2412, 0xf002, 0x1142, 0x1142, 0x2401, 0x2041, 0xf002, 0x2322, + 0x2232, 0x1312, 0x1312, 0xf001, 0x1132, 0x1301, 0xf001, 0x1031, + 0x1222, 0xf003, 0x0082, 0x008b, 0x008e, 0x0091, 0x0094, 0x0097, + 0x3ce2, 0x3dd2, 0xf003, 0x0093, 0x3eb2, 0x3be2, 0x3f92, 0x39f2, + 0x3ae2, 0x3db2, 0x3bd2, 0xf003, 0x3f82, 0x38f2, 0x3cc2, 0x008d, + 0x3e82, 0x0090, 0x27f2, 0x27f2, 0xf003, 0x2ad2, 0x2ad2, 0x3da2, + 0x3cb2, 0x3bc2, 0x36f2, 0x2f62, 0x2f62, 0xf002, 0x28e2, 0x2f52, + 0x2d92, 0x29d2, 0xf002, 0x25f2, 0x27e2, 0x2ca2, 0x2bb2, 0xf003, + 0x2f42, 0x2f42, 0x24f2, 0x24f2, 0x3ac2, 0x36e2, 0x23f2, 0x23f2, + 0xf002, 0x1f32, 0x1f32, 0x2d82, 0x28d2, 0xf001, 0x1f22, 0x12f2, + 0xf002, 0x2e62, 0x2c92, 0x1f01, 0x1f01, 0xf002, 0x29c2, 0x2e52, + 0x1ba2, 0x1ba2, 0xf002, 0x2d72, 0x27d2, 0x1e42, 0x1e42, 0xf002, + 0x28c2, 0x26d2, 0x1e32, 0x1e32, 0xf002, 0x19b2, 0x19b2, 0x2b92, + 0x2aa2, 0xf001, 0x1ab2, 0x15e2, 0xf001, 0x14e2, 0x1c82, 0xf001, + 0x1d62, 0x13e2, 0xf001, 0x1e22, 0x1e01, 0xf001, 0x10e1, 0x1d52, + 0xf001, 0x15d2, 0x1c72, 0xf001, 0x17c2, 0x1d42, 0xf001, 0x1b82, + 0x18b2, 0xf001, 0x14d2, 0x1a92, 0xf001, 0x19a2, 0x1c62, 0xf001, + 0x13d2, 0x1b72, 0xf001, 0x1c52, 0x15c2, 0xf001, 0x1992, 0x1a72, + 0xf001, 0x17a2, 0x1792, 0xf003, 0x0023, 0x3df2, 0x2de2, 0x2de2, + 0x1ff2, 0x1ff2, 0x1ff2, 0x1ff2, 0xf001, 0x1fe2, 0x1fd2, 0xf001, + 0x1ee2, 0x1fc2, 0xf001, 0x1ed2, 0x1fb2, 0xf001, 0x1bf2, 0x1ec2, + 0xf002, 0x1cd2, 0x1cd2, 0x2fa2, 0x29e2, 0xf001, 0x1af2, 0x1dc2, + 0xf001, 0x1ea2, 0x1e92, 0xf001, 0x1f72, 0x1e72, 0xf001, 0x1ef2, + 0x1cf2, + + /* huffTable15[580] */ + 0xf008, 0x0101, 0x0122, 0x0143, 0x0154, 0x0165, 0x0176, 0x017f, + 0x0188, 0x0199, 0x01a2, 0x01ab, 0x01b4, 0x01bd, 0x01c2, 0x01cb, + 0x01d4, 0x01d9, 0x01de, 0x01e3, 0x01e8, 0x01ed, 0x01f2, 0x01f7, + 0x01fc, 0x0201, 0x0204, 0x0207, 0x020a, 0x020f, 0x0212, 0x0215, + 0x021a, 0x021d, 0x0220, 0x8192, 0x0223, 0x0226, 0x0229, 0x022c, + 0x022f, 0x8822, 0x8282, 0x8812, 0x8182, 0x0232, 0x0235, 0x0238, + 0x023b, 0x8722, 0x8272, 0x8462, 0x8712, 0x8552, 0x8172, 0x023e, + 0x8632, 0x8362, 0x8542, 0x8452, 0x8622, 0x8262, 0x8612, 0x0241, + 0x8532, 0x7162, 0x7162, 0x8352, 0x8442, 0x7522, 0x7522, 0x7252, + 0x7252, 0x7512, 0x7512, 0x7152, 0x7152, 0x8501, 0x8051, 0x7432, + 0x7432, 0x7342, 0x7342, 0x7422, 0x7422, 0x7242, 0x7242, 0x7332, + 0x7332, 0x6142, 0x6142, 0x6142, 0x6142, 0x7412, 0x7412, 0x7401, + 0x7401, 0x6322, 0x6322, 0x6322, 0x6322, 0x6232, 0x6232, 0x6232, + 0x6232, 0x7041, 0x7041, 0x7301, 0x7301, 0x6312, 0x6312, 0x6312, + 0x6312, 0x6132, 0x6132, 0x6132, 0x6132, 0x6031, 0x6031, 0x6031, + 0x6031, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, + 0x5222, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, + 0x5212, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, + 0x5122, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, + 0x5201, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, + 0x5021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0xf005, 0x5ff2, 0x5fe2, 0x5ef2, 0x5fd2, 0x4ee2, 0x4ee2, + 0x5df2, 0x5fc2, 0x5cf2, 0x5ed2, 0x5de2, 0x5fb2, 0x4bf2, 0x4bf2, + 0x5ec2, 0x5ce2, 0x4dd2, 0x4dd2, 0x4fa2, 0x4fa2, 0x4af2, 0x4af2, + 0x4eb2, 0x4eb2, 0x4be2, 0x4be2, 0x4dc2, 0x4dc2, 0x4cd2, 0x4cd2, + 0x4f92, 0x4f92, 0xf005, 0x49f2, 0x49f2, 0x4ae2, 0x4ae2, 0x4db2, + 0x4db2, 0x4bd2, 0x4bd2, 0x4f82, 0x4f82, 0x48f2, 0x48f2, 0x4cc2, + 0x4cc2, 0x4e92, 0x4e92, 0x49e2, 0x49e2, 0x4f72, 0x4f72, 0x47f2, + 0x47f2, 0x4da2, 0x4da2, 0x4ad2, 0x4ad2, 0x4cb2, 0x4cb2, 0x4f62, + 0x4f62, 0x5ea2, 0x5f01, 0xf004, 0x3bc2, 0x3bc2, 0x36f2, 0x36f2, + 0x4e82, 0x48e2, 0x4f52, 0x4d92, 0x35f2, 0x35f2, 0x3e72, 0x3e72, + 0x37e2, 0x37e2, 0x3ca2, 0x3ca2, 0xf004, 0x3ac2, 0x3ac2, 0x3bb2, + 0x3bb2, 0x49d2, 0x4d82, 0x3f42, 0x3f42, 0x34f2, 0x34f2, 0x3f32, + 0x3f32, 0x33f2, 0x33f2, 0x38d2, 0x38d2, 0xf004, 0x36e2, 0x36e2, + 0x3f22, 0x3f22, 0x32f2, 0x32f2, 0x4e62, 0x40f1, 0x3f12, 0x3f12, + 0x31f2, 0x31f2, 0x3c92, 0x3c92, 0x39c2, 0x39c2, 0xf003, 0x3e52, + 0x3ba2, 0x3ab2, 0x35e2, 0x3d72, 0x37d2, 0x3e42, 0x34e2, 0xf003, + 0x3c82, 0x38c2, 0x3e32, 0x3d62, 0x36d2, 0x33e2, 0x3b92, 0x39b2, + 0xf004, 0x3e22, 0x3e22, 0x3aa2, 0x3aa2, 0x32e2, 0x32e2, 0x3e12, + 0x3e12, 0x31e2, 0x31e2, 0x4e01, 0x40e1, 0x3d52, 0x3d52, 0x35d2, + 0x35d2, 0xf003, 0x3c72, 0x37c2, 0x3d42, 0x3b82, 0x24d2, 0x24d2, + 0x38b2, 0x3a92, 0xf003, 0x39a2, 0x3c62, 0x36c2, 0x3d32, 0x23d2, + 0x23d2, 0x22d2, 0x22d2, 0xf003, 0x3d22, 0x3d01, 0x2d12, 0x2d12, + 0x2b72, 0x2b72, 0x27b2, 0x27b2, 0xf003, 0x21d2, 0x21d2, 0x3c52, + 0x30d1, 0x25c2, 0x25c2, 0x2a82, 0x2a82, 0xf002, 0x28a2, 0x2c42, + 0x24c2, 0x2b62, 0xf003, 0x26b2, 0x26b2, 0x3992, 0x3c01, 0x2c32, + 0x2c32, 0x23c2, 0x23c2, 0xf003, 0x2a72, 0x2a72, 0x27a2, 0x27a2, + 0x26a2, 0x26a2, 0x30c1, 0x3b01, 0xf002, 0x12c2, 0x12c2, 0x2c22, + 0x2b52, 0xf002, 0x25b2, 0x2c12, 0x2982, 0x2892, 0xf002, 0x21c2, + 0x2b42, 0x24b2, 0x2a62, 0xf002, 0x2b32, 0x2972, 0x13b2, 0x13b2, + 0xf002, 0x2792, 0x2882, 0x2b22, 0x2a52, 0xf002, 0x12b2, 0x12b2, + 0x25a2, 0x2b12, 0xf002, 0x11b2, 0x11b2, 0x20b1, 0x2962, 0xf002, + 0x2692, 0x2a42, 0x24a2, 0x2872, 0xf002, 0x2782, 0x2a32, 0x13a2, + 0x13a2, 0xf001, 0x1952, 0x1592, 0xf001, 0x1a22, 0x12a2, 0xf001, + 0x1a12, 0x11a2, 0xf002, 0x2a01, 0x20a1, 0x1862, 0x1862, 0xf001, + 0x1682, 0x1942, 0xf001, 0x1492, 0x1932, 0xf002, 0x1392, 0x1392, + 0x2772, 0x2901, 0xf001, 0x1852, 0x1582, 0xf001, 0x1922, 0x1762, + 0xf001, 0x1672, 0x1292, 0xf001, 0x1912, 0x1091, 0xf001, 0x1842, + 0x1482, 0xf001, 0x1752, 0x1572, 0xf001, 0x1832, 0x1382, 0xf001, + 0x1662, 0x1742, 0xf001, 0x1472, 0x1801, 0xf001, 0x1081, 0x1652, + 0xf001, 0x1562, 0x1732, 0xf001, 0x1372, 0x1642, 0xf001, 0x1701, + 0x1071, 0xf001, 0x1601, 0x1061, + + /* huffTable16[651] */ + 0xf008, 0x0101, 0x010a, 0x0113, 0x8ff2, 0x0118, 0x011d, 0x0120, + 0x82f2, 0x0131, 0x8f12, 0x81f2, 0x0134, 0x0145, 0x0156, 0x0167, + 0x0178, 0x0189, 0x019a, 0x01a3, 0x01ac, 0x01b5, 0x01be, 0x01c7, + 0x01d0, 0x01d9, 0x01de, 0x01e3, 0x01e6, 0x01eb, 0x01f0, 0x8152, + 0x01f3, 0x01f6, 0x01f9, 0x01fc, 0x8412, 0x8142, 0x01ff, 0x8322, + 0x8232, 0x7312, 0x7312, 0x7132, 0x7132, 0x8301, 0x8031, 0x7222, + 0x7222, 0x6212, 0x6212, 0x6212, 0x6212, 0x6122, 0x6122, 0x6122, + 0x6122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, + 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0xf003, 0x3fe2, 0x3ef2, 0x3fd2, 0x3df2, 0x3fc2, 0x3cf2, + 0x3fb2, 0x3bf2, 0xf003, 0x2fa2, 0x2fa2, 0x3af2, 0x3f92, 0x39f2, + 0x38f2, 0x2f82, 0x2f82, 0xf002, 0x2f72, 0x27f2, 0x2f62, 0x26f2, + 0xf002, 0x2f52, 0x25f2, 0x1f42, 0x1f42, 0xf001, 0x14f2, 0x13f2, + 0xf004, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, + 0x10f1, 0x2f32, 0x2f32, 0x2f32, 0x2f32, 0x00e2, 0x00f3, 0x00fc, + 0x0105, 0xf001, 0x1f22, 0x1f01, 0xf004, 0x00fa, 0x00ff, 0x0104, + 0x0109, 0x010c, 0x0111, 0x0116, 0x0119, 0x011e, 0x0123, 0x0128, + 0x43e2, 0x012d, 0x0130, 0x0133, 0x0136, 0xf004, 0x0128, 0x012b, + 0x012e, 0x4d01, 0x0131, 0x0134, 0x0137, 0x4c32, 0x013a, 0x4c12, + 0x40c1, 0x013d, 0x32e2, 0x32e2, 0x4e22, 0x4e12, 0xf004, 0x43d2, + 0x4d22, 0x42d2, 0x41d2, 0x4b32, 0x012f, 0x3d12, 0x3d12, 0x44c2, + 0x4b62, 0x43c2, 0x47a2, 0x3c22, 0x3c22, 0x42c2, 0x45b2, 0xf004, + 0x41c2, 0x4c01, 0x4b42, 0x44b2, 0x4a62, 0x46a2, 0x33b2, 0x33b2, + 0x4a52, 0x45a2, 0x3b22, 0x3b22, 0x32b2, 0x32b2, 0x3b12, 0x3b12, + 0xf004, 0x31b2, 0x31b2, 0x4b01, 0x40b1, 0x4962, 0x4692, 0x4a42, + 0x44a2, 0x4872, 0x4782, 0x33a2, 0x33a2, 0x4a32, 0x4952, 0x3a22, + 0x3a22, 0xf004, 0x4592, 0x4862, 0x31a2, 0x31a2, 0x4682, 0x4772, + 0x3492, 0x3492, 0x4942, 0x4752, 0x3762, 0x3762, 0x22a2, 0x22a2, + 0x22a2, 0x22a2, 0xf003, 0x2a12, 0x2a12, 0x3a01, 0x30a1, 0x3932, + 0x3392, 0x3852, 0x3582, 0xf003, 0x2922, 0x2922, 0x2292, 0x2292, + 0x3672, 0x3901, 0x2912, 0x2912, 0xf003, 0x2192, 0x2192, 0x3091, + 0x3842, 0x3482, 0x3572, 0x3832, 0x3382, 0xf003, 0x3662, 0x3822, + 0x2282, 0x2282, 0x3742, 0x3472, 0x2812, 0x2812, 0xf003, 0x2182, + 0x2182, 0x2081, 0x2081, 0x3801, 0x3652, 0x2732, 0x2732, 0xf003, + 0x2372, 0x2372, 0x3562, 0x3642, 0x2722, 0x2722, 0x2272, 0x2272, + 0xf003, 0x3462, 0x3552, 0x2701, 0x2701, 0x1712, 0x1712, 0x1712, + 0x1712, 0xf002, 0x1172, 0x1172, 0x2071, 0x2632, 0xf002, 0x2362, + 0x2542, 0x2452, 0x2622, 0xf001, 0x1262, 0x1612, 0xf002, 0x1162, + 0x1162, 0x2601, 0x2061, 0xf002, 0x1352, 0x1352, 0x2532, 0x2442, + 0xf001, 0x1522, 0x1252, 0xf001, 0x1512, 0x1501, 0xf001, 0x1432, + 0x1342, 0xf001, 0x1051, 0x1422, 0xf001, 0x1242, 0x1332, 0xf001, + 0x1401, 0x1041, 0xf004, 0x4ec2, 0x0086, 0x3ed2, 0x3ed2, 0x39e2, + 0x39e2, 0x4ae2, 0x49d2, 0x2ee2, 0x2ee2, 0x2ee2, 0x2ee2, 0x3de2, + 0x3de2, 0x3be2, 0x3be2, 0xf003, 0x2eb2, 0x2eb2, 0x2dc2, 0x2dc2, + 0x3cd2, 0x3bd2, 0x2ea2, 0x2ea2, 0xf003, 0x2cc2, 0x2cc2, 0x3da2, + 0x3ad2, 0x3e72, 0x3ca2, 0x2ac2, 0x2ac2, 0xf003, 0x39c2, 0x3d72, + 0x2e52, 0x2e52, 0x1db2, 0x1db2, 0x1db2, 0x1db2, 0xf002, 0x1e92, + 0x1e92, 0x2cb2, 0x2bc2, 0xf002, 0x2e82, 0x28e2, 0x2d92, 0x27e2, + 0xf002, 0x2bb2, 0x2d82, 0x28d2, 0x2e62, 0xf001, 0x16e2, 0x1c92, + 0xf002, 0x2ba2, 0x2ab2, 0x25e2, 0x27d2, 0xf002, 0x1e42, 0x1e42, + 0x24e2, 0x2c82, 0xf001, 0x18c2, 0x1e32, 0xf002, 0x1d62, 0x1d62, + 0x26d2, 0x2b92, 0xf002, 0x29b2, 0x2aa2, 0x11e2, 0x11e2, 0xf002, + 0x14d2, 0x14d2, 0x28b2, 0x29a2, 0xf002, 0x1b72, 0x1b72, 0x27b2, + 0x20d1, 0xf001, 0x1e01, 0x10e1, 0xf001, 0x1d52, 0x15d2, 0xf001, + 0x1c72, 0x17c2, 0xf001, 0x1d42, 0x1b82, 0xf001, 0x1a92, 0x1c62, + 0xf001, 0x16c2, 0x1d32, 0xf001, 0x1c52, 0x15c2, 0xf001, 0x1a82, + 0x18a2, 0xf001, 0x1992, 0x1c42, 0xf001, 0x16b2, 0x1a72, 0xf001, + 0x1b52, 0x1982, 0xf001, 0x1892, 0x1972, 0xf001, 0x1792, 0x1882, + 0xf001, 0x1ce2, 0x1dd2, + + /* huffTable24[705] */ + 0xf009, 0x8fe2, 0x8fe2, 0x8ef2, 0x8ef2, 0x8fd2, 0x8fd2, 0x8df2, + 0x8df2, 0x8fc2, 0x8fc2, 0x8cf2, 0x8cf2, 0x8fb2, 0x8fb2, 0x8bf2, + 0x8bf2, 0x7af2, 0x7af2, 0x7af2, 0x7af2, 0x8fa2, 0x8fa2, 0x8f92, + 0x8f92, 0x79f2, 0x79f2, 0x79f2, 0x79f2, 0x78f2, 0x78f2, 0x78f2, + 0x78f2, 0x8f82, 0x8f82, 0x8f72, 0x8f72, 0x77f2, 0x77f2, 0x77f2, + 0x77f2, 0x7f62, 0x7f62, 0x7f62, 0x7f62, 0x76f2, 0x76f2, 0x76f2, + 0x76f2, 0x7f52, 0x7f52, 0x7f52, 0x7f52, 0x75f2, 0x75f2, 0x75f2, + 0x75f2, 0x7f42, 0x7f42, 0x7f42, 0x7f42, 0x74f2, 0x74f2, 0x74f2, + 0x74f2, 0x7f32, 0x7f32, 0x7f32, 0x7f32, 0x73f2, 0x73f2, 0x73f2, + 0x73f2, 0x7f22, 0x7f22, 0x7f22, 0x7f22, 0x72f2, 0x72f2, 0x72f2, + 0x72f2, 0x71f2, 0x71f2, 0x71f2, 0x71f2, 0x8f12, 0x8f12, 0x80f1, + 0x80f1, 0x9f01, 0x0201, 0x0206, 0x020b, 0x0210, 0x0215, 0x021a, + 0x021f, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, + 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, + 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, + 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, + 0x4ff2, 0x0224, 0x0229, 0x0232, 0x0237, 0x023a, 0x023f, 0x0242, + 0x0245, 0x024a, 0x024d, 0x0250, 0x0253, 0x0256, 0x0259, 0x025c, + 0x025f, 0x0262, 0x0265, 0x0268, 0x026b, 0x026e, 0x0271, 0x0274, + 0x0277, 0x027a, 0x027d, 0x0280, 0x0283, 0x0288, 0x028b, 0x028e, + 0x0291, 0x0294, 0x0297, 0x029a, 0x029f, 0x94b2, 0x02a4, 0x02a7, + 0x02aa, 0x93b2, 0x9882, 0x02af, 0x92b2, 0x02b2, 0x02b5, 0x9692, + 0x94a2, 0x02b8, 0x9782, 0x9a32, 0x93a2, 0x9952, 0x9592, 0x9a22, + 0x92a2, 0x91a2, 0x9862, 0x9682, 0x9772, 0x9942, 0x9492, 0x9932, + 0x9392, 0x9852, 0x9582, 0x9922, 0x9762, 0x9672, 0x9292, 0x9912, + 0x9192, 0x9842, 0x9482, 0x9752, 0x9572, 0x9832, 0x9382, 0x9662, + 0x9822, 0x9282, 0x9812, 0x9742, 0x9472, 0x9182, 0x02bb, 0x9652, + 0x9562, 0x9712, 0x02be, 0x8372, 0x8372, 0x9732, 0x9722, 0x8272, + 0x8272, 0x8642, 0x8642, 0x8462, 0x8462, 0x8552, 0x8552, 0x8172, + 0x8172, 0x8632, 0x8632, 0x8362, 0x8362, 0x8542, 0x8542, 0x8452, + 0x8452, 0x8622, 0x8622, 0x8262, 0x8262, 0x8612, 0x8612, 0x8162, + 0x8162, 0x9601, 0x9061, 0x8532, 0x8532, 0x8352, 0x8352, 0x8442, + 0x8442, 0x8522, 0x8522, 0x8252, 0x8252, 0x8512, 0x8512, 0x9501, + 0x9051, 0x7152, 0x7152, 0x7152, 0x7152, 0x8432, 0x8432, 0x8342, + 0x8342, 0x7422, 0x7422, 0x7422, 0x7422, 0x7242, 0x7242, 0x7242, + 0x7242, 0x7332, 0x7332, 0x7332, 0x7332, 0x7412, 0x7412, 0x7412, + 0x7412, 0x7142, 0x7142, 0x7142, 0x7142, 0x8401, 0x8401, 0x8041, + 0x8041, 0x7322, 0x7322, 0x7322, 0x7322, 0x7232, 0x7232, 0x7232, + 0x7232, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312, + 0x6312, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, + 0x6132, 0x7301, 0x7301, 0x7301, 0x7301, 0x7031, 0x7031, 0x7031, + 0x7031, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222, + 0x6222, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, + 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, + 0x5212, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, + 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, + 0x5122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6201, 0x6201, 0x6201, + 0x6201, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021, + 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0xf002, 0x2ee2, 0x2ed2, 0x2de2, 0x2ec2, 0xf002, 0x2ce2, + 0x2dd2, 0x2eb2, 0x2be2, 0xf002, 0x2dc2, 0x2cd2, 0x2ea2, 0x2ae2, + 0xf002, 0x2db2, 0x2bd2, 0x2cc2, 0x2e92, 0xf002, 0x29e2, 0x2da2, + 0x2ad2, 0x2cb2, 0xf002, 0x2bc2, 0x2e82, 0x28e2, 0x2d92, 0xf002, + 0x29d2, 0x2e72, 0x27e2, 0x2ca2, 0xf002, 0x2ac2, 0x2bb2, 0x2d82, + 0x28d2, 0xf003, 0x3e01, 0x30e1, 0x2d01, 0x2d01, 0x16e2, 0x16e2, + 0x16e2, 0x16e2, 0xf002, 0x2e62, 0x2c92, 0x19c2, 0x19c2, 0xf001, + 0x1e52, 0x1ab2, 0xf002, 0x15e2, 0x15e2, 0x2ba2, 0x2d72, 0xf001, + 0x17d2, 0x14e2, 0xf001, 0x1c82, 0x18c2, 0xf002, 0x2e42, 0x2e22, + 0x1e32, 0x1e32, 0xf001, 0x1d62, 0x16d2, 0xf001, 0x13e2, 0x1b92, + 0xf001, 0x19b2, 0x1aa2, 0xf001, 0x12e2, 0x1e12, 0xf001, 0x11e2, + 0x1d52, 0xf001, 0x15d2, 0x1c72, 0xf001, 0x17c2, 0x1d42, 0xf001, + 0x1b82, 0x18b2, 0xf001, 0x14d2, 0x1a92, 0xf001, 0x19a2, 0x1c62, + 0xf001, 0x16c2, 0x1d32, 0xf001, 0x13d2, 0x1d22, 0xf001, 0x12d2, + 0x1d12, 0xf001, 0x1b72, 0x17b2, 0xf001, 0x11d2, 0x1c52, 0xf001, + 0x15c2, 0x1a82, 0xf001, 0x18a2, 0x1992, 0xf001, 0x1c42, 0x14c2, + 0xf001, 0x1b62, 0x16b2, 0xf002, 0x20d1, 0x2c01, 0x1c32, 0x1c32, + 0xf001, 0x13c2, 0x1a72, 0xf001, 0x17a2, 0x1c22, 0xf001, 0x12c2, + 0x1b52, 0xf001, 0x15b2, 0x1c12, 0xf001, 0x1982, 0x1892, 0xf001, + 0x11c2, 0x1b42, 0xf002, 0x20c1, 0x2b01, 0x1b32, 0x1b32, 0xf002, + 0x20b1, 0x2a01, 0x1a12, 0x1a12, 0xf001, 0x1a62, 0x16a2, 0xf001, + 0x1972, 0x1792, 0xf002, 0x20a1, 0x2901, 0x1091, 0x1091, 0xf001, + 0x1b22, 0x1a52, 0xf001, 0x15a2, 0x1b12, 0xf001, 0x11b2, 0x1962, + 0xf001, 0x1a42, 0x1872, 0xf001, 0x1801, 0x1081, 0xf001, 0x1701, + 0x1071, +}; + +#define HUFF_OFFSET_01 0 +#define HUFF_OFFSET_02 ( 9 + HUFF_OFFSET_01) +#define HUFF_OFFSET_03 ( 65 + HUFF_OFFSET_02) +#define HUFF_OFFSET_05 ( 65 + HUFF_OFFSET_03) +#define HUFF_OFFSET_06 (257 + HUFF_OFFSET_05) +#define HUFF_OFFSET_07 (129 + HUFF_OFFSET_06) +#define HUFF_OFFSET_08 (110 + HUFF_OFFSET_07) +#define HUFF_OFFSET_09 (280 + HUFF_OFFSET_08) +#define HUFF_OFFSET_10 ( 93 + HUFF_OFFSET_09) +#define HUFF_OFFSET_11 (320 + HUFF_OFFSET_10) +#define HUFF_OFFSET_12 (296 + HUFF_OFFSET_11) +#define HUFF_OFFSET_13 (185 + HUFF_OFFSET_12) +#define HUFF_OFFSET_15 (497 + HUFF_OFFSET_13) +#define HUFF_OFFSET_16 (580 + HUFF_OFFSET_15) +#define HUFF_OFFSET_24 (651 + HUFF_OFFSET_16) + +const int huffTabOffset[HUFF_PAIRTABS] = { + 0, + HUFF_OFFSET_01, + HUFF_OFFSET_02, + HUFF_OFFSET_03, + 0, + HUFF_OFFSET_05, + HUFF_OFFSET_06, + HUFF_OFFSET_07, + HUFF_OFFSET_08, + HUFF_OFFSET_09, + HUFF_OFFSET_10, + HUFF_OFFSET_11, + HUFF_OFFSET_12, + HUFF_OFFSET_13, + 0, + HUFF_OFFSET_15, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, +}; + +const HuffTabLookup huffTabLookup[HUFF_PAIRTABS] = { + { 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 }, +}; + +/* tables for quadruples + * format 0xAB + * A = length of codeword + * B = codeword + */ +const unsigned char quadTable[64+16] = { + /* table A */ + 0x6b, 0x6f, 0x6d, 0x6e, 0x67, 0x65, 0x59, 0x59, + 0x56, 0x56, 0x53, 0x53, 0x5a, 0x5a, 0x5c, 0x5c, + 0x42, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, 0x41, + 0x44, 0x44, 0x44, 0x44, 0x48, 0x48, 0x48, 0x48, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + /* table B */ + 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, + 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, +}; + +const int quadTabOffset[2] = {0, 64}; +const int quadTabMaxBits[2] = {6, 4}; diff --git a/components/driver_sndmixer/libhelix-mp3/imdct.c b/components/driver_sndmixer/libhelix-mp3/imdct.c new file mode 100644 index 0000000..86c0317 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/imdct.c @@ -0,0 +1,786 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * imdct.c - antialias, inverse transform (short/long/mixed), windowing, + * overlap-add, frequency inversion + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: AntiAlias + * + * Description: smooth transition across DCT block boundaries (every 18 coefficients) + * + * Inputs: vector of dequantized coefficients, length = (nBfly+1) * 18 + * number of "butterflies" to perform (one butterfly means one + * inter-block smoothing operation) + * + * Outputs: updated coefficient vector x + * + * Return: none + * + * Notes: weighted average of opposite bands (pairwise) from the 8 samples + * before and after each block boundary + * nBlocks = (nonZeroBound + 7) / 18, since nZB is the first ZERO sample + * above which all other samples are also zero + * max gain per sample = 1.372 + * MAX(i) (abs(csa[i][0]) + abs(csa[i][1])) + * bits gained = 0 + * assume at least 1 guard bit in x[] to avoid overflow + * (should be guaranteed from dequant, and max gain from stproc * max + * gain from AntiAlias < 2.0) + **************************************************************************************/ +// a little bit faster in RAM (< 1 ms per block) +/* __attribute__ ((section (".data"))) */ static void AntiAlias(int *x, int nBfly) +{ + int k, a0, b0, c0, c1; + const int *c; + + /* csa = Q31 */ + for (k = nBfly; k > 0; k--) { + c = csa[0]; + x += 18; + + a0 = x[-1]; c0 = *c; c++; b0 = x[0]; c1 = *c; c++; + x[-1] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[0] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-2]; c0 = *c; c++; b0 = x[1]; c1 = *c; c++; + x[-2] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[1] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-3]; c0 = *c; c++; b0 = x[2]; c1 = *c; c++; + x[-3] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[2] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-4]; c0 = *c; c++; b0 = x[3]; c1 = *c; c++; + x[-4] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[3] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-5]; c0 = *c; c++; b0 = x[4]; c1 = *c; c++; + x[-5] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[4] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-6]; c0 = *c; c++; b0 = x[5]; c1 = *c; c++; + x[-6] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[5] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-7]; c0 = *c; c++; b0 = x[6]; c1 = *c; c++; + x[-7] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[6] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-8]; c0 = *c; c++; b0 = x[7]; c1 = *c; c++; + x[-8] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[7] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + } +} + +/************************************************************************************** + * Function: WinPrevious + * + * Description: apply specified window to second half of previous IMDCT (overlap part) + * + * Inputs: vector of 9 coefficients (xPrev) + * + * Outputs: 18 windowed output coefficients (gain 1 integer bit) + * window type (0, 1, 2, 3) + * + * Return: none + * + * Notes: produces 9 output samples from 18 input samples via symmetry + * all blocks gain at least 1 guard bit via window (long blocks get extra + * sign bit, short blocks can have one addition but max gain < 1.0) + **************************************************************************************/ +/*__attribute__ ((section (".data"))) */ static void WinPrevious(int *xPrev, int *xPrevWin, int btPrev) +{ + int i, x, *xp, *xpwLo, *xpwHi, wLo, wHi; + const int *wpLo, *wpHi; + + xp = xPrev; + /* mapping (see IMDCT12x3): xPrev[0-2] = sum[6-8], xPrev[3-8] = sum[12-17] */ + if (btPrev == 2) { + /* this could be reordered for minimum loads/stores */ + wpLo = imdctWin[btPrev]; + xPrevWin[ 0] = MULSHIFT32(wpLo[ 6], xPrev[2]) + MULSHIFT32(wpLo[0], xPrev[6]); + xPrevWin[ 1] = MULSHIFT32(wpLo[ 7], xPrev[1]) + MULSHIFT32(wpLo[1], xPrev[7]); + xPrevWin[ 2] = MULSHIFT32(wpLo[ 8], xPrev[0]) + MULSHIFT32(wpLo[2], xPrev[8]); + xPrevWin[ 3] = MULSHIFT32(wpLo[ 9], xPrev[0]) + MULSHIFT32(wpLo[3], xPrev[8]); + xPrevWin[ 4] = MULSHIFT32(wpLo[10], xPrev[1]) + MULSHIFT32(wpLo[4], xPrev[7]); + xPrevWin[ 5] = MULSHIFT32(wpLo[11], xPrev[2]) + MULSHIFT32(wpLo[5], xPrev[6]); + xPrevWin[ 6] = MULSHIFT32(wpLo[ 6], xPrev[5]); + xPrevWin[ 7] = MULSHIFT32(wpLo[ 7], xPrev[4]); + xPrevWin[ 8] = MULSHIFT32(wpLo[ 8], xPrev[3]); + xPrevWin[ 9] = MULSHIFT32(wpLo[ 9], xPrev[3]); + xPrevWin[10] = MULSHIFT32(wpLo[10], xPrev[4]); + xPrevWin[11] = MULSHIFT32(wpLo[11], xPrev[5]); + xPrevWin[12] = xPrevWin[13] = xPrevWin[14] = xPrevWin[15] = xPrevWin[16] = xPrevWin[17] = 0; + } else { + /* use ARM-style pointers (*ptr++) so that ADS compiles well */ + wpLo = imdctWin[btPrev] + 18; + wpHi = wpLo + 17; + xpwLo = xPrevWin; + xpwHi = xPrevWin + 17; + for (i = 9; i > 0; i--) { + x = *xp++; wLo = *wpLo++; wHi = *wpHi--; + *xpwLo++ = MULSHIFT32(wLo, x); + *xpwHi-- = MULSHIFT32(wHi, x); + } + } +} + +/************************************************************************************** + * Function: FreqInvertRescale + * + * Description: do frequency inversion (odd samples of odd blocks) and rescale + * if necessary (extra guard bits added before IMDCT) + * + * Inputs: output vector y (18 new samples, spaced NBANDS apart) + * previous sample vector xPrev (9 samples) + * index of current block + * number of extra shifts added before IMDCT (usually 0) + * + * Outputs: inverted and rescaled (as necessary) outputs + * rescaled (as necessary) previous samples + * + * Return: updated mOut (from new outputs y) + **************************************************************************************/ +/*__attribute__ ((section (".data")))*/ static int FreqInvertRescale(int *y, int *xPrev, int blockIdx, int es) +{ + int i, d, mOut; + int y0, y1, y2, y3, y4, y5, y6, y7, y8; + + if (es == 0) { + /* fast case - frequency invert only (no rescaling) - can fuse into overlap-add for speed, if desired */ + if (blockIdx & 0x01) { + y += NBANDS; + y0 = *y; y += 2*NBANDS; + y1 = *y; y += 2*NBANDS; + y2 = *y; y += 2*NBANDS; + y3 = *y; y += 2*NBANDS; + y4 = *y; y += 2*NBANDS; + y5 = *y; y += 2*NBANDS; + y6 = *y; y += 2*NBANDS; + y7 = *y; y += 2*NBANDS; + y8 = *y; y += 2*NBANDS; + + y -= 18*NBANDS; + *y = -y0; y += 2*NBANDS; + *y = -y1; y += 2*NBANDS; + *y = -y2; y += 2*NBANDS; + *y = -y3; y += 2*NBANDS; + *y = -y4; y += 2*NBANDS; + *y = -y5; y += 2*NBANDS; + *y = -y6; y += 2*NBANDS; + *y = -y7; y += 2*NBANDS; + *y = -y8; y += 2*NBANDS; + } + return 0; + } else { + /* undo pre-IMDCT scaling, clipping if necessary */ + mOut = 0; + if (blockIdx & 0x01) { + /* frequency invert */ + for (i = 0; i < 18; i+=2) { + d = *y; CLIP_2N(d, 31 - es); *y = d << es; mOut |= FASTABS(*y); y += NBANDS; + d = -*y; CLIP_2N(d, 31 - es); *y = d << es; mOut |= FASTABS(*y); y += NBANDS; + d = *xPrev; CLIP_2N(d, 31 - es); *xPrev++ = d << es; + } + } else { + for (i = 0; i < 18; i+=2) { + d = *y; CLIP_2N(d, 31 - es); *y = d << es; mOut |= FASTABS(*y); y += NBANDS; + d = *y; CLIP_2N(d, 31 - es); *y = d << es; mOut |= FASTABS(*y); y += NBANDS; + d = *xPrev; CLIP_2N(d, 31 - es); *xPrev++ = d << es; + } + } + return mOut; + } +} + +/* 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); + */ + +static const int c9_0 = 0x6ed9eba1; +static const int c9_1 = 0x620dbe8b; +static const int c9_2 = 0x163a1a7e; +static const int c9_3 = 0x5246dd49; +static const int c9_4 = 0x7e0e2e32; + +/* format = Q31 + * cos(((0:8) + 0.5) * (pi/18)) + */ +static const int c18[9] = { + 0x7f834ed0, 0x7ba3751d, 0x7401e4c1, 0x68d9f964, 0x5a82799a, 0x496af3e2, 0x36185aee, 0x2120fb83, 0x0b27eb5c, +}; + +/* require at least 3 guard bits in x[] to ensure no overflow */ +static __inline void idct9(int *x) +{ + int a1, a2, a3, a4, a5, a6, a7, a8, a9; + int a10, a11, a12, a13, a14, a15, a16, a17, a18; + int a19, a20, a21, a22, a23, a24, a25, a26, a27; + int m1, m3, m5, m6, m7, m8, m9, m10, m11, m12; + int x0, x1, x2, x3, x4, x5, x6, x7, x8; + + x0 = x[0]; x1 = x[1]; x2 = x[2]; x3 = x[3]; x4 = x[4]; + x5 = x[5]; x6 = x[6]; x7 = x[7]; x8 = x[8]; + + a1 = x0 - x6; + a2 = x1 - x5; + a3 = x1 + x5; + a4 = x2 - x4; + a5 = x2 + x4; + a6 = x2 + x8; + a7 = x1 + x7; + + a8 = a6 - a5; /* ie x[8] - x[4] */ + a9 = a3 - a7; /* ie x[5] - x[7] */ + a10 = a2 - x7; /* ie x[1] - x[5] - x[7] */ + a11 = a4 - x8; /* ie x[2] - x[4] - x[8] */ + + /* do the << 1 as constant shifts where mX is actually used (free, no stall or extra inst.) */ + m1 = MULSHIFT32(c9_0, x3); + m3 = MULSHIFT32(c9_0, a10); + m5 = MULSHIFT32(c9_1, a5); + m6 = MULSHIFT32(c9_2, a6); + m7 = MULSHIFT32(c9_1, a8); + m8 = MULSHIFT32(c9_2, a5); + m9 = MULSHIFT32(c9_3, a9); + m10 = MULSHIFT32(c9_4, a7); + m11 = MULSHIFT32(c9_3, a3); + m12 = MULSHIFT32(c9_4, a9); + + a12 = x[0] + (x[6] >> 1); + a13 = a12 + ( m1 << 1); + a14 = a12 - ( m1 << 1); + a15 = a1 + ( a11 >> 1); + a16 = ( m5 << 1) + (m6 << 1); + a17 = ( m7 << 1) - (m8 << 1); + a18 = a16 + a17; + a19 = ( m9 << 1) + (m10 << 1); + a20 = (m11 << 1) - (m12 << 1); + + a21 = a20 - a19; + a22 = a13 + a16; + a23 = a14 + a16; + a24 = a14 + a17; + a25 = a13 + a17; + a26 = a14 - a18; + a27 = a13 - a18; + + x0 = a22 + a19; x[0] = x0; + x1 = a15 + (m3 << 1); x[1] = x1; + x2 = a24 + a20; x[2] = x2; + x3 = a26 - a21; x[3] = x3; + x4 = a1 - a11; x[4] = x4; + x5 = a27 + a21; x[5] = x5; + x6 = a25 - a20; x[6] = x6; + x7 = a15 - (m3 << 1); x[7] = x7; + x8 = a23 - a19; x[8] = x8; +} + +/* let c(j) = cos(M_PI/36 * ((j)+0.5)), s(j) = sin(M_PI/36 * ((j)+0.5)) + * then fastWin[2*j+0] = c(j)*(s(j) + c(j)), j = [0, 8] + * fastWin[2*j+1] = c(j)*(s(j) - c(j)) + * format = Q30 + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" +const int fastWin36[18] = { + 0x42aace8b, 0xc2e92724, 0x47311c28, 0xc95f619a, 0x4a868feb, 0xd0859d8c, + 0x4c913b51, 0xd8243ea0, 0x4d413ccc, 0xe0000000, 0x4c913b51, 0xe7dbc161, + 0x4a868feb, 0xef7a6275, 0x47311c28, 0xf6a09e67, 0x42aace8b, 0xfd16d8dd, +}; +#pragma GCC diagnostic pop +/************************************************************************************** + * Function: IMDCT36 + * + * Description: 36-point modified DCT, with windowing and overlap-add (50% overlap) + * + * Inputs: vector of 18 coefficients (N/2 inputs produces N outputs, by symmetry) + * overlap part of last IMDCT (9 samples - see output comments) + * window type (0,1,2,3) of current and previous block + * current block index (for deciding whether to do frequency inversion) + * number of guard bits in input vector + * + * Outputs: 18 output samples, after windowing and overlap-add with last frame + * second half of (unwindowed) 36-point IMDCT - save for next time + * only save 9 xPrev samples, using symmetry (see WinPrevious()) + * + * Notes: this is Ken's hyper-fast algorithm, including symmetric sin window + * optimization, if applicable + * total number of multiplies, general case: + * 2*10 (idct9) + 9 (last stage imdct) + 36 (for windowing) = 65 + * total number of multiplies, btCurr == 0 && btPrev == 0: + * 2*10 (idct9) + 9 (last stage imdct) + 18 (for windowing) = 47 + * + * blockType == 0 is by far the most common case, so it should be + * possible to use the fast path most of the time + * this is the fastest known algorithm for performing + * long IMDCT + windowing + overlap-add in MP3 + * + * Return: mOut (OR of abs(y) for all y calculated here) + * + * TODO: optimize for ARM (reorder window coefs, ARM-style pointers in C, + * inline asm may or may not be helpful) + **************************************************************************************/ +// barely faster in RAM +/*__attribute__ ((section (".data")))*/ static int IMDCT36(int *xCurr, int *xPrev, int *y, int btCurr, int btPrev, int blockIdx, int gb) +{ + int i, es, xBuf[18], xPrevWin[18]; + int acc1, acc2, s, d, t, mOut; + int xo, xe, c, *xp, yLo, yHi; + const int *cp, *wp; + + acc1 = acc2 = 0; + xCurr += 17; + + /* 7 gb is always adequate for antialias + accumulator loop + idct9 */ + if (gb < 7) { + /* rarely triggered - 5% to 10% of the time on normal clips (with Q25 input) */ + es = 7 - gb; + for (i = 8; i >= 0; i--) { + acc1 = ((*xCurr--) >> es) - acc1; + acc2 = acc1 - acc2; + acc1 = ((*xCurr--) >> es) - acc1; + xBuf[i+9] = acc2; /* odd */ + xBuf[i+0] = acc1; /* even */ + xPrev[i] >>= es; + } + } else { + es = 0; + /* max gain = 18, assume adequate guard bits */ + for (i = 8; i >= 0; i--) { + acc1 = (*xCurr--) - acc1; + acc2 = acc1 - acc2; + acc1 = (*xCurr--) - acc1; + xBuf[i+9] = acc2; /* odd */ + xBuf[i+0] = acc1; /* even */ + } + } + /* xEven[0] and xOdd[0] scaled by 0.5 */ + xBuf[9] >>= 1; + xBuf[0] >>= 1; + + /* do 9-point IDCT on even and odd */ + idct9(xBuf+0); /* even */ + idct9(xBuf+9); /* odd */ + + xp = xBuf + 8; + cp = c18 + 8; + mOut = 0; + if (btPrev == 0 && btCurr == 0) { + /* fast path - use symmetry of sin window to reduce windowing multiplies to 18 (N/2) */ + wp = fastWin36; + for (i = 0; i < 9; i++) { + /* do ARM-style pointer arithmetic (i still needed for y[] indexing - compiler spills if 2 y pointers) */ + c = *cp--; xo = *(xp + 9); xe = *xp--; + /* gain 2 int bits here */ + xo = MULSHIFT32(c, xo); /* 2*c18*xOdd (mul by 2 implicit in scaling) */ + xe >>= 2; + + s = -(*xPrev); /* sum from last block (always at least 2 guard bits) */ + d = -(xe - xo); /* gain 2 int bits, don't shift xo (effective << 1 to eat sign bit, << 1 for mul by 2) */ + (*xPrev++) = xe + xo; /* symmetry - xPrev[i] = xPrev[17-i] for long blocks */ + t = s - d; + + yLo = (d + (MULSHIFT32(t, *wp++) << 2)); + yHi = (s + (MULSHIFT32(t, *wp++) << 2)); + y[(i)*NBANDS] = yLo; + y[(17-i)*NBANDS] = yHi; + mOut |= FASTABS(yLo); + mOut |= FASTABS(yHi); + } + } else { + /* slower method - either prev or curr is using window type != 0 so do full 36-point window + * output xPrevWin has at least 3 guard bits (xPrev has 2, gain 1 in WinPrevious) + */ + WinPrevious(xPrev, xPrevWin, btPrev); + + wp = imdctWin[btCurr]; + for (i = 0; i < 9; i++) { + c = *cp--; xo = *(xp + 9); xe = *xp--; + /* gain 2 int bits here */ + xo = MULSHIFT32(c, xo); /* 2*c18*xOdd (mul by 2 implicit in scaling) */ + xe >>= 2; + + d = xe - xo; + (*xPrev++) = xe + xo; /* symmetry - xPrev[i] = xPrev[17-i] for long blocks */ + + yLo = (xPrevWin[i] + MULSHIFT32(d, wp[i])) << 2; + yHi = (xPrevWin[17-i] + MULSHIFT32(d, wp[17-i])) << 2; + y[(i)*NBANDS] = yLo; + y[(17-i)*NBANDS] = yHi; + mOut |= FASTABS(yLo); + mOut |= FASTABS(yHi); + } + } + + xPrev -= 9; + mOut |= FreqInvertRescale(y, xPrev, blockIdx, es); + + return mOut; +} + +static int c3_0 = 0x6ed9eba1; /* format = Q31, cos(pi/6) */ +static int c6[3] = { 0x7ba3751d, 0x5a82799a, 0x2120fb83 }; /* format = Q31, cos(((0:2) + 0.5) * (pi/6)) */ + +/* 12-point inverse DCT, used in IMDCT12x3() + * 4 input guard bits will ensure no overflow + */ +static __inline void imdct12 (int *x, int *out) +{ + int a0, a1, a2; + int x0, x1, x2, x3, x4, x5; + + x0 = *x; x+=3; x1 = *x; x+=3; + x2 = *x; x+=3; x3 = *x; x+=3; + x4 = *x; x+=3; x5 = *x; x+=3; + + x4 -= x5; + x3 -= x4; + x2 -= x3; + x3 -= x5; + x1 -= x2; + x0 -= x1; + x1 -= x3; + + x0 >>= 1; + x1 >>= 1; + + a0 = MULSHIFT32(c3_0, x2) << 1; + a1 = x0 + (x4 >> 1); + a2 = x0 - x4; + x0 = a1 + a0; + x2 = a2; + x4 = a1 - a0; + + a0 = MULSHIFT32(c3_0, x3) << 1; + a1 = x1 + (x5 >> 1); + a2 = x1 - x5; + + /* cos window odd samples, mul by 2, eat sign bit */ + x1 = MULSHIFT32(c6[0], a1 + a0) << 2; + x3 = MULSHIFT32(c6[1], a2) << 2; + x5 = MULSHIFT32(c6[2], a1 - a0) << 2; + + *out = x0 + x1; out++; + *out = x2 + x3; out++; + *out = x4 + x5; out++; + *out = x4 - x5; out++; + *out = x2 - x3; out++; + *out = x0 - x1; +} + +/************************************************************************************** + * Function: IMDCT12x3 + * + * Description: three 12-point modified DCT's for short blocks, with windowing, + * short block concatenation, and overlap-add + * + * Inputs: 3 interleaved vectors of 6 samples each + * (block0[0], block1[0], block2[0], block0[1], block1[1]....) + * overlap part of last IMDCT (9 samples - see output comments) + * window type (0,1,2,3) of previous block + * current block index (for deciding whether to do frequency inversion) + * number of guard bits in input vector + * + * Outputs: updated sample vector x, net gain of 1 integer bit + * second half of (unwindowed) IMDCT's - save for next time + * only save 9 xPrev samples, using symmetry (see WinPrevious()) + * + * Return: mOut (OR of abs(y) for all y calculated here) + * + * TODO: optimize for ARM + **************************************************************************************/ + // barely faster in RAM +/*__attribute__ ((section (".data")))*/ static int IMDCT12x3(int *xCurr, int *xPrev, int *y, int btPrev, int blockIdx, int gb) +{ + int i, es, mOut, yLo, xBuf[18], xPrevWin[18]; /* need temp buffer for reordering short blocks */ + const int *wp; + + es = 0; + /* 7 gb is always adequate for accumulator loop + idct12 + window + overlap */ + if (gb < 7) { + es = 7 - gb; + for (i = 0; i < 18; i+=2) { + xCurr[i+0] >>= es; + xCurr[i+1] >>= es; + *xPrev++ >>= es; + } + xPrev -= 9; + } + + /* requires 4 input guard bits for each imdct12 */ + imdct12(xCurr + 0, xBuf + 0); + imdct12(xCurr + 1, xBuf + 6); + imdct12(xCurr + 2, xBuf + 12); + + /* window previous from last time */ + WinPrevious(xPrev, xPrevWin, btPrev); + + /* could unroll this for speed, minimum loads (short blocks usually rare, so doesn't make much overall difference) + * xPrevWin[i] << 2 still has 1 gb always, max gain of windowed xBuf stuff also < 1.0 and gain the sign bit + * so y calculations won't overflow + */ + wp = imdctWin[2]; + mOut = 0; + for (i = 0; i < 3; i++) { + yLo = (xPrevWin[ 0+i] << 2); + mOut |= FASTABS(yLo); y[( 0+i)*NBANDS] = yLo; + yLo = (xPrevWin[ 3+i] << 2); + mOut |= FASTABS(yLo); y[( 3+i)*NBANDS] = yLo; + yLo = (xPrevWin[ 6+i] << 2) + (MULSHIFT32(wp[0+i], xBuf[3+i])); + mOut |= FASTABS(yLo); y[( 6+i)*NBANDS] = yLo; + yLo = (xPrevWin[ 9+i] << 2) + (MULSHIFT32(wp[3+i], xBuf[5-i])); + mOut |= FASTABS(yLo); y[( 9+i)*NBANDS] = yLo; + yLo = (xPrevWin[12+i] << 2) + (MULSHIFT32(wp[6+i], xBuf[2-i]) + MULSHIFT32(wp[0+i], xBuf[(6+3)+i])); + mOut |= FASTABS(yLo); y[(12+i)*NBANDS] = yLo; + yLo = (xPrevWin[15+i] << 2) + (MULSHIFT32(wp[9+i], xBuf[0+i]) + MULSHIFT32(wp[3+i], xBuf[(6+5)-i])); + mOut |= FASTABS(yLo); y[(15+i)*NBANDS] = yLo; + } + + /* save previous (unwindowed) for overlap - only need samples 6-8, 12-17 */ + for (i = 6; i < 9; i++) + *xPrev++ = xBuf[i] >> 2; + for (i = 12; i < 18; i++) + *xPrev++ = xBuf[i] >> 2; + + xPrev -= 9; + mOut |= FreqInvertRescale(y, xPrev, blockIdx, es); + + return mOut; +} + +/************************************************************************************** + * Function: HybridTransform + * + * Description: IMDCT's, windowing, and overlap-add on long/short/mixed blocks + * + * Inputs: vector of input coefficients, length = nBlocksTotal * 18) + * vector of overlap samples from last time, length = nBlocksPrev * 9) + * buffer for output samples, length = MAXNSAMP + * SideInfoSub struct for this granule/channel + * BlockCount struct with necessary info + * number of non-zero input and overlap blocks + * number of long blocks in input vector (rest assumed to be short blocks) + * number of blocks which use long window (type) 0 in case of mixed block + * (bc->currWinSwitch, 0 for non-mixed blocks) + * + * Outputs: transformed, windowed, and overlapped sample buffer + * does frequency inversion on odd blocks + * updated buffer of samples for overlap + * + * Return: number of non-zero IMDCT blocks calculated in this call + * (including overlap-add) + * + * TODO: examine mixedBlock/winSwitch logic carefully (test he_mode.bit) + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static int HybridTransform(int *xCurr, int *xPrev, int y[BLOCK_SIZE][NBANDS], SideInfoSub *sis, BlockCount *bc) +{ + int xPrevWin[18], currWinIdx, prevWinIdx; + int i, j, nBlocksOut, nonZero, mOut; + int fiBit, xp; + + ASSERT(bc->nBlocksLong <= NBANDS); + ASSERT(bc->nBlocksTotal <= NBANDS); + ASSERT(bc->nBlocksPrev <= NBANDS); + + mOut = 0; + + /* do long blocks, if any */ + for(i = 0; i < bc->nBlocksLong; i++) { + /* currWinIdx picks the right window for long blocks (if mixed, long blocks use window type 0) */ + currWinIdx = sis->blockType; + if (sis->mixedBlock && i < bc->currWinSwitch) + currWinIdx = 0; + + prevWinIdx = bc->prevType; + if (i < bc->prevWinSwitch) + prevWinIdx = 0; + + /* do 36-point IMDCT, including windowing and overlap-add */ + mOut |= IMDCT36(xCurr, xPrev, &(y[0][i]), currWinIdx, prevWinIdx, i, bc->gbIn); + xCurr += 18; + xPrev += 9; + } + + /* do short blocks (if any) */ + for ( ; i < bc->nBlocksTotal; i++) { + ASSERT(sis->blockType == 2); + + prevWinIdx = bc->prevType; + if (i < bc->prevWinSwitch) + prevWinIdx = 0; + + mOut |= IMDCT12x3(xCurr, xPrev, &(y[0][i]), prevWinIdx, i, bc->gbIn); + xCurr += 18; + xPrev += 9; + } + nBlocksOut = i; + + /* window and overlap prev if prev longer that current */ + for ( ; i < bc->nBlocksPrev; i++) { + prevWinIdx = bc->prevType; + if (i < bc->prevWinSwitch) + prevWinIdx = 0; + WinPrevious(xPrev, xPrevWin, prevWinIdx); + + nonZero = 0; + fiBit = i << 31; + for (j = 0; j < 9; j++) { + xp = xPrevWin[2*j+0] << 2; /* << 2 temp for scaling */ + nonZero |= xp; + y[2*j+0][i] = xp; + mOut |= FASTABS(xp); + + /* frequency inversion on odd blocks/odd samples (flip sign if i odd, j odd) */ + xp = xPrevWin[2*j+1] << 2; + xp = (xp ^ (fiBit >> 31)) + (i & 0x01); + nonZero |= xp; + y[2*j+1][i] = xp; + mOut |= FASTABS(xp); + + xPrev[j] = 0; + } + xPrev += 9; + if (nonZero) + nBlocksOut = i; + } + + /* clear rest of blocks */ + for ( ; i < 32; i++) { + for (j = 0; j < 18; j++) + y[j][i] = 0; + } + + bc->gbOut = CLZ(mOut) - 1; + + return nBlocksOut; +} + +/************************************************************************************** + * Function: IMDCT + * + * Description: do alias reduction, inverse MDCT, overlap-add, and frequency inversion + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader(), UnpackSideInfo(), + * UnpackScaleFactors(), and DecodeHuffman() (for this granule, channel) + * includes PCM samples in overBuf (from last call to IMDCT) for OLA + * index of current granule and channel + * + * Outputs: PCM samples in outBuf, for input to subband transform + * PCM samples in overBuf, for OLA next time + * updated hi->nonZeroBound index for this channel + * + * Return: 0 on success, -1 if null input pointers + **************************************************************************************/ + // a bit faster in RAM +/*__attribute__ ((section (".data")))*/ int IMDCT(MP3DecInfo *mp3DecInfo, int gr, int ch) +{ + int nBfly, blockCutoff; + FrameHeader *fh; + SideInfo *si; + HuffmanInfo *hi; + IMDCTInfo *mi; + BlockCount bc; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS || + !mp3DecInfo->HuffmanInfoPS || !mp3DecInfo->IMDCTInfoPS) + return -1; + + /* si is an array of up to 4 structs, stored as gr0ch0, gr0ch1, gr1ch0, gr1ch1 */ + fh = (FrameHeader *)(mp3DecInfo->FrameHeaderPS); + si = (SideInfo *)(mp3DecInfo->SideInfoPS); + hi = (HuffmanInfo*)(mp3DecInfo->HuffmanInfoPS); + mi = (IMDCTInfo *)(mp3DecInfo->IMDCTInfoPS); + + /* anti-aliasing done on whole long blocks only + * for mixed blocks, nBfly always 1, except 3 for 8 kHz MPEG 2.5 (see sfBandTab) + * nLongBlocks = number of blocks with (possibly) non-zero power + * nBfly = number of butterflies to do (nLongBlocks - 1, unless no long blocks) + */ + blockCutoff = fh->sfBand->l[(fh->ver == MPEG1 ? 8 : 6)] / 18; /* same as 3* num short sfb's in spec */ + if (si->sis[gr][ch].blockType != 2) { + /* all long transforms */ + bc.nBlocksLong = MIN((hi->nonZeroBound[ch] + 7) / 18 + 1, 32); + nBfly = bc.nBlocksLong - 1; + } else if (si->sis[gr][ch].blockType == 2 && si->sis[gr][ch].mixedBlock) { + /* mixed block - long transforms until cutoff, then short transforms */ + bc.nBlocksLong = blockCutoff; + nBfly = bc.nBlocksLong - 1; + } else { + /* all short transforms */ + bc.nBlocksLong = 0; + nBfly = 0; + } + + AntiAlias(hi->huffDecBuf[ch], nBfly); + hi->nonZeroBound[ch] = MAX(hi->nonZeroBound[ch], (nBfly * 18) + 8); + + ASSERT(hi->nonZeroBound[ch] <= MAX_NSAMP); + + /* for readability, use a struct instead of passing a million parameters to HybridTransform() */ + bc.nBlocksTotal = (hi->nonZeroBound[ch] + 17) / 18; + bc.nBlocksPrev = mi->numPrevIMDCT[ch]; + bc.prevType = mi->prevType[ch]; + bc.prevWinSwitch = mi->prevWinSwitch[ch]; + bc.currWinSwitch = (si->sis[gr][ch].mixedBlock ? blockCutoff : 0); /* where WINDOW switches (not nec. transform) */ + bc.gbIn = hi->gb[ch]; + + mi->numPrevIMDCT[ch] = HybridTransform(hi->huffDecBuf[ch], mi->overBuf[ch], mi->outBuf[ch], &si->sis[gr][ch], &bc); + mi->prevType[ch] = si->sis[gr][ch].blockType; + mi->prevWinSwitch[ch] = bc.currWinSwitch; /* 0 means not a mixed block (either all short or all long) */ + mi->gb[ch] = bc.gbOut; + + ASSERT(mi->numPrevIMDCT[ch] <= NBANDS); + + /* output has gained 2 int bits */ + return 0; +} diff --git a/components/driver_sndmixer/libhelix-mp3/mp3common.h b/components/driver_sndmixer/libhelix-mp3/mp3common.h new file mode 100644 index 0000000..07548ab --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/mp3common.h @@ -0,0 +1,124 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * mp3common.h - implementation-independent API's, datatypes, and definitions + **************************************************************************************/ + +#ifndef _MP3COMMON_H +#define _MP3COMMON_H + +#include "mp3dec.h" +#include "statname.h" /* do name-mangling for static linking */ + +#define MAX_SCFBD 4 /* max scalefactor bands per channel */ +#define NGRANS_MPEG1 2 +#define NGRANS_MPEG2 1 + +/* 11-bit syncword if MPEG 2.5 extensions are enabled */ +/* +#define SYNCWORDH 0xff +#define SYNCWORDL 0xe0 +*/ + +/* 12-bit syncword if MPEG 1,2 only are supported */ +#define SYNCWORDH 0xff +#define SYNCWORDL 0xf0 + +typedef struct _MP3DecInfo { + /* pointers to platform-specific data structures */ + void *FrameHeaderPS; + void *SideInfoPS; + void *ScaleFactorInfoPS; + void *HuffmanInfoPS; + void *DequantInfoPS; + void *IMDCTInfoPS; + void *SubbandInfoPS; + + /* buffer which must be large enough to hold largest possible main_data section */ + unsigned char mainBuf[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; + MPEGVersion version; + + int mainDataBegin; + int mainDataBytes; + + int part23Length[MAX_NGRAN][MAX_NCHAN]; + +} MP3DecInfo; + +typedef struct _SFBandTable { + int/*short*/ l[23]; + int/*short*/ s[14]; +} SFBandTable; + +/* decoder functions which must be implemented for each platform */ +MP3DecInfo *AllocateBuffers(void); +void FreeBuffers(MP3DecInfo *mp3DecInfo); +int CheckPadBit(MP3DecInfo *mp3DecInfo); +int UnpackFrameHeader(MP3DecInfo *mp3DecInfo, unsigned char *buf); +int UnpackSideInfo(MP3DecInfo *mp3DecInfo, unsigned char *buf); +int DecodeHuffman(MP3DecInfo *mp3DecInfo, unsigned char *buf, int *bitOffset, int huffBlockBits, int gr, int ch); +int Dequantize(MP3DecInfo *mp3DecInfo, int gr); +int IMDCT(MP3DecInfo *mp3DecInfo, int gr, int ch); +int UnpackScaleFactors(MP3DecInfo *mp3DecInfo, unsigned char *buf, int *bitOffset, int bitsAvail, int gr, int ch); +int Subband(MP3DecInfo *mp3DecInfo, short *pcmBuf); + +/* mp3tabs.c - global ROM tables */ +extern const int samplerateTab[3][3]; +extern const int/*short*/ bitrateTab[3][3][15]; +extern const int/*short*/ samplesPerFrameTab[3][3]; +extern const short bitsPerSlotTab[3]; +extern const int/*short*/ sideBytesTab[3][2]; +extern const int/*short*/ slotTab[3][3][15]; +extern const SFBandTable sfBandTable[3][3]; + +#endif /* _MP3COMMON_H */ diff --git a/components/driver_sndmixer/libhelix-mp3/mp3dec.c b/components/driver_sndmixer/libhelix-mp3/mp3dec.c new file mode 100644 index 0000000..03bcbbe --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/mp3dec.c @@ -0,0 +1,485 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * mp3dec.c - platform-independent top level MP3 decoder API + **************************************************************************************/ + +#include "string.h" +//#include "hlxclib/string.h" /* for memmove, memcpy (can replace with different implementations if desired) */ +#include "mp3common.h" /* includes mp3dec.h (public API) and internal, platform-independent API */ + +#include + +//#define PROFILE +#ifdef PROFILE +#include "systime.h" +#endif + +/************************************************************************************** + * Function: MP3InitDecoder + * + * Description: allocate memory for platform-specific data + * clear all the user-accessible fields + * + * Inputs: none + * + * Outputs: none + * + * Return: handle to mp3 decoder instance, 0 if malloc fails + **************************************************************************************/ +HMP3Decoder MP3InitDecoder(void) +{ + MP3DecInfo *mp3DecInfo; + + mp3DecInfo = AllocateBuffers(); + + return (HMP3Decoder)mp3DecInfo; +} + +/************************************************************************************** + * Function: MP3FreeDecoder + * + * Description: free platform-specific data allocated by InitMP3Decoder + * zero out the contents of MP3DecInfo struct + * + * Inputs: valid MP3 decoder instance pointer (HMP3Decoder) + * + * Outputs: none + * + * Return: none + **************************************************************************************/ +void MP3FreeDecoder(HMP3Decoder hMP3Decoder) +{ + MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; + + if (!mp3DecInfo) + return; + + FreeBuffers(mp3DecInfo); +} + +/************************************************************************************** + * Function: MP3FindSyncWord + * + * Description: locate the next byte-alinged sync word in the raw mp3 stream + * + * Inputs: buffer to search for sync word + * max number of bytes to search in buffer + * + * Outputs: none + * + * Return: offset to first sync word (bytes from start of buf) + * -1 if sync not found after searching nBytes + **************************************************************************************/ +int IRAM_ATTR MP3FindSyncWord(unsigned char *buf, int nBytes) +{ + int i; + + /* find byte-aligned syncword - need 12 (MPEG 1,2) or 11 (MPEG 2.5) matching bits */ + for (i = 0; i < nBytes - 1; i++) { + if ( (buf[i+0] & SYNCWORDH) == SYNCWORDH && (buf[i+1] & SYNCWORDL) == SYNCWORDL ) + return i; + } + + return -1; +} + +/************************************************************************************** + * Function: MP3FindFreeSync + * + * Description: figure out number of bytes between adjacent sync words in "free" mode + * + * Inputs: buffer to search for next sync word + * the 4-byte frame header starting at the current sync word + * max number of bytes to search in buffer + * + * Outputs: none + * + * Return: offset to next sync word, minus any pad byte (i.e. nSlots) + * -1 if sync not found after searching nBytes + * + * Notes: this checks that the first 22 bits of the next frame header are the + * same as the current frame header, but it's still not foolproof + * (could accidentally find a sequence in the bitstream which + * appears to match but is not actually the next frame header) + * this could be made more error-resilient by checking several frames + * in a row and verifying that nSlots is the same in each case + * since free mode requires CBR (see spec) we generally only call + * this function once (first frame) then store the result (nSlots) + * and just use it from then on + **************************************************************************************/ +static int IRAM_ATTR MP3FindFreeSync(unsigned char *buf, unsigned char firstFH[4], int nBytes) +{ + int offset = 0; + unsigned char *bufPtr = buf; + + /* loop until we either: + * - run out of nBytes (FindMP3SyncWord() returns -1) + * - find the next valid frame header (sync word, version, layer, CRC flag, bitrate, and sample rate + * in next header must match current header) + */ + while (1) { + offset = MP3FindSyncWord(bufPtr, nBytes); + bufPtr += offset; + if (offset < 0) { + return -1; + } else if ( (bufPtr[0] == firstFH[0]) && (bufPtr[1] == firstFH[1]) && ((bufPtr[2] & 0xfc) == (firstFH[2] & 0xfc)) ) { + /* want to return number of bytes per frame, NOT counting the padding byte, so subtract one if padFlag == 1 */ + if ((firstFH[2] >> 1) & 0x01) + bufPtr--; + return bufPtr - buf; + } + bufPtr += 3; + nBytes -= (offset + 3); + }; + + return -1; +} + +/************************************************************************************** + * Function: MP3GetLastFrameInfo + * + * Description: get info about last MP3 frame decoded (number of sampled decoded, + * sample rate, bitrate, etc.) + * + * Inputs: valid MP3 decoder instance pointer (HMP3Decoder) + * pointer to MP3FrameInfo struct + * + * Outputs: filled-in MP3FrameInfo struct + * + * Return: none + * + * Notes: call this right after calling MP3Decode + **************************************************************************************/ +void IRAM_ATTR MP3GetLastFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo) +{ + MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; + + if (!mp3DecInfo || mp3DecInfo->layer != 3) { + mp3FrameInfo->bitrate = 0; + mp3FrameInfo->nChans = 0; + mp3FrameInfo->samprate = 0; + mp3FrameInfo->bitsPerSample = 0; + mp3FrameInfo->outputSamps = 0; + mp3FrameInfo->layer = 0; + mp3FrameInfo->version = 0; + } else { + mp3FrameInfo->bitrate = mp3DecInfo->bitrate; + mp3FrameInfo->nChans = mp3DecInfo->nChans; + mp3FrameInfo->samprate = mp3DecInfo->samprate; + mp3FrameInfo->bitsPerSample = 16; + mp3FrameInfo->outputSamps = mp3DecInfo->nChans * (int)samplesPerFrameTab[mp3DecInfo->version][mp3DecInfo->layer - 1]; + mp3FrameInfo->layer = mp3DecInfo->layer; + mp3FrameInfo->version = mp3DecInfo->version; + } +} + +/************************************************************************************** + * Function: MP3GetNextFrameInfo + * + * Description: parse MP3 frame header + * + * Inputs: valid MP3 decoder instance pointer (HMP3Decoder) + * pointer to MP3FrameInfo struct + * pointer to buffer containing valid MP3 frame header (located using + * MP3FindSyncWord(), above) + * + * Outputs: filled-in MP3FrameInfo struct + * + * Return: error code, defined in mp3dec.h (0 means no error, < 0 means error) + **************************************************************************************/ +int IRAM_ATTR MP3GetNextFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo, unsigned char *buf) +{ + MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; + + if (!mp3DecInfo) + return ERR_MP3_NULL_POINTER; + + if (UnpackFrameHeader(mp3DecInfo, buf) == -1 || mp3DecInfo->layer != 3) + return ERR_MP3_INVALID_FRAMEHEADER; + + MP3GetLastFrameInfo(mp3DecInfo, mp3FrameInfo); + + return ERR_MP3_NONE; +} + +/************************************************************************************** + * Function: MP3ClearBadFrame + * + * Description: zero out pcm buffer if error decoding MP3 frame + * + * Inputs: mp3DecInfo struct with correct frame size parameters filled in + * pointer pcm output buffer + * + * Outputs: zeroed out pcm buffer + * + * Return: none + **************************************************************************************/ +static void IRAM_ATTR MP3ClearBadFrame(MP3DecInfo *mp3DecInfo, short *outbuf) +{ + int i; + + if (!mp3DecInfo) + return; + + for (i = 0; i < mp3DecInfo->nGrans * mp3DecInfo->nGranSamps * mp3DecInfo->nChans; i++) + outbuf[i] = 0; +} + +/************************************************************************************** + * Function: MP3Decode + * + * Description: decode one frame of MP3 data + * + * Inputs: valid MP3 decoder instance pointer (HMP3Decoder) + * double pointer to buffer of MP3 data (containing headers + mainData) + * number of valid bytes remaining in inbuf + * pointer to outbuf, big enough to hold one frame of decoded PCM samples + * flag indicating whether MP3 data is normal MPEG format (useSize = 0) + * or reformatted as "self-contained" frames (useSize = 1) + * + * Outputs: PCM data in outbuf, interleaved LRLRLR... if stereo + * number of output samples = nGrans * nGranSamps * nChans + * updated inbuf pointer, updated bytesLeft + * + * Return: error code, defined in mp3dec.h (0 means no error, < 0 means error) + * + * Notes: switching useSize on and off between frames in the same stream + * is not supported (bit reservoir is not maintained if useSize on) + **************************************************************************************/ +int IRAM_ATTR MP3Decode(HMP3Decoder hMP3Decoder, unsigned char **inbuf, int *bytesLeft, short *outbuf, int useSize) +{ + int offset, bitOffset, mainBits, gr, ch, fhBytes, siBytes, freeFrameBytes; + int prevBitOffset, sfBlockBits, huffBlockBits; + unsigned char *mainPtr; + MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; + + #ifdef PROFILE + long time; + #endif + + if (!mp3DecInfo) + return ERR_MP3_NULL_POINTER; + + /* unpack frame header */ + fhBytes = UnpackFrameHeader(mp3DecInfo, *inbuf); + if (fhBytes < 0) + return ERR_MP3_INVALID_FRAMEHEADER; /* don't clear outbuf since we don't know size (failed to parse header) */ + *inbuf += fhBytes; + +#ifdef PROFILE + time = systime_get(); +#endif + /* unpack side info */ + siBytes = UnpackSideInfo(mp3DecInfo, *inbuf); + if (siBytes < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_SIDEINFO; + } + *inbuf += siBytes; + *bytesLeft -= (fhBytes + siBytes); +#ifdef PROFILE + time = systime_get() - time; + printf("UnpackSideInfo: %i ms\n", time); +#endif + + + /* if free mode, need to calculate bitrate and nSlots manually, based on frame size */ + if (mp3DecInfo->bitrate == 0 || mp3DecInfo->freeBitrateFlag) { + if (!mp3DecInfo->freeBitrateFlag) { + /* first time through, need to scan for next sync word and figure out frame size */ + mp3DecInfo->freeBitrateFlag = 1; + mp3DecInfo->freeBitrateSlots = MP3FindFreeSync(*inbuf, *inbuf - fhBytes - siBytes, *bytesLeft); + if (mp3DecInfo->freeBitrateSlots < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_FREE_BITRATE_SYNC; + } + freeFrameBytes = mp3DecInfo->freeBitrateSlots + fhBytes + siBytes; + mp3DecInfo->bitrate = (freeFrameBytes * mp3DecInfo->samprate * 8) / (mp3DecInfo->nGrans * mp3DecInfo->nGranSamps); + } + mp3DecInfo->nSlots = mp3DecInfo->freeBitrateSlots + CheckPadBit(mp3DecInfo); /* add pad byte, if required */ + } + + /* useSize != 0 means we're getting reformatted (RTP) packets (see RFC 3119) + * - calling function assembles "self-contained" MP3 frames by shifting any main_data + * from the bit reservoir (in previous frames) to AFTER the sync word and side info + * - calling function should set mainDataBegin to 0, and tell us exactly how large this + * frame is (in bytesLeft) + */ + if (useSize) { + mp3DecInfo->nSlots = *bytesLeft; + if (mp3DecInfo->mainDataBegin != 0 || mp3DecInfo->nSlots <= 0) { + /* error - non self-contained frame, or missing frame (size <= 0), could do loss concealment here */ + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_FRAMEHEADER; + } + + /* can operate in-place on reformatted frames */ + mp3DecInfo->mainDataBytes = mp3DecInfo->nSlots; + mainPtr = *inbuf; + *inbuf += mp3DecInfo->nSlots; + *bytesLeft -= (mp3DecInfo->nSlots); + } else { + /* out of data - assume last or truncated frame */ + if (mp3DecInfo->nSlots > *bytesLeft) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INDATA_UNDERFLOW; + } + +#ifdef PROFILE + time = systime_get(); +#endif + /* fill main data buffer with enough new data for this frame */ + if (mp3DecInfo->mainDataBytes >= mp3DecInfo->mainDataBegin) { + /* adequate "old" main data available (i.e. bit reservoir) */ + memmove(mp3DecInfo->mainBuf, mp3DecInfo->mainBuf + mp3DecInfo->mainDataBytes - mp3DecInfo->mainDataBegin, mp3DecInfo->mainDataBegin); + memcpy(mp3DecInfo->mainBuf + mp3DecInfo->mainDataBegin, *inbuf, mp3DecInfo->nSlots); + + mp3DecInfo->mainDataBytes = mp3DecInfo->mainDataBegin + mp3DecInfo->nSlots; + *inbuf += mp3DecInfo->nSlots; + *bytesLeft -= (mp3DecInfo->nSlots); + mainPtr = mp3DecInfo->mainBuf; + } else { + /* not enough data in bit reservoir from previous frames (perhaps starting in middle of file) */ + memcpy(mp3DecInfo->mainBuf + mp3DecInfo->mainDataBytes, *inbuf, mp3DecInfo->nSlots); + mp3DecInfo->mainDataBytes += mp3DecInfo->nSlots; + *inbuf += mp3DecInfo->nSlots; + *bytesLeft -= (mp3DecInfo->nSlots); + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_MAINDATA_UNDERFLOW; + } +#ifdef PROFILE + time = systime_get() - time; + printf("data buffer filling: %i ms\n", time); +#endif + + } + bitOffset = 0; + mainBits = mp3DecInfo->mainDataBytes * 8; + + /* decode one complete frame */ + for (gr = 0; gr < mp3DecInfo->nGrans; gr++) { + for (ch = 0; ch < mp3DecInfo->nChans; ch++) { + + #ifdef PROFILE + time = systime_get(); + #endif + /* unpack scale factors and compute size of scale factor block */ + prevBitOffset = bitOffset; + offset = UnpackScaleFactors(mp3DecInfo, mainPtr, &bitOffset, mainBits, gr, ch); + #ifdef PROFILE + time = systime_get() - time; + printf("UnpackScaleFactors: %i ms\n", time); + #endif + + sfBlockBits = 8*offset - prevBitOffset + bitOffset; + huffBlockBits = mp3DecInfo->part23Length[gr][ch] - sfBlockBits; + mainPtr += offset; + mainBits -= sfBlockBits; + + if (offset < 0 || mainBits < huffBlockBits) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_SCALEFACT; + } + + #ifdef PROFILE + time = systime_get(); + #endif + /* decode Huffman code words */ + prevBitOffset = bitOffset; + offset = DecodeHuffman(mp3DecInfo, mainPtr, &bitOffset, huffBlockBits, gr, ch); + if (offset < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_HUFFCODES; + } + #ifdef PROFILE + time = systime_get() - time; + printf("Huffman: %i ms\n", time); + #endif + + mainPtr += offset; + mainBits -= (8*offset - prevBitOffset + bitOffset); + } + + #ifdef PROFILE + time = systime_get(); + #endif + /* dequantize coefficients, decode stereo, reorder short blocks */ + if (Dequantize(mp3DecInfo, gr) < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_DEQUANTIZE; + } + #ifdef PROFILE + time = systime_get() - time; + printf("Dequantize: %i ms\n", time); + #endif + + /* alias reduction, inverse MDCT, overlap-add, frequency inversion */ + for (ch = 0; ch < mp3DecInfo->nChans; ch++) + { + #ifdef PROFILE + time = systime_get(); + #endif + if (IMDCT(mp3DecInfo, gr, ch) < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_IMDCT; + } + #ifdef PROFILE + time = systime_get() - time; + printf("IMDCT: %i ms\n", time); + #endif + } + + #ifdef PROFILE + time = systime_get(); + #endif + /* subband transform - if stereo, interleaves pcm LRLRLR */ + if (Subband(mp3DecInfo, outbuf + gr*mp3DecInfo->nGranSamps*mp3DecInfo->nChans) < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_SUBBAND; + } + #ifdef PROFILE + time = systime_get() - time; + printf("Subband: %i ms\n", time); + #endif + + } + return ERR_MP3_NONE; +} diff --git a/components/driver_sndmixer/libhelix-mp3/mp3dec.h b/components/driver_sndmixer/libhelix-mp3/mp3dec.h new file mode 100644 index 0000000..d264b8f --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/mp3dec.h @@ -0,0 +1,115 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * mp3dec.h - public C API for MP3 decoder + **************************************************************************************/ + +#include +#define Word64 uint64_t + +#ifndef _MP3DEC_H +#define _MP3DEC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* determining MAINBUF_SIZE: + * max mainDataBegin = (2^9 - 1) bytes (since 9-bit offset) = 511 + * max nSlots (concatenated with mainDataBegin bytes from before) = 1440 - 9 - 4 + 1 = 1428 + * 511 + 1428 = 1939, round up to 1940 (4-byte align) + */ +#define MAINBUF_SIZE 1940 + +#define MAX_NGRAN 2 /* max granules */ +#define MAX_NCHAN 2 /* max channels */ +#define MAX_NSAMP 576 /* max samples per channel, per granule */ + +/* map to 0,1,2 to make table indexing easier */ +typedef enum { + MPEG1 = 0, + MPEG2 = 1, + MPEG25 = 2 +} MPEGVersion; + +typedef void *HMP3Decoder; + +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; + +/* public API */ +HMP3Decoder MP3InitDecoder(void); +void MP3FreeDecoder(HMP3Decoder hMP3Decoder); +int MP3Decode(HMP3Decoder hMP3Decoder, unsigned char **inbuf, int *bytesLeft, short *outbuf, int useSize); + +void MP3GetLastFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo); +int MP3GetNextFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo, unsigned char *buf); +int MP3FindSyncWord(unsigned char *buf, int nBytes); + +#ifdef __cplusplus +} +#endif + +#endif /* _MP3DEC_H */ diff --git a/components/driver_sndmixer/libhelix-mp3/mp3tabs.c b/components/driver_sndmixer/libhelix-mp3/mp3tabs.c new file mode 100644 index 0000000..1ced777 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/mp3tabs.c @@ -0,0 +1,181 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * mp3tabs.c - platform-independent tables for MP3 decoder (global, read-only) + **************************************************************************************/ + +#include "mp3common.h" + +/* indexing = [version][samplerate index] + * sample rate of frame (Hz) + */ +const int samplerateTab[3][3] = { + {44100, 48000, 32000}, /* MPEG-1 */ + {22050, 24000, 16000}, /* MPEG-2 */ + {11025, 12000, 8000}, /* MPEG-2.5 */ +}; + +/* indexing = [version][layer][bitrate index] + * bitrate (kbps) of frame + * - bitrate index == 0 is "free" mode (bitrate determined on the fly by + * counting bits between successive sync words) + */ +const int/*short*/ bitrateTab[3][3][15] = { + { + /* MPEG-1 */ + { 0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448}, /* Layer 1 */ + { 0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384}, /* Layer 2 */ + { 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320}, /* Layer 3 */ + }, + { + /* MPEG-2 */ + { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, /* Layer 1 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, /* Layer 2 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, /* Layer 3 */ + }, + { + /* MPEG-2.5 */ + { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, /* Layer 1 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, /* Layer 2 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, /* Layer 3 */ + }, +}; + +/* indexing = [version][layer] + * number of samples in one frame (per channel) + */ +const int/*short*/ samplesPerFrameTab[3][3] = { + {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] = { + {17, 32}, /* MPEG-1: mono, stereo */ + { 9, 17}, /* MPEG-2: mono, stereo */ + { 9, 17}, /* MPEG-2.5: mono, stereo */ +}; + +/* indexing = [version][sampleRate][bitRate] + * for layer3, nSlots = floor(samps/frame * bitRate / sampleRate / 8) + * - add one pad slot if necessary + */ +const int/*short*/ slotTab[3][3][15] = { + { + /* MPEG-1 */ + { 0, 104, 130, 156, 182, 208, 261, 313, 365, 417, 522, 626, 731, 835,1044 }, /* 44 kHz */ + { 0, 96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, 672, 768, 960 }, /* 48 kHz */ + { 0, 144, 180, 216, 252, 288, 360, 432, 504, 576, 720, 864,1008,1152,1440 }, /* 32 kHz */ + }, + { + /* MPEG-2 */ + { 0, 26, 52, 78, 104, 130, 156, 182, 208, 261, 313, 365, 417, 470, 522 }, /* 22 kHz */ + { 0, 24, 48, 72, 96, 120, 144, 168, 192, 240, 288, 336, 384, 432, 480 }, /* 24 kHz */ + { 0, 36, 72, 108, 144, 180, 216, 252, 288, 360, 432, 504, 576, 648, 720 }, /* 16 kHz */ + }, + { + /* MPEG-2.5 */ + { 0, 52, 104, 156, 208, 261, 313, 365, 417, 522, 626, 731, 835, 940,1044 }, /* 11 kHz */ + { 0, 48, 96, 144, 192, 240, 288, 336, 384, 480, 576, 672, 768, 864, 960 }, /* 12 kHz */ + { 0, 72, 144, 216, 288, 360, 432, 504, 576, 720, 864,1008,1152,1296,1440 }, /* 8 kHz */ + }, +}; + +/* 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 sfBandTable[3][3] = { + { + /* 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 } + }, + }, +}; + + diff --git a/components/driver_sndmixer/libhelix-mp3/mpadecobjfixpt.h b/components/driver_sndmixer/libhelix-mp3/mpadecobjfixpt.h new file mode 100644 index 0000000..a8a5c40 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/mpadecobjfixpt.h @@ -0,0 +1,108 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MPADECOBJFIXPT_H_ +#define _MPADECOBJFIXPT_H_ + +#include "mp3dec.h" /* public C API for new MP3 decoder */ + +class CMpaDecObj +{ +public: + CMpaDecObj(); + ~CMpaDecObj(); + + /////////////////////////////////////////////////////////////////////////// + // Function: Init_n + // Purpose: Initialize the mp3 decoder. + // Parameters: pSync a pointer to a syncword + // ulSize the size of the buffer pSync points to + // bUseSize this tells the decoder to use the input frame + // size on the decode instead of calculating + // the frame size. This is necessary when + // our formatted mp3 data (main_data_begin always + // equal to 0). + // + // Returns: returns 1 on success, 0 on failure + /////////////////////////////////////////////////////////////////////////// + int Init_n(unsigned char *pSync, + unsigned long ulSize, + unsigned char bUseSize=0); + + /////////////////////////////////////////////////////////////////////////// + // Function: DecodeFrame_v + // Purpose: Decodes one mp3 frame + // Parameters: pSource pointer to an mp3 frame (at a syncword) + // pulSize size of the buffer pSource points to. It will + // contain the number of mp3 bytes decoded upon + // return. + // pPCM pointer to a buffer to decode into + // pulPCMSize size of the PCM buffer. It will contain the + // number of PCM bytes prodced upon return. + /////////////////////////////////////////////////////////////////////////// + void DecodeFrame_v(unsigned char *pSource, + unsigned long *pulSize, + unsigned char *pPCM, + unsigned long *pulPCMSize); + + // overloaded new version that returns error code in errCode + void DecodeFrame_v(unsigned char *pSource, + unsigned long *pulSize, + unsigned char *pPCM, + unsigned long *pulPCMSize, + int *errCode); + + void GetPCMInfo_v(unsigned long &ulSampRate, + int &nChannels, + int &nBitsPerSample); + + // return number of samples per frame, PER CHANNEL (renderer multiplies this result by nChannels) + int GetSamplesPerFrame_n(); + + void SetTrustPackets(unsigned char bTrust) { m_bTrustPackets = bTrust; } + +private: + void * m_pDec; // generic void ptr + + void * m_pDecL1; // not implemented (could use old Xing mpadecl1.cpp) + void * m_pDecL2; // not implemented (could use old Xing mpadecl2.cpp) + HMP3Decoder m_pDecL3; + + MP3FrameInfo m_lastMP3FrameInfo; + unsigned char m_bUseFrameSize; + unsigned char m_bTrustPackets; +}; + +#endif /* _MPADECOBJFIXPT_H_ */ diff --git a/components/driver_sndmixer/libhelix-mp3/player.h b/components/driver_sndmixer/libhelix-mp3/player.h new file mode 100644 index 0000000..40f939e --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/player.h @@ -0,0 +1,13 @@ + + + +//SPI +#define PIN_SPI_SCK 14 +#define PIN_SPI_MOSI 7 +#define PIN_SPI_SDCARD_CS 10 //SD-Card CS +#define PIN_SPI_MEM_CS 6 //Flashmem CS + +//3V3 Voltage Regulator +#define PIN_SHUTDOWNPWR3V3 5 +#define PWR3V3_ON HIGH +#define PWR3V3_OFF LOW \ No newline at end of file diff --git a/components/driver_sndmixer/libhelix-mp3/polyphase.c b/components/driver_sndmixer/libhelix-mp3/polyphase.c new file mode 100644 index 0000000..bd331df --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/polyphase.c @@ -0,0 +1,295 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * polyphase.c - final stage of subband transform (polyphase synthesis filter) + * + * This is the C reference version using __int64 + * Look in the appropriate subdirectories for optimized asm implementations + * (e.g. arm/asmpoly.s) + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/* input to Polyphase = Q(DQ_FRACBITS_OUT-2), gain 2 bits in convolution + * we also have the implicit bias of 2^15 to add back, so net fraction bits = + * DQ_FRACBITS_OUT - 2 - 2 - 15 + * (see comment on Dequantize() for more info) + */ +#define DEF_NFRACBITS (DQ_FRACBITS_OUT - 2 - 2 - 15) +#define CSHIFT 12 /* coefficients have 12 leading sign bits for early-terminating mulitplies */ + +static __inline short ClipToShort(int x, int fracBits) +{ + int sign; + + /* assumes you've already rounded (x += (1 << (fracBits-1))) */ + x >>= fracBits; + + /* Ken's trick: clips to [-32768, 32767] */ + sign = x >> 31; + if (sign != (x >> 15)) + x = sign ^ ((1 << 15) - 1); + + return (short)x; +} + +#define MC0M(x) { \ + c1 = *coef; coef++; c2 = *coef; coef++; \ + vLo = *(vb1+(x)); vHi = *(vb1+(23-(x))); \ + sum1L = MADD64(sum1L, vLo, c1); sum1L = MADD64(sum1L, vHi, -c2); \ +} + +#define MC1M(x) { \ + c1 = *coef; coef++; \ + vLo = *(vb1+(x)); \ + sum1L = MADD64(sum1L, vLo, c1); \ +} + +#define MC2M(x) { \ + c1 = *coef; coef++; c2 = *coef; coef++; \ + vLo = *(vb1+(x)); vHi = *(vb1+(23-(x))); \ + sum1L = MADD64(sum1L, vLo, c1); sum2L = MADD64(sum2L, vLo, c2); \ + sum1L = MADD64(sum1L, vHi, -c2); sum2L = MADD64(sum2L, vHi, c1); \ +} + +/************************************************************************************** + * Function: PolyphaseMono + * + * Description: filter one subband and produce 32 output PCM samples for one channel + * + * Inputs: pointer to PCM output buffer + * number of "extra shifts" (vbuf format = Q(DQ_FRACBITS_OUT-2)) + * pointer to start of vbuf (preserved from last call) + * start of filter coefficient table (in proper, shuffled order) + * no minimum number of guard bits is required for input vbuf + * (see additional scaling comments below) + * + * Outputs: 32 samples of one channel of decoded PCM data, (i.e. Q16.0) + * + * Return: none + * + * TODO: add 32-bit version for platforms where 64-bit mul-acc is not supported + * (note max filter gain - see polyCoef[] comments) + **************************************************************************************/ +void PolyphaseMono(short *pcm, int *vbuf, const int *coefBase) +{ + int i; + const int *coef; + int *vb1; + int vLo, vHi, c1, c2; + Word64 sum1L, sum2L, rndVal; + + rndVal = (Word64)( 1 << (DEF_NFRACBITS - 1 + (32 - CSHIFT)) ); + + /* special case, output sample 0 */ + coef = coefBase; + vb1 = vbuf; + sum1L = rndVal; + + MC0M(0) + MC0M(1) + MC0M(2) + MC0M(3) + MC0M(4) + MC0M(5) + MC0M(6) + MC0M(7) + + *(pcm + 0) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + + /* special case, output sample 16 */ + coef = coefBase + 256; + vb1 = vbuf + 64*16; + sum1L = rndVal; + + MC1M(0) + MC1M(1) + MC1M(2) + MC1M(3) + MC1M(4) + MC1M(5) + MC1M(6) + MC1M(7) + + *(pcm + 16) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + + /* main convolution loop: sum1L = samples 1, 2, 3, ... 15 sum2L = samples 31, 30, ... 17 */ + coef = coefBase + 16; + vb1 = vbuf + 64; + pcm++; + + /* right now, the compiler creates bad asm from this... */ + for (i = 15; i > 0; i--) { + sum1L = sum2L = rndVal; + + MC2M(0) + MC2M(1) + MC2M(2) + MC2M(3) + MC2M(4) + MC2M(5) + MC2M(6) + MC2M(7) + + vb1 += 64; + *(pcm) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 2*i) = ClipToShort((int)SAR64(sum2L, (32-CSHIFT)), DEF_NFRACBITS); + pcm++; + } +} + +#define MC0S(x) { \ + c1 = *coef; coef++; c2 = *coef; coef++; \ + vLo = *(vb1+(x)); vHi = *(vb1+(23-(x))); \ + sum1L = MADD64(sum1L, vLo, c1); sum1L = MADD64(sum1L, vHi, -c2); \ + vLo = *(vb1+32+(x)); vHi = *(vb1+32+(23-(x))); \ + sum1R = MADD64(sum1R, vLo, c1); sum1R = MADD64(sum1R, vHi, -c2); \ +} + +#define MC1S(x) { \ + c1 = *coef; coef++; \ + vLo = *(vb1+(x)); \ + sum1L = MADD64(sum1L, vLo, c1); \ + vLo = *(vb1+32+(x)); \ + sum1R = MADD64(sum1R, vLo, c1); \ +} + +#define MC2S(x) { \ + c1 = *coef; coef++; c2 = *coef; coef++; \ + vLo = *(vb1+(x)); vHi = *(vb1+(23-(x))); \ + sum1L = MADD64(sum1L, vLo, c1); sum2L = MADD64(sum2L, vLo, c2); \ + sum1L = MADD64(sum1L, vHi, -c2); sum2L = MADD64(sum2L, vHi, c1); \ + vLo = *(vb1+32+(x)); vHi = *(vb1+32+(23-(x))); \ + sum1R = MADD64(sum1R, vLo, c1); sum2R = MADD64(sum2R, vLo, c2); \ + sum1R = MADD64(sum1R, vHi, -c2); sum2R = MADD64(sum2R, vHi, c1); \ +} + +/************************************************************************************** + * Function: PolyphaseStereo + * + * Description: filter one subband and produce 32 output PCM samples for each channel + * + * Inputs: pointer to PCM output buffer + * number of "extra shifts" (vbuf format = Q(DQ_FRACBITS_OUT-2)) + * pointer to start of vbuf (preserved from last call) + * start of filter coefficient table (in proper, shuffled order) + * no minimum number of guard bits is required for input vbuf + * (see additional scaling comments below) + * + * Outputs: 32 samples of two channels of decoded PCM data, (i.e. Q16.0) + * + * Return: none + * + * Notes: interleaves PCM samples LRLRLR... + * + * TODO: add 32-bit version for platforms where 64-bit mul-acc is not supported + **************************************************************************************/ +void PolyphaseStereo(short *pcm, int *vbuf, const int *coefBase) +{ + int i; + const int *coef; + int *vb1; + int vLo, vHi, c1, c2; + Word64 sum1L, sum2L, sum1R, sum2R, rndVal; + + rndVal = (Word64)( 1 << (DEF_NFRACBITS - 1 + (32 - CSHIFT)) ); + + /* special case, output sample 0 */ + coef = coefBase; + vb1 = vbuf; + sum1L = sum1R = rndVal; + + MC0S(0) + MC0S(1) + MC0S(2) + MC0S(3) + MC0S(4) + MC0S(5) + MC0S(6) + MC0S(7) + + *(pcm + 0) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 1) = ClipToShort((int)SAR64(sum1R, (32-CSHIFT)), DEF_NFRACBITS); + + /* special case, output sample 16 */ + coef = coefBase + 256; + vb1 = vbuf + 64*16; + sum1L = sum1R = rndVal; + + MC1S(0) + MC1S(1) + MC1S(2) + MC1S(3) + MC1S(4) + MC1S(5) + MC1S(6) + MC1S(7) + + *(pcm + 2*16 + 0) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 2*16 + 1) = ClipToShort((int)SAR64(sum1R, (32-CSHIFT)), DEF_NFRACBITS); + + /* main convolution loop: sum1L = samples 1, 2, 3, ... 15 sum2L = samples 31, 30, ... 17 */ + coef = coefBase + 16; + vb1 = vbuf + 64; + pcm += 2; + + /* right now, the compiler creates bad asm from this... */ + for (i = 15; i > 0; i--) { + sum1L = sum2L = rndVal; + sum1R = sum2R = rndVal; + + MC2S(0) + MC2S(1) + MC2S(2) + MC2S(3) + MC2S(4) + MC2S(5) + MC2S(6) + MC2S(7) + + vb1 += 64; + *(pcm + 0) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 1) = ClipToShort((int)SAR64(sum1R, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 2*2*i + 0) = ClipToShort((int)SAR64(sum2L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 2*2*i + 1) = ClipToShort((int)SAR64(sum2R, (32-CSHIFT)), DEF_NFRACBITS); + pcm += 2; + } +} diff --git a/components/driver_sndmixer/libhelix-mp3/scalfact.c b/components/driver_sndmixer/libhelix-mp3/scalfact.c new file mode 100644 index 0000000..4937e45 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/scalfact.c @@ -0,0 +1,392 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * scalfact.c - scalefactor unpacking functions + **************************************************************************************/ + +#include "coder.h" + +/* scale factor lengths (num bits) */ +static const char 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}, +}; + +/************************************************************************************** + * Function: UnpackSFMPEG1 + * + * Description: unpack MPEG 1 scalefactors from bitstream + * + * Inputs: BitStreamInfo, SideInfoSub, ScaleFactorInfoSub structs for this + * granule/channel + * vector of scfsi flags from side info, length = 4 (MAX_SCFBD) + * index of current granule + * ScaleFactorInfoSub from granule 0 (for granule 1, if scfsi[i] is set, + * then we just replicate the scale factors from granule 0 in the + * i'th set of scalefactor bands) + * + * Outputs: updated BitStreamInfo struct + * scalefactors in sfis (short and/or long arrays, as appropriate) + * + * Return: none + * + * Notes: set order of short blocks to s[band][window] instead of s[window][band] + * so that we index through consectutive memory locations when unpacking + * (make sure dequantizer follows same convention) + * Illegal Intensity Position = 7 (always) for MPEG1 scale factors + **************************************************************************************/ +static void UnpackSFMPEG1(BitStreamInfo *bsi, SideInfoSub *sis, ScaleFactorInfoSub *sfis, int *scfsi, int gr, ScaleFactorInfoSub *sfisGr0) +{ + int sfb; + int slen0, slen1; + + /* these can be 0, so make sure GetBits(bsi, 0) returns 0 (no >> 32 or anything) */ + slen0 = (int)SFLenTab[sis->sfCompress][0]; + slen1 = (int)SFLenTab[sis->sfCompress][1]; + + if (sis->blockType == 2) { + /* short block, type 2 (implies winSwitchFlag == 1) */ + if (sis->mixedBlock) { + /* do long block portion */ + for (sfb = 0; sfb < 8; sfb++) + sfis->l[sfb] = (char)GetBits(bsi, slen0); + sfb = 3; + } else { + /* all short blocks */ + sfb = 0; + } + + for ( ; sfb < 6; sfb++) { + sfis->s[sfb][0] = (char)GetBits(bsi, slen0); + sfis->s[sfb][1] = (char)GetBits(bsi, slen0); + sfis->s[sfb][2] = (char)GetBits(bsi, slen0); + } + + for ( ; sfb < 12; sfb++) { + sfis->s[sfb][0] = (char)GetBits(bsi, slen1); + sfis->s[sfb][1] = (char)GetBits(bsi, slen1); + sfis->s[sfb][2] = (char)GetBits(bsi, slen1); + } + + /* last sf band not transmitted */ + sfis->s[12][0] = sfis->s[12][1] = sfis->s[12][2] = 0; + } else { + /* long blocks, type 0, 1, or 3 */ + if(gr == 0) { + /* first granule */ + for (sfb = 0; sfb < 11; sfb++) + sfis->l[sfb] = (char)GetBits(bsi, slen0); + for (sfb = 11; sfb < 21; sfb++) + sfis->l[sfb] = (char)GetBits(bsi, slen1); + return; + } else { + /* second granule + * scfsi: 0 = different scalefactors for each granule, 1 = copy sf's from granule 0 into granule 1 + * for block type == 2, scfsi is always 0 + */ + sfb = 0; + if(scfsi[0]) for( ; sfb < 6 ; sfb++) sfis->l[sfb] = sfisGr0->l[sfb]; + else for( ; sfb < 6 ; sfb++) sfis->l[sfb] = (char)GetBits(bsi, slen0); + if(scfsi[1]) for( ; sfb <11 ; sfb++) sfis->l[sfb] = sfisGr0->l[sfb]; + else for( ; sfb <11 ; sfb++) sfis->l[sfb] = (char)GetBits(bsi, slen0); + if(scfsi[2]) for( ; sfb <16 ; sfb++) sfis->l[sfb] = sfisGr0->l[sfb]; + else for( ; sfb <16 ; sfb++) sfis->l[sfb] = (char)GetBits(bsi, slen1); + if(scfsi[3]) for( ; sfb <21 ; sfb++) sfis->l[sfb] = sfisGr0->l[sfb]; + else for( ; sfb <21 ; sfb++) sfis->l[sfb] = (char)GetBits(bsi, slen1); + } + /* last sf band not transmitted */ + sfis->l[21] = 0; + sfis->l[22] = 0; + } +} + +/* 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) + */ +static const char NRTab[6][3][4] = { + /* non-intensity stereo */ + { {6, 5, 5, 5}, + {3, 3, 3, 3}, /* includes / 3 */ + {6, 3, 3, 3}, /* includes / 3 except for first entry */ + }, + { {6, 5, 7, 3}, + {3, 3, 4, 2}, + {6, 3, 4, 2}, + }, + { {11, 10, 0, 0}, + {6, 6, 0, 0}, + {6, 3, 6, 0}, /* spec = [15,18,0,0], but 15 = 6L + 9S, so move 9/3=3 into col 1, 18/3=6 into col 2 and adj. slen[1,2] below */ + }, + /* intensity stereo, right chan */ + { {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}, + } +}; + +/************************************************************************************** + * Function: UnpackSFMPEG2 + * + * Description: unpack MPEG 2 scalefactors from bitstream + * + * Inputs: BitStreamInfo, SideInfoSub, ScaleFactorInfoSub structs for this + * granule/channel + * index of current granule and channel + * ScaleFactorInfoSub from this granule + * modeExt field from frame header, to tell whether intensity stereo is on + * ScaleFactorJS struct for storing IIP info used in Dequant() + * + * Outputs: updated BitStreamInfo struct + * scalefactors in sfis (short and/or long arrays, as appropriate) + * updated intensityScale and preFlag flags + * + * Return: none + * + * Notes: Illegal Intensity Position = (2^slen) - 1 for MPEG2 scale factors + * + * TODO: optimize the / and % stuff (only do one divide, get modulo x + * with (x / m) * m, etc.) + **************************************************************************************/ +static void UnpackSFMPEG2(BitStreamInfo *bsi, SideInfoSub *sis, ScaleFactorInfoSub *sfis, int gr, int ch, int modeExt, ScaleFactorJS *sfjs) +{ + + int i, sfb, sfcIdx, btIdx, nrIdx;// iipTest; + int slen[4], nr[4]; + int sfCompress, preFlag, intensityScale; + (void)gr; + + sfCompress = sis->sfCompress; + preFlag = 0; + intensityScale = 0; + + /* stereo mode bits (1 = on): bit 1 = mid-side on/off, bit 0 = intensity on/off */ + if (! ((modeExt & 0x01) && (ch == 1)) ) { + /* in other words: if ((modeExt & 0x01) == 0 || ch == 0) */ + if (sfCompress < 400) { + /* max slen = floor[(399/16) / 5] = 4 */ + slen[0] = (sfCompress >> 4) / 5; + slen[1]= (sfCompress >> 4) % 5; + slen[2]= (sfCompress & 0x0f) >> 2; + slen[3]= (sfCompress & 0x03); + sfcIdx = 0; + } else if (sfCompress < 500) { + /* max slen = floor[(99/4) / 5] = 4 */ + sfCompress -= 400; + slen[0] = (sfCompress >> 2) / 5; + slen[1]= (sfCompress >> 2) % 5; + slen[2]= (sfCompress & 0x03); + slen[3]= 0; + sfcIdx = 1; + } else { + /* max slen = floor[11/3] = 3 (sfCompress = 9 bits in MPEG2) */ + sfCompress -= 500; + slen[0] = sfCompress / 3; + slen[1] = sfCompress % 3; + slen[2] = slen[3] = 0; + if (sis->mixedBlock) { + /* adjust for long/short mix logic (see comment above in NRTab[] definition) */ + slen[2] = slen[1]; + slen[1] = slen[0]; + } + preFlag = 1; + sfcIdx = 2; + } + } else { + /* intensity stereo ch = 1 (right) */ + intensityScale = sfCompress & 0x01; + sfCompress >>= 1; + if (sfCompress < 180) { + /* max slen = floor[35/6] = 5 (from mod 36) */ + slen[0] = (sfCompress / 36); + slen[1] = (sfCompress % 36) / 6; + slen[2] = (sfCompress % 36) % 6; + slen[3] = 0; + sfcIdx = 3; + } else if (sfCompress < 244) { + /* max slen = floor[63/16] = 3 */ + sfCompress -= 180; + slen[0] = (sfCompress & 0x3f) >> 4; + slen[1] = (sfCompress & 0x0f) >> 2; + slen[2] = (sfCompress & 0x03); + slen[3] = 0; + sfcIdx = 4; + } else { + /* max slen = floor[11/3] = 3 (max sfCompress >> 1 = 511/2 = 255) */ + sfCompress -= 244; + slen[0] = (sfCompress / 3); + slen[1] = (sfCompress % 3); + slen[2] = slen[3] = 0; + sfcIdx = 5; + } + } + + /* set index based on block type: (0,1,3) --> 0, (2 non-mixed) --> 1, (2 mixed) ---> 2 */ + btIdx = 0; + if (sis->blockType == 2) + btIdx = (sis->mixedBlock ? 2 : 1); + for (i = 0; i < 4; i++) + nr[i] = (int)NRTab[sfcIdx][btIdx][i]; + + /* save intensity stereo scale factor info */ + if( (modeExt & 0x01) && (ch == 1) ) { + for (i = 0; i < 4; i++) { + sfjs->slen[i] = slen[i]; + sfjs->nr[i] = nr[i]; + } + sfjs->intensityScale = intensityScale; + } + sis->preFlag = preFlag; + + /* short blocks */ + if(sis->blockType == 2) { + if(sis->mixedBlock) { + /* do long block portion */ + //iipTest = (1 << slen[0]) - 1; + for (sfb=0; sfb < 6; sfb++) { + sfis->l[sfb] = (char)GetBits(bsi, slen[0]); + } + sfb = 3; /* start sfb for short */ + nrIdx = 1; + } else { + /* all short blocks, so start nr, sfb at 0 */ + sfb = 0; + nrIdx = 0; + } + + /* remaining short blocks, sfb just keeps incrementing */ + for ( ; nrIdx <= 3; nrIdx++) { + //iipTest = (1 << slen[nrIdx]) - 1; + for (i=0; i < nr[nrIdx]; i++, sfb++) { + sfis->s[sfb][0] = (char)GetBits(bsi, slen[nrIdx]); + sfis->s[sfb][1] = (char)GetBits(bsi, slen[nrIdx]); + sfis->s[sfb][2] = (char)GetBits(bsi, slen[nrIdx]); + } + } + /* last sf band not transmitted */ + sfis->s[12][0] = sfis->s[12][1] = sfis->s[12][2] = 0; + } else { + /* long blocks */ + sfb = 0; + for (nrIdx = 0; nrIdx <= 3; nrIdx++) { + //iipTest = (1 << slen[nrIdx]) - 1; + for(i=0; i < nr[nrIdx]; i++, sfb++) { + sfis->l[sfb] = (char)GetBits(bsi, slen[nrIdx]); + } + } + /* last sf band not transmitted */ + sfis->l[21] = sfis->l[22] = 0; + + } +} + +/************************************************************************************** + * Function: UnpackScaleFactors + * + * Description: parse the fields of the MP3 scale factor data section + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader() and UnpackSideInfo() + * buffer pointing to the MP3 scale factor data + * pointer to bit offset (0-7) indicating starting bit in buf[0] + * number of bits available in data buffer + * index of current granule and channel + * + * Outputs: updated platform-specific ScaleFactorInfo struct + * updated bitOffset + * + * Return: length (in bytes) of scale factor data, -1 if null input pointers + **************************************************************************************/ +int UnpackScaleFactors(MP3DecInfo *mp3DecInfo, unsigned char *buf, int *bitOffset, int bitsAvail, int gr, int ch) +{ + int bitsUsed; + unsigned char *startBuf; + BitStreamInfo bitStreamInfo, *bsi; + FrameHeader *fh; + SideInfo *si; + ScaleFactorInfo *sfi; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS || !mp3DecInfo->ScaleFactorInfoPS) + return -1; + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + si = ((SideInfo *)(mp3DecInfo->SideInfoPS)); + sfi = ((ScaleFactorInfo *)(mp3DecInfo->ScaleFactorInfoPS)); + + /* init GetBits reader */ + startBuf = buf; + bsi = &bitStreamInfo; + SetBitstreamPointer(bsi, (bitsAvail + *bitOffset + 7) / 8, buf); + if (*bitOffset) + GetBits(bsi, *bitOffset); + + if (fh->ver == MPEG1) + UnpackSFMPEG1(bsi, &si->sis[gr][ch], &sfi->sfis[gr][ch], si->scfsi[ch], gr, &sfi->sfis[0][ch]); + else + UnpackSFMPEG2(bsi, &si->sis[gr][ch], &sfi->sfis[gr][ch], gr, ch, fh->modeExt, &sfi->sfjs); + + mp3DecInfo->part23Length[gr][ch] = si->sis[gr][ch].part23Length; + + bitsUsed = CalcBitsUsed(bsi, buf, *bitOffset); + buf += (bitsUsed + *bitOffset) >> 3; + *bitOffset = (bitsUsed + *bitOffset) & 0x07; + + return (buf - startBuf); +} + diff --git a/components/driver_sndmixer/libhelix-mp3/statname.h b/components/driver_sndmixer/libhelix-mp3/statname.h new file mode 100644 index 0000000..c7f985e --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/statname.h @@ -0,0 +1,89 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * statname.h - name mangling macros for static linking + **************************************************************************************/ + +#ifndef _STATNAME_H +#define _STATNAME_H + +/* define STAT_PREFIX to a unique name for static linking + * all the C functions and global variables will be mangled by the preprocessor + * e.g. void FFT(int *fftbuf) becomes void cook_FFT(int *fftbuf) + */ +#define STAT_PREFIX xmp3 + + +#define STATCC1(x,y,z) STATCC2(x,y,z) +#define STATCC2(x,y,z) x##y##z + +#ifdef STAT_PREFIX +#define STATNAME(func) STATCC1(STAT_PREFIX, _, func) +#else +#define STATNAME(func) func +#endif + +/* these symbols are common to all implementations */ +#define CheckPadBit STATNAME(CheckPadBit) +#define UnpackFrameHeader STATNAME(UnpackFrameHeader) +#define UnpackSideInfo STATNAME(UnpackSideInfo) +#define AllocateBuffers STATNAME(AllocateBuffers) +#define FreeBuffers STATNAME(FreeBuffers) +#define DecodeHuffman STATNAME(DecodeHuffman) +#define Dequantize STATNAME(Dequantize) +#define IMDCT STATNAME(IMDCT) +#define UnpackScaleFactors STATNAME(UnpackScaleFactors) +#define Subband STATNAME(Subband) + +#define samplerateTab STATNAME(samplerateTab) +#define bitrateTab STATNAME(bitrateTab) +#define samplesPerFrameTab STATNAME(samplesPerFrameTab) +#define bitsPerSlotTab STATNAME(bitsPerSlotTab) +#define sideBytesTab STATNAME(sideBytesTab) +#define slotTab STATNAME(slotTab) +#define sfBandTable STATNAME(sfBandTable) + +/* in your implementation's top-level include file (e.g. real\coder.h) you should + * add new #define sym STATNAME(sym) lines for all the + * additional global functions or variables which your + * implementation uses + */ + +#endif /* _STATNAME_H */ diff --git a/components/driver_sndmixer/libhelix-mp3/stproc.c b/components/driver_sndmixer/libhelix-mp3/stproc.c new file mode 100644 index 0000000..7782a21 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/stproc.c @@ -0,0 +1,299 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * stproc.c - mid-side and intensity (MPEG1 and MPEG2) stereo processing + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: MidSideProc + * + * Description: sum-difference stereo reconstruction + * + * Inputs: vector x with dequantized samples from left and right channels + * number of non-zero samples (MAX of left and right) + * assume 1 guard bit in input + * guard bit mask (left and right channels) + * + * Outputs: updated sample vector x + * updated guard bit mask + * + * Return: none + * + * Notes: assume at least 1 GB in input + **************************************************************************************/ +void MidSideProc(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, int mOut[2]) +{ + int i, xr, xl, mOutL, mOutR; + + /* L = (M+S)/sqrt(2), R = (M-S)/sqrt(2) + * NOTE: 1/sqrt(2) done in DequantChannel() - see comments there + */ + mOutL = mOutR = 0; + for(i = 0; i < nSamps; i++) { + xl = x[0][i]; + xr = x[1][i]; + x[0][i] = xl + xr; + x[1][i] = xl - xr; + mOutL |= FASTABS(x[0][i]); + mOutR |= FASTABS(x[1][i]); + } + mOut[0] |= mOutL; + mOut[1] |= mOutR; +} + +/************************************************************************************** + * Function: IntensityProcMPEG1 + * + * Description: intensity stereo processing for MPEG1 + * + * Inputs: vector x with dequantized samples from left and right channels + * number of non-zero samples in left channel + * valid FrameHeader struct + * two each of ScaleFactorInfoSub, CriticalBandInfo structs (both channels) + * flags indicating midSide on/off, mixedBlock on/off + * guard bit mask (left and right channels) + * + * Outputs: updated sample vector x + * updated guard bit mask + * + * Return: none + * + * Notes: assume at least 1 GB in input + * + * TODO: combine MPEG1/2 into one function (maybe) + * make sure all the mixed-block and IIP logic is right + **************************************************************************************/ +void IntensityProcMPEG1(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, FrameHeader *fh, ScaleFactorInfoSub *sfis, + CriticalBandInfo *cbi, int midSideFlag, int mixFlag, int mOut[2]) +{ + int i=0, j=0, n=0, cb=0, w=0; + int sampsLeft, isf, mOutL, mOutR, xl, xr; + int fl, fr, fls[3], frs[3]; + int cbStartL=0, cbStartS=0, cbEndL=0, cbEndS=0; + int *isfTab; + (void)mixFlag; + + /* NOTE - this works fine for mixed blocks, as long as the switch point starts in the + * short block section (i.e. on or after sample 36 = sfBand->l[8] = 3*sfBand->s[3] + * is this a safe assumption? + * TODO - intensity + mixed not quite right (diff = 11 on he_mode) + * figure out correct implementation (spec ambiguous about when to do short block reorder) + */ + if (cbi[1].cbType == 0) { + /* long block */ + cbStartL = cbi[1].cbEndL + 1; + cbEndL = cbi[0].cbEndL + 1; + cbStartS = cbEndS = 0; + i = fh->sfBand->l[cbStartL]; + } else if (cbi[1].cbType == 1 || cbi[1].cbType == 2) { + /* short or mixed block */ + cbStartS = cbi[1].cbEndSMax + 1; + cbEndS = cbi[0].cbEndSMax + 1; + cbStartL = cbEndL = 0; + i = 3 * fh->sfBand->s[cbStartS]; + } + + sampsLeft = nSamps - i; /* process to length of left */ + isfTab = (int *)ISFMpeg1[midSideFlag]; + mOutL = mOutR = 0; + + /* long blocks */ + for (cb = cbStartL; cb < cbEndL && sampsLeft > 0; cb++) { + isf = sfis->l[cb]; + if (isf == 7) { + fl = ISFIIP[midSideFlag][0]; + fr = ISFIIP[midSideFlag][1]; + } else { + fl = isfTab[isf]; + fr = isfTab[6] - isfTab[isf]; + } + + n = fh->sfBand->l[cb + 1] - fh->sfBand->l[cb]; + for (j = 0; j < n && sampsLeft > 0; j++, i++) { + xr = MULSHIFT32(fr, x[0][i]) << 2; x[1][i] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fl, x[0][i]) << 2; x[0][i] = xl; mOutL |= FASTABS(xl); + sampsLeft--; + } + } + + /* short blocks */ + for (cb = cbStartS; cb < cbEndS && sampsLeft >= 3; cb++) { + for (w = 0; w < 3; w++) { + isf = sfis->s[cb][w]; + if (isf == 7) { + fls[w] = ISFIIP[midSideFlag][0]; + frs[w] = ISFIIP[midSideFlag][1]; + } else { + fls[w] = isfTab[isf]; + frs[w] = isfTab[6] - isfTab[isf]; + } + } + + n = fh->sfBand->s[cb + 1] - fh->sfBand->s[cb]; + for (j = 0; j < n && sampsLeft >= 3; j++, i+=3) { + xr = MULSHIFT32(frs[0], x[0][i+0]) << 2; x[1][i+0] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fls[0], x[0][i+0]) << 2; x[0][i+0] = xl; mOutL |= FASTABS(xl); + xr = MULSHIFT32(frs[1], x[0][i+1]) << 2; x[1][i+1] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fls[1], x[0][i+1]) << 2; x[0][i+1] = xl; mOutL |= FASTABS(xl); + xr = MULSHIFT32(frs[2], x[0][i+2]) << 2; x[1][i+2] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fls[2], x[0][i+2]) << 2; x[0][i+2] = xl; mOutL |= FASTABS(xl); + sampsLeft -= 3; + } + } + mOut[0] = mOutL; + mOut[1] = mOutR; + + return; +} + +/************************************************************************************** + * Function: IntensityProcMPEG2 + * + * Description: intensity stereo processing for MPEG2 + * + * Inputs: vector x with dequantized samples from left and right channels + * number of non-zero samples in left channel + * valid FrameHeader struct + * two each of ScaleFactorInfoSub, CriticalBandInfo structs (both channels) + * ScaleFactorJS struct with joint stereo info from UnpackSFMPEG2() + * flags indicating midSide on/off, mixedBlock on/off + * guard bit mask (left and right channels) + * + * Outputs: updated sample vector x + * updated guard bit mask + * + * Return: none + * + * Notes: assume at least 1 GB in input + * + * TODO: combine MPEG1/2 into one function (maybe) + * make sure all the mixed-block and IIP logic is right + * probably redo IIP logic to be simpler + **************************************************************************************/ +void IntensityProcMPEG2(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, FrameHeader *fh, ScaleFactorInfoSub *sfis, + CriticalBandInfo *cbi, ScaleFactorJS *sfjs, int midSideFlag, int mixFlag, int mOut[2]) +{ + int i, j, k, n, r, cb, w; + int fl, fr, mOutL, mOutR, xl, xr; + int sampsLeft; + int isf, sfIdx, tmp, il[23]; + int *isfTab; + int cbStartL, cbStartS, cbEndL, cbEndS; + + (void)mixFlag; + + isfTab = (int *)ISFMpeg2[sfjs->intensityScale][midSideFlag]; + mOutL = mOutR = 0; + + /* fill buffer with illegal intensity positions (depending on slen) */ + for (k = r = 0; r < 4; r++) { + tmp = (1 << sfjs->slen[r]) - 1; + for (j = 0; j < sfjs->nr[r]; j++, k++) + il[k] = tmp; + } + + if (cbi[1].cbType == 0) { + /* long blocks */ + il[21] = il[22] = 1; + cbStartL = cbi[1].cbEndL + 1; /* start at end of right */ + cbEndL = cbi[0].cbEndL + 1; /* process to end of left */ + i = fh->sfBand->l[cbStartL]; + sampsLeft = nSamps - i; + + for(cb = cbStartL; cb < cbEndL; cb++) { + sfIdx = sfis->l[cb]; + if (sfIdx == il[cb]) { + fl = ISFIIP[midSideFlag][0]; + fr = ISFIIP[midSideFlag][1]; + } else { + isf = (sfis->l[cb] + 1) >> 1; + fl = isfTab[(sfIdx & 0x01 ? isf : 0)]; + fr = isfTab[(sfIdx & 0x01 ? 0 : isf)]; + } + n = MIN(fh->sfBand->l[cb + 1] - fh->sfBand->l[cb], sampsLeft); + + for(j = 0; j < n; j++, i++) { + xr = MULSHIFT32(fr, x[0][i]) << 2; x[1][i] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fl, x[0][i]) << 2; x[0][i] = xl; mOutL |= FASTABS(xl); + } + + /* early exit once we've used all the non-zero samples */ + sampsLeft -= n; + if (sampsLeft == 0) + break; + } + } else { + /* short or mixed blocks */ + il[12] = 1; + + for(w = 0; w < 3; w++) { + cbStartS = cbi[1].cbEndS[w] + 1; /* start at end of right */ + cbEndS = cbi[0].cbEndS[w] + 1; /* process to end of left */ + i = 3 * fh->sfBand->s[cbStartS] + w; + + /* skip through sample array by 3, so early-exit logic would be more tricky */ + for(cb = cbStartS; cb < cbEndS; cb++) { + sfIdx = sfis->s[cb][w]; + if (sfIdx == il[cb]) { + fl = ISFIIP[midSideFlag][0]; + fr = ISFIIP[midSideFlag][1]; + } else { + isf = (sfis->s[cb][w] + 1) >> 1; + fl = isfTab[(sfIdx & 0x01 ? isf : 0)]; + fr = isfTab[(sfIdx & 0x01 ? 0 : isf)]; + } + n = fh->sfBand->s[cb + 1] - fh->sfBand->s[cb]; + + for(j = 0; j < n; j++, i+=3) { + xr = MULSHIFT32(fr, x[0][i]) << 2; x[1][i] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fl, x[0][i]) << 2; x[0][i] = xl; mOutL |= FASTABS(xl); + } + } + } + } + mOut[0] = mOutL; + mOut[1] = mOutR; + + return; +} + diff --git a/components/driver_sndmixer/libhelix-mp3/subband.c b/components/driver_sndmixer/libhelix-mp3/subband.c new file mode 100644 index 0000000..e982a9f --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/subband.c @@ -0,0 +1,96 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * subband.c - subband transform (synthesis filterbank implemented via 32-point DCT + * followed by polyphase filter) + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: Subband + * + * Description: do subband transform on all the blocks in one granule, all channels + * + * Inputs: filled MP3DecInfo structure, after calling IMDCT for all channels + * vbuf[ch] and vindex[ch] must be preserved between calls + * + * Outputs: decoded PCM data, interleaved LRLRLR... if stereo + * + * Return: 0 on success, -1 if null input pointers + **************************************************************************************/ +/*__attribute__ ((section (".data"))) */ int Subband(MP3DecInfo *mp3DecInfo, short *pcmBuf) +{ + int b; + //HuffmanInfo *hi; + IMDCTInfo *mi; + SubbandInfo *sbi; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->HuffmanInfoPS || !mp3DecInfo->IMDCTInfoPS || !mp3DecInfo->SubbandInfoPS) + return -1; + + //hi = (HuffmanInfo *)mp3DecInfo->HuffmanInfoPS; + mi = (IMDCTInfo *)(mp3DecInfo->IMDCTInfoPS); + sbi = (SubbandInfo*)(mp3DecInfo->SubbandInfoPS); + + if (mp3DecInfo->nChans == 2) { + /* stereo */ + for (b = 0; b < BLOCK_SIZE; b++) { + FDCT32(mi->outBuf[0][b], sbi->vbuf + 0*32, sbi->vindex, (b & 0x01), mi->gb[0]); + FDCT32(mi->outBuf[1][b], sbi->vbuf + 1*32, sbi->vindex, (b & 0x01), mi->gb[1]); + PolyphaseStereo(pcmBuf, sbi->vbuf + sbi->vindex + VBUF_LENGTH * (b & 0x01), polyCoef); + sbi->vindex = (sbi->vindex - (b & 0x01)) & 7; + pcmBuf += (2 * NBANDS); + } + } else { + /* mono */ + for (b = 0; b < BLOCK_SIZE; b++) { + FDCT32(mi->outBuf[0][b], sbi->vbuf + 0*32, sbi->vindex, (b & 0x01), mi->gb[0]); + PolyphaseMono(pcmBuf, sbi->vbuf + sbi->vindex + VBUF_LENGTH * (b & 0x01), polyCoef); + sbi->vindex = (sbi->vindex - (b & 0x01)) & 7; + pcmBuf += NBANDS; + } + } + + return 0; +} + diff --git a/components/driver_sndmixer/libhelix-mp3/trigtabs.c b/components/driver_sndmixer/libhelix-mp3/trigtabs.c new file mode 100644 index 0000000..b0d4fa1 --- /dev/null +++ b/components/driver_sndmixer/libhelix-mp3/trigtabs.c @@ -0,0 +1,318 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * trigtabs.c - global ROM tables for pre-calculated trig coefficients + **************************************************************************************/ + +// constants in RAM are not significantly faster + +#include "coder.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" + +/* post-IMDCT window, win[blockType][i] + * format = Q31 + * Fused sin window with final stage of IMDCT + * includes 1/sqrt(2) scaling, since we scale by sqrt(2) in dequant in order + * for fast IMDCT36 to be usable + * + * for(i=0;i<9;i++) win[0][i] = sin(pi/36 *(i+0.5)); + * for(i=9;i<36;i++) win[0][i] = -sin(pi/36 *(i+0.5)); + * + * for(i=0;i<9;i++) win[1][i] = sin(pi/36 *(i+0.5)); + * for(i=9;i<18;i++) win[1][i] = -sin(pi/36 *(i+0.5)); + * for(i=18;i<24;i++) win[1][i] = -1; + * for(i=24;i<30;i++) win[1][i] = -sin(pi/12 *(i+0.5-18)); + * for(i=30;i<36;i++) win[1][i] = 0; + * + * for(i=0;i<6;i++) win[3][i] = 0; + * for(i=6;i<9;i++) win[3][i] = sin(pi/12 *(i+0.5-6)); + * for(i=9;i<12;i++) win[3][i] = -sin(pi/12 *(i+0.5-6)); + * for(i=12;i<18;i++) win[3][i] = -1; + * for(i=18;i<36;i++) win[3][i] = -sin(pi/36*(i+0.5)); + * + * for(i=0;i<3;i++) win[2][i] = sin(pi/12*(i+0.5)); + * for(i=3;i<12;i++) win[2][i] = -sin(pi/12*(i+0.5)); + * for(i=12;i<36;i++) win[2][i] = 0; + * + * for (i = 0; i < 4; i++) { + * if (i == 2) { + * win[i][8] *= cos(pi/12 * (0+0.5)); + * win[i][9] *= cos(pi/12 * (0+0.5)); + * win[i][7] *= cos(pi/12 * (1+0.5)); + * win[i][10] *= cos(pi/12 * (1+0.5)); + * win[i][6] *= cos(pi/12 * (2+0.5)); + * win[i][11] *= cos(pi/12 * (2+0.5)); + * win[i][0] *= cos(pi/12 * (3+0.5)); + * win[i][5] *= cos(pi/12 * (3+0.5)); + * win[i][1] *= cos(pi/12 * (4+0.5)); + * win[i][4] *= cos(pi/12 * (4+0.5)); + * win[i][2] *= cos(pi/12 * (5+0.5)); + * win[i][3] *= cos(pi/12 * (5+0.5)); + * } else { + * for (j = 0; j < 9; j++) { + * win[i][8-j] *= cos(pi/36 * (17-j+0.5)); + * win[i][9+j] *= cos(pi/36 * (17-j+0.5)); + * } + * for (j = 0; j < 9; j++) { + * win[i][18+8-j] *= cos(pi/36 * (j+0.5)); + * win[i][18+9+j] *= cos(pi/36 * (j+0.5)); + * } + * } + * } + * for (i = 0; i < 4; i++) + * for (j = 0; j < 36; j++) + * win[i][j] *= 1.0 / sqrt(2); + */ + +const int imdctWin[4][36] = { + { + 0x02aace8b, 0x07311c28, 0x0a868fec, 0x0c913b52, 0x0d413ccd, 0x0c913b52, 0x0a868fec, 0x07311c28, + 0x02aace8b, 0xfd16d8dd, 0xf6a09e66, 0xef7a6275, 0xe7dbc161, 0xe0000000, 0xd8243e9f, 0xd0859d8b, + 0xc95f619a, 0xc2e92723, 0xbd553175, 0xb8cee3d8, 0xb5797014, 0xb36ec4ae, 0xb2bec333, 0xb36ec4ae, + 0xb5797014, 0xb8cee3d8, 0xbd553175, 0xc2e92723, 0xc95f619a, 0xd0859d8b, 0xd8243e9f, 0xe0000000, + 0xe7dbc161, 0xef7a6275, 0xf6a09e66, 0xfd16d8dd, + }, + { + 0x02aace8b, 0x07311c28, 0x0a868fec, 0x0c913b52, 0x0d413ccd, 0x0c913b52, 0x0a868fec, 0x07311c28, + 0x02aace8b, 0xfd16d8dd, 0xf6a09e66, 0xef7a6275, 0xe7dbc161, 0xe0000000, 0xd8243e9f, 0xd0859d8b, + 0xc95f619a, 0xc2e92723, 0xbd44ef14, 0xb831a052, 0xb3aa3837, 0xafb789a4, 0xac6145bb, 0xa9adecdc, + 0xa864491f, 0xad1868f0, 0xb8431f49, 0xc8f42236, 0xdda8e6b1, 0xf47755dc, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + { + 0x07311c28, 0x0d413ccd, 0x07311c28, 0xf6a09e66, 0xe0000000, 0xc95f619a, 0xb8cee3d8, 0xb2bec333, + 0xb8cee3d8, 0xc95f619a, 0xe0000000, 0xf6a09e66, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x028e9709, 0x04855ec0, + 0x026743a1, 0xfcde2c10, 0xf515dc82, 0xec93e53b, 0xe4c880f8, 0xdd5d0b08, 0xd63510b7, 0xcf5e834a, + 0xc8e6b562, 0xc2da4105, 0xbd553175, 0xb8cee3d8, 0xb5797014, 0xb36ec4ae, 0xb2bec333, 0xb36ec4ae, + 0xb5797014, 0xb8cee3d8, 0xbd553175, 0xc2e92723, 0xc95f619a, 0xd0859d8b, 0xd8243e9f, 0xe0000000, + 0xe7dbc161, 0xef7a6275, 0xf6a09e66, 0xfd16d8dd, + }, +}; + +/* indexing = [mid-side off/on][intensity scale factor] + * format = Q30, range = [0.0, 1.414] + * + * mid-side off: + * ISFMpeg1[0][i] = tan(i*pi/12) / [1 + tan(i*pi/12)] (left scalefactor) + * = 1 / [1 + tan(i*pi/12)] (right scalefactor) + * + * mid-side on: + * ISFMpeg1[1][i] = sqrt(2) * ISFMpeg1[0][i] + * + * output L = ISFMpeg1[midSide][isf][0] * input L + * output R = ISFMpeg1[midSide][isf][1] * input L + * + * obviously left scalefactor + right scalefactor = 1 (m-s off) or sqrt(2) (m-s on) + * so just store left and calculate right as 1 - left + * (can derive as right = ISFMpeg1[x][6] - left) + * + * if mid-side enabled, multiply joint stereo scale factors by sqrt(2) + * - we scaled whole spectrum by 1/sqrt(2) in Dequant for the M+S/sqrt(2) in MidSideProc + * - but the joint stereo part of the spectrum doesn't need this, so we have to undo it + * + * if scale factor is and illegal intensity position, this becomes a passthrough + * - gain = [1, 0] if mid-side off, since L is coded directly and R = 0 in this region + * - gain = [1, 1] if mid-side on, since L = (M+S)/sqrt(2), R = (M-S)/sqrt(2) + * - and since S = 0 in the joint stereo region (above NZB right) then L = R = M * 1.0 + */ +const int ISFMpeg1[2][7] = { + {0x00000000, 0x0d8658ba, 0x176cf5d0, 0x20000000, 0x28930a2f, 0x3279a745, 0x40000000}, + {0x00000000, 0x13207f5c, 0x2120fb83, 0x2d413ccc, 0x39617e16, 0x4761fa3d, 0x5a827999} +}; + +/* indexing = [intensity scale on/off][mid-side off/on][intensity scale factor] + * format = Q30, range = [0.0, 1.414] + * + * if (isf == 0) kl = 1.0 kr = 1.0 + * else if (isf & 0x01 == 0x01) kl = i0^((isf+1)/2), kr = 1.0 + * else if (isf & 0x01 == 0x00) kl = 1.0, kr = i0^(isf/2) + * + * if (intensityScale == 1) i0 = 1/sqrt(2) = 0x2d413ccc (Q30) + * else i0 = 1/sqrt(sqrt(2)) = 0x35d13f32 (Q30) + * + * see comments for ISFMpeg1 (just above) regarding scaling, sqrt(2), etc. + * + * compress the MPEG2 table using the obvious identities above... + * for isf = [0, 1, 2, ... 30], let sf = table[(isf+1) >> 1] + * - if isf odd, L = sf*L, R = tab[0]*R + * - if isf even, L = tab[0]*L, R = sf*R + */ +const int ISFMpeg2[2][2][16] = { +{ + { + /* intensityScale off, mid-side off */ + 0x40000000, 0x35d13f32, 0x2d413ccc, 0x260dfc14, 0x1fffffff, 0x1ae89f99, 0x16a09e66, 0x1306fe0a, + 0x0fffffff, 0x0d744fcc, 0x0b504f33, 0x09837f05, 0x07ffffff, 0x06ba27e6, 0x05a82799, 0x04c1bf82, + }, + { + /* intensityScale off, mid-side on */ + 0x5a827999, 0x4c1bf827, 0x3fffffff, 0x35d13f32, 0x2d413ccc, 0x260dfc13, 0x1fffffff, 0x1ae89f99, + 0x16a09e66, 0x1306fe09, 0x0fffffff, 0x0d744fcc, 0x0b504f33, 0x09837f04, 0x07ffffff, 0x06ba27e6, + }, +}, +{ + { + /* intensityScale on, mid-side off */ + 0x40000000, 0x2d413ccc, 0x20000000, 0x16a09e66, 0x10000000, 0x0b504f33, 0x08000000, 0x05a82799, + 0x04000000, 0x02d413cc, 0x02000000, 0x016a09e6, 0x01000000, 0x00b504f3, 0x00800000, 0x005a8279, + }, + /* intensityScale on, mid-side on */ + { + 0x5a827999, 0x3fffffff, 0x2d413ccc, 0x1fffffff, 0x16a09e66, 0x0fffffff, 0x0b504f33, 0x07ffffff, + 0x05a82799, 0x03ffffff, 0x02d413cc, 0x01ffffff, 0x016a09e6, 0x00ffffff, 0x00b504f3, 0x007fffff, + } +} +}; + +/* 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] = { + {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 int csa[8][2] = { + {0x6dc253f0, 0xbe2500aa}, + {0x70dcebe4, 0xc39e4949}, + {0x798d6e73, 0xd7e33f4a}, + {0x7ddd40a7, 0xe8b71176}, + {0x7f6d20b7, 0xf3e4fe2f}, + {0x7fe47e40, 0xfac1a3c7}, + {0x7ffcb263, 0xfe2ebdc6}, + {0x7fffc694, 0xff86c25d}, +}; + +/* format = Q30, range = [0.0981, 1.9976] + * + * n = 16; + * k = 0; + * for(i=0; i<5; i++, n=n/2) { + * for(p=0; p +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sndmixer.h" +#include "libhelix-mp3/mp3dec.h" + +#define MAX_SAMPLES_PER_FRAME (1152 * 2) +#define CHUNK_SIZE 32 +#define INTERNAL_BUFFER_SIZE 1024 * 8 +#define INTERNAL_BUFFER_FETCH_WHEN \ + 4096 // new data will be fetched when there is less than this amount of data + +#define TAG "snd_source_mp3" + +typedef struct { + HMP3Decoder hMP3Decoder; + unsigned char *dataStart; + unsigned char *dataCurr; + unsigned char *dataEnd; + int lastRate; + int lastChannels; + short *buffer; + int bufferValid; + int bufferOffset; + + unsigned char *dataPtr; // Pointer to internal buffer (if applicable) + stream_read_type stream_read; + stream_seek_type seek_func; + void *stream; // Pointer to stream +} mp3_ctx_t; + +void mp3_deinit_source(void *ctx); + +void _readData(mp3_ctx_t *mp3) { + // Fetch data for internal buffer + int dataAvailable = mp3->dataEnd - mp3->dataCurr; + int bufferAvailable = INTERNAL_BUFFER_SIZE - (mp3->dataEnd - mp3->dataStart); + int amountFetched = 0; + + if (dataAvailable < INTERNAL_BUFFER_FETCH_WHEN) { + // 1) Get rid of old data + if (mp3->dataCurr != mp3->dataStart) { + // Move available data to the begin of the buffer + // printf("Moving %d bytes of data from %p to %p.\n", dataAvailable, mp3->dataCurr, + // mp3->dataStart); + memmove(mp3->dataStart, mp3->dataCurr, dataAvailable); + mp3->dataCurr = mp3->dataStart; + mp3->dataEnd = mp3->dataStart + dataAvailable; + } + + amountFetched = mp3->stream_read(mp3->stream, mp3->dataEnd, bufferAvailable); + mp3->dataEnd += amountFetched; // Our buffer now (hopefully) contains more data + } + + // printf("_readData: %d, %d, %d\n", dataAvailable, bufferAvailable, amountFetched); +} + +int IRAM_ATTR mp3_decode(void *ctx) { + mp3_ctx_t *mp3 = (mp3_ctx_t *)ctx; + + if (mp3->stream) + _readData(mp3); + + int available = mp3->dataEnd - mp3->dataCurr; + int nextSync = MP3FindSyncWord(mp3->dataCurr, available); + + if (nextSync >= 0) { + mp3->dataCurr += nextSync; + available = mp3->dataEnd - mp3->dataCurr; + + // printf("Next syncword @ %d, available = %d\n", nextSync, available); + int ret = MP3Decode(mp3->hMP3Decoder, &mp3->dataCurr, &available, mp3->buffer, 0); + + if (ret) { + ESP_LOGE(TAG, "MP3Decode error %d\n", ret); + return 0; + } + + MP3FrameInfo fi; + MP3GetLastFrameInfo(mp3->hMP3Decoder, &fi); + + mp3->lastRate = fi.samprate; + mp3->lastChannels = fi.nChans; + int validSamples = fi.outputSamps / mp3->lastChannels; + mp3->bufferValid = validSamples; + mp3->bufferOffset = 0; + // printf("MP3Decode OK, buffer @ %p, available = %d, rate = %d, channels = %d, validSamples = + // %d\n", mp3->dataCurr, available, mp3->lastRate, mp3->lastChannels, validSamples); + + return 1; + } else { + mp3->dataCurr += available; + ESP_LOGE(TAG, "No syncword found\n"); + return 0; + } +} + +int IRAM_ATTR mp3_init_source(const void *data_start, const void *data_end, int req_sample_rate, void **ctx, + int *stereo, const void *seek_func) { + // Allocate space for the information struct + mp3_ctx_t *mp3 = calloc(sizeof(mp3_ctx_t), 1); + if (!mp3) + goto err; + + // Start the MP3 library + mp3->hMP3Decoder = MP3InitDecoder(); + if (!mp3->hMP3Decoder) { + ESP_LOGE(TAG, "Out of memory error! hMP3Decoder is NULL\n"); + goto err; + } + + // Fill the struct with info + mp3->dataStart = (unsigned char *)data_start; // Start of data + mp3->dataCurr = (unsigned char *)data_start; // Current position + mp3->seek_func = NULL; + mp3->dataEnd = (unsigned char *)data_end; // End of data + mp3->lastRate = 0; + mp3->lastChannels = 0; + mp3->buffer = calloc(MAX_SAMPLES_PER_FRAME, sizeof(short)); + mp3->bufferValid = 0; + mp3->bufferOffset = 0; + mp3->dataPtr = NULL; + mp3->stream_read = NULL; + mp3->stream = NULL; + + if (!mp3->buffer) { + ESP_LOGE(TAG, "Out of memory error! mp3->buffer is NULL\n"); + goto err; + } + + uint32_t length = data_end - data_start + 1; + + ESP_LOGD(TAG, "MP3 source started, data at %p with size %u!\n", mp3->dataStart, length); + + *ctx = (void *)mp3; + *stereo = (mp3->lastChannels == 2); + + mp3_decode(*ctx); // Decode first part + + return CHUNK_SIZE; // Chunk size + +err: + mp3_deinit_source(mp3); + return -1; +} + +int IRAM_ATTR mp3_init_source_stream(const void *stream_read_fn, const void *stream, int req_sample_rate, + void **ctx, int *stereo, const void *seek_func) { + // Allocate space for the information struct + mp3_ctx_t *mp3 = calloc(sizeof(mp3_ctx_t), 1); + if (!mp3) { + ESP_LOGE(TAG, "Out of memory error! mp3 is NULL\n"); + goto err; + } + + // Start the MP3 library + mp3->hMP3Decoder = MP3InitDecoder(); + if (!mp3->hMP3Decoder) { + ESP_LOGE(TAG, "Out of memory error! hMP3Decoder is NULL\n"); + goto err; + } + + // Fill the struct with info + mp3->lastRate = 0; + mp3->lastChannels = 0; + mp3->buffer = calloc(MAX_SAMPLES_PER_FRAME, sizeof(short)); + mp3->bufferValid = 0; + mp3->bufferOffset = 0; + + mp3->stream_read = (stream_read_type)stream_read_fn; + mp3->seek_func = (stream_seek_type)seek_func; + mp3->stream = (void *)stream; + ESP_LOGD(TAG, "stream read fn @ %p and stream at %p\n", mp3->stream_read, mp3->stream); + mp3->dataPtr = heap_caps_malloc(INTERNAL_BUFFER_SIZE, MALLOC_CAP_DMA); + mp3->dataStart = mp3->dataPtr; + mp3->dataCurr = mp3->dataPtr; + mp3->dataEnd = mp3->dataPtr; + + if (!mp3->buffer) { + ESP_LOGE(TAG, "Out of memory error! mp3->buffer is NULL\n"); + goto err; + } + + if (!mp3->dataPtr) { + ESP_LOGE(TAG, "Out of memory error! mp3->dataPtr is NULL\n"); + goto err; + } + + *ctx = (void *)mp3; + int tries = 5; + do { + ESP_LOGI(TAG, "Finding chunk of mp3 data\r\n"); + _readData(mp3); + } while (!mp3_decode(*ctx) && --tries); + if (!tries) { + goto err; + } + ESP_LOGD(TAG, "MP3 stream source started, data at %p, %d Hz, %d channels!\n", mp3->dataStart, + mp3->lastRate, mp3->lastChannels); + + *stereo = mp3->lastChannels == 2; + return CHUNK_SIZE; // Chunk size + +err: + mp3_deinit_source(mp3); + return -1; +} + +int IRAM_ATTR mp3_get_sample_rate(void *ctx) { + mp3_ctx_t *mp3 = (mp3_ctx_t *)ctx; + return mp3->lastRate; +} + +int IRAM_ATTR mp3_fill_buffer(void *ctx, int16_t *buffer, int stereo) { + mp3_ctx_t *mp3 = (mp3_ctx_t *)ctx; + if (mp3->bufferValid <= 0) + mp3_decode(ctx); + if (mp3->bufferValid > 0) { + int len = mp3->bufferValid; + if (len > CHUNK_SIZE) + len = CHUNK_SIZE; + for (int i = 0; i < len; i++) { + if (stereo && (mp3->lastChannels == 2)) { + buffer[i * 2 + 0] = mp3->buffer[mp3->bufferOffset + i * 2 + 0]; + buffer[i * 2 + 1] = mp3->buffer[mp3->bufferOffset + i * 2 + 1]; + } else { + buffer[i] = mp3->buffer[mp3->bufferOffset + i * mp3->lastChannels]; + } + } + mp3->bufferValid -= len; + mp3->bufferOffset += len * mp3->lastChannels; + return len; + } + + return 0; +} + +void mp3_deinit_source(void *ctx) { + mp3_ctx_t *mp3 = (mp3_ctx_t *)ctx; + if (mp3) { + MP3FreeDecoder(mp3->hMP3Decoder); + if (mp3->buffer) + free(mp3->buffer); + if (mp3->dataPtr) + free(mp3->dataPtr); // Stream + free(mp3); + } +} + +int mp3_reset_buffer(void *ctx) { + mp3_ctx_t *mp3 = (mp3_ctx_t *)ctx; + mp3->dataCurr = mp3->dataStart; + mp3->bufferValid = 0; + mp3->bufferOffset = 0; + return 0; +} + +int mp3_stream_reset_buffer(void *ctx) { + mp3_ctx_t *mp3 = (mp3_ctx_t *)ctx; + mp3->seek_func(mp3->stream,0,0); + return 0; +} + + +const sndmixer_source_t sndmixer_source_mp3 = {.init_source = mp3_init_source, + .get_sample_rate = mp3_get_sample_rate, + .fill_buffer = mp3_fill_buffer, + .reset_buffer = mp3_reset_buffer, + .deinit_source = mp3_deinit_source}; + +const sndmixer_source_t sndmixer_source_mp3_stream = {.init_source = mp3_init_source_stream, + .get_sample_rate = mp3_get_sample_rate, + .fill_buffer = mp3_fill_buffer, + .reset_buffer = mp3_stream_reset_buffer, + .deinit_source = mp3_deinit_source}; diff --git a/components/driver_sndmixer/snd_source_mp3.h b/components/driver_sndmixer/snd_source_mp3.h new file mode 100644 index 0000000..017603d --- /dev/null +++ b/components/driver_sndmixer/snd_source_mp3.h @@ -0,0 +1,6 @@ +#pragma once +#include "sndmixer.h" + +extern const sndmixer_source_t sndmixer_source_mp3; +extern const sndmixer_source_t sndmixer_source_mp3_stream; + diff --git a/components/driver_sndmixer/snd_source_synth.c b/components/driver_sndmixer/snd_source_synth.c new file mode 100644 index 0000000..1eebdda --- /dev/null +++ b/components/driver_sndmixer/snd_source_synth.c @@ -0,0 +1,206 @@ +// Author: Renze Nicolai 2019, badge.team + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sndmixer.h" + +#define CHUNK_SIZE 32 + +typedef struct { + int sampleRate; + int position; + uint16_t frequency; + uint8_t waveform; +} synth_ctx_t; + +const uint8_t sinewave[] = { + 0x80, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x85, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8a, 0x8a, 0x8b, + 0x8c, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x91, 0x91, 0x92, 0x93, 0x94, 0x95, 0x95, 0x96, 0x97, 0x98, + 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xaf, 0xb0, + 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb4, 0xb5, 0xb5, 0xb6, 0xb7, 0xb7, 0xb8, 0xb9, 0xba, 0xba, 0xbb, + 0xbc, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc2, 0xc2, 0xc3, 0xc4, 0xc4, 0xc5, 0xc6, + 0xc6, 0xc7, 0xc8, 0xc8, 0xc9, 0xca, 0xca, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xcf, 0xcf, 0xd0, + 0xd0, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8, 0xd9, 0xd9, + 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xde, 0xde, 0xdf, 0xdf, 0xe0, 0xe0, 0xe1, 0xe1, 0xe2, + 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe9, 0xe9, + 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, + 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf0, + 0xf0, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xed, 0xed, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xea, 0xea, + 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe6, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe3, 0xe3, 0xe2, + 0xe2, 0xe1, 0xe1, 0xe0, 0xe0, 0xdf, 0xdf, 0xde, 0xde, 0xdd, 0xdd, 0xdc, 0xdc, 0xdb, 0xdb, 0xda, + 0xd9, 0xd9, 0xd8, 0xd8, 0xd7, 0xd7, 0xd6, 0xd5, 0xd5, 0xd4, 0xd4, 0xd3, 0xd3, 0xd2, 0xd1, 0xd1, + 0xd0, 0xd0, 0xcf, 0xce, 0xce, 0xcd, 0xcc, 0xcc, 0xcb, 0xcb, 0xca, 0xc9, 0xc9, 0xc8, 0xc7, 0xc7, + 0xc6, 0xc5, 0xc5, 0xc4, 0xc3, 0xc3, 0xc2, 0xc1, 0xc1, 0xc0, 0xbf, 0xbf, 0xbe, 0xbd, 0xbd, 0xbc, + 0xbb, 0xbb, 0xba, 0xb9, 0xb9, 0xb8, 0xb7, 0xb6, 0xb6, 0xb5, 0xb4, 0xb4, 0xb3, 0xb2, 0xb1, 0xb1, + 0xb0, 0xaf, 0xaf, 0xae, 0xad, 0xac, 0xac, 0xab, 0xaa, 0xa9, 0xa9, 0xa8, 0xa7, 0xa6, 0xa6, 0xa5, + 0xa4, 0xa3, 0xa3, 0xa2, 0xa1, 0xa0, 0xa0, 0x9f, 0x9e, 0x9d, 0x9d, 0x9c, 0x9b, 0x9a, 0x9a, 0x99, + 0x98, 0x97, 0x96, 0x96, 0x95, 0x94, 0x93, 0x93, 0x92, 0x91, 0x90, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, + 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x88, 0x87, 0x86, 0x85, 0x85, 0x84, 0x83, 0x82, 0x81, 0x81, 0x80, + 0x7f, 0x7e, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x7a, 0x79, 0x78, 0x77, 0x77, 0x76, 0x75, 0x74, 0x73, + 0x73, 0x72, 0x71, 0x70, 0x6f, 0x6f, 0x6e, 0x6d, 0x6c, 0x6c, 0x6b, 0x6a, 0x69, 0x69, 0x68, 0x67, + 0x66, 0x65, 0x65, 0x64, 0x63, 0x62, 0x62, 0x61, 0x60, 0x5f, 0x5f, 0x5e, 0x5d, 0x5c, 0x5c, 0x5b, + 0x5a, 0x59, 0x59, 0x58, 0x57, 0x56, 0x56, 0x55, 0x54, 0x53, 0x53, 0x52, 0x51, 0x50, 0x50, 0x4f, + 0x4e, 0x4e, 0x4d, 0x4c, 0x4b, 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, 0x45, 0x44, 0x44, + 0x43, 0x42, 0x42, 0x41, 0x40, 0x40, 0x3f, 0x3e, 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3a, 0x3a, 0x39, + 0x38, 0x38, 0x37, 0x36, 0x36, 0x35, 0x34, 0x34, 0x33, 0x33, 0x32, 0x31, 0x31, 0x30, 0x2f, 0x2f, + 0x2e, 0x2e, 0x2d, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, 0x2a, 0x29, 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, + 0x25, 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e, 0x1d, + 0x1d, 0x1c, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, + 0x15, 0x15, 0x14, 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0xf, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, + 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, + 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x15, + 0x16, 0x16, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1c, 0x1d, + 0x1d, 0x1e, 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x21, 0x21, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, + 0x26, 0x26, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2f, + 0x2f, 0x30, 0x30, 0x31, 0x32, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x37, 0x38, 0x39, + 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x43, + 0x44, 0x45, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f, + 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x5a, 0x5a, + 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x67, + 0x67, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6e, 0x6f, 0x70, 0x71, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x75, 0x76, 0x77, 0x78, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, +}; + +const uint8_t noise[] = { + 0xfe, 0x84, 0xe0, 0xbb, 0x3b, 0x21, 0x82, 0xc3, 0x95, 0x4c, 0x18, 0xe0, 0xce, 0x70, 0xf4, 0x7d, + 0x4e, 0x5d, 0x57, 0x43, 0xb9, 0x3d, 0xdc, 0xd1, 0x10, 0x18, 0xbe, 0x24, 0xed, 0x8c, 0x9a, 0x72, + 0x67, 0x0e, 0xaf, 0xa2, 0x3c, 0x55, 0x5e, 0xb2, 0x0e, 0xc9, 0x09, 0xe3, 0xe1, 0x45, 0x55, 0x38, + 0x4f, 0x76, 0x8a, 0xc4, 0xe2, 0xd1, 0x5c, 0x20, 0x36, 0xd6, 0x83, 0xa3, 0xc6, 0xdf, 0x06, 0x14, + 0x7f, 0xa2, 0xe3, 0xe7, 0x8f, 0x22, 0xea, 0xc7, 0x49, 0x7d, 0x1b, 0x95, 0x51, 0x1e, 0x69, 0xf1, + 0xca, 0xc3, 0x04, 0x90, 0xbe, 0x45, 0x70, 0xb1, 0xdb, 0xcc, 0x34, 0x15, 0x95, 0xec, 0xe5, 0x38, + 0xfa, 0x6d, 0xf5, 0xb7, 0x20, 0xd2, 0x33, 0xff, 0x27, 0x17, 0x4e, 0x12, 0x4d, 0xe8, 0xc7, 0xc6, + 0xe2, 0x14, 0x04, 0x9c, 0xb7, 0x04, 0xa7, 0x3f, 0x16, 0xed, 0x30, 0x8e, 0x92, 0x60, 0x9d, 0xad, + 0xc2, 0x42, 0xa2, 0xfa, 0xd8, 0xa4, 0xe5, 0x31, 0xe6, 0xa7, 0x01, 0xbe, 0x09, 0xe2, 0x49, 0xe5, + 0xb8, 0xbe, 0xd5, 0x58, 0x53, 0x14, 0x35, 0x9f, 0xe1, 0x00, 0x6e, 0xff, 0xfd, 0xaa, 0x53, 0xde, + 0x22, 0xd3, 0x7b, 0x8c, 0x23, 0x28, 0x85, 0xb3, 0xb6, 0x5d, 0x68, 0xce, 0xf8, 0xb3, 0xc8, 0x6f, + 0xd4, 0xb5, 0x38, 0xfb, 0x1a, 0x69, 0x1c, 0x3b, 0xfd, 0x81, 0x9e, 0x48, 0xa6, 0x6c, 0xf1, 0x7a, + 0x6e, 0xa7, 0x6f, 0x6a, 0x4d, 0x88, 0x62, 0x13, 0x38, 0x86, 0xe7, 0xe9, 0x2e, 0x37, 0x38, 0x96, + 0x6f, 0x6b, 0xc8, 0x49, 0xfc, 0x68, 0x93, 0x7a, 0xab, 0xd7, 0x92, 0x80, 0x89, 0xd1, 0x0e, 0x34, + 0x9f, 0x9c, 0xaf, 0xf9, 0xe8, 0x48, 0x6b, 0x5c, 0x46, 0x2d, 0x8c, 0x2c, 0xb6, 0x51, 0xfb, 0x9c, + 0x72, 0xe5, 0x6f, 0x3e, 0x79, 0xa6, 0xbc, 0x6f, 0x67, 0x8f, 0xe5, 0xc8, 0x7a, 0x6c, 0xde, 0x8e}; + +int IRAM_ATTR synth_init_source(const void *data_start, const void *data_end, int req_sample_rate, void **ctx, + int *stereo, const void *seek_func) { + synth_ctx_t *synth = calloc(sizeof(synth_ctx_t), 1); + if (!synth) + return -1; + + synth->sampleRate = req_sample_rate; + synth->frequency = 0; + + *ctx = (void *)synth; + *stereo = 0; + + printf("SYNTH created, requested sample rate: %d\n", req_sample_rate); + + return CHUNK_SIZE; // Chunk size +} + +int IRAM_ATTR synth_get_sample_rate(void *ctx) { + synth_ctx_t *synth = (synth_ctx_t *)ctx; + return synth->sampleRate; +} + +int IRAM_ATTR synth_fill_buffer(void *ctx, int16_t *buffer, int stereo) { + synth_ctx_t *synth = (synth_ctx_t *)ctx; + + (void)stereo; + + if (synth->frequency == 0) { + for (int i = 0; i < CHUNK_SIZE; i++) + buffer[i] = 0; + synth->position = 0; + return CHUNK_SIZE; + } + + int samplesPerWavelength = synth->sampleRate / synth->frequency; + + for (int i = 0; i < CHUNK_SIZE; i++) { + synth->position += 1; + if (synth->position >= samplesPerWavelength) + synth->position = 0; + switch (synth->waveform) { + case 0: // Sine + { + uint16_t sinePos = ((synth->position * 1024) / samplesPerWavelength); + buffer[i] = -128 + sinewave[sinePos]; + break; + } + case 1: // Square + buffer[i] = (((synth->position * 256) / samplesPerWavelength) >= 128) ? 127 : -128; + break; + case 2: // Triangle + { + uint8_t val = ((synth->position * 256) / samplesPerWavelength); + if (val < 128) { + buffer[i] = -128 + val * 2; + } else { + buffer[i] = 255 - val * 2 + 128; + } + break; + } + case 3: // Sawtooth + buffer[i] = -128 + ((synth->position * 256) / samplesPerWavelength); + break; + case 4: // Noise + { + uint16_t noisePos = ((synth->position * 1024) / samplesPerWavelength); + buffer[i] = -128 + noise[noisePos & 0xFF]; + break; + } + default: + buffer[i] = 0; + } + buffer[i] <<= 8; + } + return CHUNK_SIZE; +} + +void synth_deinit_source(void *ctx) { + synth_ctx_t *synth = (synth_ctx_t *)ctx; + free(synth); +} + +void IRAM_ATTR synth_set_frequency(void *ctx, uint16_t frequency) { + synth_ctx_t *synth = (synth_ctx_t *)ctx; + synth->frequency = frequency; +} + +void IRAM_ATTR synth_set_waveform(void *ctx, uint8_t waveform) { + synth_ctx_t *synth = (synth_ctx_t *)ctx; + synth->waveform = waveform; +} + +const sndmixer_source_t sndmixer_source_synth = {.init_source = synth_init_source, + .get_sample_rate = synth_get_sample_rate, + .fill_buffer = synth_fill_buffer, + .deinit_source = synth_deinit_source, + .set_frequency = synth_set_frequency, + .set_waveform = synth_set_waveform}; + diff --git a/components/driver_sndmixer/snd_source_synth.h b/components/driver_sndmixer/snd_source_synth.h new file mode 100644 index 0000000..9b9c192 --- /dev/null +++ b/components/driver_sndmixer/snd_source_synth.h @@ -0,0 +1,5 @@ +#pragma once +#include "sndmixer.h" + +extern const sndmixer_source_t sndmixer_source_synth; + diff --git a/components/driver_sndmixer/snd_source_wav.c b/components/driver_sndmixer/snd_source_wav.c new file mode 100644 index 0000000..c6b973d --- /dev/null +++ b/components/driver_sndmixer/snd_source_wav.c @@ -0,0 +1,265 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sndmixer.h" +#include "snd_source_wav.h" + +#define CHUNK_SIZE 16 +#define TAG "source_wav" + +typedef struct { + const uint8_t *data; // Pointer to internal buffer (if applicable) + uint32_t pos; + uint32_t data_len; + uint32_t rate; + uint16_t channels, bits; + + stream_read_type stream_read; + stream_seek_type seek_func; + void *stream; // Pointer to stream + uint32_t data_start_offset; +} wav_ctx_t; + +typedef struct __attribute__((packed)) { + int8_t riffmagic[4]; + uint32_t size; + int8_t wavemagic[4]; +} riff_hdr_t; + +typedef struct __attribute__((packed)) { + uint16_t fmtcode; + uint16_t channels; + uint32_t samplespersec; + uint32_t avgbytespersec; + uint16_t blockalign; + uint16_t bitspersample; + uint16_t bsize; + uint16_t validbitspersample; + uint32_t channelmask; + int8_t subformat[16]; +} fmt_data_t; + +typedef struct __attribute__((packed)) { + int8_t magic[4]; + int32_t size; + union { + fmt_data_t fmt; + int8_t data[0]; + }; +} chunk_hdr_t; + +int IRAM_ATTR wav_init_source(const void *data_start, const void *data_end, int req_sample_rate, void **ctx, + int *stereo, const void *seek_func) { + // Check sanity first + char *p = (char *)data_start; + wav_ctx_t *wav = heap_caps_calloc(sizeof(wav_ctx_t), 1, MALLOC_CAP_DMA); + if (!wav) + goto err; + riff_hdr_t *riff = (riff_hdr_t *)p; + if (memcmp(riff->riffmagic, "RIFF", 4) != 0) + goto err; + if (memcmp(riff->wavemagic, "WAVE", 4) != 0) + goto err; + p += sizeof(riff_hdr_t); + while (p < (char *)data_end) { + chunk_hdr_t *ch = (chunk_hdr_t *)p; + if (memcmp(ch->magic, "fmt ", 4) == 0) { + if (ch->fmt.fmtcode != WAVE_FORMAT_PCM) { + printf("Unsupported wav format: %d\n", ch->fmt.fmtcode); + goto err; + } + wav->rate = ch->fmt.samplespersec; + wav->bits = ch->fmt.bitspersample; + wav->channels = ch->fmt.channels; + if (wav->channels == 0) + wav->channels = 1; + } else if (memcmp(ch->magic, "data", 4) == 0) { + wav->data_len = ch->size; + wav->data = (uint8_t *)ch->data; + } + p += 8 + ch->size; + if (ch->size & 1) + p++; // pad to even address + } + + if (wav->bits != 8 && wav->bits != 16) { + printf("No fmt chunk or unsupported bits/sample: %d\n", wav->bits); + goto err; + } + printf("Wav: %d bit/sample, %d Hz, %d bytes long\n", wav->bits, wav->rate, wav->data_len); + wav->pos = 0; + *ctx = (void *)wav; + *stereo = (wav->channels >= 2); + return CHUNK_SIZE; +err: + free(wav); + return -1; +} + +int IRAM_ATTR wav_init_source_stream(const void *stream_read_fn, const void *stream, int req_sample_rate, + void **ctx, int *stereo, const void *seek_func) { + ESP_LOGI(TAG, "init wav"); + wav_ctx_t *wav = heap_caps_calloc(sizeof(wav_ctx_t), 1, MALLOC_CAP_DMA); + if (!wav) { + ESP_LOGE(TAG, "Failed to allocate wave file context"); + return -1; + } + + wav->stream_read = stream_read_fn; + wav->seek_func = seek_func; + wav->stream = stream; + + ESP_LOGI(TAG, "header @ %d", wav->seek_func(wav->stream, 0, SEEK_CUR)); + riff_hdr_t *riffHdr = calloc(1, sizeof(riff_hdr_t)); + int read = wav->stream_read(wav->stream, riffHdr, sizeof(riff_hdr_t)); + if (read < 7) { + ESP_LOGW(TAG, "Failed to read WAV header"); + return -1; + } + + if (memcmp(riffHdr->riffmagic, "RIFF", 4) != 0){ + ESP_LOGW(TAG, "WAV file does not contain RIFF magic bytes"); + return -1; + } + if (memcmp(riffHdr->wavemagic, "WAVE", 4) != 0){ + ESP_LOGW(TAG, "WAV file does not contain WAVE magic bytes"); + return -1; + } + + ESP_LOGI(TAG, "fmt @ %d", wav->seek_func(wav->stream, 0, SEEK_CUR)); + chunk_hdr_t chunk; + wav->stream_read(wav->stream, &chunk, 4 + 4); + + if (memcmp(chunk.magic, "fmt ", 4) != 0){ + ESP_LOGW(TAG, "WAV file does not contain format chunk after header"); + return -1; + } + ESP_LOGI(TAG, "fmt size %d", chunk.size); + + ESP_LOGI(TAG, "fmt body @ %d", wav->seek_func(wav->stream, 0, SEEK_CUR)); + fmt_data_t format; + wav->stream_read(wav->stream, &format, chunk.size); + if (format.fmtcode != WAVE_FORMAT_PCM) { + ESP_LOGW(TAG, "Unsupported WAV format: %d\n", format.fmtcode); + return -1; + } + wav->rate = format.samplespersec; + wav->bits = format.bitspersample; + wav->channels = format.channels; + if (wav->channels == 0) { + wav->channels = 1; + } + + ESP_LOGI(TAG, "channels: %d, bits: %d, rate: %d", wav->channels, wav->bits, wav->rate); + + ESP_LOGI(TAG, "data @ %d", wav->seek_func(wav->stream, 0, SEEK_CUR)); + wav->stream_read(wav->stream, &chunk, 4 + 4); + if (memcmp(chunk.magic, "data", 4) != 0){ + ESP_LOGW(TAG, "WAV file does not contain data chunk after format chunk"); + return -1; + } + + ESP_LOGI(TAG, "seek"); + wav->data_start_offset = wav->seek_func(wav->stream, 0, SEEK_CUR); + wav->data_len = chunk.size; + ESP_LOGI(TAG, "WAV data offset: %d", wav->data_start_offset); + + wav->pos = 0; + *ctx = (void *)wav; + *stereo = (wav->channels >= 2); + return CHUNK_SIZE; +} + +int IRAM_ATTR wav_get_sample_rate(void *ctx) { + wav_ctx_t *wav = (wav_ctx_t *)ctx; + return wav->rate; +} + +int8_t get_sample_byte(wav_ctx_t *wav) { + int8_t rv = 0; + if(wav->stream) { + int read = wav->stream_read(wav->stream, &rv, 1); + wav->pos += read; + } else { + rv = wav->data[wav->pos]; + wav->pos += 1; + } + return rv; +} + +int16_t IRAM_ATTR get_sample(wav_ctx_t *wav) { + int16_t rv = 0; + if (wav->bits == 8) { + rv = (get_sample_byte(wav) - 128) << 8; + } else { + rv = get_sample_byte(wav) | get_sample_byte(wav) << 8; + } + return rv; +} + +int IRAM_ATTR wav_fill_buffer(void *ctx, int16_t *buffer, int stereo) { + wav_ctx_t *wav = (wav_ctx_t *)ctx; + int channels = 1; + if (wav->channels == 2 && stereo) { + channels = 2; + } + if(wav->stream && stereo && wav->bits == 16 && wav->channels <= 2) { + // Optimisation: if we're streaming a 1 or 2-channel 16 bit file, we can directly copy its contents + int read = wav->stream_read(wav->stream, buffer, CHUNK_SIZE * sizeof(uint16_t) * wav->channels); + wav->pos += read; + return read / (2 * 2); + } + for (int i = 0; i < CHUNK_SIZE; i++) { + if (wav->pos >= wav->data_len) + return i; + if (channels == 2) { + buffer[i * 2 + 0] = get_sample(wav); + buffer[i * 2 + 1] = get_sample(wav); + } else { + int32_t sum = 0; + for (int k = 0; k < wav->channels; k++) { + sum += get_sample(wav); + } + buffer[i] = sum / wav->channels; + } + } + return CHUNK_SIZE; +} + +int wav_reset_buffer(void *ctx) { + wav_ctx_t *wav = (wav_ctx_t *)ctx; + wav->pos = 0; + return 0; +} + +int wav_stream_reset_buffer(void *ctx) { + wav_ctx_t *wav = (wav_ctx_t *)ctx; + wav->seek_func(wav->stream, wav->data_start_offset, 0); + wav->pos = 0; + return 0; +} + + +void wav_deinit_source(void *ctx) { + wav_ctx_t *wav = (wav_ctx_t *)ctx; + free(wav); +} + +const sndmixer_source_t sndmixer_source_wav = {.init_source = wav_init_source, + .get_sample_rate = wav_get_sample_rate, + .fill_buffer = wav_fill_buffer, + .reset_buffer = wav_reset_buffer, + .deinit_source = wav_deinit_source}; + +const sndmixer_source_t sndmixer_source_wav_stream = {.init_source = wav_init_source_stream, + .get_sample_rate = wav_get_sample_rate, + .fill_buffer = wav_fill_buffer, + .reset_buffer = wav_stream_reset_buffer, + .deinit_source = wav_deinit_source}; + diff --git a/components/driver_sndmixer/snd_source_wav.h b/components/driver_sndmixer/snd_source_wav.h new file mode 100644 index 0000000..86bc89b --- /dev/null +++ b/components/driver_sndmixer/snd_source_wav.h @@ -0,0 +1,7 @@ +#pragma once +#include "sndmixer.h" +#define WAVE_FORMAT_PCM 0x01 + +extern const sndmixer_source_t sndmixer_source_wav; +extern const sndmixer_source_t sndmixer_source_wav_stream; + diff --git a/components/driver_sndmixer/sndmixer.c b/components/driver_sndmixer/sndmixer.c new file mode 100644 index 0000000..e6932dd --- /dev/null +++ b/components/driver_sndmixer/sndmixer.c @@ -0,0 +1,595 @@ +#include +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" +#include "esp_log.h" + +#include "driver_i2s.h" + +#include "snd_source_wav.h" +#include "snd_source_mp3.h" +#include "snd_source_synth.h" + +#define TAG "Sndmixer" + +#define CHFL_EVICTABLE (1 << 0) +#define CHFL_PAUSED (1 << 1) +#define CHFL_LOOP (1 << 2) +#define CHFL_STEREO (1 << 3) + +#define SYNC_NOTE_DIVISOR 8 // Beat synchroniser keeps counts in 1/8 (eighth) notes +#define SYNC_COUNT_BARS 4 // Beat synchroniser counts up to 4 bars (i.e. 4 whole notes, or 32 eighth notes) + +typedef enum { + CMD_QUEUE_WAV = 1, + CMD_QUEUE_WAV_STREAM, + CMD_QUEUE_MP3, + CMD_QUEUE_MP3_STREAM, + CMD_QUEUE_SYNTH, + CMD_LOOP, + CMD_VOLUME, + CMD_PLAY, + CMD_PAUSE, + CMD_STOP, + CMD_PAUSE_ALL, + CMD_RESUME_ALL, + CMD_FREQ, + CMD_WAVEFORM, + CMD_CALLBACK, + CMD_BEAT_SYNC_START, + CMD_BEAT_SYNC_STOP, + CMD_START_AT_NEXT +} sndmixer_cmd_ins_t; + +typedef struct { + sndmixer_cmd_ins_t cmd; + int id; + union { + struct { + const void *queue_file_start; + const void *queue_file_end; + const void *read_func; + const void *stream; + const void *seek_func; + const void *callback_func; + const void *callback_handle; + int flags; + }; + struct { + int param; + }; + }; +} sndmixer_cmd_t; + +typedef struct { + int id; + const sndmixer_source_t *source; // or NULL if channel unused + void *src_ctx; + double volume; // 0-1 + uint8_t flags; + int16_t *buffer; + int chunksz; + const void *callback_func; + const void *callback_handle; + uint32_t dds_rate; // Rate; 16.16 fixed + uint32_t dds_acc; // DDS accumulator, 16.16 fixed + + // For beat synched playback, the interval of (1/SYNC_NOTE_DIVISOR) (e.g. 1/8th) notes to synchronise to. + // 1 for starting at a 1/8th note in the default config, 2 for 1/4th, 4 for 1/2, 8 for 1 whole bar, 16 for 2 bars, 32 for 4 bars. + int8_t start_at_next; +} sndmixer_channel_t; + +static sndmixer_channel_t *channel; +static int no_channels; +static int samplerate; +static volatile uint32_t curr_id = 0; +static QueueHandle_t cmd_queue; +static int use_stereo = 0; +static uint8_t beat_sync_count = 0; // At which (1/SYNC_NOTE_DIVISOR)'th note we are currently +static bool beat_sync_enabled = false; +static TickType_t beat_sync_last_tick = 0; // When beat_sync_count was last increased +static uint8_t beat_sync_bpm = 120; // Preconfigured BPM. Can be configured with sndmixer API + +// Grabs a new ID by atomically increasing curr_id and returning its value. This is called outside +// of the audio playing thread, hence the atomicity. +static uint32_t new_id() { + uint32_t old_id, new_id; + do { + old_id = curr_id; + new_id = old_id + 1; + // compares curr_id with old_id, sets to new_id if same, returns old val in new_id + uxPortCompareSet(&curr_id, old_id, &new_id); + } while (new_id != old_id); + return old_id + 1; +} + +static void clean_up_channel(int ch) { + if(channel[ch].callback_handle) { + // exec callback + callback_type do_callback = channel[ch].callback_func; + do_callback(channel[ch].callback_handle,0,0); + } + + if (channel[ch].source) { + channel[ch].source->deinit_source(channel[ch].src_ctx); + channel[ch].source = NULL; + } + free(channel[ch].buffer); + channel[ch].buffer = NULL; + channel[ch].flags = 0; + ESP_LOGI(TAG, "Sndmixer: %d: cleaning up done", channel[ch].id); + channel[ch].id = 0; +} + +static int find_free_channel() { + for (int x = 0; x < no_channels; x++) { + if (channel[x].source == NULL) + return x; + } + // No free channels. Maybe one is evictable? + for (int x = 0; x < no_channels; x++) { + if (channel[x].flags & CHFL_EVICTABLE) { + clean_up_channel(x); + return x; + } + } + return -1; // nothing found :/ +} + +static int init_source(int* chan_id, const sndmixer_source_t *srcfns, const void *data_start, + const void *data_end, const void *seek_func) { + int ch = find_free_channel(); + *chan_id = ch; + if (ch < 0) { + return 0; // no free channels + } + ESP_LOGI(TAG, "Sndmixer: %d: initialising source\n", ch); + int stereo = 0; + int chunksz = + srcfns->init_source(data_start, data_end, samplerate, &channel[ch].src_ctx, &stereo, seek_func); + if (chunksz <= 0) + return 0; // failed + ESP_LOGI(TAG, "Sndmixer: %d: malloc chunks: %d\n", ch, chunksz); + channel[ch].source = srcfns; + channel[ch].volume = 1.f; + channel[ch].buffer = malloc(chunksz * sizeof(channel[ch].buffer[0]) * ((stereo && use_stereo) ? 2 : 1)); + if (!channel[ch].buffer) { + clean_up_channel(ch); + return 0; + } + channel[ch].chunksz = chunksz; + int64_t real_rate = srcfns->get_sample_rate(channel[ch].src_ctx); + channel[ch].dds_rate = (real_rate << 16) / samplerate; + channel[ch].dds_acc = chunksz << 16; // to force the main thread to get new data + channel[ch].flags = CHFL_PAUSED; + if (stereo && use_stereo) { + ESP_LOGI(TAG, "Starting stereo channel"); + channel[ch].flags |= CHFL_STEREO; + } + return 1; +} + +static void handle_cmd(sndmixer_cmd_t *cmd) { + bool cmd_found = true; + int chan_id; + int cmd_success = 1; + + // Global initialisation commands that are not bound to a single channel + switch(cmd->cmd) { + case CMD_QUEUE_WAV: + cmd_success = init_source(&chan_id, &sndmixer_source_wav, cmd->queue_file_start, cmd->queue_file_end, 0); + break; + case CMD_QUEUE_WAV_STREAM: + cmd_success = init_source(&chan_id, &sndmixer_source_wav_stream, cmd->read_func, cmd->stream, cmd->seek_func); + break; + case CMD_QUEUE_MP3: + cmd_success = init_source(&chan_id, &sndmixer_source_mp3, cmd->queue_file_start, cmd->queue_file_end, 0); + break; + case CMD_QUEUE_MP3_STREAM: + cmd_success = init_source(&chan_id, &sndmixer_source_mp3_stream, cmd->read_func, cmd->stream, cmd->seek_func); + break; + case CMD_QUEUE_SYNTH: + cmd_success = init_source(&chan_id, &sndmixer_source_synth, 0, 0, 0); + break; + default: + cmd_found = false; + break; + } + + if(cmd_found){ + if(cmd_success){ + channel[chan_id].id = cmd->id; // success; set ID + channel[chan_id].flags |= cmd->flags; + } else { + if(chan_id < 0){ + ESP_LOGE(TAG, "No more available channels"); + } else { + ESP_LOGE(TAG, "Failed to initialise source"); + } + } + return; + } + + // Other global commands that are not bound to a single channel + cmd_found = true; + switch(cmd->cmd) { + case CMD_BEAT_SYNC_START: + beat_sync_enabled = true; + beat_sync_bpm = (uint8_t) cmd->param; + break; + case CMD_BEAT_SYNC_STOP: + beat_sync_enabled = false; + break; + case CMD_PAUSE_ALL: + for (int x = 0; x < no_channels; x++) { + channel[x].flags |= CHFL_PAUSED; + } + break; + case CMD_RESUME_ALL: + for (int x = 0; x < no_channels; x++) { + channel[x].flags &= ~CHFL_PAUSED; + } + break; + default: + cmd_found = false; + break; + } + + if(cmd_found){ return; } + + // The rest are all commands that act on a certain channel ID. Look up if we have a channel with that ID first. + chan_id = -1; + for (int i = 0; i < no_channels; i++) { + if (channel[i].id == cmd->id) { + chan_id = i; + break; + } + } + if (chan_id == -1) { + ESP_LOGW(TAG, "Channel id %d not found, command not executed.", cmd->id); + return; // not playing/queued; can't do any of the following commands. + } + + // Channel-specific commands + switch(cmd->cmd) { + case CMD_LOOP: + if (cmd->param) { + channel[chan_id].flags |= CHFL_LOOP; + } else { + channel[chan_id].flags &= ~CHFL_LOOP; + } + break; + case CMD_VOLUME: + // Volume goes from 0-255 + channel[chan_id].volume = cmd->param / 255.; + break; + case CMD_PLAY: + channel[chan_id].flags &= ~CHFL_PAUSED; + break; + case CMD_PAUSE: + channel[chan_id].flags |= CHFL_PAUSED; + break; + case CMD_STOP: + ESP_LOGI(TAG, "%d: cleaning up source due to external stop request", cmd->id); + clean_up_channel(chan_id); + break; + case CMD_FREQ: + if (channel[chan_id].source->set_frequency) { + channel[chan_id].source->set_frequency(channel[chan_id].src_ctx, cmd->param); + } else { + ESP_LOGE(TAG, "Not a synth channel!"); + } + break; + case CMD_WAVEFORM: + if (channel[chan_id].source->set_waveform) { + channel[chan_id].source->set_waveform(channel[chan_id].src_ctx, cmd->param); + } else { + ESP_LOGE(TAG, "Not a synth channel!"); + } + break; + case CMD_CALLBACK: + channel[chan_id].callback_handle = cmd->callback_handle; + channel[chan_id].callback_func = cmd->callback_func; + break; + case CMD_START_AT_NEXT: + channel[chan_id].start_at_next = cmd->param; + break; + default: + break; + } +} + +#define CHUNK_SIZE 32 + +// Sound mixer main loop. +IRAM_ATTR static void sndmixer_task(void *arg) { + int16_t mixbuf[CHUNK_SIZE * (use_stereo ? 2 : 1)]; + ESP_LOGI(TAG, "Sndmixer task up.\n"); + + TickType_t current_ticks; + uint32_t ticks_per_subnote = (uint32_t)(1000.0 / portTICK_PERIOD_MS) / ((beat_sync_bpm / 60.0) * (SYNC_NOTE_DIVISOR/4)); + while (1) { + + // Keep track of tempo if beat sync is enabled + if(beat_sync_enabled) { + current_ticks = xTaskGetTickCount(); + if (current_ticks >= beat_sync_last_tick + ticks_per_subnote) { + beat_sync_count = (beat_sync_count + 1) % (SYNC_COUNT_BARS * SYNC_NOTE_DIVISOR); + beat_sync_last_tick = current_ticks; + if (beat_sync_count % 2 == 0) { + ESP_LOGV(TAG, "beat %d", (beat_sync_count / 2) % 4); + } + } + } + + // Handle any commands that are sent to us. + sndmixer_cmd_t cmd; + while (xQueueReceive(cmd_queue, &cmd, 0) == pdTRUE) { + handle_cmd(&cmd); + } + + // Assemble CHUNK_SIZE worth of samples and dump it into the I2S subsystem. + for (int i = 0; i < CHUNK_SIZE; i++) { + uint8_t active_channels = 0; + + // current sample value, multiplied by 255 (because of multiplies by channel volume) + int32_t s[2] = {0, 0}; + for (int ch = 0; ch < no_channels; ch++) { + sndmixer_channel_t *chan = &channel[ch]; + + // If the channel is paused, and is set to start at an interval we are currently in, unpause it + if(chan->start_at_next > 0 && (chan->flags & CHFL_PAUSED) && beat_sync_count % chan->start_at_next == 0) { + ESP_LOGI(TAG, "Starting at subnote %d", beat_sync_count); + chan->flags &= ~CHFL_PAUSED; + } + + if (chan->source && !(chan->flags & CHFL_PAUSED)) { + // Channel is active. + active_channels++; + chan->dds_acc += chan->dds_rate; // select next sample + // dds_acc>>16 now gives us which sample to get from the buffer. + while ((chan->dds_acc >> 16) >= chan->chunksz && chan->source) { + // That value is outside the channels chunk buffer. Refill that first. + int r = chan->source->fill_buffer(chan->src_ctx, chan->buffer, use_stereo); + if (r == 0) { + // if loop is enabled, reset buffer position to start when no new samples are available + if (chan->flags & CHFL_LOOP) { + ESP_LOGI(TAG, "Looping sample"); + if (chan->source->reset_buffer(chan->src_ctx) < 0) { + ESP_LOGE(TAG, "%d: cleaning up source, loop failed", chan->id); + clean_up_channel(ch); + break; + } else { + r = chan->source->fill_buffer(chan->src_ctx, chan->buffer, use_stereo); + } + } else { + // Source is done and no loops are requested + ESP_LOGI(TAG, "%d: cleaning up source because of EOF", chan->id); + clean_up_channel(ch); + break; + } + continue; + } + int64_t real_rate = chan->source->get_sample_rate(chan->src_ctx); + chan->dds_rate = (real_rate << 16) / samplerate; + chan->dds_acc -= + (chan->chunksz << 16); // reset dds acc; we have parsed chunksize samples. + chan->chunksz = r; // save new chunksize + } + if (!chan->source) { + continue; + } + // Multiply by volume, add to cumulative sample. + uint32_t acc = chan->dds_acc >> 16; + if (chan->flags & CHFL_STEREO) { + s[0] += (int32_t)((double) chan->buffer[acc * 2 + 0] * chan->volume); + s[1] += (int32_t)((double) chan->buffer[acc * 2 + 1] * chan->volume); + } else { + s[0] += (int32_t)((double) chan->buffer[acc] * chan->volume); + s[1] += (int32_t)((double) chan->buffer[acc] * chan->volume); + } +// ESP_LOGI(TAG, "buffer=%d volume=%f result=%d", chan->buffer[acc], chan->volume, s[0]); + } + } + + /*** + * Divide by the max volume of a channel to return to INT16 ranges. + * Note that before, we divided by the number of active channels here as well, + * seemingly to prevent clipping. However, channels are mixed additively in music. + * Dividing by active channels will audibly lower the volume when new channels are started + * whilst others are playing. Adding a few channels together will not cause clipping for + * most normal samples, and sound natural. For scenarios where clipping could occur, such as + * multiple synthesizers at full volume, lower the channel volumes from the app. + */ +// s[0] /= 255; +// s[1] /= 255; + + // Saturate +#define SAT(x, min, max) ((x > max) ? max : (x < min) ? min : x) + s[0] = SAT(s[0], INT16_MIN, INT16_MAX); + s[1] = SAT(s[1], INT16_MIN, INT16_MAX); + + if (use_stereo) { + mixbuf[i * 2 + 0] = s[0]; + mixbuf[i * 2 + 1] = s[1]; + } else { + mixbuf[i] = (s[0] + s[1]) / 2; + } + } + driver_i2s_sound_push(mixbuf, CHUNK_SIZE, use_stereo); + } + // ToDo: de-init channels/buffers/... if we ever implement a deinit cmd + vTaskDelete(NULL); +} + +// Run on core 1 if enabled, core 0 if not. +#define MY_CORE (portNUM_PROCESSORS - 1) + +int sndmixer_init(int p_no_channels, int stereo, i2s_pin_config_t* pin_config) { + no_channels = p_no_channels; + samplerate = 44100; + driver_i2s_sound_start(pin_config); + channel = calloc(sizeof(sndmixer_channel_t), no_channels); + use_stereo = stereo; + if (!channel) + return 0; + curr_id = 0; + cmd_queue = xQueueCreate(10, sizeof(sndmixer_cmd_t)); + if (cmd_queue == NULL) { + free(channel); + return 0; + } + int r = xTaskCreatePinnedToCore(&sndmixer_task, "sndmixer", 5 << 10, NULL, 5, NULL, MY_CORE); + if (!r) { + free(channel); + vQueueDelete(cmd_queue); + return 0; + } + return 1; +} + +// The following functions all are essentially wrappers for the axt of pushing a command into the +// command queue. + +int sndmixer_queue_wav(const void *wav_start, const void *wav_end, int evictable) { + int id = new_id(); + sndmixer_cmd_t cmd = {.id = id, + .cmd = CMD_QUEUE_WAV, + .queue_file_start = wav_start, + .queue_file_end = wav_end, + .flags = CHFL_PAUSED | (evictable ? CHFL_EVICTABLE : 0)}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); + return id; +} + +int sndmixer_queue_wav_stream(stream_read_type read_func, stream_seek_type seek_func, void *stream) { + int id = new_id(); + sndmixer_cmd_t cmd = {.id = id, + .cmd = CMD_QUEUE_WAV_STREAM, + .read_func = (void *)read_func, + .seek_func = (void *)seek_func, + .stream = stream, + .flags = CHFL_PAUSED}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); + return id; +} + +int sndmixer_queue_mp3(const void *mp3_start, const void *mp3_end) { + int id = new_id(); + sndmixer_cmd_t cmd = {.id = id, + .cmd = CMD_QUEUE_MP3, + .queue_file_start = mp3_start, + .queue_file_end = mp3_end, + .flags = CHFL_PAUSED}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); + return id; +} + +int sndmixer_queue_mp3_stream(stream_read_type read_func, stream_seek_type seek_func, void *stream) { + int id = new_id(); + sndmixer_cmd_t cmd = {.id = id, + .cmd = CMD_QUEUE_MP3_STREAM, + .read_func = (void *)read_func, + .seek_func = (void *)seek_func, + .stream = stream, + .flags = CHFL_PAUSED}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); + return id; +} + +int sndmixer_queue_synth() { + int id = new_id(); + sndmixer_cmd_t cmd = {.id = id, .cmd = CMD_QUEUE_SYNTH, .flags = CHFL_PAUSED}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); + return id; +} + +void sndmixer_set_loop(int id, int do_loop) { + sndmixer_cmd_t cmd = {.cmd = CMD_LOOP, .id = id, .param = do_loop}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_set_volume(int id, int volume) { + sndmixer_cmd_t cmd = {.cmd = CMD_VOLUME, .id = id, .param = volume}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_play(int id) { + sndmixer_cmd_t cmd = { + .cmd = CMD_PLAY, + .id = id, + }; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_pause(int id) { + sndmixer_cmd_t cmd = { + .cmd = CMD_PAUSE, + .id = id, + }; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_stop(int id) { + sndmixer_cmd_t cmd = { + .cmd = CMD_STOP, + .id = id, + }; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_pause_all() { + sndmixer_cmd_t cmd = { + .cmd = CMD_PAUSE_ALL, + }; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_resume_all() { + sndmixer_cmd_t cmd = { + .cmd = CMD_RESUME_ALL, + }; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_freq(int id, uint16_t frequency) { + sndmixer_cmd_t cmd = {.cmd = CMD_FREQ, .id = id, .param = frequency}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_waveform(int id, uint8_t waveform) { + sndmixer_cmd_t cmd = {.cmd = CMD_WAVEFORM, .id = id, .param = waveform}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_set_callback(int id, callback_type callback, void *handle) { + sndmixer_cmd_t cmd = {.id = id, + .cmd = CMD_CALLBACK, + .callback_func = (void *)callback, + .callback_handle = handle}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); + +} + +void sndmixer_beat_sync_start(uint8_t bpm) { + sndmixer_cmd_t cmd = {.cmd = CMD_BEAT_SYNC_START}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_beat_sync_stop() { + sndmixer_cmd_t cmd = {.cmd = CMD_BEAT_SYNC_STOP}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} + +void sndmixer_start_at_next(int id, int start_at_next) { + sndmixer_cmd_t cmd = {.id = id, + .cmd = CMD_START_AT_NEXT, + .param = start_at_next}; + xQueueSend(cmd_queue, &cmd, portMAX_DELAY); +} diff --git a/components/driver_sndmixer/sndmixer.h b/components/driver_sndmixer/sndmixer.h new file mode 100644 index 0000000..d06c3ce --- /dev/null +++ b/components/driver_sndmixer/sndmixer.h @@ -0,0 +1,167 @@ +#pragma once +#include +#include "driver/i2s.h" + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +/** + * @brief Structure describing a sound source + */ +typedef struct { + /*! Initialize the sound source. Returns size of data returned per call of fill_buffer. */ + int (*init_source)(const void *data_start, const void *data_end, int req_sample_rate, void **ctx, + int *stereo, const void *seek_func); + + /*! Get the actual sample rate at which the source returns data */ + int (*get_sample_rate)(void *ctx); + /*! Decode a bufferful of data. Returns 0 when file ended or something went wrong. Returns amount + * of bytes in buffer (normally what init_source returned) otherwise. */ + int (*fill_buffer)(void *ctx, int16_t *buffer, int stereo); + /*! Reset buffer, loop sample */ + int (*reset_buffer)(void *ctx); + /*! Destroy source, free resources */ + void (*deinit_source)(void *ctx); + /*! Set frequency of synthesizer */ + void (*set_frequency)(void *ctx, uint16_t frequency); + /*! Set waveform of synthesizer */ + void (*set_waveform)(void *ctx, uint8_t waveform); +} sndmixer_source_t; + +typedef ssize_t (*stream_read_type)(void *, void *, size_t); +typedef ssize_t (*stream_seek_type)(void *, size_t, size_t); + +/** + * @brief Initialize the sound mixer + * + * @note This function internally calls kchal_sound_start, there is no need to do this in your + * program if you use this function to initialize the sound mixer. + * + * @param no_channels Amount if sounds to be able to be played simultaneously. + */ +int sndmixer_init(int no_channels, int stereo, i2s_pin_config_t* pin_config); + +/** + * @brief Queue the data of a .wav file to be played + * + * This queues a sound to be played. It will not be actually played until sndmixer_play is called. + * + * @param wav_start Start of the wav-file data + * @param wav_end End of the wav-file data + * @param evictable If true, if all audio channels are filled and a new sound is queued, this + * sound can be stopped to make room for the new sound. + * @return The ID of the queued sound, for use with the other functions. + */ +int sndmixer_queue_wav(const void *wav_start, const void *wav_end, int evictable); +int sndmixer_queue_wav_stream(stream_read_type read_func, stream_seek_type seek_func, void *stream); + + +/** + * @brief Queue the data of a .mp3 file to be played + * + * This queues a piece of mp3 music to be played. It will not be actually played until sndmixer_play + * is called. + * + * @param mp3_start Start of the filedata + * @param mp3_end End of the filedata + * @return The ID of the queued sound, for use with the other functions. + */ +int sndmixer_queue_mp3(const void *mp3_start, const void *mp3_end); +int sndmixer_queue_mp3_stream(stream_read_type read_func, stream_seek_type seek_func, void *stream); + +/** + * @brief Set or unset a sound to looping mode + * + * @param id ID of the sound, obtained when queueing it + * @param loop If true, the sound will loop back to the beginning when it ends. + */ +void sndmixer_set_loop(int id, int loop); + +/** + * @brief Set volume of a sound + * + * A queued sound will always start off with a volume setting of 255 (max volume). This call can be + * used to adjust the volume of the sound at any time afterwards. + * + * @param id ID of the sound, obtained when queueing it + * @param volume New volume, between 0 (muted) and 255 (full sound). + */ +void sndmixer_set_volume(int id, int volume); + +/** + * @brief Play a sound + * + * When a sound is queued, it is not playing yet. Use this call to start playback. You can also + * use this call to resume a sound paused by sndmixer_pause or sndmixer_pause_all. + * + * @param id ID of the sound, obtained when queueing it + */ +void sndmixer_play(int id); + +/** + * @brief Pause a sound + * + * Stops playback of the sound. The sound can be resumed with sndmixer_play(). + * + * @param id ID of the sound, obtained when queueing it + */ +void sndmixer_pause(int id); + +/** + * @brief Stop a sound, free the sound source and channel it used. + * + * Stops playback of the sound and frees all associated structures. + * + * @param id ID of the sound, obtained when queueing it + */ +void sndmixer_stop(int id); + +/** + * @brief Pause all playing sounds + * + * This can be used when e.g. the game is paused. Sounds can be individually un-paused afterwards + * and new sounds can still be queued and played, given enough free/evictable channels. + */ +void sndmixer_pause_all(); + +/** + * @brief Resume all paused sounds + * + * This can be used to undo a sndmixer_pause_all() call. + */ +void sndmixer_resume_all(); + +// Basic synthesizer +int sndmixer_queue_synth(); +void sndmixer_freq(int id, uint16_t frequency); +void sndmixer_waveform(int id, uint8_t waveform); + +typedef ssize_t (*callback_type)(void *, size_t, size_t); + +/** + * @brief Set a callback function to execute after the sample has finished + * + * @param id ID of the sound, obtained when queueing it + * @param loop If true, the sound will loop back to the beginning when it ends. + */ +void sndmixer_set_callback(int id, callback_type callback, void *handle); + +/** + * @brief Start beat synchroniser, that can start samples at intervals matched to a given beat. + * @param bpm Beats per minute to configure the beat syncer to + */ +void sndmixer_beat_sync_start(uint8_t bpm); + +/** + * @brief Stop beat synchroniser + */ +void sndmixer_beat_sync_stop(); + +/** + * + * @param id Channel ID + * @param start_at_next Next interval to play sample at. 1 for the next 1/8th note, 2 for 1/4th, 4 for a 1/2 note, + * 8 for the next bar, 16 for the next two bars, or 32 for the next complete set of 4 bars. + */ +void sndmixer_start_at_next(int id, int start_at_next); diff --git a/components/gui-toolkit/CMakeLists.txt b/components/gui-toolkit/CMakeLists.txt index afc5bf2..d6b061e 100644 --- a/components/gui-toolkit/CMakeLists.txt +++ b/components/gui-toolkit/CMakeLists.txt @@ -8,6 +8,5 @@ idf_component_register( "pax-graphics" "pax-codecs" "pax-keyboard" - "spi-ili9341" "troopers24-bsp" ) diff --git a/components/gui-toolkit/graphics_wrapper.c b/components/gui-toolkit/graphics_wrapper.c index fd2194c..b7e8b76 100644 --- a/components/gui-toolkit/graphics_wrapper.c +++ b/components/gui-toolkit/graphics_wrapper.c @@ -35,6 +35,11 @@ void render_message(char* message) { bool keyboard(xQueueHandle buttonQueue, float aPosX, float aPosY, float aWidth, float aHeight, const char* aTitle, const char* aHint, char* aOutput, size_t aOutputSize) { + return keyboard_mode(buttonQueue, aPosX, aPosY, aWidth, aHeight, aTitle, aHint, aOutput, aOutputSize, PKB_LOWERCASE); +} + +bool keyboard_mode(xQueueHandle buttonQueue, float aPosX, float aPosY, float aWidth, float aHeight, const char* aTitle, + const char* aHint, char* aOutput, size_t aOutputSize, pkb_keyboard_t board) { pax_buf_t* pax_buffer = get_pax_buffer(); const pax_font_t* font = pax_font_saira_regular; bool accepted = false; @@ -51,6 +56,7 @@ bool keyboard(xQueueHandle buttonQueue, float aPosX, float aPosY, float aWidth, pax_col_t titleColor = 0xFFFFFFFF; pax_col_t selColor = 0xff007fff; + kb_ctx.board_sel = board; kb_ctx.text_col = borderColor; kb_ctx.sel_text_col = bgColor; kb_ctx.sel_col = selColor; diff --git a/components/gui-toolkit/include/graphics_wrapper.h b/components/gui-toolkit/include/graphics_wrapper.h index 0ec0a84..1831749 100644 --- a/components/gui-toolkit/include/graphics_wrapper.h +++ b/components/gui-toolkit/include/graphics_wrapper.h @@ -8,7 +8,9 @@ #include #include "pax_gfx.h" +#include "pax_keyboard.h" void render_outline(float position_x, float position_y, float width, float height, pax_col_t border_color, pax_col_t background_color); void render_message(char* message); bool keyboard(xQueueHandle button_queue, float aPosX, float aPosY, float aWidth, float aHeight, const char* aTitle, const char* aHint, char* aOutput, size_t aOutputSize); +bool keyboard_mode(xQueueHandle button_queue, float aPosX, float aPosY, float aWidth, float aHeight, const char* aTitle, const char* aHint, char* aOutput, size_t aOutputSize, pkb_keyboard_t board); diff --git a/components/keyboard b/components/keyboard index 8a3ae33..7a502e9 160000 --- a/components/keyboard +++ b/components/keyboard @@ -1 +1 @@ -Subproject commit 8a3ae33cc14326433202a22544119cb0977943cd +Subproject commit 7a502e982c951469274d2133def4f2272a0900fa diff --git a/components/qrcode/CMakeLists.txt b/components/qrcode/CMakeLists.txt new file mode 100644 index 0000000..8d81479 --- /dev/null +++ b/components/qrcode/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "qrcodegen.c" + INCLUDE_DIRS include +) diff --git a/components/qrcode/LICENSE b/components/qrcode/LICENSE new file mode 100644 index 0000000..80273d8 --- /dev/null +++ b/components/qrcode/LICENSE @@ -0,0 +1,20 @@ +QR Code generator library (C) + +Copyright (c) Project Nayuki. (MIT License) +https://www.nayuki.io/page/qr-code-generator-library + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +- The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. +- The Software is provided "as is", 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 + authors or copyright holders 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. \ No newline at end of file diff --git a/components/qrcode/README.md b/components/qrcode/README.md new file mode 100644 index 0000000..0ed342a --- /dev/null +++ b/components/qrcode/README.md @@ -0,0 +1,91 @@ +QR Code generator library +========================= + + +Introduction +------------ + +This project aims to be the best, clearest QR Code generator library in multiple languages. The primary goals are flexible options and absolute correctness. Secondary goals are compact implementation size and good documentation comments. + +Home page with live JavaScript demo, extensive descriptions, and competitor comparisons: [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) + + +Features +-------- + +Core features: + +* Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C +* Significantly shorter code but more documentation comments compared to competing libraries +* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard +* Output format: Raw modules/pixels of the QR symbol +* Detects finder-like penalty patterns more accurately than other implementations +* Encodes numeric and special-alphanumeric text in less space than general text +* Open-source code under the permissive MIT License + +Manual parameters: + +* User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data +* User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one +* User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number +* User can create a list of data segments manually and add ECI segments + +Optional advanced features (Java only): + +* Encodes Japanese Unicode text in kanji mode to save a lot of space compared to UTF-8 bytes +* Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general/kanji parts + +More information about QR Code technology and this library's design can be found on the project home page. + + +Examples +-------- + +The code below is in Java, but the other language ports are designed with essentially the same API naming and behavior. + +```java +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.List; +import javax.imageio.ImageIO; +import io.nayuki.qrcodegen.*; + +// Simple operation +QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); +BufferedImage img = toImage(qr0, 4, 10); // See QrCodeGeneratorDemo +ImageIO.write(img, "png", new File("qr-code.png")); + +// Manual operation +List segs = QrSegment.makeSegments("3141592653589793238462643383"); +QrCode qr1 = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false); +for (int y = 0; y < qr1.size; y++) { + for (int x = 0; x < qr1.size; x++) { + (... paint qr1.getModule(x, y) ...) + } +} +``` + + +License +------- + +Copyright © 2024 Project Nayuki. (MIT License) +[https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +* The Software is provided "as is", 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 + authors or copyright holders 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. \ No newline at end of file diff --git a/components/qrcode/include/qrcodegen.h b/components/qrcode/include/qrcodegen.h new file mode 100644 index 0000000..9f1d236 --- /dev/null +++ b/components/qrcode/include/qrcodegen.h @@ -0,0 +1,386 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", 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 + * authors or copyright holders 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. + */ + +#pragma once + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * This library creates QR Code symbols, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * A QR Code structure is an immutable square grid of dark and light cells. + * The library provides functions to create a QR Code from text or binary data. + * The library covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary(). + * - Low level: Custom-make the list of segments and call + * qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced(). + * (Note that all ways require supplying the desired error correction level and various byte buffers.) + */ + + +/*---- Enum and struct types----*/ + +/* + * The error correction level in a QR Code symbol. + */ +enum qrcodegen_Ecc { + // Must be declared in ascending order of error protection + // so that an internal qrcodegen function works properly + qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords +}; + + +/* + * The mask pattern used in a QR Code symbol. + */ +enum qrcodegen_Mask { + // A special value to tell the QR Code encoder to + // automatically select an appropriate mask pattern + qrcodegen_Mask_AUTO = -1, + // The eight actual mask patterns + qrcodegen_Mask_0 = 0, + qrcodegen_Mask_1, + qrcodegen_Mask_2, + qrcodegen_Mask_3, + qrcodegen_Mask_4, + qrcodegen_Mask_5, + qrcodegen_Mask_6, + qrcodegen_Mask_7, +}; + + +/* + * Describes how a segment's data bits are interpreted. + */ +enum qrcodegen_Mode { + qrcodegen_Mode_NUMERIC = 0x1, + qrcodegen_Mode_ALPHANUMERIC = 0x2, + qrcodegen_Mode_BYTE = 0x4, + qrcodegen_Mode_KANJI = 0x8, + qrcodegen_Mode_ECI = 0x7, +}; + + +/* + * A segment of character/binary/control data in a QR Code symbol. + * The mid-level way to create a segment is to take the payload data + * and call a factory function such as qrcodegen_makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and initialize a qrcodegen_Segment struct with appropriate values. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + * Moreover, the maximum allowed bit length is 32767 because + * the largest QR Code (version 40) has 31329 modules. + */ +struct qrcodegen_Segment { + // The mode indicator of this segment. + enum qrcodegen_Mode mode; + + // The length of this segment's unencoded data. Measured in characters for + // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + // Always zero or positive. Not the same as the data's bit length. + int numChars; + + // The data bits of this segment, packed in bitwise big endian. + // Can be null if the bit length is zero. + uint8_t *data; + + // The number of valid data bits used in the buffer. Requires + // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. + // The character count (numChars) must agree with the mode and the bit buffer length. + int bitLength; +}; + + + +/*---- Macro constants and functions ----*/ + +#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard +#define qrcodegen_VERSION_MAX 18 // The maximum version number supported in the QR Code Model 2 standard +// 18 should be enough for vCards up to 718/560 bytes: https://blog.qr4.nl/page/QR-Code-Data-Capacity.aspx + +// Calculates the number of bytes needed to store any QR Code up to and including the given version number, +// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' +// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16). +// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. +#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) + +// The worst-case number of bytes needed to store one QR Code, up to and including +// version 40. This value equals 3918, which is just under 4 kilobytes. +// Use this more convenient value to avoid calculating tighter memory bounds for buffers. +#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX) + + + +/*---- Functions (high level) to generate QR Codes ----*/ + +/* + * Encodes the given text string to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * The input text must be encoded in UTF-8 and contain no NULs. + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * If successful, the resulting QR Code may use numeric, + * alphanumeric, or byte mode to encode the text. + * + * In the most optimistic case, a QR Code at version 40 with low ECC + * can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string + * up to 4296 characters, or any digit string up to 7089 characters. + * These numbers represent the hard upper limit of the QR Code standard. + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/* + * Encodes the given binary data to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion): + * - Before calling the function: + * - The array ranges dataAndTemp[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The input array range dataAndTemp[0 : dataLen] should normally be + * valid UTF-8 text, but is not required by the QR Code standard. + * - The initial state of dataAndTemp[dataLen : len] and qrcode[0 : len] + * can be uninitialized because the function always writes before reading. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - dataAndTemp contains no useful data and should be treated as entirely uninitialized. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * If successful, the resulting QR Code will use byte mode to encode the data. + * + * In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte + * sequence up to length 2953. This is the hard upper limit of the QR Code standard. + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/*---- Functions (low level) to generate QR Codes ----*/ + +/* + * Encodes the given segments to a QR Code, returning true if successful. + * If the data is too long to fit in any version at the given ECC level, + * then false is returned. + * + * The smallest possible QR Code version is automatically chosen for + * the output. The ECC level of the result may be higher than the + * ecl argument if it can be done without increasing the version. + * + * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - The input array segs can contain segments whose data buffers overlap with tempBuffer. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - Any segment whose data buffer overlaps with tempBuffer[0 : len] + * must be treated as having invalid values in that array. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + * + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + */ +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Encodes the given segments to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - The input array segs can contain segments whose data buffers overlap with tempBuffer. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - Any segment whose data buffer overlaps with tempBuffer[0 : len] + * must be treated as having invalid values in that array. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + * + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + */ +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ +bool qrcodegen_isNumeric(const char *text); + + +/* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +bool qrcodegen_isAlphanumeric(const char *text); + + +/* + * Returns the number of bytes (uint8_t) needed for the data buffer of a segment + * containing the given number of characters using the given mode. Notes: + * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or the internal + * calculation of the number of needed bits exceeds INT16_MAX (i.e. 32767). + * - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096. + * - It is okay for the user to allocate more bytes for the buffer than needed. + * - For byte mode, numChars measures the number of bytes, not Unicode code points. + * - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned. + * An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. + */ +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); + + +/* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte arrays are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); + + +/* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); + + +/* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); + + +/* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); + + +/*---- Functions to extract raw data from QR Codes ----*/ + +/* + * Returns the side length of the given QR Code, assuming that encoding succeeded. + * The result is in the range [21, 177]. Note that the length of the array buffer + * is related to the side length - every 'uint8_t qrcode[]' must have length at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1). + */ +int qrcodegen_getSize(const uint8_t qrcode[]); + + +/* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. + */ +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/qrcode/qrcodegen.c b/components/qrcode/qrcodegen.c new file mode 100644 index 0000000..e06871b --- /dev/null +++ b/components/qrcode/qrcodegen.c @@ -0,0 +1,1030 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", 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 + * authors or copyright holders 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 "include/qrcodegen.h" + +#include +#include +#include + +#include "assert.h" + +#ifndef QRCODEGEN_TEST +#define testable static // Keep functions private +#else +#define testable // Expose private functions +#endif + + +/*---- Forward declarations for private functions ----*/ + +// Regarding all public and private functions defined in this source file: +// - They require all pointer/array arguments to be not null unless the array length is zero. +// - They only read input scalar/array arguments, write to output pointer/array +// arguments, and return scalar values; they are "pure" functions. +// - They don't read mutable global variables or write to any global variables. +// - They don't perform I/O, read the clock, print to console, etc. +// - They allocate a small and constant amount of stack memory. +// - They don't allocate or free any memory on the heap. +// - They don't recurse or mutually recurse. All the code +// could be inlined into the top-level public functions. +// - They run in at most quadratic time with respect to input arguments. +// Most functions run in linear time, and some in constant time. +// There are no unbounded loops or non-obvious termination conditions. +// - They are completely thread-safe if the caller does not give the +// same writable buffer to concurrent calls to these functions. + +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); + +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); +testable int getNumRawDataModules(int ver); + +testable void reedSolomonComputeDivisor(int degree, uint8_t result[]); +testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]); +testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); + +testable void initializeFunctionModules(int version, uint8_t qrcode[]); +static void drawLightFunctionModules(uint8_t qrcode[], int version); +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); +testable int getAlignmentPatternPositions(int version, uint8_t result[7]); +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); + +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); +static long getPenaltyScore(const uint8_t qrcode[]); +static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize); +static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize); +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize); + +testable bool getModuleBounded(const uint8_t qrcode[], int x, int y); +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark); +testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark); +static bool getBit(int x, int i); + +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); +static int numCharCountBits(enum qrcodegen_Mode mode, int version); + + + +/*---- Private tables of constants ----*/ + +// The set of all legal characters in alphanumeric mode, where each character +// value maps to the index in the string. For checking text and encoding segments. +static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +// Sentinel value for use in only some functions. +#define LENGTH_OVERFLOW -1 + +// For generating error correction codes. +testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +#define qrcodegen_REED_SOLOMON_DEGREE_MAX 30 // Based on the table above + +// For generating error correction codes. +testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + +// For automatic mask pattern selection. +static const int PENALTY_N1 = 3; +static const int PENALTY_N2 = 3; +static const int PENALTY_N3 = 40; +static const int PENALTY_N4 = 10; + + + +/*---- High-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + size_t textLen = strlen(text); + if (textLen == 0) + return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + size_t bufLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); + + struct qrcodegen_Segment seg; + if (qrcodegen_isNumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeNumeric(text, tempBuffer); + } else if (qrcodegen_isAlphanumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeAlphanumeric(text, tempBuffer); + } else { + if (textLen > bufLen) + goto fail; + for (size_t i = 0; i < textLen; i++) + tempBuffer[i] = (uint8_t)text[i]; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, textLen); + if (seg.bitLength == LENGTH_OVERFLOW) + goto fail; + seg.numChars = (int)textLen; + seg.data = tempBuffer; + } + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + +fail: + qrcode[0] = 0; // Set size to invalid value for safety + return false; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + struct qrcodegen_Segment seg; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, dataLen); + if (seg.bitLength == LENGTH_OVERFLOW) { + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + seg.numChars = (int)dataLen; + seg.data = dataAndTemp; + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, dataAndTemp, qrcode); +} + + +// Appends the given number of low-order bits of the given value to the given byte-based +// bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits. +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { + assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); + for (int i = numBits - 1; i >= 0; i--, (*bitLen)++) + buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7)); +} + + + +/*---- Low-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { + return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true, tempBuffer, qrcode); +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { + assert(segs != NULL || len == 0); + assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); + assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = getTotalBits(segs, len, version); + if (dataUsedBits != LENGTH_OVERFLOW && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + } + assert(dataUsedBits != LENGTH_OVERFLOW); + + // Increase the error correction level while the data still fits in the current version number + for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) + ecl = (enum qrcodegen_Ecc)i; + } + + // Concatenate all segments to create the data bit string + memset(qrcode, 0, (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); + int bitLen = 0; + for (size_t i = 0; i < len; i++) { + const struct qrcodegen_Segment *seg = &segs[i]; + appendBitsToBuffer((unsigned int)seg->mode, 4, qrcode, &bitLen); + appendBitsToBuffer((unsigned int)seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); + for (int j = 0; j < seg->bitLength; j++) { + int bit = (seg->data[j >> 3] >> (7 - (j & 7))) & 1; + appendBitsToBuffer((unsigned int)bit, 1, qrcode, &bitLen); + } + } + assert(bitLen == dataUsedBits); + + // Add terminator and pad up to a byte if applicable + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + assert(bitLen <= dataCapacityBits); + int terminatorBits = dataCapacityBits - bitLen; + if (terminatorBits > 4) + terminatorBits = 4; + appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); + appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); + assert(bitLen % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBitsToBuffer(padByte, 8, qrcode, &bitLen); + + // Compute ECC, draw modules + addEccAndInterleave(qrcode, version, ecl, tempBuffer); + initializeFunctionModules(version, qrcode); + drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); + drawLightFunctionModules(qrcode, version); + initializeFunctionModules(version, tempBuffer); + + // Do masking + if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; + applyMask(tempBuffer, qrcode, msk); + drawFormatBits(ecl, msk, qrcode); + long penalty = getPenaltyScore(qrcode); + if (penalty < minPenalty) { + mask = msk; + minPenalty = penalty; + } + applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR + } + } + assert(0 <= (int)mask && (int)mask <= 7); + applyMask(tempBuffer, qrcode, mask); // Apply the final choice of mask + drawFormatBits(ecl, mask, qrcode); // Overwrite old format bits + return true; +} + + + +/*---- Error correction code generation functions ----*/ + +// Appends error correction bytes to each block of the given data array, then interleaves +// bytes from the blocks and stores them in the result array. data[0 : dataLen] contains +// the input data. data[dataLen : rawCodewords] is used as a temporary work area and will +// be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { + // Calculate parameter numbers + assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [(int)ecl][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int dataLen = getNumDataCodewords(version, ecl); + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; + + // Split data into blocks, calculate ECC, and interleave + // (not concatenate) the bytes into a single sequence + uint8_t rsdiv[qrcodegen_REED_SOLOMON_DEGREE_MAX]; + reedSolomonComputeDivisor(blockEccLen, rsdiv); + const uint8_t *dat = data; + for (int i = 0; i < numBlocks; i++) { + int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); + uint8_t *ecc = &data[dataLen]; // Temporary storage + reedSolomonComputeRemainder(dat, datLen, rsdiv, blockEccLen, ecc); + for (int j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data + if (j == shortBlockDataLen) + k -= numShortBlocks; + result[k] = dat[j]; + } + for (int j = 0, k = dataLen + i; j < blockEccLen; j++, k += numBlocks) // Copy ECC + result[k] = ecc[j]; + dat += datLen; + } +} + + +// Returns the number of 8-bit codewords that can be used for storing data (not ECC), +// for the given version number and error correction level. The result is in the range [9, 2956]. +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { + int v = version, e = (int)ecl; + assert(0 <= e && e < 4); + return getNumRawDataModules(v) / 8 + - ECC_CODEWORDS_PER_BLOCK [e][v] + * NUM_ERROR_CORRECTION_BLOCKS[e][v]; +} + + +// Returns the number of data bits that can be stored in a QR Code of the given version number, after +// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. +// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. +testable int getNumRawDataModules(int ver) { + assert(qrcodegen_VERSION_MIN <= ver && ver <= qrcodegen_VERSION_MAX); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + assert(208 <= result && result <= 29648); + return result; +} + + + +/*---- Reed-Solomon ECC generator functions ----*/ + +// Computes a Reed-Solomon ECC generator polynomial for the given degree, storing in result[0 : degree]. +// This could be implemented as a lookup table over all possible parameter values, instead of as an algorithm. +testable void reedSolomonComputeDivisor(int degree, uint8_t result[]) { + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + memset(result, 0, (size_t)degree * sizeof(result[0])); + result[degree - 1] = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (int j = 0; j < degree; j++) { + result[j] = reedSolomonMultiply(result[j], root); + if (j + 1 < degree) + result[j] ^= result[j + 1]; + } + root = reedSolomonMultiply(root, 0x02); + } +} + + +// Computes the Reed-Solomon error correction codeword for the given data and divisor polynomials. +// The remainder when data[0 : dataLen] is divided by divisor[0 : degree] is stored in result[0 : degree]. +// All polynomials are in big endian, and the generator has an implicit leading 1 term. +testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]) { + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + memset(result, 0, (size_t)degree * sizeof(result[0])); + for (int i = 0; i < dataLen; i++) { // Polynomial division + uint8_t factor = data[i] ^ result[0]; + memmove(&result[0], &result[1], (size_t)(degree - 1) * sizeof(result[0])); + result[degree - 1] = 0; + for (int j = 0; j < degree; j++) + result[j] ^= reedSolomonMultiply(generator[j], factor); + } +} + +#undef qrcodegen_REED_SOLOMON_DEGREE_MAX + + +// Returns the product of the two given field elements modulo GF(2^8/0x11D). +// All inputs are valid. This could be implemented as a 256*256 lookup table. +testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + uint8_t z = 0; + for (int i = 7; i >= 0; i--) { + z = (uint8_t)((z << 1) ^ ((z >> 7) * 0x11D)); + z ^= ((y >> i) & 1) * x; + } + return z; +} + + + +/*---- Drawing function modules ----*/ + +// Clears the given QR Code grid with light modules for the given +// version's size, then marks every function module as dark. +testable void initializeFunctionModules(int version, uint8_t qrcode[]) { + // Initialize QR Code + int qrsize = version * 4 + 17; + memset(qrcode, 0, (size_t)((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); + qrcode[0] = (uint8_t)qrsize; + + // Fill horizontal and vertical timing patterns + fillRectangle(6, 0, 1, qrsize, qrcode); + fillRectangle(0, 6, qrsize, 1, qrcode); + + // Fill 3 finder patterns (all corners except bottom right) and format bits + fillRectangle(0, 0, 9, 9, qrcode); + fillRectangle(qrsize - 8, 0, 8, 9, qrcode); + fillRectangle(0, qrsize - 8, 9, 8, qrcode); + + // Fill numerous alignment patterns + uint8_t alignPatPos[7]; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); + } + } + + // Fill version blocks + if (version >= 7) { + fillRectangle(qrsize - 11, 0, 3, 6, qrcode); + fillRectangle(0, qrsize - 11, 6, 3, qrcode); + } +} + + +// Draws light function modules and possibly some dark modules onto the given QR Code, without changing +// non-function modules. This does not draw the format bits. This requires all function modules to be previously +// marked dark (namely by initializeFunctionModules()), because this may skip redrawing dark function modules. +static void drawLightFunctionModules(uint8_t qrcode[], int version) { + // Draw horizontal and vertical timing patterns + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 7; i < qrsize - 7; i += 2) { + setModuleBounded(qrcode, 6, i, false); + setModuleBounded(qrcode, i, 6, false); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = abs(dx); + if (abs(dy) > dist) + dist = abs(dy); + if (dist == 2 || dist == 4) { + setModuleUnbounded(qrcode, 3 + dx, 3 + dy, false); + setModuleUnbounded(qrcode, qrsize - 4 + dx, 3 + dy, false); + setModuleUnbounded(qrcode, 3 + dx, qrsize - 4 + dy, false); + } + } + } + + // Draw numerous alignment patterns + uint8_t alignPatPos[7]; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) + continue; // Don't draw on the three finder corners + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) + setModuleBounded(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dx == 0 && dy == 0); + } + } + } + + // Draw version blocks + if (version >= 7) { + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = (long)version << 12 | rem; // uint18 + assert(bits >> 18 == 0); + + // Draw two copies + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 3; j++) { + int k = qrsize - 11 + j; + setModuleBounded(qrcode, k, i, (bits & 1) != 0); + setModuleBounded(qrcode, i, k, (bits & 1) != 0); + bits >>= 1; + } + } + } +} + + +// Draws two copies of the format bits (with its own error correction code) based +// on the given mask and error correction level. This always draws all modules of +// the format bits, unlike drawLightFunctionModules() which might skip dark modules. +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { + // Calculate error correction code and pack bits + assert(0 <= (int)mask && (int)mask <= 7); + static const int table[] = {1, 0, 3, 2}; + int data = table[(int)ecl] << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert(bits >> 15 == 0); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setModuleBounded(qrcode, 8, i, getBit(bits, i)); + setModuleBounded(qrcode, 8, 7, getBit(bits, 6)); + setModuleBounded(qrcode, 8, 8, getBit(bits, 7)); + setModuleBounded(qrcode, 7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setModuleBounded(qrcode, 14 - i, 8, getBit(bits, i)); + + // Draw second copy + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 0; i < 8; i++) + setModuleBounded(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setModuleBounded(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); + setModuleBounded(qrcode, 8, qrsize - 8, true); // Always dark +} + + +// Calculates and stores an ascending list of positions of alignment patterns +// for this version number, returning the length of the list (in the range [0,7]). +// Each position is in the range [0,177), and are used on both the x and y axes. +// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. +testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { + if (version == 1) + return 0; + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; + for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) + result[i] = (uint8_t)pos; + result[0] = 6; + return numAlign; +} + + +// Sets every module in the range [left : left + width] * [top : top + height] to dark. +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { + for (int dy = 0; dy < height; dy++) { + for (int dx = 0; dx < width; dx++) + setModuleBounded(qrcode, left + dx, top + dy, true); + } +} + + + +/*---- Drawing data modules and masking ----*/ + +// Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of +// the QR Code to be dark at function modules and light at codeword modules (including unused remainder bits). +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + int i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < qrsize; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate + if (!getModuleBounded(qrcode, x, y) && i < dataLen * 8) { + bool dark = getBit(data[i >> 3], 7 - (i & 7)); + setModuleBounded(qrcode, x, y, dark); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/light by the constructor and are left unchanged by this method + } + } + } + assert(i == dataLen * 8); +} + + +// XORs the codeword modules in this QR Code with the given mask pattern +// and given pattern of function modules. The codeword bits must be drawn +// before masking. Due to the arithmetic of XOR, calling applyMask() with +// the same mask value a second time will undo the mask. A final well-formed +// QR Code needs exactly one (not zero, two, etc.) mask applied. +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { + assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO + int qrsize = qrcodegen_getSize(qrcode); + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(functionModules, x, y)) + continue; + bool invert; + switch ((int)mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: assert(false); return; + } + bool val = getModuleBounded(qrcode, x, y); + setModuleBounded(qrcode, x, y, val ^ invert); + } + } +} + + +// Calculates and returns the penalty score based on state of the given QR Code's current modules. +// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. +static long getPenaltyScore(const uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < qrsize; y++) { + bool runColor = false; + int runX = 0; + int runHistory[7] = {0}; + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(qrcode, x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory, qrsize); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; + runColor = getModuleBounded(qrcode, x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory, qrsize) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < qrsize; x++) { + bool runColor = false; + int runY = 0; + int runHistory[7] = {0}; + for (int y = 0; y < qrsize; y++) { + if (getModuleBounded(qrcode, x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory, qrsize); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; + runColor = getModuleBounded(qrcode, x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory, qrsize) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < qrsize - 1; y++) { + for (int x = 0; x < qrsize - 1; x++) { + bool color = getModuleBounded(qrcode, x, y); + if ( color == getModuleBounded(qrcode, x + 1, y) && + color == getModuleBounded(qrcode, x, y + 1) && + color == getModuleBounded(qrcode, x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of dark and light modules + int dark = 0; + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(qrcode, x, y)) + dark++; + } + } + int total = qrsize * qrsize; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = (int)((labs(dark * 20L - total * 10L) + total - 1) / total) - 1; + assert(0 <= k && k <= 9); + result += k * PENALTY_N4; + assert(0 <= result && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 + return result; +} + + +// Can only be called immediately after a light run is added, and +// returns either 0, 1, or 2. A helper function for getPenaltyScore(). +static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { + int n = runHistory[1]; + assert(n <= qrsize * 3); (void)qrsize; + bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; + // The maximum QR Code size is 177, hence the dark run length n <= 177. + // Arithmetic is promoted to int, so n*4 will not overflow. + return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); +} + + +// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). +static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) { + if (currentRunColor) { // Terminate dark run + finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); + currentRunLength = 0; + } + currentRunLength += qrsize; // Add light border to final run + finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); + return finderPenaltyCountPatterns(runHistory, qrsize); +} + + +// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize) { + if (runHistory[0] == 0) + currentRunLength += qrsize; // Add light border to initial run + memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0])); + runHistory[0] = currentRunLength; +} + + + +/*---- Basic QR Code information ----*/ + +// Public function - see documentation comment in header file. +int qrcodegen_getSize(const uint8_t qrcode[]) { + assert(qrcode != NULL); + int result = qrcode[0]; + assert((qrcodegen_VERSION_MIN * 4 + 17) <= result + && result <= (qrcodegen_VERSION_MAX * 4 + 17)); + return result; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y) { + assert(qrcode != NULL); + int qrsize = qrcode[0]; + return (0 <= x && x < qrsize && 0 <= y && y < qrsize) && getModuleBounded(qrcode, x, y); +} + + +// Returns the color of the module at the given coordinates, which must be in bounds. +testable bool getModuleBounded(const uint8_t qrcode[], int x, int y) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + return getBit(qrcode[(index >> 3) + 1], index & 7); +} + + +// Sets the color of the module at the given coordinates, which must be in bounds. +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + int bitIndex = index & 7; + int byteIndex = (index >> 3) + 1; + if (isDark) + qrcode[byteIndex] |= 1 << bitIndex; + else + qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; +} + + +// Sets the color of the module at the given coordinates, doing nothing if out of bounds. +testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark) { + int qrsize = qrcode[0]; + if (0 <= x && x < qrsize && 0 <= y && y < qrsize) + setModuleBounded(qrcode, x, y, isDark); +} + + +// Returns true iff the i'th bit of x is set to 1. Requires x >= 0 and 0 <= i <= 14. +static bool getBit(int x, int i) { + return ((x >> i) & 1) != 0; +} + + + +/*---- Segment handling ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_isNumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (*text < '0' || *text > '9') + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_isAlphanumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars) { + int temp = calcSegmentBitLength(mode, numChars); + if (temp == LENGTH_OVERFLOW) + return SIZE_MAX; + assert(0 <= temp && temp <= INT16_MAX); + return ((size_t)temp + 7) / 8; +} + + +// Returns the number of data bits needed to represent a segment +// containing the given number of characters using the given mode. Notes: +// - Returns LENGTH_OVERFLOW on failure, i.e. numChars > INT16_MAX +// or the number of needed bits exceeds INT16_MAX (i.e. 32767). +// - Otherwise, all valid results are in the range [0, INT16_MAX]. +// - For byte mode, numChars measures the number of bytes, not Unicode code points. +// - For ECI mode, numChars must be 0, and the worst-case number of bits is returned. +// An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { + // All calculations are designed to avoid overflow on all platforms + if (numChars > (unsigned int)INT16_MAX) + return LENGTH_OVERFLOW; + long result = (long)numChars; + if (mode == qrcodegen_Mode_NUMERIC) + result = (result * 10 + 2) / 3; // ceil(10/3 * n) + else if (mode == qrcodegen_Mode_ALPHANUMERIC) + result = (result * 11 + 1) / 2; // ceil(11/2 * n) + else if (mode == qrcodegen_Mode_BYTE) + result *= 8; + else if (mode == qrcodegen_Mode_KANJI) + result *= 13; + else if (mode == qrcodegen_Mode_ECI && numChars == 0) + result = 3 * 8; + else { // Invalid argument + assert(false); + return LENGTH_OVERFLOW; + } + assert(result >= 0); + if (result > INT16_MAX) + return LENGTH_OVERFLOW; + return (int)result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]) { + assert(data != NULL || len == 0); + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_BYTE; + result.bitLength = calcSegmentBitLength(result.mode, len); + assert(result.bitLength != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (len > 0) + memcpy(buf, data, len * sizeof(buf[0])); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) { + assert(digits != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(digits); + result.mode = qrcodegen_Mode_NUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *digits != '\0'; digits++) { + char c = *digits; + assert('0' <= c && c <= '9'); + accumData = accumData * 10 + (unsigned int)(c - '0'); + accumCount++; + if (accumCount == 3) { + appendBitsToBuffer(accumData, 10, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) { + assert(text != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(text); + result.mode = qrcodegen_Mode_ALPHANUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *text != '\0'; text++) { + const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); + assert(temp != NULL); + accumData = accumData * 45 + (unsigned int)(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + appendBitsToBuffer(accumData, 11, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + appendBitsToBuffer(accumData, 6, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_ECI; + result.numChars = 0; + result.bitLength = 0; + if (assignVal < 0) + assert(false); + else if (assignVal < (1 << 7)) { + memset(buf, 0, 1 * sizeof(buf[0])); + appendBitsToBuffer((unsigned int)assignVal, 8, buf, &result.bitLength); + } else if (assignVal < (1 << 14)) { + memset(buf, 0, 2 * sizeof(buf[0])); + appendBitsToBuffer(2, 2, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)assignVal, 14, buf, &result.bitLength); + } else if (assignVal < 1000000L) { + memset(buf, 0, 3 * sizeof(buf[0])); + appendBitsToBuffer(6, 3, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)(assignVal >> 10), 11, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)(assignVal & 0x3FF), 10, buf, &result.bitLength); + } else + assert(false); + result.data = buf; + return result; +} + + +// Calculates the number of bits needed to encode the given segments at the given version. +// Returns a non-negative number if successful. Otherwise returns LENGTH_OVERFLOW if a segment +// has too many characters to fit its length field, or the total bits exceeds INT16_MAX. +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { + assert(segs != NULL || len == 0); + long result = 0; + for (size_t i = 0; i < len; i++) { + int numChars = segs[i].numChars; + int bitLength = segs[i].bitLength; + assert(0 <= numChars && numChars <= INT16_MAX); + assert(0 <= bitLength && bitLength <= INT16_MAX); + int ccbits = numCharCountBits(segs[i].mode, version); + assert(0 <= ccbits && ccbits <= 16); + if (numChars >= (1L << ccbits)) + return LENGTH_OVERFLOW; // The segment's length doesn't fit the field's bit width + result += 4L + ccbits + bitLength; + if (result > INT16_MAX) + return LENGTH_OVERFLOW; // The sum might overflow an int type + } + assert(0 <= result && result <= INT16_MAX); + return (int)result; +} + + +// Returns the bit width of the character count field for a segment in the given mode +// in a QR Code at the given version number. The result is in the range [0, 16]. +static int numCharCountBits(enum qrcodegen_Mode mode, int version) { + assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int i = (version + 7) / 17; + switch (mode) { + case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } + case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; } + case qrcodegen_Mode_BYTE : { static const int temp[] = { 8, 16, 16}; return temp[i]; } + case qrcodegen_Mode_KANJI : { static const int temp[] = { 8, 10, 12}; return temp[i]; } + case qrcodegen_Mode_ECI : return 0; + default: assert(false); return -1; // Dummy value + } +} + + +#undef LENGTH_OVERFLOW \ No newline at end of file diff --git a/components/spi-ili9341 b/components/spi-ili9341 deleted file mode 160000 index 642dcf8..0000000 --- a/components/spi-ili9341 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 642dcf8ef391cff3b2375256f616c96ab4a5e2d4 diff --git a/components/spi-st25r3911b/CMakeLists.txt b/components/spi-st25r3911b/CMakeLists.txt index af55a97..de2e57e 100644 --- a/components/spi-st25r3911b/CMakeLists.txt +++ b/components/spi-st25r3911b/CMakeLists.txt @@ -24,13 +24,22 @@ idf_component_register( "en.STSW-ST25RFAL001/source/st25r3911/st25r3911.c" "en.STSW-ST25RFAL001/source/st25r3911/st25r3911_com.c" "en.STSW-ST25RFAL001/source/st25r3911/st25r3911_interrupt.c" - "NDEF/source" + "NDEF/source/poller/ndef_poller.c" + "NDEF/source/poller/ndef_poller_rf.c" + "NDEF/source/poller/ndef_poller_message.c" + "NDEF/source/poller/ndef_t2t.c" + "NDEF/source/poller/ndef_t3t.c" + "NDEF/source/poller/ndef_t4t.c" + "NDEF/source/poller/ndef_t5t.c" + "NDEF/source/poller/ndef_t5t_rf.c" INCLUDE_DIRS "include" "en.STSW-ST25RFAL001" "en.STSW-ST25RFAL001/include" + "en.STSW-ST25RFAL001/source" "en.STSW-ST25RFAL001/source/st25r3911" "NDEF/include" "NDEF/include/message" "NDEF/include/poller" + "st25common" ) diff --git a/components/spi-st25r3911b/NDEF/include/ndef_config.h b/components/spi-st25r3911b/NDEF/include/ndef_config.h index 4b94274..b0e7f92 100644 --- a/components/spi-st25r3911b/NDEF/include/ndef_config.h +++ b/components/spi-st25r3911b/NDEF/include/ndef_config.h @@ -142,21 +142,21 @@ #define NDEF_FEATURE_T5T RFAL_FEATURE_NFCV /*!< T5T Support control */ -#define NDEF_FEATURE_FULL_API true /*!< Support Write, Format, Check Presence, set Read-only in addition to the Read feature */ +#define NDEF_FEATURE_FULL_API false /*!< Support Write, Format, Check Presence, set Read-only in addition to the Read feature */ -#define NDEF_TYPE_EMPTY_SUPPORT true /*!< Support Empty type */ -#define NDEF_TYPE_FLAT_SUPPORT true /*!< Support Flat type */ -#define NDEF_TYPE_RTD_DEVICE_INFO_SUPPORT true /*!< Support RTD Device Information type */ +#define NDEF_TYPE_EMPTY_SUPPORT false /*!< Support Empty type */ +#define NDEF_TYPE_FLAT_SUPPORT false /*!< Support Flat type */ +#define NDEF_TYPE_RTD_DEVICE_INFO_SUPPORT false /*!< Support RTD Device Information type */ #define NDEF_TYPE_RTD_TEXT_SUPPORT true /*!< Support RTD Text type */ -#define NDEF_TYPE_RTD_URI_SUPPORT true /*!< Support RTD URI type */ -#define NDEF_TYPE_RTD_AAR_SUPPORT true /*!< Support RTD Android Application Record type */ -#define NDEF_TYPE_RTD_WLC_SUPPORT true /*!< Support RTD WLC Types */ -#define NDEF_TYPE_RTD_WPCWLC_SUPPORT true /*!< Support RTD WPC WLC type */ -#define NDEF_TYPE_RTD_TNEP_SUPPORT true /*!< Support RTD TNEP Types */ -#define NDEF_TYPE_MEDIA_SUPPORT true /*!< Support Media type */ -#define NDEF_TYPE_BLUETOOTH_SUPPORT true /*!< Support Bluetooth types */ +#define NDEF_TYPE_RTD_URI_SUPPORT false /*!< Support RTD URI type */ +#define NDEF_TYPE_RTD_AAR_SUPPORT false /*!< Support RTD Android Application Record type */ +#define NDEF_TYPE_RTD_WLC_SUPPORT false /*!< Support RTD WLC Types */ +#define NDEF_TYPE_RTD_WPCWLC_SUPPORT false /*!< Support RTD WPC WLC type */ +#define NDEF_TYPE_RTD_TNEP_SUPPORT false /*!< Support RTD TNEP Types */ +#define NDEF_TYPE_MEDIA_SUPPORT false /*!< Support Media type */ +#define NDEF_TYPE_BLUETOOTH_SUPPORT false /*!< Support Bluetooth types */ #define NDEF_TYPE_VCARD_SUPPORT true /*!< Support vCard type */ -#define NDEF_TYPE_WIFI_SUPPORT true /*!< Support Wifi type */ +#define NDEF_TYPE_WIFI_SUPPORT false /*!< Support Wifi type */ #endif /* NDEF_CONFIG_CUSTOM */ diff --git a/components/spi-st25r3911b/en.STSW-ST25RFAL001/rfal_platform.h b/components/spi-st25r3911b/en.STSW-ST25RFAL001/rfal_platform.h index 8dde088..95e7332 100644 --- a/components/spi-st25r3911b/en.STSW-ST25RFAL001/rfal_platform.h +++ b/components/spi-st25r3911b/en.STSW-ST25RFAL001/rfal_platform.h @@ -75,7 +75,7 @@ #define platformTimerDestroy( timer ) /*!< Stop and release the given timer */ #define platformDelay( t ) vTaskDelay( pdMS_TO_TICKS(t) ) /*!< Performs a delay for the given time (ms) */ -#define platformGetSysTick() /*!< Get System Tick ( 1 tick = 1 ms) */ +#define platformGetSysTick() xTaskGetTickCount() /*!< Get System Tick ( 1 tick = 1 ms) */ //#define platformErrorHandle() _Error_Handler(__FILE__,__LINE__) /*!< Global error handler or trap */ // @@ -83,28 +83,28 @@ #define platformSpiDeselect() gpio_set_level(global_st25r3911b->pin_cs, true) /*!< SPI SS\CS: Chip|Slave Deselect */ #define platformSpiTxRx( txBuf, rxBuf, len ) st25r3911b_rxtx(global_st25r3911b, txBuf, rxBuf, len) /*!< SPI transceive */ // -//#define platformLog(...) ESP_LOGI("nfc", __VA_ARGS__) /*!< Log method */ +#define platformLog(...) ESP_LOGI("nfc", __VA_ARGS__) /*!< Log method */ //extern uint8_t globalCommProtectCnt; /* Global Protection Counter provided per platform - instantiated in main.c */ -#define RFAL_FEATURE_LISTEN_MODE false /*!< Enable/Disable RFAL support for Listen Mode */ +#define RFAL_FEATURE_LISTEN_MODE true /*!< Enable/Disable RFAL support for Listen Mode */ #define RFAL_FEATURE_WAKEUP_MODE true /*!< Enable/Disable RFAL support for the Wake-Up mode */ #define RFAL_FEATURE_LOWPOWER_MODE false /*!< Enable/Disable RFAL support for the Low Power mode */ #define RFAL_FEATURE_NFCA true /*!< Enable/Disable RFAL support for NFC-A (ISO14443A) */ -#define RFAL_FEATURE_NFCB false /*!< Enable/Disable RFAL support for NFC-B (ISO14443B) */ -#define RFAL_FEATURE_NFCF false /*!< Enable/Disable RFAL support for NFC-F (FeliCa) */ -#define RFAL_FEATURE_NFCV false /*!< Enable/Disable RFAL support for NFC-V (ISO15693) */ -#define RFAL_FEATURE_T1T false /*!< Enable/Disable RFAL support for T1T (Topaz) */ -#define RFAL_FEATURE_T2T false /*!< Enable/Disable RFAL support for T2T */ -#define RFAL_FEATURE_T4T false /*!< Enable/Disable RFAL support for T4T */ +#define RFAL_FEATURE_NFCB true /*!< Enable/Disable RFAL support for NFC-B (ISO14443B) */ +#define RFAL_FEATURE_NFCF true /*!< Enable/Disable RFAL support for NFC-F (FeliCa) */ +#define RFAL_FEATURE_NFCV true /*!< Enable/Disable RFAL support for NFC-V (ISO15693) */ +#define RFAL_FEATURE_T1T true /*!< Enable/Disable RFAL support for T1T (Topaz) */ +#define RFAL_FEATURE_T2T true /*!< Enable/Disable RFAL support for T2T */ +#define RFAL_FEATURE_T4T true /*!< Enable/Disable RFAL support for T4T */ #define RFAL_FEATURE_ST25TB false /*!< Enable/Disable RFAL support for ST25TB */ #define RFAL_FEATURE_ST25xV false /*!< Enable/Disable RFAL support for ST25TV/ST25DV */ #define RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG false /*!< Enable/Disable Analog Configs to be dynamically updated (RAM) */ #define RFAL_FEATURE_DPO false /*!< Enable/Disable RFAL Dynamic Power Output support */ #define RFAL_FEATURE_ISO_DEP true /*!< Enable/Disable RFAL support for ISO-DEP (ISO14443-4) */ #define RFAL_FEATURE_ISO_DEP_POLL true /*!< Enable/Disable RFAL support for Poller mode (PCD) ISO-DEP (ISO14443-4) */ -#define RFAL_FEATURE_ISO_DEP_LISTEN false /*!< Enable/Disable RFAL support for Listen mode (PICC) ISO-DEP (ISO14443-4) */ +#define RFAL_FEATURE_ISO_DEP_LISTEN true /*!< Enable/Disable RFAL support for Listen mode (PICC) ISO-DEP (ISO14443-4) */ #define RFAL_FEATURE_NFC_DEP true /*!< Enable/Disable RFAL support for NFC-DEP (NFCIP1/P2P) */ #define RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN 256U /*!< ISO-DEP I-Block max length. Please use values as defined by rfalIsoDepFSx */ diff --git a/components/spi-st25r3911b/en.STSW-ST25RFAL001/source/st25r3911/rfal_features.h b/components/spi-st25r3911b/en.STSW-ST25RFAL001/source/st25r3911/rfal_features.h index e3d343a..88a737a 100644 --- a/components/spi-st25r3911b/en.STSW-ST25RFAL001/source/st25r3911/rfal_features.h +++ b/components/spi-st25r3911b/en.STSW-ST25RFAL001/source/st25r3911/rfal_features.h @@ -46,14 +46,14 @@ ****************************************************************************** */ -#define RFAL_SUPPORT_MODE_POLL_NFCA true /*!< RFAL Poll NFCA mode support switch */ -#define RFAL_SUPPORT_MODE_POLL_NFCB true /*!< RFAL Poll NFCB mode support switch */ -#define RFAL_SUPPORT_MODE_POLL_NFCF true /*!< RFAL Poll NFCF mode support switch */ -#define RFAL_SUPPORT_MODE_POLL_NFCV true /*!< RFAL Poll NFCV mode support switch */ +#define RFAL_SUPPORT_MODE_POLL_NFCA true /*!< RFAL Poll NFCA mode support switch */ +#define RFAL_SUPPORT_MODE_POLL_NFCB false /*!< RFAL Poll NFCB mode support switch */ +#define RFAL_SUPPORT_MODE_POLL_NFCF false /*!< RFAL Poll NFCF mode support switch */ +#define RFAL_SUPPORT_MODE_POLL_NFCV false /*!< RFAL Poll NFCV mode support switch */ #define RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P true /*!< RFAL Poll AP2P mode support switch */ -#define RFAL_SUPPORT_MODE_LISTEN_NFCA false /*!< RFAL Listen NFCA mode support switch */ -#define RFAL_SUPPORT_MODE_LISTEN_NFCB false /*!< RFAL Listen NFCB mode support switch */ -#define RFAL_SUPPORT_MODE_LISTEN_NFCF false /*!< RFAL Listen NFCF mode support switch */ +#define RFAL_SUPPORT_MODE_LISTEN_NFCA false /*!< RFAL Listen NFCA mode support switch */ +#define RFAL_SUPPORT_MODE_LISTEN_NFCB false /*!< RFAL Listen NFCB mode support switch */ +#define RFAL_SUPPORT_MODE_LISTEN_NFCF false /*!< RFAL Listen NFCF mode support switch */ #define RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2P true /*!< RFAL Listen AP2P mode support switch */ /*******************************************************************************/ diff --git a/components/spi-st25r3911b/include/st25r3911b.h b/components/spi-st25r3911b/include/st25r3911b.h index 731ffc6..f4850fb 100644 --- a/components/spi-st25r3911b/include/st25r3911b.h +++ b/components/spi-st25r3911b/include/st25r3911b.h @@ -11,9 +11,19 @@ #define ST25R3911 #define ST25R_SELFTEST +#define MAX_NFC_BUFFER_SIZE 540 + #include "rfal_defConfig.h" #include "rfal_platform.h" #include "rfal_nfc.h" +#include "rfal_rf.h" + +#define LWIP_ERR_TIMEOUT ERR_TIMEOUT +#undef ERR_TIMEOUT +#include "ndef_poller.h" +#undef ERR_TIMEOUT +#define ERR_TIMEOUT LWIP_ERR_TIMEOUT +#undef LWIP_ERR_TIMEOUT typedef struct ST25R3911B { int spi_bus; @@ -28,8 +38,23 @@ typedef struct ST25R3911B { void (*irq_callback)(void); } ST25R3911B; +typedef enum { + DISCOVER_MODE_LISTEN_NFCA, + DISCOVER_MODE_P2P_PASSIVE, + DISCOVER_MODE_P2P_ACTIVE, +} st25r3911b_discover_mode; + +typedef esp_err_t (*NfcDeviceCallback)(rfalNfcDevice *nfcDevice); + esp_err_t st25r3911b_init(ST25R3911B * device); esp_err_t st25r3911b_chip_id(uint8_t *id); -esp_err_t st25r3911b_discover(rfalNfcDevice *nfcDevice, uint32_t timeout_ms); +esp_err_t st25r3911b_discover(NfcDeviceCallback callback, uint32_t timeout_ms, st25r3911b_discover_mode discover_mode); +esp_err_t st25r3911b_p2p_transceiveBlocking(uint32_t timeout_ms, uint8_t *txBuf, uint16_t txBufSize, uint8_t **rxData, uint16_t **rcvLen, uint32_t fwt); +esp_err_t st25r3911b_p2p_transmitBlocking(uint32_t timeout_ms, uint8_t *txBuf, uint16_t txBufSize); +esp_err_t st25r3911b_p2p_receiveBlocking(uint32_t timeout_ms, uint8_t **rxData, uint16_t **rcvLen); +esp_err_t st25r3911b_read_data(rfalNfcDevice *nfcDevice, ndefConstBuffer* bufConstRawMessage); +esp_err_t st25r3911b_poll_active_p2p(uint32_t timeout_ms); +esp_err_t st25r3911b_listen_p2p(uint32_t timeout_ms); + esp_err_t st25r3911b_rxtx(ST25R3911B *device, const uint8_t* tx, const uint8_t* rx, uint8_t length); diff --git a/components/spi-st25r3911b/st25common/st_errno.h b/components/spi-st25r3911b/st25common/st_errno.h new file mode 100644 index 0000000..a74a5a1 --- /dev/null +++ b/components/spi-st25r3911b/st25common/st_errno.h @@ -0,0 +1,164 @@ +/****************************************************************************** + * @attention + * + * COPYRIGHT 2016 STMicroelectronics, all rights reserved + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. + * See the License for the specific language governing permissions and + * limitations under the License. + * +******************************************************************************/ + + + +/* + * PROJECT: STxxxx firmware + * LANGUAGE: ISO C99 + */ + +/*! \file + * + * \author + * + * \brief Main error codes + * + */ + +#ifndef ST_ERRNO_H +#define ST_ERRNO_H + +/* +****************************************************************************** +* INCLUDES +****************************************************************************** +*/ +#include + +/* +****************************************************************************** +* GLOBAL DATA TYPES +****************************************************************************** +*/ + +typedef uint16_t stError; /*!< ST error type */ + +/* +****************************************************************************** +* DEFINES +****************************************************************************** +*/ + + +/* + * Error codes to be used within the application. + * They are represented by an uint8_t + */ + +#define ERR_NONE ((stError)0U) /*!< no error occurred */ +#define ERR_NOMEM ((stError)1U) /*!< not enough memory to perform the requested operation */ +#define ERR_BUSY ((stError)2U) /*!< device or resource busy */ +#define ERR_IO ((stError)3U) /*!< generic IO error */ +#define ERR_TIMEOUT ((stError)4U) /*!< error due to timeout */ +#define ERR_REQUEST ((stError)5U) /*!< invalid request or requested function can't be executed at the moment */ +#define ERR_NOMSG ((stError)6U) /*!< No message of desired type */ +#define ERR_PARAM ((stError)7U) /*!< Parameter error */ +#define ERR_SYSTEM ((stError)8U) /*!< System error */ +#define ERR_FRAMING ((stError)9U) /*!< Framing error */ +#define ERR_OVERRUN ((stError)10U) /*!< lost one or more received bytes */ +#define ERR_PROTO ((stError)11U) /*!< protocol error */ +#define ERR_INTERNAL ((stError)12U) /*!< Internal Error */ +#define ERR_AGAIN ((stError)13U) /*!< Call again */ +#define ERR_MEM_CORRUPT ((stError)14U) /*!< memory corruption */ +#define ERR_NOT_IMPLEMENTED ((stError)15U) /*!< not implemented */ +#define ERR_PC_CORRUPT ((stError)16U) /*!< Program Counter has been manipulated or spike/noise trigger illegal operation */ +#define ERR_SEND ((stError)17U) /*!< error sending*/ +#define ERR_IGNORE ((stError)18U) /*!< indicates error detected but to be ignored */ +#define ERR_SEMANTIC ((stError)19U) /*!< indicates error in state machine (unexpected cmd) */ +#define ERR_SYNTAX ((stError)20U) /*!< indicates error in state machine (unknown cmd) */ +#define ERR_CRC ((stError)21U) /*!< crc error */ +#define ERR_NOTFOUND ((stError)22U) /*!< transponder not found */ +#define ERR_NOTUNIQUE ((stError)23U) /*!< transponder not unique - more than one transponder in field */ +#define ERR_NOTSUPP ((stError)24U) /*!< requested operation not supported */ +#define ERR_WRITE ((stError)25U) /*!< write error */ +#define ERR_FIFO ((stError)26U) /*!< fifo over or underflow error */ +#define ERR_PAR ((stError)27U) /*!< parity error */ +#define ERR_DONE ((stError)28U) /*!< transfer has already finished */ +#define ERR_RF_COLLISION ((stError)29U) /*!< collision error (Bit Collision or during RF Collision avoidance ) */ +#define ERR_HW_OVERRUN ((stError)30U) /*!< lost one or more received bytes */ +#define ERR_RELEASE_REQ ((stError)31U) /*!< device requested release */ +#define ERR_SLEEP_REQ ((stError)32U) /*!< device requested sleep */ +#define ERR_WRONG_STATE ((stError)33U) /*!< incorrent state for requested operation */ +#define ERR_MAX_RERUNS ((stError)34U) /*!< blocking procedure reached maximum runs */ +#define ERR_DISABLED ((stError)35U) /*!< operation aborted due to disabled configuration */ +#define ERR_HW_MISMATCH ((stError)36U) /*!< expected hw do not match */ +#define ERR_LINK_LOSS ((stError)37U) /*!< Other device's field didn't behave as expected: turned off by Initiator in Passive mode, or AP2P did not turn on field */ +#define ERR_INVALID_HANDLE ((stError)38U) /*!< invalid or not initialized device handle */ + +#define ERR_INCOMPLETE_BYTE ((stError)40U) /*!< Incomplete byte rcvd */ +#define ERR_INCOMPLETE_BYTE_01 ((stError)41U) /*!< Incomplete byte rcvd - 1 bit */ +#define ERR_INCOMPLETE_BYTE_02 ((stError)42U) /*!< Incomplete byte rcvd - 2 bit */ +#define ERR_INCOMPLETE_BYTE_03 ((stError)43U) /*!< Incomplete byte rcvd - 3 bit */ +#define ERR_INCOMPLETE_BYTE_04 ((stError)44U) /*!< Incomplete byte rcvd - 4 bit */ +#define ERR_INCOMPLETE_BYTE_05 ((stError)45U) /*!< Incomplete byte rcvd - 5 bit */ +#define ERR_INCOMPLETE_BYTE_06 ((stError)46U) /*!< Incomplete byte rcvd - 6 bit */ +#define ERR_INCOMPLETE_BYTE_07 ((stError)47U) /*!< Incomplete byte rcvd - 7 bit */ + +/* General Sub-category number */ +#define ERR_GENERIC_GRP (0x0000) /*!< Reserved value for generic error no */ +#define ERR_WARN_GRP (0x0100) /*!< Errors which are not expected in normal operation */ +#define ERR_PROCESS_GRP (0x0200) /*!< Processes management errors */ +#define ERR_SIO_GRP (0x0800) /*!< SIO errors due to logging */ +#define ERR_RINGBUF_GRP (0x0900) /*!< Ring Buffer errors */ +#define ERR_MQ_GRP (0x0A00) /*!< MQ errors */ +#define ERR_TIMER_GRP (0x0B00) /*!< Timer errors */ +#define ERR_RFAL_GRP (0x0C00) /*!< RFAL errors */ +#define ERR_UART_GRP (0x0D00) /*!< UART errors */ +#define ERR_SPI_GRP (0x0E00) /*!< SPI errors */ +#define ERR_I2C_GRP (0x0F00) /*!< I2c errors */ + + +#define ERR_INSERT_SIO_GRP(x) (ERR_SIO_GRP | (x)) /*!< Insert the SIO grp */ +#define ERR_INSERT_RINGBUF_GRP(x) (ERR_RINGBUF_GRP | (x)) /*!< Insert the Ring Buffer grp */ +#define ERR_INSERT_RFAL_GRP(x) (ERR_RFAL_GRP | (x)) /*!< Insert the RFAL grp */ +#define ERR_INSERT_SPI_GRP(x) (ERR_SPI_GRP | (x)) /*!< Insert the spi grp */ +#define ERR_INSERT_I2C_GRP(x) (ERR_I2C_GRP | (x)) /*!< Insert the i2c grp */ +#define ERR_INSERT_UART_GRP(x) (ERR_UART_GRP | (x)) /*!< Insert the uart grp */ +#define ERR_INSERT_TIMER_GRP(x) (ERR_TIMER_GRP | (x)) /*!< Insert the timer grp */ +#define ERR_INSERT_MQ_GRP(x) (ERR_MQ_GRP | (x)) /*!< Insert the mq grp */ +#define ERR_INSERT_PROCESS_GRP(x) (ERR_PROCESS_GRP | (x)) /*!< Insert the process grp */ +#define ERR_INSERT_WARN_GRP(x) (ERR_WARN_GRP | (x)) /*!< Insert the i2c grp */ +#define ERR_INSERT_GENERIC_GRP(x) (ERR_GENERIC_GRP | (x)) /*!< Insert the generic grp */ + + +/* +****************************************************************************** +* GLOBAL MACROS +****************************************************************************** +*/ + +#define ERR_NO_MASK(x) ((uint16_t)(x) & 0x00FFU) /*!< Mask the error number */ + + + +/*! Common code to exit a function with the error if function f return error */ +#define EXIT_ON_ERR(r, f) \ + (r) = (f); \ + if (ERR_NONE != (r)) \ + { \ + return (r); \ + } + + +/*! Common code to exit a function if process/function f has not concluded */ +#define EXIT_ON_BUSY(r, f) \ + (r) = (f); \ + if (ERR_BUSY == (r)) \ + { \ + return (r); \ + } +#endif /* ST_ERRNO_H */ + diff --git a/components/spi-st25r3911b/st25common/utils.h b/components/spi-st25r3911b/st25common/utils.h new file mode 100644 index 0000000..8f451e7 --- /dev/null +++ b/components/spi-st25r3911b/st25common/utils.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * @attention + * + * COPYRIGHT 2016 STMicroelectronics, all rights reserved + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. + * See the License for the specific language governing permissions and + * limitations under the License. + * +******************************************************************************/ + + + +/* + * PROJECT: NFCC firmware + * $Revision: $ + * LANGUAGE: ISO C99 + */ + +/*! \file + * + * \author Ulrich Herrmann + * + * \brief Common and helpful macros + * + */ + +#ifndef UTILS_H +#define UTILS_H + +/* +****************************************************************************** +* INCLUDES +****************************************************************************** +*/ +#include + +/* +****************************************************************************** +* GLOBAL MACROS +****************************************************************************** +*/ +/*! + * this macro evaluates an error variable \a ERR against an error code \a EC. + * in case it is not equal it jumps to the given label \a LABEL. + */ +#define EVAL_ERR_NE_GOTO(EC, ERR, LABEL) \ + if ((EC) != (ERR)) goto LABEL; + +/*! + * this macro evaluates an error variable \a ERR against an error code \a EC. + * in case it is equal it jumps to the given label \a LABEL. + */ +#define EVAL_ERR_EQ_GOTO(EC, ERR, LABEL) \ + if ((EC) == (ERR)) goto LABEL; + +#define SIZEOF_ARRAY(a) (sizeof(a) / sizeof((a)[0])) /*!< Compute the size of an array */ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) /*!< Return the maximum of the 2 values */ +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) /*!< Return the minimum of the 2 values */ +#define BITMASK_1 (0x01) /*!< Bit mask for lsb bit */ +#define BITMASK_2 (0x03) /*!< Bit mask for two lsb bits */ +#define BITMASK_3 (0x07) /*!< Bit mask for three lsb bits */ +#define BITMASK_4 (0x0F) /*!< Bit mask for four lsb bits */ +#define U16TOU8(a) ((a) & 0x00FF) /*!< Cast 16-bit unsigned to 8-bit unsigned */ +#define GETU16(a) (((uint16_t)(a)[0] << 8) | (uint16_t)(a)[1]) /*!< Cast two Big Endian 8-bits byte array to 16-bits unsigned */ +#define GETU32(a) (((uint32_t)(a)[0] << 24) | ((uint32_t)(a)[1] << 16) | ((uint32_t)(a)[2] << 8) | ((uint32_t)(a)[3])) /*!< Cast four Big Endian 8-bit byte array to 32-bit unsigned */ +#define WORD2ARRAY(w, a) (memcpy((a), &(w), sizeof(uint16_t))) /*!< Copy 16-bit unsigned to array */ +#define DWORD2ARRAY(dw, a) (memcpy((a), &(dw), sizeof(uint32_t))) /*!< Copy 32-bit unsigned to array */ + +#define REVERSE_BYTES(pData, nDataSize) \ + {unsigned char swap, *lo = ((unsigned char *)(pData)), *hi = ((unsigned char *)(pData)) + (nDataSize) - 1; \ + while (lo < hi) { swap = *lo; *lo++ = *hi; *hi-- = swap; }} + +#ifdef __CSMC__ +/* STM8 COSMIC */ +#define ST_MEMMOVE(s1,s2,n) memmove(s1,s2,n) /*!< map memmove to string library code */ +static inline void * ST_MEMCPY(void *s1, const void *s2, uint32_t n) { return memcpy(s1,s2,(uint16_t)n); } /* PRQA S 0431 # MISRA 1.1 - string.h from Cosmic only provides functions with low qualified parameters */ +#define ST_MEMSET(s1,c,n) memset(s1,(char)(c),n) /*!< map memset to string library code */ +static inline int32_t ST_BYTECMP(void *s1, const void *s2, uint32_t n) { return (int32_t)memcmp(s1,s2,(uint16_t)n); } /* PRQA S 0431 # MISRA 1.1 - string.h from Cosmic only provides functions with low qualified parameters */ + +#else /* __CSMC__ */ + +#define ST_MEMMOVE memmove /*!< map memmove to string library code */ +#define ST_MEMCPY memcpy /*!< map memcpy to string library code */ +#define ST_MEMSET memset /*!< map memset to string library code */ +#define ST_BYTECMP memcmp /*!< map bytecmp to string library code */ +#endif /* __CSMC__ */ + +#define NO_WARNING(v) ((void) (v)) /*!< Macro to suppress compiler warning */ + +#ifndef NULL + #define NULL (void*)0 /*!< represents a NULL pointer */ +#endif /* !NULL */ + +/* +****************************************************************************** +* GLOBAL FUNCTION PROTOTYPES +****************************************************************************** +*/ + +#endif /* UTILS_H */ + diff --git a/components/spi-st25r3911b/st25r3911b.c b/components/spi-st25r3911b/st25r3911b.c index 771c3bf..bf3a2dd 100644 --- a/components/spi-st25r3911b/st25r3911b.c +++ b/components/spi-st25r3911b/st25r3911b.c @@ -22,15 +22,26 @@ #include "st25r3911.h" #include "st25r3911_com.h" #include "st25r3911b_global.h" +#include "rfal_platform.h" static const char *TAG = "st25r3911b"; /* P2P communication data */ -static uint8_t NFCID3[] = {0x01, 0xFE, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}; +static uint8_t NFCID3_ACTIVE[] = {0x01, 0xFE, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}; +static uint8_t NFCID3_PASSIVE[] = {0x01, 0xFE, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0B}; static uint8_t GB[] = {0x46, 0x66, 0x6d, 0x01, 0x01, 0x11, 0x02, 0x02, 0x07, 0x80, 0x03, 0x02, 0x00, 0x03, 0x04, 0x01, 0x32, 0x07, 0x01, 0x03}; #define NFC_LOG_SPI 0 +/* 4-byte UIDs with first byte 0x08 would need random number for the subsequent 3 bytes. + * 4-byte UIDs with first byte 0x*F are Fixed number, not unique, use for this demo + * 7-byte UIDs need a manufacturer ID and need to assure uniqueness of the rest.*/ +static uint8_t ceNFCA_NFCID[] = {0x5F, 'S', 'T', 'M'}; /* =_STM, 5F 53 54 4D NFCID1 / UID (4 bytes) */ +static uint8_t ceNFCA_SENS_RES[] = {0x02, 0x00}; /* SENS_RES / ATQA for 4-byte UID */ +static uint8_t ceNFCA_SEL_RES = 0x20; /* SEL_RES / SAK */ + +static uint8_t rawMessageBuf[MAX_NFC_BUFFER_SIZE]; + #define MAX_HEX_STR_LENGTH 64 char hexStr[MAX_HEX_STR_LENGTH * 2]; @@ -100,7 +111,7 @@ esp_err_t st25r3911b_rxtx(ST25R3911B *device, const uint8_t* tx, const uint8_t* return res; } -bool st25r3911b_get_discovery_prams(rfalNfcDiscoverParam * discParam) { +bool st25r3911b_get_discovery_prams(rfalNfcDiscoverParam * discParam, st25r3911b_discover_mode discover_mode) { if (discParam == NULL) { return false; } @@ -109,7 +120,7 @@ bool st25r3911b_get_discovery_prams(rfalNfcDiscoverParam * discParam) { discParam->devLimit = 1U; - memcpy( discParam->nfcid3, NFCID3, sizeof(NFCID3) ); + memcpy( discParam->nfcid3, NFCID3_PASSIVE, sizeof(NFCID3_PASSIVE) ); memcpy( discParam->GB, GB, sizeof(GB) ); discParam->GBLen = sizeof(GB); discParam->p2pNfcaPrio = true; @@ -117,91 +128,529 @@ bool st25r3911b_get_discovery_prams(rfalNfcDiscoverParam * discParam) { discParam->notifyCb = NULL; discParam->totalDuration = 1000U; discParam->techs2Find = RFAL_NFC_TECH_NONE; /* For the demo, enable the NFC Technologies based on RFAL Feature switches */ + discParam->techs2Find |= RFAL_NFC_POLL_TECH_A; + switch (discover_mode) { + case DISCOVER_MODE_LISTEN_NFCA: + discParam->techs2Find |= RFAL_NFC_POLL_TECH_A; + break; + case DISCOVER_MODE_P2P_ACTIVE: + memcpy( discParam->nfcid3, NFCID3_ACTIVE, sizeof(NFCID3_ACTIVE) ); + discParam->techs2Find |= RFAL_NFC_POLL_TECH_AP2P; + break; + case DISCOVER_MODE_P2P_PASSIVE: + discParam->techs2Find |= RFAL_NFC_LISTEN_TECH_AP2P; + break; + } -#if RFAL_FEATURE_NFCA - discParam->techs2Find |= RFAL_NFC_POLL_TECH_A; -#endif /* RFAL_FEATURE_NFCA */ + return true; +} -#if RFAL_FEATURE_NFCB - discParam->techs2Find |= RFAL_NFC_POLL_TECH_B; -#endif /* RFAL_FEATURE_NFCB */ +esp_err_t st25r3911b_discover(NfcDeviceCallback callback, uint32_t timeout_ms, st25r3911b_discover_mode discover_mode) { + rfalNfcDevice nfcDevice = {0}; + rfalNfcDevice* pNfcDevice = &nfcDevice; + rfalNfcDiscoverParam discParam; + st25r3911b_get_discovery_prams(&discParam, discover_mode); -#if RFAL_FEATURE_NFCF - discParam->techs2Find |= RFAL_NFC_POLL_TECH_F; -#endif /* RFAL_FEATURE_NFCF */ + ReturnCode err = rfalNfcDiscover( &discParam ); + if( err != RFAL_ERR_NONE ) { + ESP_LOGE(TAG, "Failed to call rfalNfcDiscover"); + return ESP_FAIL; + } -#if RFAL_FEATURE_NFCV - discParam->techs2Find |= RFAL_NFC_POLL_TECH_V; -#endif /* RFAL_FEATURE_NFCV */ + int64_t end = esp_timer_get_time() / 1000 + timeout_ms; -#if RFAL_FEATURE_ST25TB - discParam->techs2Find |= RFAL_NFC_POLL_TECH_ST25TB; -#endif /* RFAL_FEATURE_ST25TB */ + while(esp_timer_get_time() / 1000 < end) { + rfalNfcWorker(); +// vTaskDelay(10 / portTICK_PERIOD_MS); -#if RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P && RFAL_FEATURE_NFC_DEP - discParam->techs2Find |= RFAL_NFC_POLL_TECH_AP2P; -#endif /* RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P && RFAL_FEATURE_NFC_DEP */ + if (rfalNfcIsDevActivated(rfalNfcGetState())) { + rfalNfcGetActiveDevice( &pNfcDevice ); +// vTaskDelay(50 / portTICK_PERIOD_MS); -#if RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2P && RFAL_FEATURE_NFC_DEP && RFAL_FEATURE_LISTEN_MODE - discParam->techs2Find |= RFAL_NFC_LISTEN_TECH_AP2P; -#endif /* RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2P && RFAL_FEATURE_NFC_DEP && RFAL_FEATURE_LISTEN_MODE */ + ESP_LOGI(TAG, "Discovered NFC device type=%d, uid=%s", pNfcDevice->type, hex2Str(pNfcDevice->nfcid, pNfcDevice->nfcidLen )); -#if RFAL_SUPPORT_CE && RFAL_FEATURE_LISTEN_MODE + esp_err_t res = callback == NULL ? ESP_OK : callback(pNfcDevice); + rfalNfcDeactivate( RFAL_NFC_DEACTIVATE_IDLE ); + return res; + } + } + + rfalNfcDeactivate( RFAL_NFC_DEACTIVATE_IDLE ); + return ESP_ERR_TIMEOUT; +} -#if RFAL_SUPPORT_MODE_LISTEN_NFCA - /* Set configuration for NFC-A CE */ - ST_MEMCPY( discParam->lmConfigPA.SENS_RES, ceNFCA_SENS_RES, RFAL_LM_SENS_RES_LEN ); /* Set SENS_RES / ATQA */ - ST_MEMCPY( discParam->lmConfigPA.nfcid, ceNFCA_NFCID, RFAL_LM_NFCID_LEN_04 ); /* Set NFCID / UID */ - discParam->lmConfigPA.nfcidLen = RFAL_LM_NFCID_LEN_04; /* Set NFCID length to 7 bytes */ - discParam->lmConfigPA.SEL_RES = ceNFCA_SEL_RES; /* Set SEL_RES / SAK */ +esp_err_t st25r3911b_p2p_transceiveBlocking(uint32_t timeout_ms, uint8_t *txBuf, uint16_t txBufSize, uint8_t **rxData, uint16_t **rcvLen, uint32_t fwt) { + ReturnCode err; + + int64_t end = esp_timer_get_time() / 1000 + timeout_ms; + err = rfalNfcDataExchangeStart( txBuf, txBufSize, rxData, rcvLen, fwt ); + if (err != ERR_NONE) { + ESP_LOGE(TAG, "Failed to start data exchange: %d", err); + return ESP_FAIL; + } + do { + if (esp_timer_get_time() / 1000 > end) { + ESP_LOGE(TAG, "Timeout while transceiving"); + return ESP_ERR_TIMEOUT; + } + rfalNfcWorker(); + err = rfalNfcDataExchangeGetStatus(); + } while( err == ERR_BUSY ); + if (err != ERR_NONE) { + ESP_LOGE(TAG, "Failed to transmit data: %d", err); + } + return ESP_OK; +} - discParam->techs2Find |= RFAL_NFC_LISTEN_TECH_A; -#endif /* RFAL_SUPPORT_MODE_LISTEN_NFCA */ +esp_err_t st25r3911b_p2p_transmitBlocking(uint32_t timeout_ms, uint8_t *txBuf, uint16_t txBufSize) { + uint16_t *rxLen; + uint8_t *rxData; + return st25r3911b_p2p_transceiveBlocking(timeout_ms, txBuf, txBufSize, &rxData, &rxLen, RFAL_FWT_NONE); +} -#if RFAL_SUPPORT_MODE_LISTEN_NFCF - /* Set configuration for NFC-F CE */ - ST_MEMCPY( discParam->lmConfigPF.SC, ceNFCF_SC, RFAL_LM_SENSF_SC_LEN ); /* Set System Code */ - ST_MEMCPY( &ceNFCF_SENSF_RES[RFAL_NFCF_CMD_LEN], ceNFCF_nfcid2, RFAL_NFCID2_LEN ); /* Load NFCID2 on SENSF_RES */ - ST_MEMCPY( discParam->lmConfigPF.SENSF_RES, ceNFCF_SENSF_RES, RFAL_LM_SENSF_RES_LEN ); /* Set SENSF_RES / Poll Response */ +esp_err_t st25r3911b_p2p_receiveBlocking(uint32_t timeout_ms, uint8_t **rxData, uint16_t **rcvLen) { + return st25r3911b_p2p_transceiveBlocking(timeout_ms, NULL, 0, rxData, rcvLen, RFAL_FWT_NONE); +} - discParam->techs2Find |= RFAL_NFC_LISTEN_TECH_F; -#endif /* RFAL_SUPPORT_MODE_LISTEN_NFCF */ -#endif /* RFAL_SUPPORT_CE && RFAL_FEATURE_LISTEN_MODE */ - return true; +static esp_err_t st25r3911b_activate_p2p(bool isActive, rfalNfcDepDevice *nfcDepDev) { + rfalNfcDepAtrParam nfcDepParams; + + nfcDepParams.nfcid = isActive ? NFCID3_ACTIVE : NFCID3_PASSIVE; + nfcDepParams.nfcidLen = RFAL_NFCDEP_NFCID3_LEN; + nfcDepParams.BS = RFAL_NFCDEP_Bx_NO_HIGH_BR; +#define ESP_BR BR +#undef BR + nfcDepParams.BR = RFAL_NFCDEP_Bx_NO_HIGH_BR; +#define BR ESP_BR +#undef ESP_BR + nfcDepParams.LR = RFAL_NFCDEP_LR_254; + nfcDepParams.DID = RFAL_NFCDEP_DID_NO; + nfcDepParams.NAD = RFAL_NFCDEP_NAD_NO; + nfcDepParams.GBLen = sizeof(GB); + nfcDepParams.GB = GB; + nfcDepParams.commMode = ((isActive) ? RFAL_NFCDEP_COMM_ACTIVE : RFAL_NFCDEP_COMM_PASSIVE); + nfcDepParams.operParam = (RFAL_NFCDEP_OPER_FULL_MI_EN | RFAL_NFCDEP_OPER_EMPTY_DEP_DIS | RFAL_NFCDEP_OPER_ATN_EN | RFAL_NFCDEP_OPER_RTOX_REQ_EN); + + /* Initialize NFC-DEP protocol layer */ + rfalNfcDepInitialize(); + + /* Handle NFC-DEP Activation (ATR_REQ and PSL_REQ if applicable) */ + return rfalNfcDepInitiatorHandleActivation( &nfcDepParams, RFAL_BR_424, nfcDepDev ); } -esp_err_t st25r3911b_discover(rfalNfcDevice *nfcDevice, uint32_t timeout_ms) { - rfalNfcDiscoverParam discParam; - st25r3911b_get_discovery_prams(&discParam); +esp_err_t st25r3911b_p2p_active(rfalNfcDepDevice *nfcDepDev) { + ReturnCode res; - ReturnCode err = rfalNfcDiscover( &discParam ); - if( err != RFAL_ERR_NONE ) { - ESP_LOGE(TAG, "Failed to call rfalNfcDiscover"); + res = rfalSetMode(RFAL_MODE_POLL_ACTIVE_P2P, RFAL_BR_424, RFAL_BR_424); + if (res != RFAL_ERR_NONE) { + ESP_LOGE(TAG, "Failed to set mode: %d", res); return ESP_FAIL; } + rfalSetErrorHandling(RFAL_ERRORHANDLING_EMD); + rfalSetFDTListen(RFAL_FDT_LISTEN_AP2P_POLLER); + rfalSetFDTPoll(RFAL_TIMING_NONE); + + rfalSetGT( RFAL_GT_AP2P_ADJUSTED ); + res = rfalFieldOnAndStartGT(); + if (res != RFAL_ERR_NONE) { + ESP_LOGE(TAG, "Failed to turn field on: %d", res); + return ESP_FAIL; + } + + res = st25r3911b_activate_p2p(true, nfcDepDev); + if (res == RFAL_ERR_NONE) { + ESP_LOGI(TAG, "Discovered NFC device uid=%s", hex2Str(nfcDepDev->activation.Target.ATR_RES.NFCID3, RFAL_NFCDEP_NFCID3_LEN)); + } + + rfalFieldOff(); + return res; +} + +esp_err_t st25r3911b_poll_active_p2p(uint32_t timeout_ms) { + rfalFieldOff(); + rfalWakeUpModeStop(); + vTaskDelay(pdMS_TO_TICKS(300)); + int64_t end = esp_timer_get_time() / 1000 + timeout_ms; + rfalWakeUpModeStart(NULL); + + bool wokeup = false; + while(esp_timer_get_time() / 1000 < end) { - rfalNfcWorker(); - vTaskDelay(10 / portTICK_PERIOD_MS); + if(rfalWakeUpModeHasWoke()) { + /* If awake, go directly to Poll */ + rfalWakeUpModeStop(); + wokeup = true; + break; + } + } - if (rfalNfcIsDevActivated(rfalNfcGetState())) { - rfalNfcGetActiveDevice( &nfcDevice ); - vTaskDelay(50 / portTICK_PERIOD_MS); + if (!wokeup) { + return ESP_ERR_TIMEOUT; + } - ESP_LOGD(TAG, "Discovered NFC device type=%d, uid=%s", nfcDevice->type, hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen )); - rfalNfcDeactivate( RFAL_NFC_DEACTIVATE_IDLE ); + ESP_LOGI(TAG, "wakeup complete"); - return ESP_OK; + ReturnCode res; + rfalNfcDepDevice nfcDepDev; + while(esp_timer_get_time() / 1000 < end) { + res = st25r3911b_p2p_active(&nfcDepDev); + if (res == ESP_OK) { + return res; } + vTaskDelay(pdMS_TO_TICKS(40)); } - - rfalNfcDeactivate( RFAL_NFC_DEACTIVATE_IDLE ); return ESP_ERR_TIMEOUT; } +static esp_err_t st25r3911b_listen_nfca() { + ReturnCode err; + bool found = false; + uint8_t devIt = 0; + rfalNfcaSensRes sensRes; + rfalIsoDepDevice isoDepDev; /* ISO-DEP Device details */ + rfalNfcDepDevice nfcDepDev; /* NFC-DEP Device details */ + + rfalNfcaPollerInitialize(); /* Initialize for NFC-A */ + rfalFieldOnAndStartGT(); /* Turns the Field On if not already and start GT timer */ + + err = rfalNfcaPollerTechnologyDetection( RFAL_COMPLIANCE_MODE_NFC, &sensRes ); + if(err == ERR_NONE) + { + rfalNfcaListenDevice nfcaDevList[1]; + uint8_t devCnt; + + err = rfalNfcaPollerFullCollisionResolution( RFAL_COMPLIANCE_MODE_NFC, 1, nfcaDevList, &devCnt); + + if ( (err == ERR_NONE) && (devCnt > 0) ) + { + found = true; + devIt = 0; + + platformLedOn(PLATFORM_LED_A_PORT, PLATFORM_LED_A_PIN); + + /* Check if it is Topaz aka T1T */ + if( nfcaDevList[devIt].type == RFAL_NFCA_T1T ) + { + /********************************************/ + /* NFC-A T1T card found */ + /* NFCID/UID is contained in: t1tRidRes.uid */ + platformLog("ISO14443A/Topaz (NFC-A T1T) TAG found. UID: %s\r\n", hex2Str(nfcaDevList[devIt].ridRes.uid, RFAL_T1T_UID_LEN)); + } + else + { + /*********************************************/ + /* NFC-A device found */ + /* NFCID/UID is contained in: nfcaDev.nfcId1 */ + platformLog("ISO14443A/NFC-A card found. UID: %s\r\n", hex2Str(nfcaDevList[0].nfcId1, nfcaDevList[0].nfcId1Len)); + } + + + /* Check if device supports P2P/NFC-DEP */ + if( (nfcaDevList[devIt].type == RFAL_NFCA_NFCDEP) || (nfcaDevList[devIt].type == RFAL_NFCA_T4T_NFCDEP)) + { + /* Continue with P2P Activation .... */ + + err = st25r3911b_activate_p2p(false, &nfcDepDev ); + if (err == ERR_NONE) + { + /*********************************************/ + /* Passive P2P device activated */ + platformLog("NFCA Passive P2P device found. NFCID: %s\r\n", hex2Str(nfcDepDev.activation.Target.ATR_RES.NFCID3, RFAL_NFCDEP_NFCID3_LEN)); + + /* Send an URI record */ +// demoSendNdefUri(); + } + } + /* Check if device supports ISO14443-4/ISO-DEP */ + else if (nfcaDevList[devIt].type == RFAL_NFCA_T4T) + { + /* Activate the ISO14443-4 / ISO-DEP layer */ + + rfalIsoDepInitialize(); + err = rfalIsoDepPollAHandleActivation((rfalIsoDepFSxI)RFAL_ISODEP_FSDI_DEFAULT, RFAL_ISODEP_NO_DID, RFAL_BR_424, &isoDepDev); + if( err == ERR_NONE ) + { + platformLog("ISO14443-4/ISO-DEP layer activated. \r\n"); + + /* Exchange APDUs */ +// demoSendAPDUs(); + } + } + } + } + return ESP_OK; +} + +#define DEMO_BUF_LEN 255 +static rfalNfcDepBufFormat nfcDepRxBuf; /* NFC-DEP Rx buffer format (with header/prologue) */ +static uint8_t rxBuf[DEMO_BUF_LEN]; /* Generic buffer abstraction */ +static uint16_t gRxLen; +static bool gIsRxChaining; /*!< Received data is not complete */ +static rfalNfcDepDevice gNfcDepDev; /*!< NFC-DEP device info */ + +static bool handle_listen(uint8_t *state, rfalLmState *lmSt, rfalBitRate *bitRate, bool *dataFlag, uint8_t *hdrLen) { + switch(*state){ + case 0: + *lmSt = rfalListenGetState( dataFlag, bitRate ); /* Check if Initator has sent some data */ + if( (*lmSt != RFAL_LM_STATE_IDLE)) { + break; + } + ESP_LOGI(TAG, "RFAL_LM_STATE_IDLE: dataFlag=%d, bitRate=%d", *dataFlag, *bitRate); + if (!*dataFlag) { + break; + } + *state = *state + 1; + case 1: + /* SB Byte only in NFC-A */ + if (*bitRate == RFAL_BR_106) { + *hdrLen += RFAL_NFCDEP_SB_LEN; + } + ESP_LOGI(TAG, "Using %d as hdrLen", *hdrLen); + *state = *state + 1; + case 2: + ESP_LOGI(TAG, "%s", hex2Str(rxBuf, rfalConvBitsToBytes(gRxLen))); + if(!rfalNfcDepIsAtrReq( &rxBuf[*hdrLen], (rfalConvBitsToBytes(gRxLen) - *hdrLen), NULL ) ) { + ESP_LOGI(TAG, "not rfalNfcDepIsAtrReq: len=%d", rfalConvBitsToBytes(gRxLen)); + break; + } + rfalNfcDepTargetParam param; + rfalNfcDepListenActvParam rxParam; + + rfalListenSetState((RFAL_BR_106 == *bitRate) ? RFAL_LM_STATE_TARGET_A : RFAL_LM_STATE_TARGET_F); + rfalSetMode( RFAL_MODE_LISTEN_ACTIVE_P2P, *bitRate, *bitRate); + + platformLog(" Activated as AP2P listener device \r\n" ); + + memcpy(param.nfcid3, NFCID3_PASSIVE, RFAL_NFCDEP_NFCID3_LEN); + param.bst = RFAL_NFCDEP_Bx_NO_HIGH_BR; + param.brt = RFAL_NFCDEP_Bx_NO_HIGH_BR; + param.to = RFAL_NFCDEP_WT_TRG_MAX_D11; + param.ppt = (RFAL_NFCDEP_LR_254 << RFAL_NFCDEP_PP_LR_SHIFT); + param.GBtLen = 0; + param.operParam = (RFAL_NFCDEP_OPER_FULL_MI_DIS | RFAL_NFCDEP_OPER_EMPTY_DEP_EN | RFAL_NFCDEP_OPER_ATN_EN | RFAL_NFCDEP_OPER_RTOX_REQ_EN); + param.commMode = RFAL_NFCDEP_COMM_ACTIVE; + + rxParam.rxBuf = &nfcDepRxBuf; + rxParam.rxLen = &gRxLen; + rxParam.isRxChaining = &gIsRxChaining; + rxParam.nfcDepDev = &gNfcDepDev; + + ReturnCode res; + + /* ATR_REQ received, trigger NFC-DEP layer to handle activation (sends ATR_RES and handles PSL_REQ) */ + res = rfalNfcDepListenStartActivation( ¶m, &rxBuf[*hdrLen], (rfalConvBitsToBytes(gRxLen) - *hdrLen), rxParam ); + if(res != RFAL_ERR_NONE) { + ESP_LOGE(TAG, "rfalNfcDepListenStartActivation: %d", res); + return false; + } + + *state = *state + 1; + case 3: + res = rfalNfcDepListenGetActivationStatus(); + if( res == RFAL_ERR_BUSY ){ + break; + } + + if (res != RFAL_ERR_NONE) { + ESP_LOGE(TAG, "rfalNfcDepListenGetActivationStatus: %d", res); + return false; + } + + *state = *state + 1; + case 4: + res = rfalNfcDepGetTransceiveStatus(); + if( res == RFAL_ERR_BUSY ){ + break; + } + if( res != RFAL_ERR_NONE ){ + ESP_LOGE(TAG, "rfalNfcDepGetTransceiveStatus: %d", res); + return false; + } + + rfalNfcDepTxRxParam rfalNfcDepTxRx; + + platformLog(" Received %d bytes of data: %s\r\n", gRxLen, hex2Str((uint8_t*)nfcDepRxBuf.inf, gRxLen) ); + + /* Loop/Send back the same data that has been received */ + rfalNfcDepTxRx.txBuf = &nfcDepRxBuf; + rfalNfcDepTxRx.txBufLen = gRxLen; + rfalNfcDepTxRx.rxBuf = &nfcDepRxBuf; + rfalNfcDepTxRx.rxLen = &gRxLen; + rfalNfcDepTxRx.DID = RFAL_NFCDEP_DID_NO; + rfalNfcDepTxRx.FSx = rfalNfcDepLR2FS( rfalNfcDepPP2LR( gNfcDepDev.activation.Initiator.ATR_REQ.PPi ) ); + rfalNfcDepTxRx.FWT = gNfcDepDev.info.FWT; + rfalNfcDepTxRx.dFWT = gNfcDepDev.info.dFWT; + rfalNfcDepTxRx.isRxChaining = &gIsRxChaining; + rfalNfcDepTxRx.isTxChaining = gIsRxChaining; + + res = rfalNfcDepStartTransceive( &rfalNfcDepTxRx ); + if (res != RFAL_ERR_NONE) { + ESP_LOGE(TAG, "rfalNfcDepStartTransceive: %d", res); + return false; + } + } + return true; +} + +esp_err_t st25r3911b_listen_p2p(uint32_t timeout_ms) { + ReturnCode res; + bool dataFlag; + rfalLmState lmSt; + rfalBitRate bitRate; + uint8_t hdrLen = RFAL_NFCDEP_LEN_LEN; + + rfalFieldOff(); + res = rfalListenStart( RFAL_LM_MASK_ACTIVE_P2P, NULL, NULL, NULL, rxBuf, DEMO_BUF_LEN, &gRxLen ); + if (res != RFAL_ERR_NONE) { + ESP_LOGE(TAG, "failed to start listening: %d", res); + rfalFieldOff(); + return ESP_FAIL; + } + + uint8_t state = 0; + + int64_t end = esp_timer_get_time() / 1000 + timeout_ms; + while(esp_timer_get_time() / 1000 < end) { + rfalWorker(); + if (!handle_listen(&state, &lmSt, &bitRate, &dataFlag, &hdrLen)) { + res = ESP_FAIL; + break; + } + } + + rfalListenStop(); + rfalFieldOff(); + return res == RFAL_ERR_NONE ? ESP_ERR_TIMEOUT : res; +} + +esp_err_t st25r3911b_read_data(rfalNfcDevice *nfcDevice, ndefConstBuffer* bufConstRawMessage) { + if (nfcDevice == NULL) { + ESP_LOGE(TAG, "No NFC device given"); + return ESP_ERR_INVALID_ARG; + } + if (nfcDevice->type != RFAL_NFC_LISTEN_TYPE_NFCA) { + ESP_LOGE(TAG, "Invalid NFC device type: %d", nfcDevice->type); + return ESP_ERR_INVALID_ARG; + } + + ndefContext ndefCtx; + uint32_t rawMessageLen; + ndefInfo info; + + /* + * Perform NDEF Context Initialization + */ + ReturnCode err = ndefPollerContextInitialization(&ndefCtx, nfcDevice); + if( err != RFAL_ERR_NONE ) { + ESP_LOGE(TAG, "context init failed: %d", err); + return ESP_FAIL; + } + /* + * Perform NDEF Detect procedure + */ + err = ndefPollerNdefDetect(&ndefCtx, &info); + if(err != RFAL_ERR_NONE) { + ESP_LOGE(TAG, "NFC tag not found"); + return ESP_FAIL; + } + + err = ndefPollerReadRawMessage(&ndefCtx, rawMessageBuf, sizeof(rawMessageBuf), &rawMessageLen, true); + if(err != RFAL_ERR_NONE) { + return ESP_FAIL; + } + bufConstRawMessage->buffer = rawMessageBuf; + bufConstRawMessage->length = rawMessageLen; + + // err = ndefMessageDecode(&bufConstRawMessage, &message); + // if (err != RFAL_ERR_NONE) { + // return ESP_FAIL; + // } + + ESP_LOGI(TAG, "found message with length %d", rawMessageLen); + + // err = ndefMessageDump(&message, verbose); + // if (err != RFAL_ERR_NONE) { + // return ESP_FAIL; + // } + + rfalNfcaPollerSleep(); + return ESP_OK; +} + +static ReturnCode transceiveBlocking(uint8_t *txBuf, uint16_t txBufSize, uint8_t **rxData, uint16_t **rcvLen, uint32_t fwt) { + ReturnCode err; + + err = rfalNfcDataExchangeStart(txBuf, txBufSize, rxData, rcvLen, fwt); + if (err == RFAL_ERR_NONE) { + do { + rfalNfcWorker(); + err = rfalNfcDataExchangeGetStatus(); + } while(err == RFAL_ERR_BUSY); + } + return err; +} + +esp_err_t st25r3911b_p2p_listen(rfalNfcDevice *nfcDevice, ndefConstBuffer* bufConstRawMessage) { + if (nfcDevice == NULL) { + ESP_LOGE(TAG, "No NFC device given"); + return ESP_ERR_INVALID_ARG; + } + // case RFAL_NFC_LISTEN_TYPE_AP2P: + // case RFAL_NFC_POLL_TYPE_AP2P: + if (nfcDevice->type != RFAL_NFC_POLL_TYPE_NFCA) { + ESP_LOGE(TAG, "Invalid NFC device type: %d", nfcDevice->type); + return ESP_ERR_INVALID_ARG; + } + if (nfcDevice->rfInterface != RFAL_NFC_INTERFACE_NFCDEP) { + ESP_LOGE(TAG, "NFC DEP not supported: %d", nfcDevice->rfInterface); + return ESP_ERR_INVALID_ARG; + } + +// +// +// ReturnCode err = RFAL_ERR_INTERNAL; +// uint8_t *rxData; +// uint16_t *rcvLen; +// uint8_t txBuf[150]; +// uint16_t txLen; +// +// do +// { +// rfalNfcWorker(); +// +// switch( rfalNfcGetState() ) +// { +// case RFAL_NFC_STATE_ACTIVATED: +// err = transceiveBlocking( NULL, 0, &rxData, &rcvLen, 0); +// break; +// +// case RFAL_NFC_STATE_DATAEXCHANGE: +// case RFAL_NFC_STATE_DATAEXCHANGE_DONE: +// +// txLen = demoCeT4T( rxData, *rcvLen, txBuf, sizeof(txBuf) ); +// err = transceiveBlocking( txBuf, txLen, &rxData, &rcvLen, RFAL_FWT_NONE ); +// break; +// +// case RFAL_NFC_STATE_START_DISCOVERY: +// return ESP_OK; +// +// case RFAL_NFC_STATE_LISTEN_SLEEP: +// default: +// break; +// } +// } +// while( (err == RFAL_ERR_NONE) || (err == RFAL_ERR_SLEEP_REQ) ); +// if (err != RFAL_ERR_NONE) { +// ESP_LOGE(TAG, "Failed to write to NFC device: %d", err); +// return ESP_FAIL; +// } + return ESP_OK; +} + esp_err_t st25r3911b_test() { esp_err_t res; @@ -228,7 +677,6 @@ esp_err_t st25r3911b_test() { ESP_LOGE(TAG, "Currently unknown silicon rev. 0x%02x", id); return ESP_FAIL; } - static rfalNfcDevice *nfcDevice; return ESP_OK; } diff --git a/components/spi-st77xx b/components/spi-st77xx new file mode 160000 index 0000000..5e5d152 --- /dev/null +++ b/components/spi-st77xx @@ -0,0 +1 @@ +Subproject commit 5e5d152e8a208bdc3ee274b79b20693e39e5264c diff --git a/components/spi-st77xx/CMakeLists.txt b/components/spi-st77xx/CMakeLists.txt deleted file mode 100644 index 0c0f62e..0000000 --- a/components/spi-st77xx/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -idf_component_register( - SRCS "st77xx.c" - INCLUDE_DIRS include -) diff --git a/components/spi-st77xx/LICENSE b/components/spi-st77xx/LICENSE deleted file mode 100644 index 00221d3..0000000 --- a/components/spi-st77xx/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -Copyright (c) 2024 Tom Bennellick & Malte Heinzelmann -Based on the ILI9341 driver by Nicolai Electronics. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", 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 AUTHORS OR COPYRIGHT HOLDERS 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. diff --git a/components/spi-st77xx/NOTES.txt b/components/spi-st77xx/NOTES.txt deleted file mode 100644 index e69de29..0000000 diff --git a/components/spi-st77xx/README.md b/components/spi-st77xx/README.md deleted file mode 100644 index 5087ded..0000000 --- a/components/spi-st77xx/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# ESP32 component: ST77XX LCD display - -This *should* work with all ST77* drivers. However, it has only been tested with ST7789VI. - - diff --git a/components/spi-st77xx/component.mk b/components/spi-st77xx/component.mk deleted file mode 100644 index 369ec2e..0000000 --- a/components/spi-st77xx/component.mk +++ /dev/null @@ -1,3 +0,0 @@ -# Component Makefile - -COMPONENT_ADD_INCLUDEDIRS := . diff --git a/components/spi-st77xx/include/st77xx.h b/components/spi-st77xx/include/st77xx.h deleted file mode 100644 index a8acb50..0000000 --- a/components/spi-st77xx/include/st77xx.h +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (c) 2024 Tom Bennellick & Malte Heinzelmann - * Based on the ILI9341 driver by Nicolai Electronics. - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif //__cplusplus - -#include -#include -#include -#include -#include -#include -#include - -#define ST77XX_WIDTH 320 -#define ST77XX_HEIGHT 240 -#define ST77XX_BUFFER_SIZE ST77XX_WIDTH * ST77XX_HEIGHT * 2 // Each pixel takes 16 bits -#define ST77XX_BPP 16 - -// Registers -#define ST77XX_NOP 0x00 -#define ST77XX_SWRESET 0x01 // Software Reset -#define ST77XX_RDDID 0x04 // Read Display ID -#define ST77XX_RDDST 0x09 // Read Display Status -#define ST77XX_RDDPM 0x0A // Read Display Power -#define ST77XX_RDDMADCTL 0x0B // Read Display Memory Data Access Mode -#define ST77XX_RDDCOLMOD 0x0C // Read Display Pixel -#define ST77XX_RDDIM 0x0D // Read Display Image -#define ST77XX_RDDSM 0x0E // Read Display Signal -#define ST77XX_RDDSDR 0x0F // Read Display Self Diagnostics -#define ST77XX_SLPIN 0x10 // Sleep In -#define ST77XX_SLPOUT 0x11 // Sleep Out -#define ST77XX_PTLON 0x12 // Partial Mode On -#define ST77XX_NORON 0x13 // Partial Mode Off -#define ST77XX_INVOFF 0x20 // Display Invert Off -#define ST77XX_INVON 0x21 // Display Invert On -#define ST77XX_GAMSET 0x26 // Display Invert On Gamma -#define ST77XX_DISPOFF 0x28 // Display Off -#define ST77XX_DISPON 0x29 // Display On -#define ST77XX_CASET 0x2A // Column Address Set -#define ST77XX_RASET 0x2B // Row Address Set -#define ST77XX_RAMWR 0x2C // Memory Write -#define ST77XX_RAMRD 0x2E // Memory Read -#define ST77XX_PTLAR 0x30 // Partial Start/End Address Set -#define ST77XX_VSCRDEF 0x33 // Vertical Scrolling Definition -#define ST77XX_TEOFF 0x34 // Tearing Effect Line Off -#define ST77XX_TEON 0x35 // Tearing Effect Line On -#define ST77XX_MADCTL 0x36 // Memory Data Access Control -#define ST77XX_VSCRSADD 0x37 // Vertical Scrolling Start Address -#define ST77XX_IDMOFF 0x38 // Idle Mode Off -#define ST77XX_IDMON 0x39 // Idle Mode On -#define ST77XX_COLMOD 0x3A // Interface Pixel Format -#define ST77XX_RAMWRC 0x3C // Memory Write Continue -#define ST77XX_RAMRDC 0x3E // Memory Read Continue -#define ST77XX_TESCAN 0x44 // Set Tear Scan Line -#define ST77XX_RDTESCAN 0x45 // Get Tear Scan Line -#define ST77XX_WRDISBV 0x51 // Set Display Brightness -#define ST77XX_RDDISBV 0x52 // Get Display Brightness -#define ST77XX_WRCTRLD 0x53 // Set Display Control -#define ST77XX_RDCTRLD 0x54 // Get Display Control -#define ST77XX_WRCACE 0x55 // Write content adaptive brightness control and Color enhancement -#define ST77XX_RDCABC 0x56 // Read content adaptive brightness control and Color enhancement -#define ST77XX_WRCABCMB 0x5E // Write CABC minimum brightness -#define ST77XX_RDCABCMB 0x5F // Read CABC minimum brightness -#define ST77XX_RDABCSDR 0x68 // Read Automatic Brightness Control Self-Diagnostic Result -#define ST77XX_PORCTRK 0xB2 // Porch setting -#define ST77XX_GCTRL 0xB7 // Gate Control -#define ST77XX_VCOMS 0xBB // VCOM setting -#define ST77XX_LCMCTRL 0xC0 // LCM Control -#define ST77XX_VDVVRHEN 0xC2 // VDV and VRH Command Enable -#define ST77XX_VRHS 0xC3 // VRH Set -#define ST77XX_VDVS 0xC4 // VDV Set -#define ST77XX_FRCTRL2 0xC6 // Frame Rate control in normal mode -#define ST77XX_PWCTRL1 0xD0 // Power Control 1 -#define ST77XX_RDID1 0xDA // Read ID1 -#define ST77XX_RDID2 0xDB // Read ID2 -#define ST77XX_RDID3 0xDC // Read ID3 -#define ST77XX_PVGAMCTRL 0xE0 // Positive Voltage Gamma control -#define ST77XX_NVGAMCTRL 0xE1 // Negative Voltage Gamma control - -// Extended command set -#define ST77XX_RGB_INTERFACE 0xB0 // RGB Interface Signal Control -#define ST77XX_FRMCTR1 0xB1 // Frame Rate Control (In Normal Mode) -#define ST77XX_FRMCTR2 0xB2 // Frame Rate Control (In Idle Mode) -#define ST77XX_FRMCTR3 0xB3 // Frame Rate Control (In Partial Mode) -#define ST77XX_INVTR 0xB4 // Display Inversion Control -#define ST77XX_BPC 0xB5 // Blanking Porch Control register -#define ST77XX_DFC 0xB6 // Display Function Control register -#define ST77XX_ETMOD 0xB7 // Entry Mode Set -#define ST77XX_BACKLIGHT1 0xB8 // Backlight Control 1 -#define ST77XX_BACKLIGHT2 0xB9 // Backlight Control 2 -#define ST77XX_BACKLIGHT3 0xBA // Backlight Control 3 -#define ST77XX_BACKLIGHT4 0xBB // Backlight Control 4 -#define ST77XX_BACKLIGHT5 0xBC // Backlight Control 5 -#define ST77XX_BACKLIGHT7 0xBE // Backlight Control 7 -#define ST77XX_BACKLIGHT8 0xBF // Backlight Control 8 -#define ST77XX_POWER1 0xC0 // Power Control 1 register -#define ST77XX_POWER2 0xC1 // Power Control 2 register -#define ST77XX_VCOM1 0xC5 // VCOM Control 1 register -#define ST77XX_VCOM2 0xC7 // VCOM Control 2 register -#define ST77XX_NVMWR 0xD0 // NV Memory Write -#define ST77XX_NVMPKEY 0xD1 // NV Memory Protection Key -#define ST77XX_RDNVM 0xD2 // NV Memory Status Read -#define ST77XX_READ_ID4 0xD3 // Read ID4 -#define ST77XX_PGAMMA 0xE0 // Positive Gamma Correction register -#define ST77XX_NGAMMA 0xE1 // Negative Gamma Correction register -#define ST77XX_DGAMCTRL1 0xE2 // Digital Gamma Control 1 -#define ST77XX_DGAMCTRL2 0xE3 // Digital Gamma Control 2 -#define ST77XX_INTERFACE 0xF6 // Interface control register - -// Extend register commands -#define ST77XX_POWERA 0xCB // Power control A register -#define ST77XX_POWERB 0xCF // Power control B register -#define ST77XX_DTCA 0xE8 // Driver timing control A -#define ST77XX_DTCB 0xEA // Driver timing control B -#define ST77XX_POWER_SEQ 0xED // Power on sequence register -#define ST77XX_3GAMMA_EN 0xF2 // 3 Gamma enable register -#define ST77XX_PRC 0xF7 // Pump ratio control register - -typedef void (*ST77XX_cb_t)(bool); // Callback for init / deinit - -typedef struct ST77XX { - // Pins - int spi_bus; - int pin_cs; - int pin_dcx; - int pin_reset; - // Configuration - uint8_t rotation; - bool color_mode; - uint32_t spi_speed; - uint32_t spi_max_transfer_size; - ST77XX_cb_t callback; - // Internal state - spi_device_handle_t spi_device; - // Mutex - SemaphoreHandle_t mutex; - SemaphoreHandle_t spi_semaphore; -} ST77XX; - -esp_err_t st77xx_init(ST77XX* device); -esp_err_t st77xx_deinit(ST77XX* device); - -esp_err_t st77xx_set_display(ST77XX* device, const bool state); -esp_err_t st77xx_set_cfg(ST77XX* device, uint8_t rotation, bool color_mode); - -esp_err_t st77xx_write(ST77XX* device, const uint8_t *data); -esp_err_t st77xx_write_partial_direct(ST77XX* device, const uint8_t *buffer, uint16_t x, uint16_t y, uint16_t width, uint16_t height); - -#ifdef __cplusplus -} -#endif //__cplusplus diff --git a/components/spi-st77xx/st77xx.c b/components/spi-st77xx/st77xx.c deleted file mode 100644 index 943148b..0000000 --- a/components/spi-st77xx/st77xx.c +++ /dev/null @@ -1,355 +0,0 @@ -/** - * Copyright (c) 2024 Tom Bennellick & Malte Heinzelmann - * Based on the spi ILI9341 driver by Nicolai Electronics. - * - * SPDX-License-Identifier: MIT - */ - -#include "include/st77xx.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char *TAG = "st77XX"; - -static void IRAM_ATTR st77xx_spi_pre_transfer_callback(spi_transaction_t *t) { -// ILI9341* device = ((ILI9341*) t->user); -// gpio_set_level(device->pin_dcx, device->dc_level); -} - -const uint8_t st77xx_init_data[] = { - // Turn off display - ST77XX_DISPOFF, 0, - // Exit sleep mode - ST77XX_SLPOUT, 0, - // MADCTL: memory data access control Old: 0x88 - ST77XX_MADCTL, 1, 0xa8, /* Page address order RGB order */ - // COLMOD: Interface Pixel format (16-bits per pixel) - ST77XX_COLMOD, 1, 0x55, /* 16 bits per pixel */ - // PORCTRK: Porch setting - ST77XX_PORCTRK, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33, /* Back porch, Front Porch, Separate porch control, Back porch idle, Back porch partial */ - // GCTRL: Gate Control - ST77XX_GCTRL, 1, 0x35, /* Probably dont change */ - // VCOMS: VCOM setting - ST77XX_VCOMS, 1, 0x2B, /* Probably dont change */ - // LCMCTRL: LCM Control - ST77XX_LCMCTRL, 1, 0x2C, /* Not sure what this does */ - // VDVVRHEN: VDV and VRH Command Enable - ST77XX_VDVVRHEN, 2, 0x01, 0xFF, /* Enable the below */ - // VRHS: VRH set - ST77XX_VRHS, 1, 0x11, /* Maybe colour correction? */ - // VDVS: VDV Set - ST77XX_VDVS, 1, 0x20, /* Maybe colour correction? */ - // FRCTRL2: Frame Rate control in normal mode - ST77XX_FRCTRL2, 1, 0x0F, /* 60Hz */ - // PWCTRL1: Power Control 1 - ST77XX_PWCTRL1, 2, 0xA4, 0xA1, /* Set voltages)*/ - // PVGAMCTRL: Positive Voltage Gamma control - ST77XX_PVGAMCTRL, 14, 0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19, - // NVGAMCTRL: Negative Voltage Gamma control - ST77XX_NVGAMCTRL, 14, 0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19, - // X address set - ST77XX_CASET, 4, 0x00, 0x00, 0x01, 0x3F, - // Y address set - ST77XX_RASET, 4, 0x00, 0x00, 0x00, 0xEF, - // Display on - ST77XX_DISPON, 0, - 0x00, -}; - -esp_err_t st77xx_send(ST77XX* device, const uint8_t *data, const int len) { - if (len == 0) return ESP_OK; - if (device->spi_device == NULL) return ESP_FAIL; - spi_transaction_t transaction = { - .length = len * 8, // transaction length is in bits - .tx_buffer = data, - .user = (void*) device, - }; - if (device->spi_semaphore != NULL) xSemaphoreTake(device->spi_semaphore, portMAX_DELAY); - esp_err_t res = spi_device_transmit(device->spi_device, &transaction); - if (device->spi_semaphore != NULL) xSemaphoreGive(device->spi_semaphore); - return res; -} - - -esp_err_t st77xx_send_command(ST77XX* device, const uint8_t cmd) -{ - esp_err_t err; - gpio_set_level(device->pin_dcx, 0); - err = st77xx_send(device, &cmd, 1); - return err; -} - -esp_err_t st77xx_send_data(ST77XX* device, const uint8_t* data, const uint16_t length) -{ - esp_err_t err; - gpio_set_level(device->pin_dcx, 1); - err = st77xx_send(device, data, length); - return err; -} - - -esp_err_t st77xx_reset(ST77XX* device) { - if (device->mutex != NULL) xSemaphoreTake(device->mutex, portMAX_DELAY); - esp_err_t res; - res = gpio_set_level(device->pin_reset, false); - if (res != ESP_OK) { - if (device->mutex != NULL) xSemaphoreGive(device->mutex); - return res; - } - - vTaskDelay(50 / portTICK_PERIOD_MS); - - res = gpio_set_level(device->pin_reset, true); - if (res != ESP_OK) { - if (device->mutex != NULL) xSemaphoreGive(device->mutex); - return res; - } - - vTaskDelay(120 / portTICK_PERIOD_MS); /* This could possibly be shorter if a problem. */ - - ESP_LOGD(TAG, "Reset done"); - if (device->mutex != NULL) xSemaphoreGive(device->mutex); - return ESP_OK; -} - -esp_err_t st77xx_write_init_data(ST77XX* device, const uint8_t * data) { - if (device->spi_device == NULL) return ESP_FAIL; - esp_err_t res; - uint8_t cmd, len; - while (true) { - cmd = *data++; - if (!cmd) break; - len = *data++; -// ESP_LOGD(TAG, "Sending command %x", cmd); - res = st77xx_send_command(device, cmd); - if (res != ESP_OK) break; - if (len > 0) { -// ESP_LOGD(TAG, "Sending %d bytes of data", len); - res = st77xx_send_data(device, data, len); - if (res != ESP_OK) break; - } - data += len; - } - vTaskDelay(50 / portTICK_PERIOD_MS); - return ESP_OK; -} - -esp_err_t st77xx_init(ST77XX* device) { - esp_err_t res; - - if (device->pin_dcx < 0) return ESP_FAIL; - if (device->pin_cs < 0) return ESP_FAIL; - if (device->pin_reset < 0) return ESP_FAIL; - - /*if (device->mutex == NULL) { - device->mutex = xSemaphoreCreateMutex(); - }*/ - - if (device->mutex != NULL) xSemaphoreGive(device->mutex); - - ESP_LOGD(TAG, "pin_reset: %d", device->pin_reset); - ESP_LOGD(TAG, "pin_dcx: %d", device->pin_dcx); - - /* Setup reset */ - gpio_config_t reset_io_conf = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = 1LL << device->pin_reset, - .pull_down_en = 0, - .pull_up_en = 0, - }; - res = gpio_config(&reset_io_conf); - if (res != ESP_OK) return res; - - /* Setup DS */ - gpio_config_t dc_io_conf = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = 1LL << device->pin_dcx, - .pull_down_en = 0, - .pull_up_en = 0, - }; - res = gpio_config(&dc_io_conf); - if (res != ESP_OK) return res; - res = gpio_set_level(device->pin_dcx, true); - if (res != ESP_OK) return res; - - /* Setup SPI */ - if (device->spi_device == NULL) { - spi_device_interface_config_t devcfg = { - .command_bits = 0, - .address_bits = 0, - .dummy_bits = 0, - .mode = 0, // SPI mode 0 - .duty_cycle_pos = 128, - .cs_ena_pretrans = 0, - .cs_ena_posttrans = 0, - .clock_speed_hz = device->spi_speed, - .input_delay_ns = 0, - .spics_io_num = device->pin_cs, - .flags = SPI_DEVICE_HALFDUPLEX, - .queue_size = 1, - .pre_cb = st77xx_spi_pre_transfer_callback, // Handles D/C line - .post_cb = NULL - }; - res = spi_bus_add_device(device->spi_bus, &devcfg, &device->spi_device); - if (res != ESP_OK) return res; - } - - if (device->callback != NULL) { - device->callback(false); - } - - ESP_LOGE(TAG, "IO Setup complete "); - - //Reset the LCD display - res = st77xx_reset(device); - if (res != ESP_OK) return res; - - ESP_LOGE(TAG, "DC pin %d", device->pin_dcx); - - //Send the initialization data to the LCD display - res = st77xx_write_init_data(device, st77xx_init_data); - if (res != ESP_OK) return res; - - res = st77xx_set_cfg(device, device->rotation, device->color_mode); - if (res != ESP_OK) return res; - - return ESP_OK; -} - -esp_err_t st77xx_deinit(ST77XX* device) { - return ESP_OK; -} - -esp_err_t st77xx_write(ST77XX* device, const uint8_t *buffer) { - return st77xx_write_partial_direct(device, buffer, 0, 0, ST77XX_WIDTH, ST77XX_HEIGHT); -} - -#define MADCTL_MY 0x80 ///< Bottom to top -#define MADCTL_MX 0x40 ///< Right to left -#define MADCTL_MV 0x20 ///< Reverse Mode -#define MADCTL_ML 0x10 ///< LCD refresh Bottom to top -#define MADCTL_RGB 0x00 ///< Red-Green-Blue pixel order -#define MADCTL_BGR 0x08 ///< Blue-Green-Red pixel order -#define MADCTL_MH 0x04 ///< LCD refresh right to left - -esp_err_t st77xx_set_cfg(ST77XX * device, uint8_t rotation, bool color_mode) { - rotation = rotation & 0x07; - uint8_t m = 0; - - switch (rotation) { - case 0: - m |= MADCTL_MX; - break; - case 1: - m |= MADCTL_MV; - break; - case 2: - m |= MADCTL_MY; - break; - case 3: - m |= (MADCTL_MX | MADCTL_MY | MADCTL_MV); - break; - case 4: - m |= (MADCTL_MY | MADCTL_MV); - break; - } - - if (color_mode) { - m |= MADCTL_BGR; - } else { - m |= MADCTL_RGB; - } - ESP_LOGD(TAG, "MADCTL = 0x%x", m); - - uint8_t data[1] = {m}; - esp_err_t res = st77xx_send_command(device, ST77XX_MADCTL); - if (res != ESP_OK) return res; - res = st77xx_send_data(device, data, 1); - return res; -} - -esp_err_t st77xx_send_u32(ST77XX* device, const uint32_t data) { - uint8_t buffer[4]; - buffer[0] = (data>>24)&0xFF; - buffer[1] = (data>>16)&0xFF; - buffer[2] = (data>> 8)&0xFF; - buffer[3] = data &0xFF; - return st77xx_send_data(device, buffer, 4); -} - -esp_err_t st77xx_set_addr_window(ST77XX* device, uint16_t x, uint16_t y, uint16_t w, uint16_t h) { - uint32_t xa = ((uint32_t)x << 16) | (x+w-1); - uint32_t ya = ((uint32_t)y << 16) | (y+h-1); - esp_err_t res; -// ESP_LOGD(TAG, "CASET %x, RASET %x", xa, ya); - res = st77xx_send_command(device, ST77XX_CASET); - if (res != ESP_OK) return res; - res = st77xx_send_u32(device, xa); - if (res != ESP_OK) return res; - res = st77xx_send_command(device, ST77XX_RASET); - if (res != ESP_OK) return res; - res = st77xx_send_u32(device, ya); - if (res != ESP_OK) return res; - res = st77xx_send_command(device, ST77XX_RAMWR); - return res; -} - -esp_err_t st77xx_write_partial_direct(ST77XX* device, const uint8_t *buffer, uint16_t x, uint16_t y, uint16_t width, uint16_t height) { - if (device->spi_device == NULL) return ESP_FAIL; - if (device->mutex != NULL) xSemaphoreTake(device->mutex, portMAX_DELAY); - esp_err_t res; - res = st77xx_set_addr_window(device, x, y, width, height); - if (res != ESP_OK) { - if (device->mutex != NULL) xSemaphoreGive(device->mutex); - return res; - } - - uint32_t position = 0; - while (width * height * 2 - position > 0) { - uint32_t length = device->spi_max_transfer_size; - if (width * height * 2 - position < device->spi_max_transfer_size) length = width * height * 2 - position; - - res = st77xx_send_data(device, &buffer[position], length); - if (res != ESP_OK) { - if (device->mutex != NULL) xSemaphoreGive(device->mutex); - return res; - } - position += length; - } - if (device->mutex != NULL) xSemaphoreGive(device->mutex); - return res; -} - -esp_err_t st77xx_set_display(ST77XX* device, const bool state) { - esp_err_t res; - ESP_LOGI(TAG, "sleep display %s", state ? "on" : "off"); - if (device->mutex != NULL) xSemaphoreTake(device->mutex, portMAX_DELAY); - - if (state) { - res = st77xx_send_command(device, ST77XX_DISPON); - if (res != ESP_OK) return res; - } else { - res = st77xx_send_command(device, ST77XX_DISPOFF); - if (res != ESP_OK) return res; - } - if (device->mutex != NULL) xSemaphoreGive(device->mutex); - return res; -} diff --git a/components/troopers24-bsp b/components/troopers24-bsp index 389cf17..10a3698 160000 --- a/components/troopers24-bsp +++ b/components/troopers24-bsp @@ -1 +1 @@ -Subproject commit 389cf173312dab3438e5aaa05d46b31ee6ec232b +Subproject commit 10a3698c801bb1e6717082e1a5c7fa7a0a6bb5ca diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 69d40c8..1c68769 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -7,6 +7,7 @@ idf_component_register( "audio.c" "bootscreen.c" "ntp_helper.c" + "menus/utils.c" "menus/hatchery.c" "menus/settings.c" "menus/start.c" @@ -16,6 +17,8 @@ idf_component_register( "menus/launcher.c" "menus/id.c" "menus/agenda.c" + "menus/contacts.c" + "menus/nfcreader.c" "nametag.c" "file_browser.c" "test_common.c" @@ -37,33 +40,42 @@ idf_component_register( "menus" EMBED_TXTFILES ${project_dir}/resources/isrgrootx1.pem ${project_dir}/resources/custom_ota_cert.pem - EMBED_FILES ${project_dir}/resources/boot.pcm - ${project_dir}/resources/boot0.png - ${project_dir}/resources/boot1.png - ${project_dir}/resources/boot2.png - ${project_dir}/resources/boot3.png - ${project_dir}/resources/boot4.png - ${project_dir}/resources/boot5.png - ${project_dir}/resources/boot6.png - ${project_dir}/resources/troopers1.png - ${project_dir}/resources/icons/dev.png - ${project_dir}/resources/icons/home.png - ${project_dir}/resources/icons/settings.png - ${project_dir}/resources/icons/apps.png - ${project_dir}/resources/icons/hatchery.png - ${project_dir}/resources/icons/tag.png - ${project_dir}/resources/icons/bitstream.png - ${project_dir}/resources/icons/python.png - ${project_dir}/resources/icons/hourglass.png - ${project_dir}/resources/icons/update.png - ${project_dir}/resources/icons/sao.png - ${project_dir}/resources/icons/calendar.png - ${project_dir}/resources/icons/clock.png - ${project_dir}/resources/icons/bookmark.png - ${project_dir}/resources/id/id_shield.png - ${project_dir}/resources/id/id_ernw.png - ${project_dir}/resources/id/id_fucss.png - ${project_dir}/resources/id/id_fishbowl.png - ${project_dir}/resources/id/id_badgeteam.png - ${project_dir}/resources/nametag.png + EMBED_FILES + ${project_dir}/resources/happy.mp3 + ${project_dir}/resources/boot.mp3 + ${project_dir}/resources/boot.png + ${project_dir}/resources/boot0.png + ${project_dir}/resources/boot1.png + ${project_dir}/resources/boot2.png + ${project_dir}/resources/boot3.png + ${project_dir}/resources/boot4.png + ${project_dir}/resources/boot5.png + ${project_dir}/resources/boot6.png + ${project_dir}/resources/troopers1.png + ${project_dir}/resources/icons/dev.png + ${project_dir}/resources/icons/home.png + ${project_dir}/resources/icons/settings.png + ${project_dir}/resources/icons/apps.png + ${project_dir}/resources/icons/hatchery.png + ${project_dir}/resources/icons/tag.png + ${project_dir}/resources/icons/bitstream.png + ${project_dir}/resources/icons/python.png + ${project_dir}/resources/icons/hourglass.png + ${project_dir}/resources/icons/update.png + ${project_dir}/resources/icons/sao.png + ${project_dir}/resources/icons/calendar.png + ${project_dir}/resources/icons/clock.png + ${project_dir}/resources/icons/bookmark.png + ${project_dir}/resources/icons/addressbook.png + ${project_dir}/resources/icons/edit.png + ${project_dir}/resources/icons/badge.png + ${project_dir}/resources/icons/share.png + ${project_dir}/resources/icons/receive.png + ${project_dir}/resources/id/id_shield.png + ${project_dir}/resources/id/id_15.png + ${project_dir}/resources/id/id_glass.png + ${project_dir}/resources/id/id_storytellers.png + ${project_dir}/resources/id/id_ernw.png + ${project_dir}/resources/nametag.png + ${project_dir}/resources/tr23_nametag.png ) diff --git a/main/audio.c b/main/audio.c index a10b780..73acd96 100644 --- a/main/audio.c +++ b/main/audio.c @@ -9,6 +9,12 @@ #include "driver/i2s.h" #include "driver/rtc_io.h" #include "esp_system.h" +#include "esp_log.h" + +static const char* TAG = "audio"; + +static xSemaphoreHandle audio_mutex; +bool audio_playing = false; void _audio_init(int i2s_num) { i2s_config_t i2s_config = {.mode = I2S_MODE_MASTER | I2S_MODE_TX, @@ -27,57 +33,82 @@ void _audio_init(int i2s_num) { i2s_pin_config_t pin_config = {.mck_io_num = -1, .bck_io_num = GPIO_I2S_BCLK, .ws_io_num = GPIO_I2S_WS, .data_out_num = GPIO_I2S_DATA, .data_in_num = I2S_PIN_NO_CHANGE}; i2s_set_pin(i2s_num, &pin_config); + audio_mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(audio_mutex); } typedef struct _audio_player_cfg { uint8_t* buffer; + size_t current; size_t size; - bool free_buffer; } audio_player_cfg_t; -void audio_player_task(void* arg) { - audio_player_cfg_t* config = (audio_player_cfg_t*) arg; - size_t sample_length = config->size; - uint8_t* sample_buffer = config->buffer; - - size_t count; - size_t position = 0; - - while (position < sample_length) { - size_t length = sample_length - position; - if (length > 256) length = 256; - uint8_t buffer[256]; - memcpy(buffer, &sample_buffer[position], length); - for (size_t l = 0; l < length; l += 2) { - int16_t* sample = (int16_t*) &buffer[l]; - *sample *= 0.55; - } - i2s_write(0, buffer, length, &count, portMAX_DELAY); - if (count != length) { - printf("i2s_write_bytes: count (%d) != length (%d)\n", count, length); - abort(); - } - position += length; - } - i2s_zero_dma_buffer(0); // Fill buffer with silence - if (config->free_buffer) free(sample_buffer); - vTaskDelete(NULL); // Tell FreeRTOS that the task is done +extern const uint8_t boot_mp3_start[] asm("_binary_boot_mp3_start"); +extern const uint8_t boot_mp3_end[] asm("_binary_boot_mp3_end"); + +//extern const uint8_t happy_snd_start[] asm("_binary_happy_pcm_start"); +//extern const uint8_t happy_snd_end[] asm("_binary_happy_pcm_end"); + +extern const uint8_t happy_mp3_start[] asm("_binary_happy_mp3_start"); +extern const uint8_t happy_mp3_end[] asm("_binary_happy_mp3_end"); + +static ssize_t play_from_resource(void* config, void* buf, size_t len) { + audio_player_cfg_t* cfg = (audio_player_cfg_t*) config; + size_t remaining = cfg->size - cfg->current; + size_t read = remaining < len ? remaining : len; + memcpy(buf, cfg->buffer + cfg->current, read); + cfg->current += read; + return (ssize_t) read; } -void audio_init() { _audio_init(0); } +static ssize_t seek_from_resource(void* config, size_t pos, size_t unknown) { + audio_player_cfg_t* cfg = (audio_player_cfg_t*) config; + cfg->current = pos; + return 0; +} -extern const uint8_t boot_snd_start[] asm("_binary_boot_pcm_start"); -extern const uint8_t boot_snd_end[] asm("_binary_boot_pcm_end"); +static ssize_t audio_ended(void* handle, size_t a, size_t b) { + audio_playing = false; + return 0; +} -audio_player_cfg_t bootsound; +int play_from_resources(audio_player_cfg_t* cfg, const uint8_t* start, const uint8_t* end) { + cfg->buffer = (uint8_t*) (start); + cfg->size = end - start; + cfg->current = 0; -void play_bootsound() { - TaskHandle_t handle; + int id = sndmixer_queue_mp3_stream(play_from_resource, seek_from_resource, (void*) cfg); + sndmixer_set_volume(id, 128); + sndmixer_play(id); + return id; +} - bootsound.buffer = (uint8_t*) (boot_snd_start); - bootsound.size = boot_snd_end - boot_snd_start; - bootsound.free_buffer = false; +audio_player_cfg_t cfg_boot; +audio_player_cfg_t cfg_happy; - xTaskCreate(&audio_player_task, "Audio player", 4096, (void*) &bootsound, 10, &handle); +void play_bootsound() { + play_from_resources(&cfg_boot, boot_mp3_start, boot_mp3_end); } + +void play_happy_birthday(bool connected) { + if (!connected || audio_playing) { + return; + } + audio_playing = true; + int id = play_from_resources(&cfg_happy, happy_mp3_start, happy_mp3_end); + sndmixer_set_callback(id, audio_ended, NULL); +} + +void audio_init() { +// _audio_init(0); + i2s_pin_config_t pin_config = { + .mck_io_num = -1, + .bck_io_num = GPIO_I2S_BCLK, + .ws_io_num = GPIO_I2S_WS, + .data_out_num = GPIO_I2S_DATA, + .data_in_num = I2S_PIN_NO_CHANGE + }; + sndmixer_init(1, false, &pin_config); + set_sao_callback_tr24(&play_happy_birthday); +} \ No newline at end of file diff --git a/main/bootscreen.c b/main/bootscreen.c index 2403335..18161a0 100644 --- a/main/bootscreen.c +++ b/main/bootscreen.c @@ -32,6 +32,9 @@ extern const uint8_t frame5_png_end[] asm("_binary_boot5_png_end"); extern const uint8_t frame6_png_start[] asm("_binary_boot6_png_start"); extern const uint8_t frame6_png_end[] asm("_binary_boot6_png_end"); +extern const uint8_t boot_png_start[] asm("_binary_boot_png_start"); +extern const uint8_t boot_png_end[] asm("_binary_boot_png_end"); + void display_frame(const uint8_t start[], const uint8_t end[]) { pax_buf_t* pax_buffer = get_pax_buffer(); pax_noclip(pax_buffer); @@ -65,12 +68,9 @@ void display_boot_animation() { void display_story_splash() { pax_buf_t* pax_buffer = get_pax_buffer(); - const pax_font_t* font = pax_font_saira_regular; - const char* text = "You enter a forest\nclearing, and find\nan old shield\nresting on the\nground.\n\nMaybe this can\nprotect you on the\njourney ahead..."; pax_noclip(pax_buffer); - pax_background(pax_buffer, 0x131313); - pax_vec1_t size = pax_text_size(font, 22, text); - pax_draw_text(pax_buffer, 0xFFF1AA13, font, 22, 160 - (size.x/2), 120 - (size.y/2), text); + pax_background(pax_buffer, 0xFFFFFF); + pax_insert_png_buf(pax_buffer, boot_png_start, boot_png_end - boot_png_start, 0, 0, 0); display_flush(); vTaskDelay(pdMS_TO_TICKS(500)); } diff --git a/main/factory_test.c b/main/factory_test.c index 2a88cd7..bc768a6 100644 --- a/main/factory_test.c +++ b/main/factory_test.c @@ -184,13 +184,12 @@ bool test_nfc_read_uid(uint32_t* rc) { return false; } - rfalNfcDevice nfcDevice = {0}; bool found = false; int tries = 30; int remaining = tries; while (!found && remaining-- > 0) { - esp_err_t res = st25r3911b_discover(&nfcDevice, 1000); + esp_err_t res = st25r3911b_discover(NULL, 1000, DISCOVER_MODE_LISTEN_NFCA); if (res == ESP_ERR_TIMEOUT) { continue; } @@ -199,6 +198,8 @@ bool test_nfc_read_uid(uint32_t* rc) { } } + + *rc = (uint32_t) tries - remaining; return (found == true); } @@ -237,7 +238,7 @@ uint8_t led_red[NUM_LEDS*3] = {0}; uint8_t led_blue[NUM_LEDS*3] = {0}; uint8_t led_white[NUM_LEDS*3] = {0}; -_Noreturn void factory_test() { +void factory_test() { for (int i = 0; i < NUM_LEDS; i++) { led_green[3*i] = 50; led_green[3*i+1] = 0; @@ -258,54 +259,53 @@ _Noreturn void factory_test() { pax_buf_t* pax_buffer = get_pax_buffer(); uint8_t factory_test_done = nvs_get_u8_default("system", "factory_test", 0); - if (!factory_test_done) { - st77xx_backlight(true); - bool result; + ESP_LOGI(TAG, "factory_test_done %d", factory_test_done); - ESP_LOGI(TAG, "Factory test start"); + if (!key_currently_pressed(BUTTON_START) || !key_currently_pressed(BUTTON_SELECT)) { + return; + } - result = run_basic_tests(); + st77xx_backlight(true); + bool result; - if (result) { - ws2812_send_data(led_blue, sizeof(led_blue)); - } else { - ws2812_send_data(led_red, sizeof(led_red)); - } + ESP_LOGI(TAG, "Factory test start"); - if (!result) goto test_end; + result = run_basic_tests(); - // Wait for the operator to unplug the badge - test_end: + if (result) { + ws2812_send_data(led_blue, sizeof(led_blue)); + } else { + ws2812_send_data(led_red, sizeof(led_red)); + } - if (result) { - esp_err_t res = nvs_set_u8_fixed("system", "factory_test", 0x01); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to store test result %d\n", res); - result = false; - ws2812_send_data(led_red, sizeof(led_red)); - pax_noclip(pax_buffer); - pax_background(pax_buffer, 0xa85a32); - display_flush(); - } - wifi_set_defaults(); + if (result) { + esp_err_t res = nvs_set_u8_fixed("system", "factory_test", 0x01); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Failed to store test result %d\n", res); + result = false; + ws2812_send_data(led_red, sizeof(led_red)); pax_noclip(pax_buffer); - pax_background(pax_buffer, 0x00FF00); + pax_background(pax_buffer, 0xa85a32); display_flush(); - ws2812_send_data(led_green, sizeof(led_green)); - - ESP_LOGI(TAG, "Make sure the speaker is NOT muted and a sound is playing"); - pax_draw_text(pax_buffer, 0xffff0000, pax_font_sky_mono, 36, 0, 20, "SUCCESS!"); - pax_draw_text(pax_buffer, 0xffff0000, pax_font_sky_mono, 16, 0, 56, "Does the speaker work?"); - display_flush(); - - while (true) { - play_bootsound(); - vTaskDelay(2000 / portTICK_PERIOD_MS); - } } + wifi_set_defaults(); + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0x00FF00); + display_flush(); + ws2812_send_data(led_green, sizeof(led_green)); + + ESP_LOGI(TAG, "Make sure the speaker is NOT muted and a sound is playing"); + pax_draw_text(pax_buffer, 0xffff0000, pax_font_sky_mono, 36, 0, 20, "SUCCESS!"); + pax_draw_text(pax_buffer, 0xffff0000, pax_font_sky_mono, 16, 0, 56, "Does the speaker work?"); + display_flush(); while (true) { - vTaskDelay(1000 / portTICK_PERIOD_MS); + play_bootsound(); + vTaskDelay(2000 / portTICK_PERIOD_MS); } } + + while (true) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } } diff --git a/main/file_browser.c b/main/file_browser.c index 88e2edd..a47cd0a 100644 --- a/main/file_browser.c +++ b/main/file_browser.c @@ -14,7 +14,6 @@ #include "esp_vfs.h" #include "esp_vfs_fat.h" #include "hardware.h" -#include "ili9341.h" #include "menu.h" #include "pax_gfx.h" #include "system_wrapper.h" diff --git a/main/include/audio.h b/main/include/audio.h index 31c6b67..c873fbc 100644 --- a/main/include/audio.h +++ b/main/include/audio.h @@ -1,4 +1,7 @@ #pragma once +#include +extern bool audio_playing; + void audio_init(); void play_bootsound(); diff --git a/main/include/factory_test.h b/main/include/factory_test.h index 6968f02..9cd7ed4 100644 --- a/main/include/factory_test.h +++ b/main/include/factory_test.h @@ -1,4 +1,4 @@ #include #pragma once -_Noreturn void factory_test(); +void factory_test(); diff --git a/main/include/http_download.h b/main/include/http_download.h index 1dd44b9..b3ec6ed 100644 --- a/main/include/http_download.h +++ b/main/include/http_download.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include diff --git a/main/main.c b/main/main.c index 6468289..52d1d46 100644 --- a/main/main.c +++ b/main/main.c @@ -44,6 +44,8 @@ #include "wifi_ota.h" #include "ws2812.h" +#define DEBUG_BOOT 0 + extern const uint8_t logo_screen_png_start[] asm("_binary_logo_screen_png_start"); extern const uint8_t logo_screen_png_end[] asm("_binary_logo_screen_png_end"); @@ -76,7 +78,7 @@ void stop() { } } -const char* fatal_error_str = "A fatal error occured"; +const char* fatal_error_str = "A fatal error occurred"; const char* reset_board_str = "Reset the board to try again"; static xSemaphoreHandle boot_mutex; @@ -205,45 +207,26 @@ _Noreturn void app_main(void) { stop(); } - // TODO: This is resetting the factory test status, so that factory test will run on every boot - nvs_set_u8_fixed("system", "factory_test", 0x00); factory_test(); /* Initialize LCD screen */ pax_buf_t* pax_buffer = get_pax_buffer(); + pax_background(pax_buffer, 0xFF1E1E1E); + display_flush(); + +#if DEBUG_BOOT == 0 xTaskCreate(boot_animation_task, "boot_anim_task", 4096, NULL, 12, NULL); +#endif /* Turning the backlight on */ -#ifdef TR23 - gpio_config_t io_conf = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = 1LL << GPIO_LCD_BL, - .pull_down_en = 0, - .pull_up_en = 0, - }; - res = gpio_config(&io_conf); - printf("set pin direction\n"); - if (res != ESP_OK) { - ESP_LOGE(TAG, "LCD Backlight set_direction failed: %d", res); - display_fatal_error(fatal_error_str, "Failed to set LCD backlight pin mode", "Flash may be corrupted", reset_board_str); - stop(); - } - res = gpio_set_level(GPIO_LCD_BL, true); - if (res != ESP_OK) { - ESP_LOGE(TAG, "LCD Backlight set_level failed: %d", res); - display_fatal_error(fatal_error_str, "Failed to turn on LCD backlight", "Flash may be corrupted", reset_board_str); - stop(); - } -#else st77xx_backlight(true); -#endif - +#if DEBUG_BOOT == 0 if (!wakeup_deepsleep) { /* TROOPERS */ xTaskCreate(audio_player_task, "audio_player_task", 2048, NULL, 12, NULL); } +#endif /* Start AppFS */ res = appfs_init(); @@ -292,6 +275,7 @@ _Noreturn void app_main(void) { Controller *controller = get_controller(); controller_enable(controller); +#if DEBUG_BOOT == 0 /* Start WiFi */ wifi_init(); @@ -317,15 +301,18 @@ _Noreturn void app_main(void) { display_fatal_error(fatal_error_str, "Failed to initialize", "TLS certificate storage", reset_board_str); stop(); } +#endif /* Clear RTC memory */ rtc_memory_clear(); +#if DEBUG_BOOT == 0 /* Try to update the RTC */ xTaskCreate(ntp_sync_task, "ntp_sync_task", 4096, NULL, 12, NULL); /* Wait for boot animation to complete */ wait_for_boot_anim(); +#endif ESP_LOGW(TAG, "done"); diff --git a/main/menus/agenda.c b/main/menus/agenda.c index f7be24f..98629b3 100644 --- a/main/menus/agenda.c +++ b/main/menus/agenda.c @@ -2,7 +2,6 @@ #include #include "app_management.h" -#include "esp_http_client.h" #include "graphics_wrapper.h" #include "hardware.h" #include "http_download.h" @@ -12,6 +11,7 @@ #include "pax_gfx.h" #include "system_wrapper.h" #include "wifi_connect.h" +#include "utils.h" static const char* TAG = "agenda"; @@ -115,7 +115,7 @@ static inline void do_init() { ESP_LOGI(TAG, "Successfully written initial agenda data"); } -void agenda_render_background(pax_buf_t* pax_buffer) { +static void agenda_render_background(pax_buf_t* pax_buffer) { const pax_font_t* font = pax_font_saira_regular; pax_background(pax_buffer, 0xFF1E1E1E); pax_noclip(pax_buffer); @@ -123,7 +123,7 @@ void agenda_render_background(pax_buf_t* pax_buffer) { pax_draw_text(pax_buffer, 0xffffffff, font, 18, 5, 240 - 18, "🅰 Accept 🅱 Exit"); } -void details_render_background(pax_buf_t* pax_buffer, bool tracks, bool days) { +static void details_render_background(pax_buf_t* pax_buffer, bool tracks, bool days) { const pax_font_t* font = pax_font_saira_regular; pax_background(pax_buffer, 0xFF1E1E1E); pax_noclip(pax_buffer); @@ -137,14 +137,14 @@ void details_render_background(pax_buf_t* pax_buffer, bool tracks, bool days) { } } -void render_topbar(pax_buf_t* pax_buffer, pax_buf_t* icon, const char* text) { +static void render_topbar(pax_buf_t* pax_buffer, pax_buf_t* icon, const char* text) { const pax_font_t* font = pax_font_saira_regular; pax_simple_rect(pax_buffer, 0xff131313, 0, 0, 320, 34); pax_draw_image(pax_buffer, icon, 1, 1); pax_draw_text(pax_buffer, 0xFFF1AA13, font, 18, 34, 8, text); } -int render_track(pax_buf_t* pax_buffer, pax_buf_t* icon_top, pax_buf_t* icon_bookmarked, cJSON* tracks, int track, int talk, int render_talks, int slot_height) { +static int render_track(pax_buf_t* pax_buffer, pax_buf_t* icon_top, pax_buf_t* icon_bookmarked, cJSON* tracks, int track, int talk, int render_talks, int slot_height) { const pax_font_t* font = pax_font_saira_regular; cJSON* track_data = cJSON_GetArrayItem(tracks, track); @@ -220,7 +220,7 @@ int render_track(pax_buf_t* pax_buffer, pax_buf_t* icon_top, pax_buf_t* icon_boo return talk_count; } -int render_bookmarks(pax_buf_t* pax_buffer, pax_buf_t* icon, cJSON* bookmarks, int day, int talk, int render_talks, int slot_height) { +static int render_bookmarks(pax_buf_t* pax_buffer, pax_buf_t* icon, cJSON* bookmarks, int day, int talk, int render_talks, int slot_height) { const pax_font_t* font = pax_font_saira_regular; int talk_count = cJSON_GetArraySize(bookmarks); @@ -307,7 +307,7 @@ int render_bookmarks(pax_buf_t* pax_buffer, pax_buf_t* icon, cJSON* bookmarks, i return talk_count; } -bool save_bookmarks() { +static bool save_bookmarks() { char* json_string = cJSON_PrintUnformatted(json_my); if (json_string == NULL) { ESP_LOGE(TAG, "Cannot serialize bookmarks"); @@ -326,7 +326,7 @@ bool save_bookmarks() { return true; } -bool toggle_bookmark(cJSON* track, cJSON* talk, cJSON* my_day, cJSON* bookmarks) { +static bool toggle_bookmark(cJSON* track, cJSON* talk, cJSON* my_day, cJSON* bookmarks) { if (talk == NULL) { ESP_LOGW(TAG, "Cannot toggle bookmark if no talk is given"); return false; @@ -387,7 +387,7 @@ bool toggle_bookmark(cJSON* track, cJSON* talk, cJSON* my_day, cJSON* bookmarks) return true; } -void details_day(pax_buf_t* pax_buffer, xQueueHandle button_queue, cJSON* data, cJSON* my_day, cJSON* bookmarks, pax_buf_t* icon_top, pax_buf_t* icon_bookmarked) { +static void details_day(pax_buf_t* pax_buffer, xQueueHandle button_queue, cJSON* data, cJSON* my_day, cJSON* bookmarks, pax_buf_t* icon_top, pax_buf_t* icon_bookmarked) { int track = 0; cJSON* tracks = cJSON_GetObjectItem(data, "tracks"); int track_count = cJSON_GetArraySize(tracks); @@ -461,7 +461,7 @@ void details_day(pax_buf_t* pax_buffer, xQueueHandle button_queue, cJSON* data, } } -void my_agenda(pax_buf_t* pax_buffer, xQueueHandle button_queue, cJSON* day1, cJSON* day2, pax_buf_t* icon) { +static void my_agenda(pax_buf_t* pax_buffer, xQueueHandle button_queue, cJSON* day1, cJSON* day2, pax_buf_t* icon) { if (day1 == NULL || day2 == NULL) { render_message("No talks found"); display_flush(); @@ -534,7 +534,7 @@ void my_agenda(pax_buf_t* pax_buffer, xQueueHandle button_queue, cJSON* day1, cJ } -void details_upcoming(pax_buf_t* pax_buffer, cJSON* data, pax_buf_t* icon) { +static void details_upcoming(pax_buf_t* pax_buffer, cJSON* data, pax_buf_t* icon) { const pax_font_t* font = pax_font_saira_regular; if (data == NULL) { @@ -622,7 +622,7 @@ void details_upcoming(pax_buf_t* pax_buffer, cJSON* data, pax_buf_t* icon) { wait_for_button(); } -bool need_update(unsigned long *remote_last_update) { +static bool need_update(unsigned long *remote_last_update) { FILE* last_update_fd = fopen(last_update_path, "r"); if (last_update_fd == NULL) { return true; @@ -652,54 +652,10 @@ bool need_update(unsigned long *remote_last_update) { return *remote_last_update > local_last_update; } -bool load_file(const char* filename, char** buf, size_t* len) { - FILE* fd = fopen(filename, "r"); - if (fd == NULL) { - ESP_LOGE(TAG, "Unable to open file: %s", filename); - return false; - } - - /* Go to the end of the file. */ - if (fseek(fd, 0L, SEEK_END) == 0) { - /* Get the size of the file. */ - *len = ftell(fd); - - if (*buf != NULL) { - free(*buf); - } - - /* Allocate our buffer to that size. */ - *buf = malloc(*len); - - /* Go back to the start of the file. */ - if (fseek(fd, 0L, SEEK_SET) != 0) { - free(*buf); - *len = 0; - ESP_LOGE(TAG, "Failed to seek to start"); - return false; - } - - /* Read the entire file into memory. */ - fread(*buf, 1, *len, fd); - int err = ferror(fd); - if (err != 0) { - free(*buf); - *len = 0; - ESP_LOGE(TAG, "Failed to read file: %d", err); - return false; - } - } else { - ESP_LOGE(TAG, "Failed to seek to end"); - return false; - } - fclose(fd); - return true; -} - -bool test_load_data() { - if (!load_file(day1_path_tmp, &data_day1, &size_day1)) return false; +static bool test_load_data() { + if (!load_file(TAG, day1_path_tmp, &data_day1, &size_day1)) return false; - if (!load_file(day2_path_tmp, &data_day2, &size_day2)) return false; + if (!load_file(TAG, day2_path_tmp, &data_day2, &size_day2)) return false; json_day1 = cJSON_ParseWithLength(data_day1, size_day1); if (json_day1 == NULL) { @@ -714,7 +670,7 @@ bool test_load_data() { return true; } -uint find_correct_position(cJSON* my_day, long start) { +static uint find_correct_position(cJSON* my_day, long start) { cJSON* next = cJSON_GetArrayItem(my_day, 0); int i = 0; while(next != NULL && cJSON_GetNumberValue(cJSON_GetObjectItem(cJSON_GetObjectItem(next, "talk"), "start")) <= start) { @@ -724,7 +680,7 @@ uint find_correct_position(cJSON* my_day, long start) { return i; } -bool load_my_data(cJSON* day, cJSON* bookmarks, cJSON* results) { +static bool load_my_data(cJSON* day, cJSON* bookmarks, cJSON* results) { cJSON *bookmark; int bookmark_count = cJSON_GetArraySize(bookmarks); @@ -792,22 +748,22 @@ bool load_my_data(cJSON* day, cJSON* bookmarks, cJSON* results) { return true; } -bool load_data() { - if (!load_file(day1_path, &data_day1, &size_day1)) { +static bool load_data() { + if (!load_file(TAG, day1_path, &data_day1, &size_day1)) { ESP_LOGE(TAG, "Failed to read agenda file: %s", day1_path); render_message("Failed to read agenda. 🅰 to retry."); display_flush(); return false; } - if (!load_file(day2_path, &data_day2, &size_day2)) { + if (!load_file(TAG, day2_path, &data_day2, &size_day2)) { ESP_LOGE(TAG, "Failed to read agenda file: %s", day2_path); render_message("Failed to read agenda. 🅰 to retry."); display_flush(); return false; } - if (!load_file(my_agenda_path, &data_my, &size_my)) { + if (!load_file(TAG, my_agenda_path, &data_my, &size_my)) { ESP_LOGE(TAG, "Failed to read agenda file: %s", my_agenda_path); render_message("Failed to read agenda. 🅰 to retry."); display_flush(); @@ -870,21 +826,7 @@ bool load_data() { return true; } -bool rename_or_replace(const char* old, const char* new) { - if (access(new, F_OK) == 0) { - ESP_LOGD(TAG, "Destination file exists, deleting..."); - // File exists, try to delete - if (remove(new) != 0) { - ESP_LOGD(TAG, "Destination file could not be deleted"); - // Deleting failed - return false; - } - } - - return rename(old, new) != 0; -} - -bool update_agenda(xQueueHandle button_queue, bool force) { +static bool update_agenda(xQueueHandle button_queue, bool force) { render_message("Updating agenda..."); display_flush(); @@ -895,7 +837,7 @@ bool update_agenda(xQueueHandle button_queue, bool force) { return false; } - unsigned long last_update; + unsigned long last_update = 0; if (!need_update(&last_update) && !force) { ESP_LOGI(TAG, "No update needed"); wifi_disconnect_and_disable(); @@ -929,7 +871,7 @@ bool update_agenda(xQueueHandle button_queue, bool force) { - if (rename_or_replace(day1_path_tmp,day1_path)) { + if (rename_or_replace(TAG, day1_path_tmp,day1_path)) { ESP_LOGE(TAG, "Failed to rename %s to %s", day1_path_tmp, day1_path); render_message("Failed to store file"); display_flush(); @@ -938,7 +880,7 @@ bool update_agenda(xQueueHandle button_queue, bool force) { return true; } - if (rename_or_replace(day2_path_tmp, day2_path)) { + if (rename_or_replace(TAG, day2_path_tmp, day2_path)) { ESP_LOGE(TAG, "Failed to rename %s to %s", day2_path_tmp, day2_path); render_message("Failed to store file"); display_flush(); @@ -963,7 +905,7 @@ bool update_agenda(xQueueHandle button_queue, bool force) { return true; } -cJSON* get_current_day() { +static cJSON* get_current_day() { time_t now; struct tm timeinfo; time(&now); @@ -979,7 +921,7 @@ cJSON* get_current_day() { return NULL; } -bool try_update_or_load(xQueueHandle button_queue, bool first_attempt) { +static bool try_update_or_load(xQueueHandle button_queue, bool first_attempt) { if (update_agenda(button_queue, !first_attempt)) { return true; } diff --git a/main/menus/contacts.c b/main/menus/contacts.c new file mode 100644 index 0000000..85613e0 --- /dev/null +++ b/main/menus/contacts.c @@ -0,0 +1,859 @@ +#include +#include + +#include "app_management.h" +#include "efuse.h" +#include "esp_http_client.h" +#include "graphics_wrapper.h" +#include "hardware.h" +#include "http_download.h" +#include "menu.h" +#include "ntp_helper.h" +#include "pax_codecs.h" +#include "pax_gfx.h" +#include "rtc_wdt.h" +#include "system_wrapper.h" +#include "utils.h" +#include "wifi_connect.h" +#include "qrcodegen.h" + +static const char* TAG = "contacts"; + +static const char* self_path = "/internal/apps/contacts/self.json"; +static const char* database_path = "/internal/apps/contacts/db.json"; + +static const char* DEFAULT_SELF = "{\"id\": 0, \"name\": null, \"tel\": null, \"email\": null, \"url\": null, \"nick\": null}"; +static const char* DEFAULT_DATABASE = "{}"; + +char VCARD[MAX_NFC_BUFFER_SIZE]; + +extern const uint8_t edit_png_start[] asm("_binary_edit_png_start"); +extern const uint8_t edit_png_end[] asm("_binary_edit_png_end"); + +extern const uint8_t badge_png_start[] asm("_binary_badge_png_start"); +extern const uint8_t badge_png_end[] asm("_binary_badge_png_end"); + +extern const uint8_t addressbook_png_start[] asm("_binary_addressbook_png_start"); +extern const uint8_t addressbook_png_end[] asm("_binary_addressbook_png_end"); + +extern const uint8_t share_png_start[] asm("_binary_share_png_start"); +extern const uint8_t share_png_end[] asm("_binary_share_png_end"); + +extern const uint8_t receive_png_start[] asm("_binary_receive_png_start"); +extern const uint8_t receive_png_end[] asm("_binary_receive_png_end"); + + +typedef enum action { + ACTION_NONE, + ACTION_EDIT, + ACTION_LIST, + ACTION_QRCODE, + ACTION_SHARE, + ACTION_IMPORT, + // Edit Self + ACTION_EDIT_NAME, + ACTION_EDIT_TEL, + ACTION_EDIT_EMAIL, + ACTION_EDIT_URL, + ACTION_EDIT_NICK, +} menu_contacts_action_t; + +#define MIN(a, b) ((a < b) ? a : b) + +static char* data_self = NULL; +static size_t size_self = 0; +static cJSON* json_self = NULL; + +static char* data_db = NULL; +static size_t size_db = 0; +static cJSON* json_db = NULL; + +static void render_background(pax_buf_t* pax_buffer, const char* text) { + const pax_font_t* font = pax_font_saira_regular; + pax_background(pax_buffer, 0xFF1E1E1E); + pax_noclip(pax_buffer); + pax_simple_rect(pax_buffer, 0xff131313, 0, 220, 320, 20); + pax_draw_text(pax_buffer, 0xffffffff, font, 18, 5, 240 - 18, text); +} + +static void render_topbar(pax_buf_t* pax_buffer, pax_buf_t* icon, const char* text) { + const pax_font_t* font = pax_font_saira_regular; + pax_simple_rect(pax_buffer, 0xff131313, 0, 0, 320, 34); + pax_draw_image(pax_buffer, icon, 1, 1); + pax_draw_text(pax_buffer, 0xFFF1AA13, font, 18, 34, 8, text); +} + +static uint find_correct_position(cJSON* contacts, long id) { + cJSON* next = cJSON_GetArrayItem(contacts, 0); + int i = 0; + while(next != NULL && cJSON_GetNumberValue(cJSON_GetObjectItem(cJSON_GetObjectItem(next, "talk"), "start")) <= id) { + i++; + next = next->next; + } + return i; +} + +static bool save(cJSON* data, const char* filename) { + if (data == NULL) { + return false; + } + char* repr = cJSON_PrintUnformatted(data); + + FILE* fd = fopen(self_path, "w"); + if (fd == NULL) { + ESP_LOGE(TAG, "Failed to open %s for writing", filename); + return false; + } + + ESP_LOGI(TAG, "Saving: %s", repr); + + fwrite(repr, 1, strlen(repr), fd); + fclose(fd); + + return true; +} + +static bool save_self() { + return save(json_self, self_path); +} + +static bool save_db() { + return save(json_db, database_path); +} + +static bool do_init() { + if (file_exists(self_path)) { + return true; + } + + FILE* self_fd = fopen(self_path, "w"); + if (self_fd == NULL) { + ESP_LOGE(TAG, "Failed to create own contact details file: %s", self_path); + return false; + } + fwrite(DEFAULT_SELF, 1, strlen(DEFAULT_SELF), self_fd); + fclose(self_fd); + + FILE* db_fd = fopen(database_path, "w"); + if (db_fd == NULL) { + ESP_LOGE(TAG, "Failed to create contacts file: %s", database_path); + return false; + } + fwrite(DEFAULT_DATABASE, 1, strlen(DEFAULT_DATABASE), db_fd); + fclose(db_fd); + + return true; +} + +static bool load_data() { + if (!do_init()) { + render_message("Failed to initialize files"); + display_flush(); + return false; + } + + if (!load_file(TAG, self_path, &data_self, &size_self)) { + ESP_LOGE(TAG, "Failed to read own contact details file: %s", self_path); + render_message("Failed to read own contact details"); + display_flush(); + return false; + } + + if (!load_file(TAG, database_path, &data_db, &size_db)) { + ESP_LOGE(TAG, "Failed to read agenda file: %s", database_path); + render_message("Failed to read contacts"); + display_flush(); + return false; + } + + + if (json_self != NULL) { + cJSON_Delete(json_self); + } + json_self = cJSON_ParseWithLength(data_self, size_self); + if (json_self == NULL) { + ESP_LOGE(TAG, "Failed to parse own contact details file: %s", self_path); + render_message("Failed to parse own contact details"); + display_flush(); + return false; + } + + if (!cJSON_HasObjectItem(json_self, "name") || cJSON_GetStringValue(cJSON_GetObjectItem(json_self, "name")) == NULL) { + cJSON_DeleteItemFromObject(json_self, "name"); + } + + if (!cJSON_HasObjectItem(json_self, "name")) { + char name[15] = {0}; + snprintf(name, 15, "Trooper #%d", badge_id()); + cJSON_AddStringToObject(json_self, "name", name); + } + + // Always overwrite with badge_id + if (cJSON_HasObjectItem(json_self, "id")) { + cJSON_SetIntValue(cJSON_GetObjectItem(json_self, "id"), badge_id()); + } else { + cJSON_AddNumberToObject(json_self, "id", badge_id()); + } + + ESP_LOGI(TAG, "%s", cJSON_Print(json_self)); + + if (json_db != NULL) { + cJSON_Delete(json_db); + } + json_db = cJSON_ParseWithLength(data_db, size_db); + if (json_db == NULL) { + ESP_LOGE(TAG, "Failed to parse contacts file: %s", database_path); + ESP_LOGE(TAG, "DEBUG %d: %s", size_db, data_db); + render_message("Failed to parse contacts"); + display_flush(); + return false; + } + + ESP_LOGI(TAG, "%s", cJSON_Print(json_db)); + + return true; +} + +static void append_str(char **dst, char *src, size_t len) { + memcpy(*dst, src, len); + *dst += len; +} + +static void add_if_not_null(cJSON* data, char **dst, const char *prefix, const char *key, size_t maxLen) { + cJSON* elem = cJSON_GetObjectItem(data, key); + char* str = NULL; + if (cJSON_IsNumber(elem)) { + char buf[4]; + sprintf(buf, "%d", ((uint16_t) cJSON_GetNumberValue(elem)) % 999); + str = buf; + } else { + str = cJSON_GetStringValue(elem); + } + if (str == NULL) { + return; + } + + ESP_LOGI(TAG, "elem is string"); + append_str(dst, (char *) prefix, strlen(prefix)); + ESP_LOGI(TAG, "appended prefix"); + ESP_LOGI(TAG, "%s", VCARD); + + ESP_LOGI(TAG, "adding string str=%p", str); + uint len = strlen(str); + ESP_LOGI(TAG, "adding string len=%d, str=%s", len, str); + append_str(dst, str, MIN(len, maxLen)); +} + +static void create_vcard(cJSON* elem, size_t *len) { + memset(VCARD, 0, MAX_NFC_BUFFER_SIZE); + char* current = VCARD; + +// append_str(¤t, "jtext/vcard", 11); + append_str(¤t, "BEGIN:VCARD\n", 12); + append_str(¤t, "VERSION:3.0", 11); + + add_if_not_null(elem, ¤t, "\nUID:", "id", 3); + add_if_not_null(elem, ¤t, "\nFN:", "name", 64); + add_if_not_null(elem, ¤t, "\nTEL:", "tel", 32); + add_if_not_null(elem, ¤t, "\nEMAIL:", "email", 128); + add_if_not_null(elem, ¤t, "\nURL:", "url", 128); + + append_str(¤t, "\nEND:VCARD\n", 11); + + // maximum without header: 12 + 11 + 11 + 5 + 3 + 4 + 64 + 5 + 32 + 7 + 128 + 5 + 128 = 415 bytes + // maximum: 11 + 12 + 11 + 11 + 5 + 3 + 4 + 64 + 5 + 32 + 7 + 128 + 5 + 128 = 426 bytes + if (len != NULL) { + *len = current - VCARD; + } +} + +static esp_err_t show_qr_code(char *text) { + enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_LOW; // Error correction level + + // Make and print the QR Code symbol + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok = qrcodegen_encodeText(text, tempBuffer, qrcode, errCorLvl, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (!ok) { + return ESP_FAIL; + } + + int size = qrcodegen_getSize(qrcode); + + int maxWidth = ST77XX_WIDTH; + int maxHeight = ST77XX_HEIGHT - 34 - 20; + int pixelSize = MIN(maxWidth / size, maxHeight / size); + int dx = (maxWidth - pixelSize * size) / 2; + int dy = 34 + (maxHeight - pixelSize * size) / 2; + + int y, x; + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++) { + pax_draw_rect(get_pax_buffer(), qrcodegen_getModule(qrcode, x, y) ? 0xFFFFFFFF : 0xFF131313, dx + x * pixelSize, dy + y * pixelSize, pixelSize, pixelSize); + } + } + display_flush(); + + return ESP_OK; +} + +static esp_err_t handle_device(rfalNfcDevice *nfcDevice) { + ESP_LOGI(TAG, "Found NFC device"); + ndefConstBuffer bufConstRawMessage; + + esp_err_t res = st25r3911b_read_data(nfcDevice, &bufConstRawMessage); + if (res != ESP_OK) { + ESP_LOGE(TAG, "failed to read data: %d", res); + return res; + } + + printf("%.*s\n", bufConstRawMessage.length, bufConstRawMessage.buffer); + return ESP_OK; +} + +static esp_err_t handle_device_p2p_write(__attribute__((unused)) rfalNfcDevice *nfcDevice) { + ESP_LOGI(TAG, "Found NFC device. I'm the INITIATOR. Sending data"); + + char* info = cJSON_PrintUnformatted(json_self); + + esp_err_t res = st25r3911b_p2p_transmitBlocking(1000, (uint8_t*) info, strlen(info)); + if (res != ESP_OK) { + ESP_LOGE(TAG, "failed to send data: %d", res); + return res; + } + return ESP_OK; +} + +static esp_err_t handle_device_p2p_read(__attribute__((unused)) rfalNfcDevice *nfcDevice) { + ESP_LOGI(TAG, "Found NFC device. I'm the TARGET. Reading data"); + // Max is 401+1 bytes {"id":999,"name":"1234567890123456789012345678901234567890123456789012345678901234","tel":"12345678901234567890123456789012","email":"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678","url":"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"} + uint16_t *rxLen; + uint8_t *rxData; + + esp_err_t res = st25r3911b_p2p_receiveBlocking(1000, &rxData, &rxLen); + if (res != ESP_OK) { + ESP_LOGE(TAG, "failed to send data: %d", res); + return res; + } + + cJSON* received = cJSON_ParseWithLength((char*) rxData, *rxLen); + if (received == NULL + || !cJSON_HasObjectItem(received, "id") + || !cJSON_IsNumber(cJSON_GetObjectItem(received, "id")) + || !cJSON_HasObjectItem(received, "name") + || !cJSON_HasObjectItem(received, "tel") + || !cJSON_HasObjectItem(received, "email") + || !cJSON_HasObjectItem(received, "url") + ) { + ESP_LOGE(TAG, "Received invalid data: %.*s", *rxLen, rxData); + return ESP_FAIL; + } + + uint16_t id = (uint16_t) cJSON_GetNumberValue(cJSON_GetObjectItem(received, "id")); + char id_str[6]; + itoa(id, id_str, 10); + + if (cJSON_HasObjectItem(json_db, id_str)) { + cJSON_DeleteItemFromObject(json_db, id_str); + } + + cJSON_AddItemToObject(json_db, id_str, received); + + return save_db() ? ESP_OK : ESP_FAIL; +} + +static void read_nfc() { + esp_err_t res; + + clear_keyboard_queue(); + ESP_LOGI(TAG, "Reading NFC"); + while (1) { + res = st25r3911b_discover(&handle_device, 1000, DISCOVER_MODE_LISTEN_NFCA); + if (res == ESP_ERR_TIMEOUT) { + rtc_wdt_feed(); + vTaskDelay(10 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "Retrying..."); + continue; + } + if (res == ESP_OK) { + break; + } else if (key_was_pressed(BUTTON_BACK)) { + break; + } + } + + size_t len; + create_vcard(json_self, &len); + ESP_LOGI(TAG, "VCARD with len %d:\n%.*s\n", len, len, VCARD); +} + +static void passive_p2p() { + esp_err_t res; + + clear_keyboard_queue(); + ESP_LOGI(TAG, "Waiting for NFC P2P connection as PASSIVE"); + while (1) { + res = st25r3911b_listen_p2p(1000); + if (res == ESP_ERR_TIMEOUT) { + ESP_LOGI(TAG, "Retrying..."); + rtc_wdt_feed(); + vTaskDelay(10 / portTICK_PERIOD_MS); + continue; + } + if (res == ESP_OK) { + break; + } else if (key_was_pressed(BUTTON_BACK)) { + break; + } + } + + size_t len; + create_vcard(json_self, &len); + ESP_LOGI(TAG, "VCARD with len %d:\n%.*s\n", len, len, VCARD); +} + +static void p2p_active() { + esp_err_t res; + + clear_keyboard_queue(); + ESP_LOGI(TAG, "Waiting for NFC P2P connection as PASSIVE"); + while (1) { + res = st25r3911b_poll_active_p2p(1000); + if (res == ESP_ERR_TIMEOUT) { + ESP_LOGI(TAG, "Retrying..."); + rtc_wdt_feed(); + vTaskDelay(10 / portTICK_PERIOD_MS); + continue; + } + if (res == ESP_OK) { + break; + } else if (key_was_pressed(BUTTON_BACK)) { + break; + } + } +} + +static bool p2p_passive2(pax_buf_t* pax_buffer, pax_buf_t* icon) { + esp_err_t res; + render_background(pax_buffer, "🅱 Cancel"); + render_topbar(pax_buffer, icon, "Importing contact"); + display_flush(); + + clear_keyboard_queue(); + ESP_LOGI(TAG, "Waiting for NFC P2P connection as TARGET"); + while (1) { + res = st25r3911b_discover(&handle_device_p2p_read, 1000, DISCOVER_MODE_P2P_PASSIVE); + if (res == ESP_ERR_TIMEOUT && !key_was_pressed(BUTTON_BACK)) { + ESP_LOGI(TAG, "Retrying..."); + rtc_wdt_feed(); + vTaskDelay(10 / portTICK_PERIOD_MS); + continue; + } + return res == ESP_OK; + } +} + +static bool p2p_active2(pax_buf_t* pax_buffer, pax_buf_t* icon) { + esp_err_t res; + render_background(pax_buffer, "🅱 Abort"); + render_topbar(pax_buffer, icon, "Sharing own information"); + display_flush(); + + clear_keyboard_queue(); + ESP_LOGI(TAG, "Waiting for NFC P2P connection as INITIATOR"); + while (1) { + res = st25r3911b_discover(&handle_device_p2p_write, 1000, DISCOVER_MODE_P2P_ACTIVE); + if (res == ESP_ERR_TIMEOUT && !key_was_pressed(BUTTON_BACK)) { + ESP_LOGI(TAG, "Retrying..."); + rtc_wdt_feed(); + vTaskDelay(10 / portTICK_PERIOD_MS); + continue; + } + return res == ESP_OK; + } +} + +static void configure_menu(menu_t* menu) { + menu->fgColor = 0xFFF1AA13; + menu->bgColor = 0xFF131313; + menu->bgTextColor = 0xFF000000; + menu->selectedItemColor = 0xFFF1AA13; + menu->borderColor = 0xFF1E1E1E; + menu->titleColor = 0xFFF1AA13; + menu->titleBgColor = 0xFF1E1E1E; + menu->scrollbarBgColor = 0xFFCCCCCC; + menu->scrollbarFgColor = 0xFF555555; +} + +static void edit_self(pax_buf_t* pax_buffer, xQueueHandle button_queue, pax_buf_t* icon) { + menu_t* menu = menu_alloc("TROOPERS24 - Agenda", 34, 18); + configure_menu(menu); + + bool render = true; + menu_contacts_action_t action = ACTION_NONE; + + menu_set_icon(menu, icon); + menu_insert_item_icon(menu, "Name", NULL, (void*) ACTION_EDIT_NAME, -1, icon); + menu_insert_item_icon(menu, "Telephone", NULL, (void*) ACTION_EDIT_TEL, -1, icon); + menu_insert_item_icon(menu, "Email", NULL, (void*) ACTION_EDIT_EMAIL, -1, icon); + menu_insert_item_icon(menu, "Website", NULL, (void*) ACTION_EDIT_URL, -1, icon); + + bool full_redraw = true; + bool exit = false; + while (!exit) { + if (render) { + if (full_redraw) { + render_background(pax_buffer, "🅰 Accept 🅱 Exit"); + } + + if (full_redraw) { + menu_render_grid(pax_buffer, menu, 0, 0, 320, 220); + display_flush(); + } else { + menu_render_grid_changes(pax_buffer, menu, 0, 0, 320, 220); + display_flush(); + } + + render = false; + full_redraw = false; + } + + clear_keyboard_queue(); + keyboard_input_message_t buttonMessage = {0}; + if (xQueueReceive(button_queue, &buttonMessage, portMAX_DELAY) == pdTRUE) { + if (buttonMessage.state) { + switch (buttonMessage.input) { + case JOYSTICK_DOWN: + menu_navigate_next_row(menu); + render = true; + full_redraw = true; + break; + case JOYSTICK_UP: + menu_navigate_previous_row(menu); + render = true; + full_redraw = true; + break; + case JOYSTICK_LEFT: + menu_navigate_previous(menu); + render = true; + break; + case JOYSTICK_RIGHT: + menu_navigate_next(menu); + render = true; + break; + case BUTTON_BACK: + exit = true; + break; + case BUTTON_ACCEPT: + case BUTTON_SELECT: + action = (menu_contacts_action_t) menu_get_callback_args(menu, menu_get_position(menu)); + break; + default: + break; + } + } + } + + if (action != ACTION_NONE) { + char data[243] = {0}; + int maxLen = 0; + char* key; + char* title = NULL; + pkb_keyboard_t board = PKB_LOWERCASE; + + switch (action) { + case ACTION_EDIT_NAME: + maxLen = 64; + title = "Change Name"; + key = "name"; + break; + case ACTION_EDIT_TEL: + maxLen = 32; + title = "Change Telephone"; + key = "tel"; + board = PKB_NUMBERS; + break; + case ACTION_EDIT_EMAIL: + maxLen = 128; + title = "Change Email"; + key = "email"; + break; + case ACTION_EDIT_URL: + maxLen = 242; + title = "Change Website"; + key = "url"; + break; + default: + break; + } + if (title != NULL) { + char* current = cJSON_GetStringValue(cJSON_GetObjectItem(json_self, key)); + if (current != NULL) { + memcpy(data, current, strlen(current)); + } + + bool accepted = + keyboard_mode(button_queue, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, title, "🆂 Cancel 🅴 Mode 🅱 Delete", data, maxLen, board); + + if (accepted) { + ESP_LOGI(TAG, "Setting %s to %s", key, data); + if (cJSON_HasObjectItem(json_self, key)) { + cJSON_DeleteItemFromObject(json_self, key); + } + cJSON_AddStringToObject(json_self, key, data); + save_self(); + } + } + action = ACTION_NONE; + render = true; + full_redraw = true; + } + } + + menu_free(menu); +} + +static void show_vcard(pax_buf_t* pax_buffer, cJSON* data, pax_buf_t* icon) { + render_background(pax_buffer, "🅱 Exit"); + render_topbar(pax_buffer, icon, "Share vCard"); + create_vcard(data, NULL); + show_qr_code(VCARD); + + wait_for_button(); +} + +static void show_self(pax_buf_t* pax_buffer, pax_buf_t* icon) { + show_vcard(pax_buffer, json_self, icon); +} + +static void render_entry(pax_buf_t* pax_buffer, int height, int y, bool highlighted, cJSON* elem) { + const pax_font_t* font = pax_font_saira_regular; + uint16_t id = (uint16_t) cJSON_GetNumberValue(cJSON_GetObjectItem(elem, "id")); + char* name = cJSON_GetStringValue(cJSON_GetObjectItem(elem, "name")); + char* id_str[5] = {0}; + itoa(id % 1000, id_str, 10); + pax_col_t background = 0xFF131313; + pax_col_t color = 0xffeaa307; + if (highlighted) { + background = 0xffeaa307; + color = 0xff131313; + } + pax_simple_rect(pax_buffer, background, 0, y, ST77XX_WIDTH, height); + pax_draw_text(pax_buffer, color, font, height - 6, 8, y + 3, id_str); + pax_draw_text(pax_buffer, color, font, height - 4, 80, y + 2, name); +} + +static void show_list(pax_buf_t* pax_buffer, xQueueHandle button_queue, pax_buf_t* icon) { + render_background(pax_buffer, "🅱 Exit 🅴 Export"); + render_topbar(pax_buffer, icon, "Addressbook"); + + int height = 22; + int rows = 8; + int offset = 0; + int cursor = 0; + int i; + + int len = cJSON_GetArraySize(json_db); + + keyboard_input_message_t buttonMessage = {0}; + bool render = true; + bool exit = false; + cJSON* elem; + + while(!exit) { + if (render) { + for (i = offset; i < offset + rows; i++) { + elem = cJSON_GetArrayItem(json_db, i); + render_entry(pax_buffer, height, 40 + height * (i - offset), i - offset == cursor, elem); + } + display_flush(); + render = false; + } + + clear_keyboard_queue(); + if (xQueueReceive(button_queue, &buttonMessage, portMAX_DELAY) == pdTRUE) { + if (buttonMessage.state) { + switch (buttonMessage.input) { + case JOYSTICK_DOWN: + if (cursor < rows - 1) { + cursor++; + render = true; + } else if (offset + rows < len) { + offset++; + render = true; + } + break; + case JOYSTICK_UP: + if (cursor > 0) { + cursor--; + render = true; + } else if (offset > 0) { + offset--; + render = true; + } + render = true; + break; + case BUTTON_BACK: + exit = true; + break; + case BUTTON_SELECT: + case JOYSTICK_PUSH: + show_vcard(pax_buffer, cJSON_GetArrayItem(json_db, offset + cursor), icon); + render = true; + break; + default: + break; + } + } + } + } +} + +void menu_contacts(xQueueHandle button_queue) { + pax_buf_t* pax_buffer = get_pax_buffer(); + + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0xFF131313); + + // Ensure directory exists + if (!create_dir("/internal/apps")) { + ESP_LOGE(TAG, "Failed to create directory in internal storage"); + render_message("Failed to create data dir"); + display_flush(); + wait_for_button(); + return; + } + if (!create_dir("/internal/apps/contacts")) { + ESP_LOGE(TAG, "Failed to create directory in internal storage"); + render_message("Failed to create data dir"); + display_flush(); + wait_for_button(); + return; + } + + if (!load_data()) { + wait_for_button(); + return; + } + + +// read_nfc(); +// +// p2p_active(); + + menu_t* menu = menu_alloc("TROOPERS24 - Addressbook", 34, 18); + configure_menu(menu); + + pax_buf_t icon_edit; + pax_decode_png_buf(&icon_edit, (void*) edit_png_start, edit_png_end - edit_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_badge; + pax_decode_png_buf(&icon_badge, (void*) badge_png_start, badge_png_end - badge_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_addressbook; + pax_decode_png_buf(&icon_addressbook, (void*) addressbook_png_start, addressbook_png_end - addressbook_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_share; + pax_decode_png_buf(&icon_share, (void*) share_png_start, share_png_end - share_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_receive; + pax_decode_png_buf(&icon_receive, (void*) receive_png_start, receive_png_end - receive_png_start, PAX_BUF_32_8888ARGB, 0); + + menu_set_icon(menu, &icon_addressbook); + menu_insert_item_icon(menu, "Edit", NULL, (void*) ACTION_EDIT, -1, &icon_edit); + menu_insert_item_icon(menu, "Export", NULL, (void*) ACTION_QRCODE, -1, &icon_badge); + menu_insert_item_icon(menu, "List", NULL, (void*) ACTION_LIST, -1, &icon_addressbook); + menu_insert_item_icon(menu, "Share", NULL, (void*) ACTION_SHARE, -1, &icon_share); + menu_insert_item_icon(menu, "Receive", NULL, (void*) ACTION_IMPORT, -1, &icon_receive); + + + bool render = true; + menu_contacts_action_t action = ACTION_NONE; + + bool full_redraw = true; + bool exit = false; + while (!exit) { + if (render) { + if (full_redraw) { + render_background(pax_buffer, "🅰 Accept 🅱 Exit"); + } + + if (full_redraw) { + menu_render_grid(pax_buffer, menu, 0, 0, 320, 220); + display_flush(); + } else { + menu_render_grid_changes(pax_buffer, menu, 0, 0, 320, 220); + display_flush(); + } + + render = false; + full_redraw = false; + } + + clear_keyboard_queue(); + keyboard_input_message_t buttonMessage = {0}; + if (xQueueReceive(button_queue, &buttonMessage, portMAX_DELAY) == pdTRUE) { + if (buttonMessage.state) { + switch (buttonMessage.input) { + case JOYSTICK_DOWN: + menu_navigate_next_row(menu); + render = true; + full_redraw = true; + break; + case JOYSTICK_UP: + menu_navigate_previous_row(menu); + render = true; + full_redraw = true; + break; + case JOYSTICK_LEFT: + menu_navigate_previous(menu); + render = true; + break; + case JOYSTICK_RIGHT: + menu_navigate_next(menu); + render = true; + break; + case BUTTON_BACK: + exit = true; + break; + case BUTTON_ACCEPT: + case BUTTON_SELECT: + action = (menu_contacts_action_t) menu_get_callback_args(menu, menu_get_position(menu)); + break; + default: + break; + } + } + } + + if (action != ACTION_NONE) { + if (action == ACTION_EDIT) { + edit_self(pax_buffer, button_queue, &icon_edit); + } else if (action == ACTION_QRCODE) { + show_self(pax_buffer, &icon_badge); + } else if (action == ACTION_SHARE) { + if (p2p_active2(pax_buffer, &icon_share)) { + ESP_LOGI(TAG, "sent own info"); + } + } else if (action == ACTION_IMPORT) { + if (p2p_passive2(pax_buffer, &icon_receive)) { + ESP_LOGI(TAG, "received new entry"); + } + } else if (action == ACTION_LIST) { + show_list(pax_buffer, button_queue, &icon_addressbook); + } + action = ACTION_NONE; + render = true; + full_redraw = true; + } + } + + menu_free(menu); + + cJSON_Delete(json_self); + json_self = NULL; + cJSON_Delete(json_db); + json_db = NULL; + + pax_buf_destroy(&icon_receive); + pax_buf_destroy(&icon_share); + pax_buf_destroy(&icon_badge); + pax_buf_destroy(&icon_edit); + pax_buf_destroy(&icon_addressbook); +} diff --git a/main/menus/contacts.h b/main/menus/contacts.h new file mode 100644 index 0000000..e86b05f --- /dev/null +++ b/main/menus/contacts.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +void menu_contacts(xQueueHandle button_queue); diff --git a/main/menus/hatchery.c b/main/menus/hatchery.c index cd27013..d7b816f 100644 --- a/main/menus/hatchery.c +++ b/main/menus/hatchery.c @@ -208,7 +208,7 @@ static void show_communication_error(xQueueHandle button_queue) { static bool load_types() { if (data_types == NULL) { - bool success = download_ram("https://mch2022.badge.team/v2/troopers23/types", (uint8_t**) &data_types, &size_types); + bool success = download_ram("https://mch2022.badge.team/v2/troopers24/types", (uint8_t**) &data_types, &size_types); if (!success) return false; } if (data_types == NULL) return false; @@ -219,7 +219,7 @@ static bool load_types() { static bool load_categories(const char* type_slug) { char url[128]; - snprintf(url, sizeof(url) - 1, "https://mch2022.badge.team/v2/troopers23/%s/categories", type_slug); + snprintf(url, sizeof(url) - 1, "https://mch2022.badge.team/v2/troopers24/%s/categories", type_slug); bool success = download_ram(url, (uint8_t**) &data_categories, &size_categories); if (!success) return false; if (data_categories == NULL) return false; @@ -230,7 +230,7 @@ static bool load_categories(const char* type_slug) { static bool load_apps(const char* type_slug, const char* category_slug) { char url[128]; - snprintf(url, sizeof(url) - 1, "https://mch2022.badge.team/v2/troopers23/%s/%s", type_slug, category_slug); + snprintf(url, sizeof(url) - 1, "https://mch2022.badge.team/v2/troopers24/%s/%s", type_slug, category_slug); bool success = download_ram(url, (uint8_t**) &data_apps, &size_apps); if (!success) return false; if (data_apps == NULL) return false; @@ -241,7 +241,7 @@ static bool load_apps(const char* type_slug, const char* category_slug) { static bool load_app_info(const char* type_slug, const char* category_slug, const char* app_slug) { char url[128]; - snprintf(url, sizeof(url) - 1, "https://mch2022.badge.team/v2/troopers23/%s/%s/%s", type_slug, category_slug, app_slug); + snprintf(url, sizeof(url) - 1, "https://mch2022.badge.team/v2/troopers24/%s/%s/%s", type_slug, category_slug, app_slug); bool success = download_ram(url, (uint8_t**) &data_app_info, &size_app_info); if (!success) return false; if (data_app_info == NULL) return false; diff --git a/main/menus/id.c b/main/menus/id.c index 3d6c109..30b3ed7 100644 --- a/main/menus/id.c +++ b/main/menus/id.c @@ -14,17 +14,17 @@ static const char* TAG = "id"; extern const uint8_t shield_png_start[] asm("_binary_id_shield_png_start"); extern const uint8_t shield_png_end[] asm("_binary_id_shield_png_end"); -extern const uint8_t ernw_png_start[] asm("_binary_id_ernw_png_start"); -extern const uint8_t ernw_png_end[] asm("_binary_id_ernw_png_end"); +extern const uint8_t fifteen_png_start[] asm("_binary_id_15_png_start"); +extern const uint8_t fifteen_png_end[] asm("_binary_id_15_png_end"); -extern const uint8_t fucss_png_start[] asm("_binary_id_fucss_png_start"); -extern const uint8_t fucss_png_end[] asm("_binary_id_fucss_png_end"); +extern const uint8_t glass_png_start[] asm("_binary_id_glass_png_start"); +extern const uint8_t glass_png_end[] asm("_binary_id_glass_png_end"); -extern const uint8_t fishbowl_png_start[] asm("_binary_id_fishbowl_png_start"); -extern const uint8_t fishbowl_png_end[] asm("_binary_id_fishbowl_png_end"); +extern const uint8_t storytellers_png_start[] asm("_binary_id_storytellers_png_start"); +extern const uint8_t storytellers_png_end[] asm("_binary_id_storytellers_png_end"); -extern const uint8_t badgeteam_png_start[] asm("_binary_id_badgeteam_png_start"); -extern const uint8_t badgeteam_png_end[] asm("_binary_id_badgeteam_png_end"); +extern const uint8_t ernw_png_start[] asm("_binary_id_ernw_png_start"); +extern const uint8_t ernw_png_end[] asm("_binary_id_ernw_png_end"); void render_icon(pax_buf_t* pax_buffer, int pos, const uint8_t start[], const uint8_t end[]) { // Place 4 icons on a horizontal line, each icon is 80x80 pixels @@ -38,16 +38,16 @@ void render_icon_id(pax_buf_t* pax_buffer, int pos, int index) { render_icon(pax_buffer, pos, shield_png_start, shield_png_end); break; case 1: - render_icon(pax_buffer, pos, fucss_png_start, fucss_png_end); + render_icon(pax_buffer, pos, fifteen_png_start, fifteen_png_end); break; case 2: - render_icon(pax_buffer, pos, fishbowl_png_start, fishbowl_png_end); + render_icon(pax_buffer, pos, glass_png_start, glass_png_end); break; case 3: - render_icon(pax_buffer, pos, ernw_png_start, ernw_png_end); + render_icon(pax_buffer, pos, storytellers_png_start, storytellers_png_end); break; case 4: - render_icon(pax_buffer, pos, badgeteam_png_start, badgeteam_png_end); + render_icon(pax_buffer, pos, ernw_png_start, ernw_png_end); break; } } diff --git a/main/menus/launcher.c b/main/menus/launcher.c index b341538..2053398 100644 --- a/main/menus/launcher.c +++ b/main/menus/launcher.c @@ -19,7 +19,6 @@ #include "graphics_wrapper.h" #include "gui_element_header.h" #include "hardware.h" -#include "ili9341.h" #include "menu.h" #include "metadata.h" #include "pax_codecs.h" diff --git a/main/menus/nfcreader.c b/main/menus/nfcreader.c new file mode 100644 index 0000000..b7e000b --- /dev/null +++ b/main/menus/nfcreader.c @@ -0,0 +1,142 @@ +#include +#include + +#include "app_management.h" +#include "efuse.h" +#include "esp_http_client.h" +#include "graphics_wrapper.h" +#include "hardware.h" +#include "http_download.h" +#include "menu.h" +#include "ntp_helper.h" +#include "pax_codecs.h" +#include "pax_gfx.h" +#include "rtc_wdt.h" +#include "system_wrapper.h" +#include "utils.h" +#include "wifi_connect.h" +#include "qrcodegen.h" + +static const char* TAG = "nfcreader"; + +char VCARD[MAX_NFC_BUFFER_SIZE]; + +extern const uint8_t badge_png_start[] asm("_binary_badge_png_start"); +extern const uint8_t badge_png_end[] asm("_binary_badge_png_end"); + +static void render_background(pax_buf_t* pax_buffer, const char* text) { + const pax_font_t* font = pax_font_saira_regular; + pax_background(pax_buffer, 0xFF1E1E1E); + pax_noclip(pax_buffer); + pax_simple_rect(pax_buffer, 0xff131313, 0, 220, 320, 20); + pax_draw_text(pax_buffer, 0xffffffff, font, 18, 5, 240 - 18, text); +} + +static void render_topbar(pax_buf_t* pax_buffer, pax_buf_t* icon, const char* text) { + const pax_font_t* font = pax_font_saira_regular; + pax_simple_rect(pax_buffer, 0xff131313, 0, 0, 320, 34); + pax_draw_image(pax_buffer, icon, 1, 1); + pax_draw_text(pax_buffer, 0xFFF1AA13, font, 18, 34, 8, text); +} + +static esp_err_t handle_device(rfalNfcDevice *nfcDevice) { + ESP_LOGI(TAG, "Found NFC device"); + ndefConstBuffer bufConstRawMessage; + + esp_err_t res = st25r3911b_read_data(nfcDevice, &bufConstRawMessage); + if (res != ESP_OK) { + ESP_LOGE(TAG, "failed to read data: %d", res); + return res; + } + + // https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.3.0%2Fnfc_ndef_format_dox.html + uint8_t tnf = bufConstRawMessage.buffer[0] & 0b111; + // Short records have 1 byte length field, otherwise 4 + bool sr = bufConstRawMessage.buffer[0] & (1 << 4); + bool il = bufConstRawMessage.buffer[0] & (1 << 3); + int i = 1; + uint8_t type_len = bufConstRawMessage.buffer[i++]; + uint32_t length = bufConstRawMessage.buffer[i++]; + if (!sr) { + length = length << 24; + length += bufConstRawMessage.buffer[i++] << 16; + length += bufConstRawMessage.buffer[i++] << 8; + length += bufConstRawMessage.buffer[i++]; + } + uint8_t id_len = 0; + if (il) { + // ID length + id_len = bufConstRawMessage.buffer[i++]; + } + i += type_len; + i += id_len; + int payload_start = i; + + +#define MAX_PER_LINE 25 +#define MAX_LINES 8 + char lines[MAX_LINES * (MAX_PER_LINE + 1)] = {0}; + int cursor = 0; + char c; + + for (i = 0; i < length; i++) { + c = (char) bufConstRawMessage.buffer[i + payload_start]; + if (c < 33 || c > 126) { + // Skip non ASCII + continue; + } + if ((cursor > 0 && (cursor + 1) % (MAX_PER_LINE + 1) == 0) && cursor + 1 < MAX_LINES * (MAX_PER_LINE + 1)) { + lines[cursor++] = '\n'; + } + if (cursor == MAX_LINES * (MAX_PER_LINE + 1) - 1) { + lines[cursor] = 0; + break; + } + lines[cursor++] = c; + } + + char hint[20]; + sprintf(hint, "Received %4d bytes", length); + + pax_draw_text(get_pax_buffer(), 0xffeaa307, pax_font_saira_regular, 12, 4, 40, hint); + pax_draw_text(get_pax_buffer(), 0xffeaa307, pax_font_sky_mono, 16, 4, 58, lines); + display_flush(); + + ESP_LOGI(TAG, "%.*s\n", length, bufConstRawMessage.buffer + payload_start); + return ESP_OK; +} + +static void read_nfc() { + esp_err_t res; + + clear_keyboard_queue(); + ESP_LOGI(TAG, "Reading NFC"); + while (1) { + res = st25r3911b_discover(&handle_device, 1000, DISCOVER_MODE_LISTEN_NFCA); + if (res == ESP_ERR_TIMEOUT) { + if (key_was_pressed(BUTTON_BACK)) { + break; + } + rtc_wdt_feed(); + vTaskDelay(10 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "Retrying..."); + continue; + } + } +} + +void menu_nfcreader(xQueueHandle button_queue) { + pax_buf_t* pax_buffer = get_pax_buffer(); + + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0xFF131313); + render_background(pax_buffer, "🅱 Cancel"); + pax_buf_t icon_badge; + pax_decode_png_buf(&icon_badge, (void*) badge_png_start, badge_png_end - badge_png_start, PAX_BUF_32_8888ARGB, 0); + render_topbar(pax_buffer, &icon_badge, "Read NFC-A Tag"); + display_flush(); + + read_nfc(); + + pax_buf_destroy(&icon_badge); +} diff --git a/main/menus/nfcreader.h b/main/menus/nfcreader.h new file mode 100644 index 0000000..44b689b --- /dev/null +++ b/main/menus/nfcreader.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +void menu_nfcreader(xQueueHandle button_queue); diff --git a/main/menus/start.c b/main/menus/start.c index f29b365..991cdd7 100644 --- a/main/menus/start.c +++ b/main/menus/start.c @@ -9,6 +9,8 @@ #include #include "agenda.h" +#include "contacts.h" +#include "nfcreader.h" #include "app_update.h" #include "bootscreen.h" #include "dev.h" @@ -53,6 +55,12 @@ extern const uint8_t update_png_end[] asm("_binary_update_png_end"); extern const uint8_t agenda_png_start[] asm("_binary_calendar_png_start"); extern const uint8_t agenda_png_end[] asm("_binary_calendar_png_end"); +extern const uint8_t addressbook_png_start[] asm("_binary_addressbook_png_start"); +extern const uint8_t addressbook_png_end[] asm("_binary_addressbook_png_end"); + +extern const uint8_t nfc_png_start[] asm("_binary_badge_png_start"); +extern const uint8_t nfc_png_end[] asm("_binary_badge_png_end"); + extern const uint8_t id_png_start[] asm("_binary_sao_png_start"); extern const uint8_t id_png_end[] asm("_binary_sao_png_end"); @@ -68,7 +76,9 @@ typedef enum action { ACTION_OTA, ACTION_SAO, ACTION_ID, - ACTION_AGENDA + ACTION_AGENDA, + ACTION_CONTACTS, + ACTION_NFCREADER, } menu_start_action_t; void render_background(pax_buf_t* pax_buffer, const char* text) { @@ -82,15 +92,12 @@ void render_background(pax_buf_t* pax_buffer, const char* text) { } void menu_start(xQueueHandle button_queue, const char* version, bool wakeup_deepsleep) { - // TODO: Debugging - menu_agenda(button_queue); - if (wakeup_deepsleep) { show_nametag(button_queue); } pax_buf_t* pax_buffer = get_pax_buffer(); - menu_t* menu = menu_alloc("TROOPERS24", 34, 18); + menu_t* menu = menu_alloc("TROOPERS24", 34, 16); menu->fgColor = 0xFFF1AA13; menu->bgColor = 0xFF131313; @@ -122,11 +129,17 @@ void menu_start(xQueueHandle button_queue, const char* version, bool wakeup_deep pax_decode_png_buf(&icon_id, (void*) id_png_start, id_png_end - id_png_start, PAX_BUF_32_8888ARGB, 0); pax_buf_t icon_agenda; pax_decode_png_buf(&icon_agenda, (void*) agenda_png_start, agenda_png_end - agenda_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_addressbook; + pax_decode_png_buf(&icon_addressbook, (void*) addressbook_png_start, addressbook_png_end - addressbook_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_nfc; + pax_decode_png_buf(&icon_nfc, (void*) nfc_png_start, nfc_png_end - nfc_png_start, PAX_BUF_32_8888ARGB, 0); menu_set_icon(menu, &icon_home); menu_insert_item_icon(menu, "Name tag", NULL, (void*) ACTION_NAMETAG, -1, &icon_tag); menu_insert_item_icon(menu, "ID", NULL, (void*) ACTION_ID, -1, &icon_tag); menu_insert_item_icon(menu, "Agenda", NULL, (void*) ACTION_AGENDA, -1, &icon_agenda); + menu_insert_item_icon(menu, "Addressbook", NULL, (void*) ACTION_CONTACTS, -1, &icon_addressbook); + menu_insert_item_icon(menu, "NFC Reader", NULL, (void*) ACTION_NFCREADER, -1, &icon_nfc); menu_insert_item_icon(menu, "Apps", NULL, (void*) ACTION_LAUNCHER, -1, &icon_apps); menu_insert_item_icon(menu, "Hatchery", NULL, (void*) ACTION_HATCHERY, -1, &icon_hatchery); menu_insert_item_icon(menu, "Tools", NULL, (void*) ACTION_DEV, -1, &icon_dev); @@ -212,6 +225,10 @@ void menu_start(xQueueHandle button_queue, const char* version, bool wakeup_deep menu_id(button_queue); } else if (action == ACTION_AGENDA) { menu_agenda(button_queue); + } else if (action == ACTION_CONTACTS) { + menu_contacts(button_queue); + } else if (action == ACTION_NFCREADER) { + menu_nfcreader(button_queue); } action = ACTION_NONE; render = true; @@ -228,4 +245,6 @@ void menu_start(xQueueHandle button_queue, const char* version, bool wakeup_deep pax_buf_destroy(&icon_settings); pax_buf_destroy(&icon_update); pax_buf_destroy(&icon_id); + pax_buf_destroy(&icon_addressbook); + pax_buf_destroy(&icon_nfc); } diff --git a/main/menus/utils.c b/main/menus/utils.c new file mode 100644 index 0000000..cdf4935 --- /dev/null +++ b/main/menus/utils.c @@ -0,0 +1,63 @@ +#include "utils.h" + +bool rename_or_replace(const char* TAG, const char* old, const char* new) { + if (access(new, F_OK) == 0) { + ESP_LOGD(TAG, "Destination file exists, deleting..."); + // File exists, try to delete + if (remove(new) != 0) { + ESP_LOGD(TAG, "Destination file could not be deleted"); + // Deleting failed + return false; + } + } + + return rename(old, new) != 0; +} + +bool file_exists(const char* filename) { + return access(filename, F_OK) == 0; +} + +bool load_file(const char* TAG, const char* filename, char** buf, size_t* len) { + FILE* fd = fopen(filename, "r"); + if (fd == NULL) { + ESP_LOGE(TAG, "Unable to open file: %s", filename); + return false; + } + + /* Go to the end of the file. */ + if (fseek(fd, 0L, SEEK_END) == 0) { + /* Get the size of the file. */ + *len = ftell(fd); + + if (*buf != NULL) { + free(*buf); + } + + /* Allocate our buffer to that size. */ + *buf = malloc(*len); + + /* Go back to the start of the file. */ + if (fseek(fd, 0L, SEEK_SET) != 0) { + free(*buf); + *len = 0; + ESP_LOGE(TAG, "Failed to seek to start"); + return false; + } + + /* Read the entire file into memory. */ + fread(*buf, 1, *len, fd); + int err = ferror(fd); + if (err != 0) { + free(*buf); + *len = 0; + ESP_LOGE(TAG, "Failed to read file: %d", err); + return false; + } + } else { + ESP_LOGE(TAG, "Failed to seek to end"); + return false; + } + fclose(fd); + return true; +} \ No newline at end of file diff --git a/main/menus/utils.h b/main/menus/utils.h new file mode 100644 index 0000000..9a638f1 --- /dev/null +++ b/main/menus/utils.h @@ -0,0 +1,8 @@ +#include "http_download.h" +#include "ntp_helper.h" +#include "pax_gfx.h" +#include "system_wrapper.h" + +bool rename_or_replace(const char* TAG, const char* old, const char* new); +bool file_exists(const char* filename); +bool load_file(const char* TAG, const char* filename, char** buf, size_t* len); \ No newline at end of file diff --git a/main/nametag.c b/main/nametag.c index a77fcbb..bb6c5a6 100644 --- a/main/nametag.c +++ b/main/nametag.c @@ -27,10 +27,13 @@ #define SLEEP_DELAY 10000 static const char *TAG = "nametag"; +extern const uint8_t troopers23_png_start[] asm("_binary_tr23_nametag_png_start"); +extern const uint8_t troopers23_png_end[] asm("_binary_tr23_nametag_png_end"); + extern const uint8_t troopers_png_start[] asm("_binary_nametag_png_start"); extern const uint8_t troopers_png_end[] asm("_binary_nametag_png_end"); -typedef enum { NICKNAME_THEME_HELLO = 0, NICKNAME_THEME_SIMPLE, NICKNAME_THEME_GAMER, NICKNAME_THEME_TROOPERS, NICKNAME_THEME_LAST } nickname_theme_t; +typedef enum { NICKNAME_THEME_HELLO = 0, NICKNAME_THEME_SIMPLE, NICKNAME_THEME_GAMER, NICKNAME_THEME_TROOPERS, NICKNAME_THEME_TROOPERS23, NICKNAME_THEME_LAST } nickname_theme_t; static int hue = 0; @@ -53,7 +56,7 @@ void edit_nickname(xQueueHandle button_queue) { clear_keyboard_queue(); bool accepted = - keyboard(button_queue, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "Nickname", "🆂 cancel 🅳 D 🅴 Select 🅱 delete", nickname, sizeof(nickname) - 1); + keyboard(button_queue, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "Nickname", "🆂 Cancel 🅴 Mode 🅱 Delete", nickname, sizeof(nickname) - 1); if (accepted) { nvs_set_str(handle, "nickname", nickname); @@ -69,7 +72,7 @@ static void show_name(xQueueHandle button_queue, const char *name, nickname_them const pax_font_t *name_font; if (theme == NICKNAME_THEME_HELLO || theme == NICKNAME_THEME_GAMER) { name_font = pax_font_marker; - } else if (theme == NICKNAME_THEME_TROOPERS) { + } else if (theme == NICKNAME_THEME_TROOPERS || theme == NICKNAME_THEME_TROOPERS23) { name_font = pax_font_sky_mono; } else { name_font = pax_font_saira_condensed; @@ -112,6 +115,9 @@ static void show_name(xQueueHandle button_queue, const char *name, nickname_them } else if (theme == NICKNAME_THEME_TROOPERS) { pax_insert_png_buf(pax_buffer, troopers_png_start, troopers_png_end - troopers_png_start, 0, 0, 0); pax_center_text(pax_buffer, 0xFFF1AA13, name_font, 24, pax_buffer->width / 2, 140, name); + } else if (theme == NICKNAME_THEME_TROOPERS23) { + pax_insert_png_buf(pax_buffer, troopers23_png_start, troopers23_png_end - troopers23_png_start, 0, 0, 0); + pax_center_text(pax_buffer, 0xFFF1AA13, name_font, 24, pax_buffer->width / 2, 140, name); } else { pax_background(pax_buffer, 0x000000); pax_center_text(pax_buffer, 0xFFFFFFFF, name_font, scale, pax_buffer->width / 2, (pax_buffer->height - dims.y) / 2, name); @@ -131,10 +137,8 @@ static void place_in_sleep(xQueueHandle button_queue) { fflush(stdout); fflush(stderr); vTaskDelay(pdMS_TO_TICKS(100)); -#ifdef TR23 - gpio_hold_en(GPIO_LCD_BL); - ili9341_power_en(get_ili9341()); -#endif + + st77xx_power_en(get_st77xx()); gpio_deep_sleep_hold_en(); vTaskDelay(pdMS_TO_TICKS(100)); esp_deep_sleep_start(); diff --git a/main/wifi_ota.c b/main/wifi_ota.c index 4b202ca..9a87b4f 100644 --- a/main/wifi_ota.c +++ b/main/wifi_ota.c @@ -73,7 +73,7 @@ static esp_err_t validate_image_header(esp_app_desc_t *new_app_info) { } static esp_err_t _http_client_init_cb(esp_http_client_handle_t http_client) { - if (esp_http_client_set_header(http_client, "Badge-Type", "MCH2022") != ESP_OK) { + if (esp_http_client_set_header(http_client, "Badge-Type", "TROOPERS24") != ESP_OK) { ESP_LOGW(TAG, "Failed to add type header"); } const esp_partition_t *running = esp_ota_get_running_partition(); @@ -128,7 +128,7 @@ void display_ota_state(const char *text, bool nightly) { void ota_update(bool nightly) { display_ota_state("Connecting to WiFi...", nightly); - char *ota_url = nightly ? "https://mch2022.ota.bodge.team/troopers23_dev.bin" : "https://mch2022.ota.bodge.team/troopers23.bin"; + char *ota_url = nightly ? "https://mch2022.ota.bodge.team/troopers24_dev.bin" : "https://mch2022.ota.bodge.team/troopers24.bin"; if (!wifi_connect_to_stored()) { display_ota_state("Failed to connect to WiFi", nightly); diff --git a/partitions.csv b/partitions.csv index 8debf96..dc0aa4a 100644 --- a/partitions.csv +++ b/partitions.csv @@ -3,7 +3,7 @@ nvs, data, nvs, 0x9000, 16K, otadata, data, ota, 0xD000, 8K, phy_init, data, phy, 0xF000, 4K, - ota_0, 0, ota_0, 0x10000, 1600K, - ota_1, 0, ota_1, 0x1A0000, 1600K, - appfs, 0x43, 3, 0x330000, 8000K, + ota_0, 0, ota_0, 0x10000, 1920K, + ota_1, 0, ota_1, 0x1f0000, 1920K, + appfs, 0x43, 3, 0x3d0000, 7360K, locfd, data, fat, 0xB00000, 5120K, diff --git a/resources/boot.mp3 b/resources/boot.mp3 index e3aba78..7faeeb2 100644 Binary files a/resources/boot.mp3 and b/resources/boot.mp3 differ diff --git a/resources/boot.png b/resources/boot.png new file mode 100644 index 0000000..ec4ab9b Binary files /dev/null and b/resources/boot.png differ diff --git a/resources/boot0.png b/resources/boot0.png index b90873c..bab7e0e 100644 Binary files a/resources/boot0.png and b/resources/boot0.png differ diff --git a/resources/boot1.png b/resources/boot1.png index 4a229f7..016d450 100644 Binary files a/resources/boot1.png and b/resources/boot1.png differ diff --git a/resources/boot2.png b/resources/boot2.png index 7e2fd68..db265b5 100644 Binary files a/resources/boot2.png and b/resources/boot2.png differ diff --git a/resources/boot3.png b/resources/boot3.png index cda30f7..ae45a9b 100644 Binary files a/resources/boot3.png and b/resources/boot3.png differ diff --git a/resources/boot4.png b/resources/boot4.png index 3bd43c5..4474a9f 100644 Binary files a/resources/boot4.png and b/resources/boot4.png differ diff --git a/resources/boot5.png b/resources/boot5.png index cab7e02..b686c9b 100644 Binary files a/resources/boot5.png and b/resources/boot5.png differ diff --git a/resources/boot6.png b/resources/boot6.png index 319cee0..34a0c11 100644 Binary files a/resources/boot6.png and b/resources/boot6.png differ diff --git a/resources/happy.mp3 b/resources/happy.mp3 new file mode 100644 index 0000000..c46748e Binary files /dev/null and b/resources/happy.mp3 differ diff --git a/resources/icons/address-book-solid.svg b/resources/icons/address-book-solid.svg new file mode 100644 index 0000000..d8aa601 --- /dev/null +++ b/resources/icons/address-book-solid.svg @@ -0,0 +1,40 @@ + + + + + + + diff --git a/resources/icons/addressbook.png b/resources/icons/addressbook.png new file mode 100644 index 0000000..ea0ac5f Binary files /dev/null and b/resources/icons/addressbook.png differ diff --git a/resources/icons/badge.png b/resources/icons/badge.png new file mode 100644 index 0000000..ef2043b Binary files /dev/null and b/resources/icons/badge.png differ diff --git a/resources/icons/edit.png b/resources/icons/edit.png new file mode 100644 index 0000000..ef66c39 Binary files /dev/null and b/resources/icons/edit.png differ diff --git a/resources/icons/file-arrow-down-solid.svg b/resources/icons/file-arrow-down-solid.svg new file mode 100644 index 0000000..0d43fe1 --- /dev/null +++ b/resources/icons/file-arrow-down-solid.svg @@ -0,0 +1,40 @@ + + + + + + + diff --git a/resources/icons/id-badge-solid.svg b/resources/icons/id-badge-solid.svg new file mode 100644 index 0000000..43d713a --- /dev/null +++ b/resources/icons/id-badge-solid.svg @@ -0,0 +1,40 @@ + + + + + + + diff --git a/resources/icons/pen-solid.svg b/resources/icons/pen-solid.svg new file mode 100644 index 0000000..260377b --- /dev/null +++ b/resources/icons/pen-solid.svg @@ -0,0 +1,40 @@ + + + + + + + diff --git a/resources/icons/receive.png b/resources/icons/receive.png new file mode 100644 index 0000000..f269684 Binary files /dev/null and b/resources/icons/receive.png differ diff --git a/resources/icons/share-solid.svg b/resources/icons/share-solid.svg new file mode 100644 index 0000000..b7c9d6c --- /dev/null +++ b/resources/icons/share-solid.svg @@ -0,0 +1,40 @@ + + + + + + + diff --git a/resources/icons/share.png b/resources/icons/share.png new file mode 100644 index 0000000..0a64237 Binary files /dev/null and b/resources/icons/share.png differ diff --git a/resources/id/id_15.png b/resources/id/id_15.png new file mode 100644 index 0000000..bbf551a Binary files /dev/null and b/resources/id/id_15.png differ diff --git a/resources/id/id_badgeteam.png b/resources/id/id_badgeteam.png deleted file mode 100644 index 84556e9..0000000 Binary files a/resources/id/id_badgeteam.png and /dev/null differ diff --git a/resources/id/id_badgeteam.svg b/resources/id/id_badgeteam.svg deleted file mode 100644 index 57024fb..0000000 --- a/resources/id/id_badgeteam.svg +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/resources/id/id_ernw.png b/resources/id/id_ernw.png index 9df93d7..9ccbece 100644 Binary files a/resources/id/id_ernw.png and b/resources/id/id_ernw.png differ diff --git a/resources/id/id_ernw.svg b/resources/id/id_ernw.svg deleted file mode 100644 index af65b41..0000000 --- a/resources/id/id_ernw.svg +++ /dev/null @@ -1,8548 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/id/id_fishbowl.png b/resources/id/id_fishbowl.png deleted file mode 100644 index 9ddd1f7..0000000 Binary files a/resources/id/id_fishbowl.png and /dev/null differ diff --git a/resources/id/id_fishbowl.svg b/resources/id/id_fishbowl.svg deleted file mode 100644 index d1d1255..0000000 --- a/resources/id/id_fishbowl.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - -image/svg+xml diff --git a/resources/id/id_fucss.png b/resources/id/id_fucss.png deleted file mode 100644 index 1161f1c..0000000 Binary files a/resources/id/id_fucss.png and /dev/null differ diff --git a/resources/id/id_fucss.svg b/resources/id/id_fucss.svg deleted file mode 100644 index eaca1b5..0000000 --- a/resources/id/id_fucss.svg +++ /dev/null @@ -1,477 +0,0 @@ - - - - - - image/svg+xml - - logo - - - - - - logo - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/id/id_glass.png b/resources/id/id_glass.png new file mode 100644 index 0000000..d29dff9 Binary files /dev/null and b/resources/id/id_glass.png differ diff --git a/resources/id/id_shield.png b/resources/id/id_shield.png index 4e1304f..a10b513 100644 Binary files a/resources/id/id_shield.png and b/resources/id/id_shield.png differ diff --git a/resources/id/id_shield.svg b/resources/id/id_shield.svg deleted file mode 100644 index 43c874c..0000000 --- a/resources/id/id_shield.svg +++ /dev/null @@ -1,9909 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/id/id_storytellers.png b/resources/id/id_storytellers.png new file mode 100644 index 0000000..eca7c70 Binary files /dev/null and b/resources/id/id_storytellers.png differ diff --git a/resources/nametag.png b/resources/nametag.png index f8021d7..a9a1826 100644 Binary files a/resources/nametag.png and b/resources/nametag.png differ diff --git a/resources/tr23_nametag.png b/resources/tr23_nametag.png new file mode 100644 index 0000000..f8021d7 Binary files /dev/null and b/resources/tr23_nametag.png differ