The simple wave recorder and player project is a audio mini-project.

Dependencies:   SDHCFileSystem FatFileSystem mbed wavfile

main.cpp

Committer:
shintamainjp
Date:
2012-04-14
Revision:
0:1a515b688b8c

File content as of revision 0:1a515b688b8c:

/*
 * ===============================================================
 *  A simple wave recorder & player on mbed
 * ===============================================================
 * Copyright (c) 2011-2012 Shinichiro Nakamura
 *
 * 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 "mbed.h"
#include "SDHCFileSystem.h"
#include "wavfile.h"

#define RAM_TOTAL   0x1000

AnalogOut dacout(p18);
AnalogIn adcin(p20);
DigitalOut led_play_ok(LED1);
DigitalOut led_rec_ok(LED2);

Timer timer;
Timer iometer;
Ticker ticker;
SDFileSystem sdc(p5, p6, p7, p8, "sdc");

float buffer[RAM_TOTAL];
int rp = 0;
int wp = 0;
int dropout = 0;

#define WAVFILE_ERROR_PRINT(RESULT) \
    do { \
        WavFileResult R = RESULT; \
        if (R != WavFileResultOK) { \
            char wavfile_error_print_text[BUFSIZ]; \
            wavfile_result_string(R, wavfile_error_print_text, sizeof(wavfile_error_print_text)); \
            printf("%s (code=%d)\r\n", wavfile_error_print_text, R); \
            return 1; \
        } \
    } while(0)

void tickdummy(void)
{
}

void tickplay(void)
{
    /*
     * Check the play underflow
     */
    if (rp != wp) {
        dacout = buffer[rp];
        rp = (rp + 1) & (RAM_TOTAL - 1);
    } else {
        dropout++;
    }
    led_play_ok = !led_play_ok;
}

void tickrec(void)
{
    /*
     * Check the rec overflow
     */
    int np = (wp + 1) & (RAM_TOTAL - 1);
    if (np != rp) {
        buffer[wp] = adcin;
        wp = np;
    } else {
        dropout++;
    }
    led_rec_ok = !led_rec_ok;
}

int play(const char *filename)
{
    WavFileResult result;
    wavfile_info_t info;
    wavfile_data_t data;

    WAVFILE *wf = wavfile_open(filename, WavFileModeRead, &result);
    WAVFILE_ERROR_PRINT(result);
    WAVFILE_ERROR_PRINT(wavfile_read_info(wf, &info));

    printf("[PLAY:%s]\r\n", filename);
    printf("\tWAVFILE_INFO_AUDIO_FORMAT(&info)    = %d\r\n", WAVFILE_INFO_AUDIO_FORMAT(&info));
    printf("\tWAVFILE_INFO_NUM_CHANNELS(&info)    = %d\r\n", WAVFILE_INFO_NUM_CHANNELS(&info));
    printf("\tWAVFILE_INFO_SAMPLE_RATE(&info)     = %d\r\n", WAVFILE_INFO_SAMPLE_RATE(&info));
    printf("\tWAVFILE_INFO_BYTE_RATE(&info)       = %d\r\n", WAVFILE_INFO_BYTE_RATE(&info));
    printf("\tWAVFILE_INFO_BLOCK_ALIGN(&info)     = %d\r\n", WAVFILE_INFO_BLOCK_ALIGN(&info));
    printf("\tWAVFILE_INFO_BITS_PER_SAMPLE(&info) = %d\r\n", WAVFILE_INFO_BITS_PER_SAMPLE(&info));

    const int interval_us =  1000000 / WAVFILE_INFO_SAMPLE_RATE(&info);

    rp = 0;
    wp = 0;
    dropout = 0;
    ticker.attach_us(tickplay, interval_us);
    while (1) {
        int np = (wp + 1) & (RAM_TOTAL - 1);
        while (np == rp) {
            wait_us(1);
        }
        WAVFILE_ERROR_PRINT(wavfile_read_data(wf, &data));
        if (WAVFILE_DATA_IS_END_OF_DATA(&data)) {
            break;
        }
        buffer[wp] = WAVFILE_DATA_CHANNEL_DATA(&data, 0);
        wp = np;
    }
    ticker.detach();
    dacout = 0.5;
    led_play_ok = 0;
    printf("\t-- Play done. (dropout=%d) --\r\n", dropout);

    WAVFILE_ERROR_PRINT(wavfile_close(wf));
    return 0;
}

int rec(const char *filename, const int nsec)
{
    WavFileResult result;
    wavfile_info_t info;
    wavfile_data_t data;

    WAVFILE_INFO_AUDIO_FORMAT(&info)    = 1;
    WAVFILE_INFO_NUM_CHANNELS(&info)    = 1;
    WAVFILE_INFO_SAMPLE_RATE(&info)     = 11025;
    WAVFILE_INFO_BYTE_RATE(&info)       = 22050;
    WAVFILE_INFO_BLOCK_ALIGN(&info)     = 2;
    WAVFILE_INFO_BITS_PER_SAMPLE(&info) = 16;

    WAVFILE *wf = wavfile_open(filename, WavFileModeWrite, &result);
    WAVFILE_ERROR_PRINT(result);
    WAVFILE_ERROR_PRINT(wavfile_write_info(wf, &info));

    printf("[REC:%s]\r\n", filename);
    printf("\tWAVFILE_INFO_AUDIO_FORMAT(&info)    = %d\r\n", WAVFILE_INFO_AUDIO_FORMAT(&info));
    printf("\tWAVFILE_INFO_NUM_CHANNELS(&info)    = %d\r\n", WAVFILE_INFO_NUM_CHANNELS(&info));
    printf("\tWAVFILE_INFO_SAMPLE_RATE(&info)     = %d\r\n", WAVFILE_INFO_SAMPLE_RATE(&info));
    printf("\tWAVFILE_INFO_BYTE_RATE(&info)       = %d\r\n", WAVFILE_INFO_BYTE_RATE(&info));
    printf("\tWAVFILE_INFO_BLOCK_ALIGN(&info)     = %d\r\n", WAVFILE_INFO_BLOCK_ALIGN(&info));
    printf("\tWAVFILE_INFO_BITS_PER_SAMPLE(&info) = %d\r\n", WAVFILE_INFO_BITS_PER_SAMPLE(&info));

    const int interval_us =  1000000 / WAVFILE_INFO_SAMPLE_RATE(&info);
    const unsigned int samples_for_nsec = WAVFILE_INFO_SAMPLE_RATE(&info) * nsec;

    rp = 0;
    wp = 0;
    dropout = 0;
    unsigned int count = 0;
    ticker.attach_us(tickrec, interval_us);
    WAVFILE_DATA_NUM_CHANNELS(&data) = 1;
    while (1) {
        while (rp == wp) {
            wait_us(1);
        }

        WAVFILE_DATA_CHANNEL_DATA(&data, 0) = buffer[rp];
        rp = (rp + 1) & (RAM_TOTAL - 1);
        WAVFILE_ERROR_PRINT(wavfile_write_data(wf, &data));

        count++;
        if (count > samples_for_nsec) {
            break;
        }
    }
    ticker.detach();
    led_rec_ok = 0;
    printf("\t-- Rec done. (dropout=%d) --\r\n", dropout);

    WAVFILE_ERROR_PRINT(wavfile_close(wf));
    return 0;
}

int perf_read(const char *filename, const int word_size, const int nsec)
{
    FILE *fp = fopen(filename, "rb");
    if (fp == NULL) {
        printf("File open failed.\r\n");
        return 1;
    }

    printf("[Read performance checking...]\r\n");

    unsigned int total_byte_size = 0;
    int min_us = 0;
    int max_us = 0;
    timer.reset();
    timer.start();
    while (timer.read() < nsec) {
        for (int i = 0; i < word_size; i++) {
            iometer.reset();
            iometer.start();
            volatile int c = fgetc(fp);
            iometer.stop();
            int iomv = iometer.read_us();
            if (iomv < min_us) {
                min_us = iomv;
            }
            if (max_us < iomv) {
                max_us = iomv;
            }
            total_byte_size++;
        }
    }
    timer.stop();

    printf("\tfilename(%s), word_size(%d)\r\n", filename, word_size);
    printf("\ttime(%d[ms]), total_byte_size(%d)\r\n", timer.read_ms(), total_byte_size);
    printf("\tThroughput=%f[Bytes/Sec]\r\n", (double)total_byte_size / timer.read_ms() * 1000.0);
    printf("\tmin_us(%d), max_us(%d), average_us(%d)\r\n", min_us, max_us, timer.read_us() / total_byte_size);

    fclose(fp);
    return 0;
}

int perf_write(const char *filename, const int word_size, const int nsec)
{
    FILE *fp = fopen(filename, "wb");
    if (fp == NULL) {
        printf("File open failed.\r\n");
        return 1;
    }

    printf("[Write performance checking...]\r\n");

    unsigned int total_byte_size = 0;
    int min_us = 0;
    int max_us = 0;
    timer.reset();
    timer.start();
    while (timer.read() < nsec) {
        for (int i = 0; i < word_size; i++) {
            volatile int c = 0x55;
            iometer.reset();
            iometer.start();
            fputc(c, fp);
            iometer.stop();
            int iomv = iometer.read_us();
            if (iomv < min_us) {
                min_us = iomv;
            }
            if (max_us < iomv) {
                max_us = iomv;
            }
            total_byte_size++;
        }
    }
    timer.stop();

    printf("\tfilename(%s), word_size(%d)\r\n", filename, word_size);
    printf("\ttime(%d[ms]), total_byte_size(%d)\r\n", timer.read_ms(), total_byte_size);
    printf("\tThroughput=%f[Bytes/Sec]\r\n", (double)total_byte_size / timer.read_ms() * 1000.0);
    printf("\tmin_us(%d), max_us(%d), average_us(%d)\r\n", min_us, max_us, timer.read_us() / total_byte_size);

    fclose(fp);
    return 0;
}

int main(void)
{
    printf("\r\n\r\n");
    printf("=======================================================\r\n");
    printf(" A simple wave recorder & player on mbed               \r\n");
    printf(" Copyright(C) 2012 Shinichiro Nakamura (CuBeatSystems) \r\n");
    printf("=======================================================\r\n");

    ticker.attach_us(tickdummy, 100);
    wait(1);
    ticker.detach();

    {
        const char *perf_filename = "/sdc/perf.bin";
        const int word_size = 2;
        const int nsec_write = 20;
        const int nsec_read = 10;
        perf_write(perf_filename, word_size, nsec_write);
        perf_read(perf_filename, word_size, nsec_read);
    }

    {
        static const char *target_filename = "/sdc/rec-test.wav";
        while (1) {
            /*
             * 30 seconds recording.
             */
            if (rec(target_filename, 30) != 0) {
                break;
            }
            /*
             * Play it!
             */
            if (play(target_filename) != 0) {
                break;
            }
        }
    }

    return 0;
}