1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28#include <stdio.h>
29#include <stdlib.h>
30#include <time.h>
31#include <stdint.h>
32#include <assert.h>
33#include "gsmamr_enc.h"
34
35enum {
36    kInputSize = 320, // 160 samples * 16-bit per sample.
37    kOutputSize = 1024
38};
39
40struct AmrNbEncState {
41    void *encCtx;
42    void *pidSyncCtx;
43};
44
45void usage(void) {
46    printf("Usage:\n");
47    printf("AMRNBEnc [options] <input file> <output file>\n");
48    printf("\n");
49    printf("Options +M* for setting compression bitrate mode, default is 4.75 kbps\n");
50    printf(" +M0 = 4.75 kbps\n");
51    printf(" +M1 = 5.15 kbps\n");
52    printf(" +M2 = 5.90 kbps\n");
53    printf(" +M3 = 6.70 kbps\n");
54    printf(" +M4 = 7.40 kbps\n");
55    printf(" +M5 = 7.95 kbps\n");
56    printf(" +M6 = 10.2 kbps\n");
57    printf(" +M7 = 12.2 kbps\n");
58    printf("\n");
59}
60
61int encode(int mode, const char *srcFile, const char *dstFile) {
62    int           retVal     = EXIT_SUCCESS;
63    FILE          *fSrc      = NULL;
64    FILE          *fDst      = NULL;
65    int           frameNum   = 0;
66    bool          eofReached = false;
67    uint16_t      *inputBuf  = NULL;
68    uint8_t       *outputBuf = NULL;
69    AmrNbEncState *amr       = NULL;
70
71    clock_t   start, finish;
72    double    duration = 0.0;
73
74    // Open input file.
75    fSrc = fopen(srcFile, "rb");
76    if (fSrc == NULL) {
77        fprintf(stderr, "Error opening input file\n");
78        retVal = EXIT_FAILURE;
79        goto safe_exit;
80    }
81
82    // Open output file.
83    fDst = fopen(dstFile, "wb");
84    if (fDst == NULL) {
85        fprintf(stderr, "Error opening output file\n");
86        retVal = EXIT_FAILURE;
87        goto safe_exit;
88    }
89
90    // Allocate input buffer.
91    inputBuf = (uint16_t*) malloc(kInputSize);
92    assert(inputBuf != NULL);
93
94    // Allocate output buffer.
95    outputBuf = (uint8_t*) malloc(kOutputSize);
96    assert(outputBuf != NULL);
97
98    // Initialize encoder.
99    amr = (AmrNbEncState*) malloc(sizeof(AmrNbEncState));
100    AMREncodeInit(&amr->encCtx, &amr->pidSyncCtx, 0);
101
102    // Write file header.
103    fwrite("#!AMR\n", 1, 6, fDst);
104
105    while (1) {
106        // Read next input frame.
107        int bytesRead;
108        bytesRead = fread(inputBuf, 1, kInputSize, fSrc);
109        if (bytesRead != kInputSize && !feof(fSrc)) {
110            retVal = EXIT_FAILURE; // Invalid magic number.
111            fprintf(stderr, "Error reading input file\n");
112            goto safe_exit;
113        } else if (feof(fSrc) && bytesRead == 0) {
114            eofReached = true;
115            break;
116        }
117
118        start = clock();
119
120        // Encode the frame.
121        Frame_Type_3GPP frame_type = (Frame_Type_3GPP) mode;
122        int bytesGenerated;
123        bytesGenerated = AMREncode(amr->encCtx, amr->pidSyncCtx, (Mode)mode,
124                                   (Word16*)inputBuf, outputBuf, &frame_type,
125                                   AMR_TX_WMF);
126
127        // Convert from WMF to RFC 3267 format.
128        if (bytesGenerated > 0) {
129            outputBuf[0] = ((outputBuf[0] << 3) | 4) & 0x7c;
130        }
131
132        finish = clock();
133        duration += finish - start;
134
135        if (bytesGenerated < 0) {
136            retVal = EXIT_FAILURE;
137            fprintf(stderr, "Encoding error\n");
138            goto safe_exit;
139        }
140
141        frameNum++;
142        printf(" Frames processed: %d\n", frameNum);
143
144        // Write the output.
145        fwrite(outputBuf, 1, bytesGenerated, fDst);
146    }
147
148    // Dump the time taken by encode.
149    printf("\n%2.5lf seconds\n", (double)duration/CLOCKS_PER_SEC);
150
151safe_exit:
152
153    // Free the encoder instance.
154    if (amr) {
155        AMREncodeExit(&amr->encCtx, &amr->pidSyncCtx);
156        free(amr);
157    }
158
159    // Free input and output buffer.
160    free(inputBuf);
161    free(outputBuf);
162
163    // Close the input and output files.
164    if (fSrc) {
165        fclose(fSrc);
166    }
167    if (fDst) {
168        fclose(fDst);
169    }
170
171    return retVal;
172}
173
174int main(int argc, char *argv[]) {
175    Mode  mode = MR475;
176    int   retVal;
177    char  *inFileName = NULL;
178    char  *outFileName = NULL;
179    int   arg, filename = 0;
180
181    if (argc < 3) {
182        usage();
183        return EXIT_FAILURE;
184    } else {
185        for (arg = 1; arg < argc; arg++) {
186            if (argv[arg][0] == '+') {
187                if (argv[arg][1] == 'M') {
188                    switch (argv[arg][2]) {
189                    case '0': mode = MR475;
190                        break;
191                    case '1': mode = MR515;
192                        break;
193                    case '2': mode = MR59;
194                        break;
195                    case '3': mode = MR67;
196                        break;
197                    case '4': mode = MR74;
198                        break;
199                    case '5': mode = MR795;
200                        break;
201                    case '6': mode = MR102;
202                        break;
203                    case '7': mode = MR122;
204                        break;
205                    default:
206                        usage();
207                        fprintf(stderr, "Invalid parameter '%s'.\n", argv[arg]);
208                        return EXIT_FAILURE;
209                        break;
210                    }
211                } else {
212                    usage();
213                    fprintf(stderr, "Invalid parameter '%s'.\n", argv[arg]);
214                    return EXIT_FAILURE;
215                }
216            } else {
217                switch (filename) {
218                case 0:
219                    inFileName  = argv[arg];
220                    break;
221                case 1:
222                    outFileName = argv[arg];
223                    break;
224                default:
225                    usage();
226                    fprintf(stderr, "Invalid parameter '%s'.\n", argv[arg]);
227                    return EXIT_FAILURE;
228                }
229                filename++;
230            }
231        }
232    }
233
234    retVal = encode(mode, inFileName, outFileName);
235    return retVal;
236}
237
238