From efb40105d657da935d3854e6ea7a513c6210224b Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 4 Oct 2020 17:20:41 +0300 Subject: Working on audio playback Audio players are displayed the same way as images. When playing, a decoder runs in a background thread producing samples suitable for output. --- src/media.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 112 insertions(+), 23 deletions(-) (limited to 'src/media.c') diff --git a/src/media.c b/src/media.c index 9f6acd19..5d88fbc4 100644 --- a/src/media.c +++ b/src/media.c @@ -23,26 +23,44 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "media.h" #include "gmdocument.h" #include "ui/window.h" +#include "audio/player.h" #include #include #include #include +iDeclareType(GmMediaProps) + +struct Impl_GmMediaProps { + iGmLinkId linkId; + iString mime; + iBool isPermanent; +}; + +static void init_GmMediaProps_(iGmMediaProps *d) { + d->linkId = 0; + init_String(&d->mime); + d->isPermanent = iFalse; +} + +static void deinit_GmMediaProps_(iGmMediaProps *d) { + deinit_String(&d->mime); +} + +/*----------------------------------------------------------------------------------------------*/ + iDeclareType(GmImage) struct Impl_GmImage { + iGmMediaProps props; iInt2 size; size_t numBytes; - iString mime; - iGmLinkId linkId; - iBool isPermanent; SDL_Texture *texture; }; void init_GmImage(iGmImage *d, const iBlock *data) { - init_String(&d->mime); - d->isPermanent = iFalse; + init_GmMediaProps_(&d->props); d->numBytes = size_Block(data); uint8_t *imgData = stbi_load_from_memory( constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4); @@ -60,30 +78,53 @@ void init_GmImage(iGmImage *d, const iBlock *data) { SDL_FreeSurface(surface); stbi_image_free(imgData); } - d->linkId = 0; } void deinit_GmImage(iGmImage *d) { SDL_DestroyTexture(d->texture); - deinit_String(&d->mime); + deinit_GmMediaProps_(&d->props); } iDefineTypeConstructionArgs(GmImage, (const iBlock *data), data) /*----------------------------------------------------------------------------------------------*/ +iDeclareType(GmAudio) + +struct Impl_GmAudio { + iGmMediaProps props; + iPlayer *player; +}; + +void init_GmAudio(iGmAudio *d) { + init_GmMediaProps_(&d->props); + d->player = new_Player(); +} + +void deinit_GmAudio(iGmAudio *d) { + delete_Player(d->player); + deinit_GmMediaProps_(&d->props); +} + +iDefineTypeConstruction(GmAudio) + +/*----------------------------------------------------------------------------------------------*/ + struct Impl_Media { iPtrArray images; + iPtrArray audio; }; iDefineTypeConstruction(Media) void init_Media(iMedia *d) { init_PtrArray(&d->images); + init_PtrArray(&d->audio); } void deinit_Media(iMedia *d) { clear_Media(d); + deinit_PtrArray(&d->audio); deinit_PtrArray(&d->images); } @@ -92,25 +133,35 @@ void clear_Media(iMedia *d) { deinit_GmImage(i.ptr); } clear_PtrArray(&d->images); + iForEach(PtrArray, a, &d->audio) { + deinit_GmAudio(a.ptr); + } + clear_PtrArray(&d->audio); } -void setImage_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, - iBool allowHide) { +void setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, + iBool allowHide) { if (!mime || !data) { - iGmImage *img; - const iMediaId existing = findLinkImage_Media(d, linkId); + iMediaId existing = findLinkImage_Media(d, linkId); if (existing) { + iGmImage *img; take_PtrArray(&d->images, existing - 1, (void **) &img); delete_GmImage(img); } + else if ((existing = findLinkAudio_Media(d, linkId)) != 0) { + iGmAudio *audio; + take_PtrArray(&d->audio, existing - 1, (void **) &audio); + delete_GmAudio(audio); + } } else { - /* TODO: check if we know this MIME type */ - /* Upload the image. */ { + if (startsWith_String(mime, "image/")) { + /* Copy the image to a texture. */ + /* TODO: Resize down to min(maximum texture size, window size). */ iGmImage *img = new_GmImage(data); - img->linkId = linkId; /* TODO: use a hash? */ - img->isPermanent = !allowHide; - set_String(&img->mime, mime); + img->props.linkId = linkId; /* TODO: use a hash? */ + img->props.isPermanent = !allowHide; + set_String(&img->props.mime, mime); if (img->texture) { pushBack_PtrArray(&d->images, img); } @@ -118,15 +169,34 @@ void setImage_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo delete_GmImage(img); } } + else if (startsWith_String(mime, "audio/")) { + iGmAudio *audio = new_GmAudio(); + audio->props.linkId = linkId; + audio->props.isPermanent = !allowHide; + set_String(&audio->props.mime, mime); + /* TODO: What about update/complete, for streaming? */ + updateSourceData_Player(audio->player, data, replace_PlayerUpdate); + pushBack_PtrArray(&d->audio, audio); + } } -// doLayout_GmDocument_(d); } iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) { /* TODO: use a hash */ iConstForEach(PtrArray, i, &d->images) { const iGmImage *img = i.ptr; - if (img->linkId == linkId) { + if (img->props.linkId == linkId) { + return index_PtrArrayConstIterator(&i) + 1; + } + } + return 0; +} + +iMediaId findLinkAudio_Media(const iMedia *d, iGmLinkId linkId) { + /* TODO: use a hash */ + iConstForEach(PtrArray, i, &d->audio) { + const iGmAudio *audio = i.ptr; + if (audio->props.linkId == linkId) { return index_PtrArrayConstIterator(&i) + 1; } } @@ -141,15 +211,34 @@ SDL_Texture *imageTexture_Media(const iMedia *d, uint16_t imageId) { return NULL; } -void imageInfo_Media(const iMedia *d, uint16_t imageId, iGmImageInfo *info_out) { +iBool imageInfo_Media(const iMedia *d, iMediaId imageId, iGmImageInfo *info_out) { if (imageId > 0 && imageId <= size_PtrArray(&d->images)) { const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1); info_out->size = img->size; info_out->numBytes = img->numBytes; - info_out->mime = cstr_String(&img->mime); - info_out->isPermanent = img->isPermanent; + info_out->mime = cstr_String(&img->props.mime); + info_out->isPermanent = img->props.isPermanent; + return iTrue; } - else { - iZap(*info_out); + iZap(*info_out); + return iFalse; +} + +iPlayer *audioData_Media(const iMedia *d, iMediaId audioId) { + if (audioId > 0 && audioId <= size_PtrArray(&d->audio)) { + const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1); + return audio->player; + } + return NULL; +} + +iBool audioInfo_Media(const iMedia *d, iMediaId audioId, iGmAudioInfo *info_out) { + if (audioId > 0 && audioId <= size_PtrArray(&d->audio)) { + const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1); + info_out->mime = cstr_String(&audio->props.mime); + info_out->isPermanent = audio->props.isPermanent; + return iTrue; } + iZap(*info_out); + return iFalse; } -- cgit v1.2.3