~ubuntu-branches/ubuntu/gutsy/audacity/gutsy-backports

« back to all changes in this revision

Viewing changes to lib-src/libsndfile/src/vox_adpcm.c

  • Committer: Bazaar Package Importer
  • Author(s): John Dong
  • Date: 2008-02-18 21:58:19 UTC
  • mfrom: (13.1.2 hardy)
  • Revision ID: james.westby@ubuntu.com-20080218215819-tmbcf1rx238r8gdv
Tags: 1.3.4-1.1ubuntu1~gutsy1
Automated backport upload; no source changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
** Copyright (C) 2002-2005 Erik de Castro Lopo <erikd@mega-nerd.com>
3
 
**
4
 
** This program is free software; you can redistribute it and/or modify
5
 
** it under the terms of the GNU Lesser General Public License as published by
6
 
** the Free Software Foundation; either version 2.1 of the License, or
7
 
** (at your option) any later version.
8
 
**
9
 
** This program is distributed in the hope that it will be useful,
10
 
** but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
** GNU Lesser General Public License for more details.
13
 
**
14
 
** You should have received a copy of the GNU Lesser General Public License
15
 
** along with this program; if not, write to the Free Software
16
 
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
 
*/
18
 
 
19
 
/*
20
 
**      This is the OKI / Dialogic ADPCM encoder/decoder. It converts from
21
 
**      12 bit linear sample data to a 4 bit ADPCM.
22
 
**
23
 
**      Implemented from the description found here:
24
 
**
25
 
**              http://www.comptek.ru:8100/telephony/tnotes/tt1-13.html
26
 
**
27
 
**      and compared against the encoder/decoder found here:
28
 
**
29
 
**              http://ibiblio.org/pub/linux/apps/sound/convert/vox.tar.gz
30
 
*/
31
 
 
32
 
#include        "sfconfig.h"
33
 
 
34
 
#include        <stdio.h>
35
 
#include        <stdlib.h>
36
 
#include        <string.h>
37
 
 
38
 
#include        "sndfile.h"
39
 
#include        "sfendian.h"
40
 
#include        "float_cast.h"
41
 
#include        "common.h"
42
 
 
43
 
#define         VOX_DATA_LEN    2048
44
 
#define         PCM_DATA_LEN    (VOX_DATA_LEN *2)
45
 
 
46
 
typedef struct
47
 
{       short last ;
48
 
        short step_index ;
49
 
 
50
 
        int             vox_bytes, pcm_samples ;
51
 
 
52
 
        unsigned char   vox_data [VOX_DATA_LEN] ;
53
 
        short                   pcm_data [PCM_DATA_LEN] ;
54
 
} VOX_ADPCM_PRIVATE ;
55
 
 
56
 
static int vox_adpcm_encode_block (VOX_ADPCM_PRIVATE *pvox) ;
57
 
static int vox_adpcm_decode_block (VOX_ADPCM_PRIVATE *pvox) ;
58
 
 
59
 
static short vox_adpcm_decode (char code, VOX_ADPCM_PRIVATE *pvox) ;
60
 
static char vox_adpcm_encode (short samp, VOX_ADPCM_PRIVATE *pvox) ;
61
 
 
62
 
static sf_count_t vox_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
63
 
static sf_count_t vox_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
64
 
static sf_count_t vox_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
65
 
static sf_count_t vox_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
66
 
 
67
 
static sf_count_t vox_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
68
 
static sf_count_t vox_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
69
 
static sf_count_t vox_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
70
 
static sf_count_t vox_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
71
 
 
72
 
static int vox_read_block (SF_PRIVATE *psf, VOX_ADPCM_PRIVATE *pvox, short *ptr, int len) ;
73
 
 
74
 
/*============================================================================================
75
 
** Predefined OKI ADPCM encoder/decoder tables.
76
 
*/
77
 
 
78
 
static short step_size_table [49] =
79
 
{       16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60,
80
 
        66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209,
81
 
        230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
82
 
        724, 796, 876, 963, 1060, 1166, 1282, 1408, 1552
83
 
} ; /* step_size_table */
84
 
 
85
 
static short step_adjust_table [8] =
86
 
{       -1, -1, -1, -1, 2, 4, 6, 8
87
 
} ; /* step_adjust_table */
88
 
 
89
 
/*------------------------------------------------------------------------------
90
 
*/
91
 
 
92
 
int
93
 
vox_adpcm_init (SF_PRIVATE *psf)
94
 
{       VOX_ADPCM_PRIVATE *pvox = NULL ;
95
 
 
96
 
        if (psf->mode == SFM_RDWR)
97
 
                return SFE_BAD_MODE_RW ;
98
 
 
99
 
        if (psf->mode == SFM_WRITE && psf->sf.channels != 1)
100
 
                return SFE_CHANNEL_COUNT ;
101
 
 
102
 
        if ((pvox = malloc (sizeof (VOX_ADPCM_PRIVATE))) == NULL)
103
 
                return SFE_MALLOC_FAILED ;
104
 
 
105
 
        psf->fdata = (void*) pvox ;
106
 
        memset (pvox, 0, sizeof (VOX_ADPCM_PRIVATE)) ;
107
 
 
108
 
        if (psf->mode == SFM_WRITE)
109
 
        {       psf->write_short        = vox_write_s ;
110
 
                psf->write_int          = vox_write_i ;
111
 
                psf->write_float        = vox_write_f ;
112
 
                psf->write_double       = vox_write_d ;
113
 
                }
114
 
        else
115
 
        {       psf_log_printf (psf, "Header-less OKI Dialogic ADPCM encoded file.\n") ;
116
 
                psf_log_printf (psf, "Setting up for 8kHz, mono, Vox ADPCM.\n") ;
117
 
 
118
 
                psf->read_short         = vox_read_s ;
119
 
                psf->read_int           = vox_read_i ;
120
 
                psf->read_float         = vox_read_f ;
121
 
                psf->read_double        = vox_read_d ;
122
 
                } ;
123
 
 
124
 
        /* Standard sample rate chennels etc. */
125
 
        if (psf->sf.samplerate < 1)
126
 
                psf->sf.samplerate      = 8000 ;
127
 
        psf->sf.channels        = 1 ;
128
 
 
129
 
        psf->sf.frames = psf->filelength * 2 ;
130
 
 
131
 
        psf->sf.seekable = SF_FALSE ;
132
 
 
133
 
        /* Seek back to start of data. */
134
 
        if (psf_fseek (psf, 0 , SEEK_SET) == -1)
135
 
                return SFE_BAD_SEEK ;
136
 
 
137
 
        return 0 ;
138
 
} /* vox_adpcm_init */
139
 
 
140
 
/*------------------------------------------------------------------------------
141
 
*/
142
 
 
143
 
static char
144
 
vox_adpcm_encode (short samp, VOX_ADPCM_PRIVATE *pvox)
145
 
{       short code ;
146
 
        short diff, error, stepsize ;
147
 
 
148
 
        stepsize = step_size_table [pvox->step_index] ;
149
 
        code = 0 ;
150
 
 
151
 
        diff = samp - pvox->last ;
152
 
        if (diff < 0)
153
 
        {       code = 0x08 ;
154
 
                error = -diff ;
155
 
                }
156
 
        else
157
 
                error = diff ;
158
 
 
159
 
        if (error >= stepsize)
160
 
        {       code = code | 0x04 ;
161
 
                error -= stepsize ;
162
 
                } ;
163
 
 
164
 
        if (error >= stepsize / 2)
165
 
        {       code = code | 0x02 ;
166
 
                error -= stepsize / 2 ;
167
 
                } ;
168
 
 
169
 
        if (error >= stepsize / 4)
170
 
                code = code | 0x01 ;
171
 
 
172
 
        /*
173
 
        ** To close the feedback loop, the deocder is used to set the
174
 
        ** estimate of last sample and in doing so, also set the step_index.
175
 
        */
176
 
        pvox->last = vox_adpcm_decode (code, pvox) ;
177
 
 
178
 
        return code ;
179
 
} /* vox_adpcm_encode */
180
 
 
181
 
static short
182
 
vox_adpcm_decode (char code, VOX_ADPCM_PRIVATE *pvox)
183
 
{       short diff, error, stepsize, samp ;
184
 
 
185
 
        stepsize = step_size_table [pvox->step_index] ;
186
 
 
187
 
        error = stepsize / 8 ;
188
 
 
189
 
        if (code & 0x01)
190
 
                error += stepsize / 4 ;
191
 
 
192
 
        if (code & 0x02)
193
 
                error += stepsize / 2 ;
194
 
 
195
 
        if (code & 0x04)
196
 
                error += stepsize ;
197
 
 
198
 
        diff = (code & 0x08) ? -error : error ;
199
 
        samp = pvox->last + diff ;
200
 
 
201
 
        /*
202
 
        **  Apply clipping.
203
 
        */
204
 
        if (samp > 2048)
205
 
                samp = 2048 ;
206
 
        if (samp < -2048)
207
 
                samp = -2048 ;
208
 
 
209
 
        pvox->last = samp ;
210
 
        pvox->step_index += step_adjust_table [code & 0x7] ;
211
 
 
212
 
        if (pvox->step_index < 0)
213
 
                pvox->step_index = 0 ;
214
 
        if (pvox->step_index > 48)
215
 
                pvox->step_index = 48 ;
216
 
 
217
 
        return samp ;
218
 
} /* vox_adpcm_decode */
219
 
 
220
 
static int
221
 
vox_adpcm_encode_block (VOX_ADPCM_PRIVATE *pvox)
222
 
{       unsigned char code ;
223
 
        int j, k ;
224
 
 
225
 
        /* If data_count is odd, add an extra zero valued sample. */
226
 
        if (pvox->pcm_samples & 1)
227
 
                pvox->pcm_data [pvox->pcm_samples++] = 0 ;
228
 
 
229
 
        for (j = k = 0 ; k < pvox->pcm_samples ; j++)
230
 
        {       code = vox_adpcm_encode (pvox->pcm_data [k++] / 16, pvox) << 4 ;
231
 
                code |= vox_adpcm_encode (pvox->pcm_data [k++] / 16, pvox) ;
232
 
                pvox->vox_data [j] = code ;
233
 
                } ;
234
 
 
235
 
        pvox->vox_bytes = j ;
236
 
 
237
 
        return 0 ;
238
 
} /* vox_adpcm_encode_block */
239
 
 
240
 
static int
241
 
vox_adpcm_decode_block (VOX_ADPCM_PRIVATE *pvox)
242
 
{       unsigned char code ;
243
 
        int j, k ;
244
 
 
245
 
        for (j = k = 0 ; j < pvox->vox_bytes ; j++)
246
 
        {       code = pvox->vox_data [j] ;
247
 
                pvox->pcm_data [k++] = 16 * vox_adpcm_decode ((code >> 4) & 0x0f, pvox) ;
248
 
                pvox->pcm_data [k++] = 16 * vox_adpcm_decode (code & 0x0f, pvox) ;
249
 
                } ;
250
 
 
251
 
        pvox->pcm_samples = k ;
252
 
 
253
 
        return 0 ;
254
 
} /* vox_adpcm_decode_block */
255
 
 
256
 
/*==============================================================================
257
 
*/
258
 
 
259
 
static int
260
 
vox_read_block (SF_PRIVATE *psf, VOX_ADPCM_PRIVATE *pvox, short *ptr, int len)
261
 
{       int     indx = 0, k ;
262
 
 
263
 
        while (indx < len)
264
 
        {       pvox->vox_bytes = (len - indx > PCM_DATA_LEN) ? VOX_DATA_LEN : (len - indx + 1) / 2 ;
265
 
 
266
 
                if ((k = psf_fread (pvox->vox_data, 1, pvox->vox_bytes, psf)) != pvox->vox_bytes)
267
 
                {       if (psf_ftell (psf) + k != psf->filelength)
268
 
                                psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pvox->vox_bytes) ;
269
 
                        if (k == 0)
270
 
                                break ;
271
 
                        } ;
272
 
 
273
 
                pvox->vox_bytes = k ;
274
 
 
275
 
                vox_adpcm_decode_block (pvox) ;
276
 
 
277
 
                memcpy (&(ptr [indx]), pvox->pcm_data, pvox->pcm_samples * sizeof (short)) ;
278
 
                indx += pvox->pcm_samples ;
279
 
                } ;
280
 
 
281
 
        return indx ;
282
 
} /* vox_read_block */
283
 
 
284
 
 
285
 
static sf_count_t
286
 
vox_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
287
 
{       VOX_ADPCM_PRIVATE       *pvox ;
288
 
        int                     readcount, count ;
289
 
        sf_count_t      total = 0 ;
290
 
 
291
 
        if (! psf->fdata)
292
 
                return 0 ;
293
 
        pvox = (VOX_ADPCM_PRIVATE*) psf->fdata ;
294
 
 
295
 
        while (len > 0)
296
 
        {       readcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
297
 
 
298
 
                count = vox_read_block (psf, pvox, ptr, readcount) ;
299
 
 
300
 
                total += count ;
301
 
                len -= count ;
302
 
                if (count != readcount)
303
 
                        break ;
304
 
                } ;
305
 
 
306
 
        return total ;
307
 
} /* vox_read_s */
308
 
 
309
 
static sf_count_t
310
 
vox_read_i      (SF_PRIVATE *psf, int *ptr, sf_count_t len)
311
 
{       VOX_ADPCM_PRIVATE *pvox ;
312
 
        short           *sptr ;
313
 
        int                     k, bufferlen, readcount, count ;
314
 
        sf_count_t      total = 0 ;
315
 
 
316
 
        if (! psf->fdata)
317
 
                return 0 ;
318
 
        pvox = (VOX_ADPCM_PRIVATE*) psf->fdata ;
319
 
 
320
 
        sptr = psf->u.sbuf ;
321
 
        bufferlen = ARRAY_LEN (psf->u.sbuf) ;
322
 
        while (len > 0)
323
 
        {       readcount = (len >= bufferlen) ? bufferlen : (int) len ;
324
 
                count = vox_read_block (psf, pvox, sptr, readcount) ;
325
 
                for (k = 0 ; k < readcount ; k++)
326
 
                        ptr [total + k] = ((int) sptr [k]) << 16 ;
327
 
                total += count ;
328
 
                len -= readcount ;
329
 
                if (count != readcount)
330
 
                        break ;
331
 
                } ;
332
 
 
333
 
        return total ;
334
 
} /* vox_read_i */
335
 
 
336
 
static sf_count_t
337
 
vox_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
338
 
{       VOX_ADPCM_PRIVATE *pvox ;
339
 
        short           *sptr ;
340
 
        int                     k, bufferlen, readcount, count ;
341
 
        sf_count_t      total = 0 ;
342
 
        float           normfact ;
343
 
 
344
 
        if (! psf->fdata)
345
 
                return 0 ;
346
 
        pvox = (VOX_ADPCM_PRIVATE*) psf->fdata ;
347
 
 
348
 
        normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
349
 
 
350
 
        sptr = psf->u.sbuf ;
351
 
        bufferlen = ARRAY_LEN (psf->u.sbuf) ;
352
 
        while (len > 0)
353
 
        {       readcount = (len >= bufferlen) ? bufferlen : (int) len ;
354
 
                count = vox_read_block (psf, pvox, sptr, readcount) ;
355
 
                for (k = 0 ; k < readcount ; k++)
356
 
                        ptr [total + k] = normfact * (float) (sptr [k]) ;
357
 
                total += count ;
358
 
                len -= readcount ;
359
 
                if (count != readcount)
360
 
                        break ;
361
 
                } ;
362
 
 
363
 
        return total ;
364
 
} /* vox_read_f */
365
 
 
366
 
static sf_count_t
367
 
vox_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
368
 
{       VOX_ADPCM_PRIVATE *pvox ;
369
 
        short           *sptr ;
370
 
        int                     k, bufferlen, readcount, count ;
371
 
        sf_count_t      total = 0 ;
372
 
        double          normfact ;
373
 
 
374
 
        if (! psf->fdata)
375
 
                return 0 ;
376
 
        pvox = (VOX_ADPCM_PRIVATE*) psf->fdata ;
377
 
 
378
 
        normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
379
 
 
380
 
        sptr = psf->u.sbuf ;
381
 
        bufferlen = ARRAY_LEN (psf->u.sbuf) ;
382
 
        while (len > 0)
383
 
        {       readcount = (len >= bufferlen) ? bufferlen : (int) len ;
384
 
                count = vox_read_block (psf, pvox, sptr, readcount) ;
385
 
                for (k = 0 ; k < readcount ; k++)
386
 
                        ptr [total + k] = normfact * (double) (sptr [k]) ;
387
 
                total += count ;
388
 
                len -= readcount ;
389
 
                if (count != readcount)
390
 
                        break ;
391
 
                } ;
392
 
 
393
 
        return total ;
394
 
} /* vox_read_d */
395
 
 
396
 
/*------------------------------------------------------------------------------
397
 
*/
398
 
 
399
 
static int
400
 
vox_write_block (SF_PRIVATE *psf, VOX_ADPCM_PRIVATE *pvox, const short *ptr, int len)
401
 
{       int     indx = 0, k ;
402
 
 
403
 
        while (indx < len)
404
 
        {       pvox->pcm_samples = (len - indx > PCM_DATA_LEN) ? PCM_DATA_LEN : len - indx ;
405
 
 
406
 
                memcpy (pvox->pcm_data, &(ptr [indx]), pvox->pcm_samples * sizeof (short)) ;
407
 
 
408
 
                vox_adpcm_encode_block (pvox) ;
409
 
 
410
 
                if ((k = psf_fwrite (pvox->vox_data, 1, pvox->vox_bytes, psf)) != pvox->vox_bytes)
411
 
                        psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pvox->vox_bytes) ;
412
 
 
413
 
                indx += pvox->pcm_samples ;
414
 
                } ;
415
 
 
416
 
        return indx ;
417
 
} /* vox_write_block */
418
 
 
419
 
static sf_count_t
420
 
vox_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
421
 
{       VOX_ADPCM_PRIVATE       *pvox ;
422
 
        int                     writecount, count ;
423
 
        sf_count_t      total = 0 ;
424
 
 
425
 
        if (! psf->fdata)
426
 
                return 0 ;
427
 
        pvox = (VOX_ADPCM_PRIVATE*) psf->fdata ;
428
 
 
429
 
        while (len)
430
 
        {       writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
431
 
 
432
 
                count = vox_write_block (psf, pvox, ptr, writecount) ;
433
 
 
434
 
                total += count ;
435
 
                len -= count ;
436
 
                if (count != writecount)
437
 
                        break ;
438
 
                } ;
439
 
 
440
 
        return total ;
441
 
} /* vox_write_s */
442
 
 
443
 
static sf_count_t
444
 
vox_write_i     (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
445
 
{       VOX_ADPCM_PRIVATE *pvox ;
446
 
        short           *sptr ;
447
 
        int                     k, bufferlen, writecount, count ;
448
 
        sf_count_t      total = 0 ;
449
 
 
450
 
        if (! psf->fdata)
451
 
                return 0 ;
452
 
        pvox = (VOX_ADPCM_PRIVATE*) psf->fdata ;
453
 
 
454
 
        sptr = psf->u.sbuf ;
455
 
        bufferlen = ARRAY_LEN (psf->u.sbuf) ;
456
 
        while (len > 0)
457
 
        {       writecount = (len >= bufferlen) ? bufferlen : (int) len ;
458
 
                for (k = 0 ; k < writecount ; k++)
459
 
                        sptr [k] = ptr [total + k] >> 16 ;
460
 
                count = vox_write_block (psf, pvox, sptr, writecount) ;
461
 
                total += count ;
462
 
                len -= writecount ;
463
 
                if (count != writecount)
464
 
                        break ;
465
 
                } ;
466
 
 
467
 
        return total ;
468
 
} /* vox_write_i */
469
 
 
470
 
static sf_count_t
471
 
vox_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
472
 
{       VOX_ADPCM_PRIVATE *pvox ;
473
 
        short           *sptr ;
474
 
        int                     k, bufferlen, writecount, count ;
475
 
        sf_count_t      total = 0 ;
476
 
        float           normfact ;
477
 
 
478
 
        if (! psf->fdata)
479
 
                return 0 ;
480
 
        pvox = (VOX_ADPCM_PRIVATE*) psf->fdata ;
481
 
 
482
 
        normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
483
 
 
484
 
        sptr = psf->u.sbuf ;
485
 
        bufferlen = ARRAY_LEN (psf->u.sbuf) ;
486
 
        while (len > 0)
487
 
        {       writecount = (len >= bufferlen) ? bufferlen : (int) len ;
488
 
                for (k = 0 ; k < writecount ; k++)
489
 
                        sptr [k] = lrintf (normfact * ptr [total + k]) ;
490
 
                count = vox_write_block (psf, pvox, sptr, writecount) ;
491
 
                total += count ;
492
 
                len -= writecount ;
493
 
                if (count != writecount)
494
 
                        break ;
495
 
                } ;
496
 
 
497
 
        return total ;
498
 
} /* vox_write_f */
499
 
 
500
 
static sf_count_t
501
 
vox_write_d     (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
502
 
{       VOX_ADPCM_PRIVATE *pvox ;
503
 
        short           *sptr ;
504
 
        int                     k, bufferlen, writecount, count ;
505
 
        sf_count_t      total = 0 ;
506
 
        double          normfact ;
507
 
 
508
 
        if (! psf->fdata)
509
 
                return 0 ;
510
 
        pvox = (VOX_ADPCM_PRIVATE*) psf->fdata ;
511
 
 
512
 
        normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
513
 
 
514
 
        sptr = psf->u.sbuf ;
515
 
        bufferlen = ARRAY_LEN (psf->u.sbuf) ;
516
 
        while (len > 0)
517
 
        {       writecount = (len >= bufferlen) ? bufferlen : (int) len ;
518
 
                for (k = 0 ; k < writecount ; k++)
519
 
                        sptr [k] = lrint (normfact * ptr [total + k]) ;
520
 
                count = vox_write_block (psf, pvox, sptr, writecount) ;
521
 
                total += count ;
522
 
                len -= writecount ;
523
 
                if (count != writecount)
524
 
                        break ;
525
 
                } ;
526
 
 
527
 
        return total ;
528
 
} /* vox_write_d */
529
 
 
530
 
 
531
 
/*
532
 
** Do not edit or modify anything in this comment block.
533
 
** The arch-tag line is a file identity tag for the GNU Arch 
534
 
** revision control system.
535
 
**
536
 
** arch-tag: e15e97fe-ff9d-4b46-a489-7059fb2d0b1e
537
 
*/