/*
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
 *
 * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include "Util/logger.h"
#include "AudioSRC.h"
#include "SDLAudioDevice.h"

using namespace std;
using namespace toolkit;

INSTANCE_IMP(SDLAudioDevice);

SDLAudioDevice::~SDLAudioDevice() {
    SDL_CloseAudio();
}

SDLAudioDevice::SDLAudioDevice() {
    SDL_AudioSpec wanted_spec;
    wanted_spec.freq = DEFAULT_SAMPLERATE;
    wanted_spec.format = DEFAULT_FORMAT;
    wanted_spec.channels = DEFAULT_CHANNEL;
    wanted_spec.silence = 0;
    wanted_spec.samples = DEFAULT_SAMPLES;
    wanted_spec.userdata = this;
    wanted_spec.callback = [](void *userdata, Uint8 *stream, int len) {
        SDLAudioDevice *_this = (SDLAudioDevice *) userdata;
        _this->onReqPCM((char *) stream, len);
    };
    if (SDL_OpenAudio(&wanted_spec, &_audio_config) < 0) {
        throw std::runtime_error("SDL_OpenAudio failed");
    }

    InfoL << "actual audioSpec, " << "freq:" << _audio_config.freq
          << ", format:" << hex << _audio_config.format << dec
          << ", channels:" << (int) _audio_config.channels
          << ", samples:" << _audio_config.samples
          << ", pcm size:" << _audio_config.size;

    _play_buf.reset(new char[_audio_config.size], [](char *ptr) {
        delete[] ptr;
    });
}

void SDLAudioDevice::addChannel(AudioSRC *chn) {
    lock_guard<recursive_mutex> lck(_channel_mtx);
    if (_channels.empty()) {
        SDL_PauseAudio(0);
    }
    chn->setOutputAudioConfig(_audio_config);
    _channels.emplace(chn);
}

void SDLAudioDevice::delChannel(AudioSRC *chn) {
    lock_guard<recursive_mutex> lck(_channel_mtx);
    _channels.erase(chn);
    if (_channels.empty()) {
        SDL_PauseAudio(true);
    }
}

void SDLAudioDevice::onReqPCM(char *stream, int len) {
    lock_guard<recursive_mutex> lck(_channel_mtx);
    int size;
    int channel = 0;
    for (auto &chn : _channels) {
        if (channel == 0) {
            size = chn->getPCMData(_play_buf.get(), len);
            if (size) {
                memcpy(stream, _play_buf.get(), size);
            }
        } else {
            size = chn->getPCMData(_play_buf.get(), len);
            if (size) {
                SDL_MixAudio((Uint8 *) stream, (Uint8 *) _play_buf.get(), size, SDL_MIX_MAXVOLUME);
            }
        }
        if (size) {
            channel++;
        }
    }

    if (!channel) {
        memset(stream, 0, len);
    }
}