summaryrefslogtreecommitdiff
path: root/src/media.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-10-04 17:20:41 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-10-04 17:20:41 +0300
commitefb40105d657da935d3854e6ea7a513c6210224b (patch)
tree0327d88d68c0a402f0f90307756bb8536c54dc8b /src/media.c
parent25346114f96a29e8af6125e0cac3d5f8a2ffd551 (diff)
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.
Diffstat (limited to 'src/media.c')
-rw-r--r--src/media.c135
1 files changed, 112 insertions, 23 deletions
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. */
23#include "media.h" 23#include "media.h"
24#include "gmdocument.h" 24#include "gmdocument.h"
25#include "ui/window.h" 25#include "ui/window.h"
26#include "audio/player.h"
26 27
27#include <the_Foundation/ptrarray.h> 28#include <the_Foundation/ptrarray.h>
28#include <stb_image.h> 29#include <stb_image.h>
29#include <SDL_hints.h> 30#include <SDL_hints.h>
30#include <SDL_render.h> 31#include <SDL_render.h>
31 32
33iDeclareType(GmMediaProps)
34
35struct Impl_GmMediaProps {
36 iGmLinkId linkId;
37 iString mime;
38 iBool isPermanent;
39};
40
41static void init_GmMediaProps_(iGmMediaProps *d) {
42 d->linkId = 0;
43 init_String(&d->mime);
44 d->isPermanent = iFalse;
45}
46
47static void deinit_GmMediaProps_(iGmMediaProps *d) {
48 deinit_String(&d->mime);
49}
50
51/*----------------------------------------------------------------------------------------------*/
52
32iDeclareType(GmImage) 53iDeclareType(GmImage)
33 54
34struct Impl_GmImage { 55struct Impl_GmImage {
56 iGmMediaProps props;
35 iInt2 size; 57 iInt2 size;
36 size_t numBytes; 58 size_t numBytes;
37 iString mime;
38 iGmLinkId linkId;
39 iBool isPermanent;
40 SDL_Texture *texture; 59 SDL_Texture *texture;
41}; 60};
42 61
43void init_GmImage(iGmImage *d, const iBlock *data) { 62void init_GmImage(iGmImage *d, const iBlock *data) {
44 init_String(&d->mime); 63 init_GmMediaProps_(&d->props);
45 d->isPermanent = iFalse;
46 d->numBytes = size_Block(data); 64 d->numBytes = size_Block(data);
47 uint8_t *imgData = stbi_load_from_memory( 65 uint8_t *imgData = stbi_load_from_memory(
48 constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4); 66 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) {
60 SDL_FreeSurface(surface); 78 SDL_FreeSurface(surface);
61 stbi_image_free(imgData); 79 stbi_image_free(imgData);
62 } 80 }
63 d->linkId = 0;
64} 81}
65 82
66void deinit_GmImage(iGmImage *d) { 83void deinit_GmImage(iGmImage *d) {
67 SDL_DestroyTexture(d->texture); 84 SDL_DestroyTexture(d->texture);
68 deinit_String(&d->mime); 85 deinit_GmMediaProps_(&d->props);
69} 86}
70 87
71iDefineTypeConstructionArgs(GmImage, (const iBlock *data), data) 88iDefineTypeConstructionArgs(GmImage, (const iBlock *data), data)
72 89
73/*----------------------------------------------------------------------------------------------*/ 90/*----------------------------------------------------------------------------------------------*/
74 91
92iDeclareType(GmAudio)
93
94struct Impl_GmAudio {
95 iGmMediaProps props;
96 iPlayer *player;
97};
98
99void init_GmAudio(iGmAudio *d) {
100 init_GmMediaProps_(&d->props);
101 d->player = new_Player();
102}
103
104void deinit_GmAudio(iGmAudio *d) {
105 delete_Player(d->player);
106 deinit_GmMediaProps_(&d->props);
107}
108
109iDefineTypeConstruction(GmAudio)
110
111/*----------------------------------------------------------------------------------------------*/
112
75struct Impl_Media { 113struct Impl_Media {
76 iPtrArray images; 114 iPtrArray images;
115 iPtrArray audio;
77}; 116};
78 117
79iDefineTypeConstruction(Media) 118iDefineTypeConstruction(Media)
80 119
81void init_Media(iMedia *d) { 120void init_Media(iMedia *d) {
82 init_PtrArray(&d->images); 121 init_PtrArray(&d->images);
122 init_PtrArray(&d->audio);
83} 123}
84 124
85void deinit_Media(iMedia *d) { 125void deinit_Media(iMedia *d) {
86 clear_Media(d); 126 clear_Media(d);
127 deinit_PtrArray(&d->audio);
87 deinit_PtrArray(&d->images); 128 deinit_PtrArray(&d->images);
88} 129}
89 130
@@ -92,25 +133,35 @@ void clear_Media(iMedia *d) {
92 deinit_GmImage(i.ptr); 133 deinit_GmImage(i.ptr);
93 } 134 }
94 clear_PtrArray(&d->images); 135 clear_PtrArray(&d->images);
136 iForEach(PtrArray, a, &d->audio) {
137 deinit_GmAudio(a.ptr);
138 }
139 clear_PtrArray(&d->audio);
95} 140}
96 141
97void setImage_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, 142void setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data,
98 iBool allowHide) { 143 iBool allowHide) {
99 if (!mime || !data) { 144 if (!mime || !data) {
100 iGmImage *img; 145 iMediaId existing = findLinkImage_Media(d, linkId);
101 const iMediaId existing = findLinkImage_Media(d, linkId);
102 if (existing) { 146 if (existing) {
147 iGmImage *img;
103 take_PtrArray(&d->images, existing - 1, (void **) &img); 148 take_PtrArray(&d->images, existing - 1, (void **) &img);
104 delete_GmImage(img); 149 delete_GmImage(img);
105 } 150 }
151 else if ((existing = findLinkAudio_Media(d, linkId)) != 0) {
152 iGmAudio *audio;
153 take_PtrArray(&d->audio, existing - 1, (void **) &audio);
154 delete_GmAudio(audio);
155 }
106 } 156 }
107 else { 157 else {
108 /* TODO: check if we know this MIME type */ 158 if (startsWith_String(mime, "image/")) {
109 /* Upload the image. */ { 159 /* Copy the image to a texture. */
160 /* TODO: Resize down to min(maximum texture size, window size). */
110 iGmImage *img = new_GmImage(data); 161 iGmImage *img = new_GmImage(data);
111 img->linkId = linkId; /* TODO: use a hash? */ 162 img->props.linkId = linkId; /* TODO: use a hash? */
112 img->isPermanent = !allowHide; 163 img->props.isPermanent = !allowHide;
113 set_String(&img->mime, mime); 164 set_String(&img->props.mime, mime);
114 if (img->texture) { 165 if (img->texture) {
115 pushBack_PtrArray(&d->images, img); 166 pushBack_PtrArray(&d->images, img);
116 } 167 }
@@ -118,15 +169,34 @@ void setImage_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
118 delete_GmImage(img); 169 delete_GmImage(img);
119 } 170 }
120 } 171 }
172 else if (startsWith_String(mime, "audio/")) {
173 iGmAudio *audio = new_GmAudio();
174 audio->props.linkId = linkId;
175 audio->props.isPermanent = !allowHide;
176 set_String(&audio->props.mime, mime);
177 /* TODO: What about update/complete, for streaming? */
178 updateSourceData_Player(audio->player, data, replace_PlayerUpdate);
179 pushBack_PtrArray(&d->audio, audio);
180 }
121 } 181 }
122// doLayout_GmDocument_(d);
123} 182}
124 183
125iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) { 184iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) {
126 /* TODO: use a hash */ 185 /* TODO: use a hash */
127 iConstForEach(PtrArray, i, &d->images) { 186 iConstForEach(PtrArray, i, &d->images) {
128 const iGmImage *img = i.ptr; 187 const iGmImage *img = i.ptr;
129 if (img->linkId == linkId) { 188 if (img->props.linkId == linkId) {
189 return index_PtrArrayConstIterator(&i) + 1;
190 }
191 }
192 return 0;
193}
194
195iMediaId findLinkAudio_Media(const iMedia *d, iGmLinkId linkId) {
196 /* TODO: use a hash */
197 iConstForEach(PtrArray, i, &d->audio) {
198 const iGmAudio *audio = i.ptr;
199 if (audio->props.linkId == linkId) {
130 return index_PtrArrayConstIterator(&i) + 1; 200 return index_PtrArrayConstIterator(&i) + 1;
131 } 201 }
132 } 202 }
@@ -141,15 +211,34 @@ SDL_Texture *imageTexture_Media(const iMedia *d, uint16_t imageId) {
141 return NULL; 211 return NULL;
142} 212}
143 213
144void imageInfo_Media(const iMedia *d, uint16_t imageId, iGmImageInfo *info_out) { 214iBool imageInfo_Media(const iMedia *d, iMediaId imageId, iGmImageInfo *info_out) {
145 if (imageId > 0 && imageId <= size_PtrArray(&d->images)) { 215 if (imageId > 0 && imageId <= size_PtrArray(&d->images)) {
146 const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1); 216 const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1);
147 info_out->size = img->size; 217 info_out->size = img->size;
148 info_out->numBytes = img->numBytes; 218 info_out->numBytes = img->numBytes;
149 info_out->mime = cstr_String(&img->mime); 219 info_out->mime = cstr_String(&img->props.mime);
150 info_out->isPermanent = img->isPermanent; 220 info_out->isPermanent = img->props.isPermanent;
221 return iTrue;
151 } 222 }
152 else { 223 iZap(*info_out);
153 iZap(*info_out); 224 return iFalse;
225}
226
227iPlayer *audioData_Media(const iMedia *d, iMediaId audioId) {
228 if (audioId > 0 && audioId <= size_PtrArray(&d->audio)) {
229 const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1);
230 return audio->player;
231 }
232 return NULL;
233}
234
235iBool audioInfo_Media(const iMedia *d, iMediaId audioId, iGmAudioInfo *info_out) {
236 if (audioId > 0 && audioId <= size_PtrArray(&d->audio)) {
237 const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1);
238 info_out->mime = cstr_String(&audio->props.mime);
239 info_out->isPermanent = audio->props.isPermanent;
240 return iTrue;
154 } 241 }
242 iZap(*info_out);
243 return iFalse;
155} 244}